Skip to content

Commit 9f3e82b

Browse files
authored
Merge pull request #3 from SebbeJohansson/replacements-2
Replacements for EMO features
2 parents 7f94a29 + 5a6bef2 commit 9f3e82b

5 files changed

Lines changed: 200 additions & 6 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@ This part of the knowledge bank is a Proxy server to analyze the traffic that go
1010
## Docker setup
1111
You can find a simplified setup using Docker Compose by SebbeJohansson here: https://github.com/SebbeJohansson/emo-proxy-docker
1212
It uses nginx, dnsmasq, and mitmproxy to be able to pass through the api requests to the EMO Proxy.
13+
14+
## Experimental
15+
### ChatGPT Speak server
16+
One of the most common responses from EMO is to respond using ChatGPT.
17+
An experimental feature is to use a local Speak server to generate EMO's chatgpt speak responses instead of using the living.ai servers.
18+
You can find a proof of concept server here: https://github.com/SebbeJohansson/EmoChatGptSpeakPOC
19+
20+
Connect the server by adding the following line to your emoProxy.conf file:
21+
`"chatGptSpeakServer": "http://localhost:8085"`

emoProxy.conf.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
"postFS": "/tmp/",
88
"logFileName": "/var/log/emoProxyss.log",
99
"enableDatabaseAndAPI": false, # For now, default behavior is still without db and api.
10-
"sqliteLocation": "/var/data/emo_logs.db"
10+
"enableReplacements": false, # For now, default behavior is still without replacements.
11+
12+
"sqliteLocation": "/var/data/emo_logs.db",
13+
"chatGptSpeakServer": ""
1114
}

emoProxy.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ type Configuration struct {
3333
PostFS string `json:"postFS"`
3434
LogFileName string `json:"logFileName"`
3535
EnableDatabaseAndAPI bool `json:"enableDatabaseAndAPI"`
36+
EnableReplacements bool `json:"enableReplacements"`
3637
SqliteLocation string `json:"sqliteLocation"`
38+
ChatGptSpeakServer string `json:"chatGptSpeakServer"`
3739
}
3840

3941
var (
@@ -104,7 +106,9 @@ func loadConfig(filename string) error {
104106
PostFS: "/tmp/",
105107
LogFileName: "/var/log/emoProxy.log",
106108
EnableDatabaseAndAPI: false,
109+
EnableReplacements: false,
107110
SqliteLocation: "/var/data/emo_logs.db",
111+
ChatGptSpeakServer: "",
108112
}
109113

110114
bytes, err := os.ReadFile(filename)
@@ -317,6 +321,11 @@ func makeApiRequest(r *http.Request) string {
317321

318322
logResponse(response)
319323

324+
if conf.EnableReplacements {
325+
log.Println("Replacements enabled, checking for replacements...")
326+
body = runReplacementsAndReturnModifiedBody(body, r)
327+
}
328+
320329
if useDatabaseAndAPI {
321330
saveRequest(r.URL.RequestURI(), string(requestBody), string(body))
322331
}
@@ -346,10 +355,8 @@ func makeTtsRequest(r *http.Request) string {
346355
}
347356
defer response.Body.Close()
348357

349-
// read response
350358
body, _ := io.ReadAll(response.Body)
351359

352-
// write post request body to fs
353360
logBody(response.Header.Get("Content-Type"), body, "tts_")
354361
logResponse(response)
355362

@@ -382,10 +389,8 @@ func makeApiTtsRequest(r *http.Request) string {
382389
}
383390
defer response.Body.Close()
384391

385-
// read response
386392
body, _ := io.ReadAll(response.Body)
387393

388-
// write post request body to fs
389394
logBody(response.Header.Get("Content-Type"), body, "apitts_")
390395
logResponse(response)
391396

@@ -418,7 +423,6 @@ func makeResRequest(r *http.Request, w http.ResponseWriter) string {
418423
}
419424
defer response.Body.Close()
420425

421-
// read response
422426
body, _ := io.ReadAll(response.Body)
423427

424428
logBody(response.Header.Get("Content-Type"), body, "res_")

replacements.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"io"
7+
"log"
8+
"net/http"
9+
"net/url"
10+
)
11+
12+
func runReplacementsAndReturnModifiedBody(body []byte, r *http.Request) []byte {
13+
typedBody := QueryResponse{}
14+
decoder := json.NewDecoder(bytes.NewReader(body))
15+
decoder.DisallowUnknownFields()
16+
17+
err := decoder.Decode(&typedBody)
18+
if err != nil {
19+
log.Println("Error when decoding JSON (", string(body), ") will return unhandled:", err)
20+
return body
21+
} else {
22+
if typedBody.QueryResult == nil || typedBody.QueryId == "" {
23+
log.Println("Unexpected query response, will return unhandled.")
24+
return body
25+
}
26+
27+
if typedBody.QueryResult.Intent.Name == "chatgpt_speak" && conf.ChatGptSpeakServer != "" {
28+
speakResponse := makeChatGptSpeakRequest(typedBody.QueryResult.QueryText, typedBody.LanguageCode, typedBody.QueryResult.BehaviorParas.Txt, r)
29+
if speakResponse.Url != "" && speakResponse.Txt != "" {
30+
log.Println("Successfully replaced chatgpt_speak response for request.")
31+
typedBody.QueryResult.BehaviorParas.Url = speakResponse.Url
32+
typedBody.QueryResult.BehaviorParas.Txt = speakResponse.Txt
33+
} else {
34+
log.Println("Failed to get valid response from ChatGptSpeakServer, keeping original response.")
35+
}
36+
}
37+
modifiedBody, err := json.Marshal(typedBody)
38+
if err != nil {
39+
log.Println("Error when marshaling modified JSON, will return unhandled:", err)
40+
return body
41+
}
42+
return modifiedBody
43+
}
44+
}
45+
46+
func makeEmoSpeechRequest(text string, languageCode string, r *http.Request) EmoSpeechResponse {
47+
request, _ := http.NewRequest("GET", "https://"+conf.Livingio_API_Server+"/emo/speech/tts?q="+url.QueryEscape(text)+"&l="+url.QueryEscape(languageCode), nil)
48+
49+
val, exists := r.Header["Authorization"]
50+
if exists {
51+
request.Header.Add("Authorization", val[0])
52+
}
53+
54+
val, exists = r.Header["Secret"]
55+
if exists {
56+
request.Header.Add("Secret", val[0])
57+
}
58+
59+
request.Header.Del("User-Agent")
60+
61+
httpclient := &http.Client{}
62+
response, err := httpclient.Do(request)
63+
64+
if err != nil {
65+
log.Fatalf("An Error Occured %v", err)
66+
}
67+
defer response.Body.Close()
68+
69+
body, _ := io.ReadAll(response.Body)
70+
71+
var emoSpeechResponse EmoSpeechResponse
72+
if err := json.Unmarshal([]byte(body), &emoSpeechResponse); err != nil {
73+
log.Printf("Error unmarshaling ChatGptSpeakServer response: %v\n", err)
74+
return EmoSpeechResponse{}
75+
}
76+
77+
return emoSpeechResponse
78+
}
79+
80+
func makeChatGptSpeakRequest(queryText string, languageCode string, fallbackResponse string, r *http.Request) BehaviorParas {
81+
type ChatGptSpeakRequest struct {
82+
QueryText string `json:"queryText"`
83+
LanguageCode string `json:"languageCode"`
84+
FallbackResponse string `json:"fallbackResponse,omitempty"`
85+
}
86+
type ChatGptSpeakResponse struct {
87+
ResponseText string `json:"responseText"`
88+
}
89+
90+
chatGptRequestData := ChatGptSpeakRequest{
91+
QueryText: queryText,
92+
LanguageCode: languageCode,
93+
FallbackResponse: fallbackResponse,
94+
}
95+
chatGptRequestBody, _ := json.Marshal(chatGptRequestData)
96+
chatGptRequest, _ := http.NewRequest("POST", conf.ChatGptSpeakServer+"/speak", bytes.NewBuffer(chatGptRequestBody))
97+
chatGptRequest.Header.Add("Content-Type", "application/json")
98+
99+
chatGptClient := &http.Client{}
100+
chatGptResponse, err := chatGptClient.Do(chatGptRequest)
101+
if err != nil {
102+
log.Fatalf("An Error Occured while calling ChatGptSpeakServer %v", err)
103+
}
104+
defer chatGptResponse.Body.Close()
105+
106+
chatGptResponseBody, _ := io.ReadAll(chatGptResponse.Body)
107+
108+
var chatGptTypedResponse ChatGptSpeakResponse
109+
if err := json.Unmarshal([]byte(chatGptResponseBody), &chatGptTypedResponse); err != nil {
110+
log.Printf("Error unmarshaling ChatGptSpeakServer response: %v\n", err)
111+
return BehaviorParas{}
112+
}
113+
114+
if chatGptTypedResponse.ResponseText == "" {
115+
log.Println("ChatGptSpeakServer returned empty response text")
116+
return BehaviorParas{}
117+
}
118+
119+
emoSpeechResponse := makeEmoSpeechRequest(chatGptTypedResponse.ResponseText, languageCode, r)
120+
if emoSpeechResponse.Code != 200 || emoSpeechResponse.Url == "" {
121+
log.Printf("Error in EmoSpeechResponse: Code %d, Errmessage: %s\n", emoSpeechResponse.Code, emoSpeechResponse.Errmessage)
122+
return BehaviorParas{}
123+
}
124+
behaviorParasResponse := BehaviorParas{
125+
Txt: chatGptTypedResponse.ResponseText,
126+
Url: emoSpeechResponse.Url,
127+
}
128+
129+
return behaviorParasResponse
130+
}

types.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
type Intent struct {
4+
Name string `json:"name,omitempty"`
5+
Confidence float64 `json:"confidence,omitempty"`
6+
}
7+
8+
type BehaviorParas struct {
9+
UtilityType string `json:"utility_type,omitempty"`
10+
Time []string `json:"time,omitempty"`
11+
Txt string `json:"txt,omitempty"`
12+
Url string `json:"url,omitempty"`
13+
PreAnimation string `json:"pre_animation,omitempty"`
14+
PostAnimation string `json:"post_animation,omitempty"`
15+
PostBehavior string `json:"post_behavior,omitempty"`
16+
RecBehavior string `json:"rec_behavior,omitempty"`
17+
BehaviorParas *BehaviorParas `json:"behavior_paras,omitempty"`
18+
Sentiment string `json:"sentiment,omitempty"`
19+
Listen int `json:"listen,omitempty"`
20+
AnimationName string `json:"animation_name,omitempty"`
21+
}
22+
23+
type QueryResult struct {
24+
ResultCode string `json:"resultCode,omitempty"`
25+
QueryText string `json:"queryText,omitempty"`
26+
Intent *Intent `json:"intent,omitempty"`
27+
RecBehavior string `json:"rec_behavior,omitempty"`
28+
BehaviorParas *BehaviorParas `json:"behavior_paras,omitempty"`
29+
}
30+
31+
type QueryResponse struct {
32+
QueryId string `json:"queryId,omitempty"`
33+
QueryResult *QueryResult `json:"queryResult,omitempty"`
34+
LanguageCode string `json:"languageCode,omitempty"`
35+
Index int `json:"index,omitempty"`
36+
}
37+
38+
type TokenResponse struct {
39+
AccessToken string `json:"access_token,omitempty"`
40+
ExpireIn int `json:"expire_in,omitempty"`
41+
Type string `json:"type,omitempty"`
42+
}
43+
44+
type EmoSpeechResponse struct {
45+
Code int64 `json:"code"`
46+
Errmessage string `json:"errmessage"`
47+
Url string `json:"url"`
48+
}

0 commit comments

Comments
 (0)