wrhb123 commited on
Commit
17fea6b
·
verified ·
1 Parent(s): 648e2cc

Create api/chat.go

Browse files
Files changed (1) hide show
  1. api/chat.go +362 -0
api/chat.go ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "bufio"
5
+ "encoding/json"
6
+ "fmt"
7
+ "io"
8
+ "net/http"
9
+ "os"
10
+ "strings"
11
+ "time"
12
+
13
+ "github.com/google/uuid"
14
+ )
15
+
16
+ type OpenAIRequest struct {
17
+ Messages []Message `json:"messages"`
18
+ Stream bool `json:"stream"`
19
+ Model string `json:"model"`
20
+ }
21
+
22
+ type Message struct {
23
+ Role string `json:"role"`
24
+ Content string `json:"content"`
25
+ }
26
+
27
+ type MerlinRequest struct {
28
+ Attachments []interface{} `json:"attachments"`
29
+ ChatId string `json:"chatId"`
30
+ Language string `json:"language"`
31
+ Message struct {
32
+ Content string `json:"content"`
33
+ Context string `json:"context"`
34
+ ChildId string `json:"childId"`
35
+ Id string `json:"id"`
36
+ ParentId string `json:"parentId"`
37
+ } `json:"message"`
38
+ Metadata struct {
39
+ LargeContext bool `json:"largeContext"`
40
+ MerlinMagic bool `json:"merlinMagic"`
41
+ ProFinderMode bool `json:"proFinderMode"`
42
+ WebAccess bool `json:"webAccess"`
43
+ } `json:"metadata"`
44
+ Mode string `json:"mode"`
45
+ Model string `json:"model"`
46
+ }
47
+
48
+ type MerlinResponse struct {
49
+ Data struct {
50
+ Content string `json:"content"`
51
+ } `json:"data"`
52
+ }
53
+
54
+ type OpenAIResponse struct {
55
+ Id string `json:"id"`
56
+ Object string `json:"object"`
57
+ Created int64 `json:"created"`
58
+ Model string `json:"model"`
59
+ Choices []struct {
60
+ Delta struct {
61
+ Content string `json:"content"`
62
+ } `json:"delta"`
63
+ Index int `json:"index"`
64
+ FinishReason string `json:"finish_reason"`
65
+ } `json:"choices"`
66
+ }
67
+
68
+ type TokenResponse struct {
69
+ IdToken string `json:"idToken"`
70
+ }
71
+
72
+ func getEnvOrDefault(key, defaultValue string) string {
73
+ if value := os.Getenv(key); value != "" {
74
+ return value
75
+ }
76
+ return defaultValue
77
+ }
78
+
79
+ func getToken() (string, error) {
80
+ tokenReq := struct {
81
+ UUID string `json:"uuid"`
82
+ }{
83
+ UUID: getEnvOrDefault("UUID", ""),
84
+ }
85
+
86
+ tokenReqBody, _ := json.Marshal(tokenReq)
87
+ resp, err := http.Post(
88
+ "https://getmerlin-main-server.vercel.app/generate",
89
+ "application/json",
90
+ strings.NewReader(string(tokenReqBody)),
91
+ )
92
+ if err != nil {
93
+ return "", err
94
+ }
95
+ defer resp.Body.Close()
96
+
97
+ var tokenResp TokenResponse
98
+ if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
99
+ return "", err
100
+ }
101
+
102
+ return tokenResp.IdToken, nil
103
+ }
104
+
105
+ func Handler(w http.ResponseWriter, r *http.Request) {
106
+ authToken := r.Header.Get("Authorization")
107
+ envToken := getEnvOrDefault("AUTH_TOKEN", "")
108
+
109
+ if envToken != "" && authToken != "Bearer "+envToken {
110
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
111
+ return
112
+ }
113
+
114
+ if r.URL.Path != "/hf/v1/chat/completions" {
115
+ w.Header().Set("Content-Type", "application/json")
116
+ w.WriteHeader(http.StatusOK)
117
+ fmt.Fprintf(w, `{"status":"GetMerlin2Api Service Running...","message":"MoLoveSze..."}`)
118
+ return
119
+ }
120
+
121
+ if r.Method != http.MethodPost {
122
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
123
+ return
124
+ }
125
+
126
+ var openAIReq OpenAIRequest
127
+ if err := json.NewDecoder(r.Body).Decode(&openAIReq); err != nil {
128
+ http.Error(w, err.Error(), http.StatusBadRequest)
129
+ return
130
+ }
131
+ var contextMessages []string
132
+ for i := 0; i < len(openAIReq.Messages)-1; i++ {
133
+ msg := openAIReq.Messages[i]
134
+ contextMessages = append(contextMessages, fmt.Sprintf("%s: %s", msg.Role, msg.Content))
135
+ }
136
+ context := strings.Join(contextMessages, "\n")
137
+ merlinReq := MerlinRequest{
138
+ Attachments: make([]interface{}, 0),
139
+ ChatId: generateV1UUID(),
140
+ Language: "AUTO",
141
+ Message: struct {
142
+ Content string `json:"content"`
143
+ Context string `json:"context"`
144
+ ChildId string `json:"childId"`
145
+ Id string `json:"id"`
146
+ ParentId string `json:"parentId"`
147
+ }{
148
+ Content: openAIReq.Messages[len(openAIReq.Messages)-1].Content,
149
+ Context: context,
150
+ ChildId: generateUUID(),
151
+ Id: generateUUID(),
152
+ ParentId: "root",
153
+ },
154
+ Mode: "UNIFIED_CHAT",
155
+ Model: openAIReq.Model,
156
+ Metadata: struct {
157
+ LargeContext bool `json:"largeContext"`
158
+ MerlinMagic bool `json:"merlinMagic"`
159
+ ProFinderMode bool `json:"proFinderMode"`
160
+ WebAccess bool `json:"webAccess"`
161
+ }{
162
+ LargeContext: false,
163
+ MerlinMagic: false,
164
+ ProFinderMode: false,
165
+ WebAccess: false,
166
+ },
167
+ }
168
+ token, err := getToken()
169
+ if err != nil {
170
+ http.Error(w, "Failed to get token: "+err.Error(), http.StatusInternalServerError)
171
+ return
172
+ }
173
+ client := &http.Client{}
174
+ merlinReqBody, _ := json.Marshal(merlinReq)
175
+
176
+ req, _ := http.NewRequest("POST", "https://arcane.getmerlin.in/v1/thread/unified", strings.NewReader(string(merlinReqBody)))
177
+ req.Header.Set("Content-Type", "application/json")
178
+ req.Header.Set("Accept", "text/event-stream, text/event-stream")
179
+ req.Header.Set("Authorization", "Bearer "+token)
180
+ req.Header.Set("x-merlin-version", "web-merlin")
181
+ req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36")
182
+ req.Header.Set("sec-ch-ua", `"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"`)
183
+ req.Header.Set("sec-ch-ua-mobile", "?0")
184
+ req.Header.Set("sec-ch-ua-platform", "Windows")
185
+ req.Header.Set("Sec-Fetch-Site", "same-site")
186
+ req.Header.Set("Sec-Fetch-Mode", "cors")
187
+ req.Header.Set("Sec-Fetch-Dest", "empty")
188
+ req.Header.Set("host", "arcane.getmerlin.in")
189
+ var flusher http.Flusher
190
+ if openAIReq.Stream {
191
+ var ok bool
192
+ flusher, ok = w.(http.Flusher)
193
+ if !ok {
194
+ http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
195
+ return
196
+ }
197
+ w.Header().Set("Content-Type", "text/event-stream")
198
+ w.Header().Set("Cache-Control", "no-cache")
199
+ w.Header().Set("Connection", "keep-alive")
200
+ w.Header().Set("X-Accel-Buffering", "no")
201
+ w.Header().Set("Transfer-Encoding", "chunked")
202
+ defer func() {
203
+ if flusher != nil {
204
+ flusher.Flush()
205
+ }
206
+ }()
207
+ } else {
208
+ w.Header().Set("Content-Type", "application/json")
209
+ }
210
+
211
+ resp, err := client.Do(req)
212
+ if err != nil {
213
+ http.Error(w, err.Error(), http.StatusInternalServerError)
214
+ return
215
+ }
216
+ defer resp.Body.Close()
217
+
218
+ if !openAIReq.Stream {
219
+ var fullContent string
220
+ reader := bufio.NewReader(resp.Body)
221
+ for {
222
+ line, err := reader.ReadString('\n')
223
+ if err != nil {
224
+ if err == io.EOF {
225
+ break
226
+ }
227
+ continue
228
+ }
229
+
230
+ line = strings.TrimSpace(line)
231
+
232
+ if strings.HasPrefix(line, "event: message") {
233
+ dataLine, err := reader.ReadString('\n')
234
+ if err != nil {
235
+ continue
236
+ }
237
+ dataLine = strings.TrimSpace(dataLine)
238
+
239
+ if strings.HasPrefix(dataLine, "data: ") {
240
+ dataStr := strings.TrimPrefix(dataLine, "data: ")
241
+ var merlinResp MerlinResponse
242
+ if err := json.Unmarshal([]byte(dataStr), &merlinResp); err != nil {
243
+ continue
244
+ }
245
+ if merlinResp.Data.Content != " " {
246
+ fullContent += merlinResp.Data.Content
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ response := map[string]interface{}{
253
+ "id": generateUUID(),
254
+ "object": "chat.completion",
255
+ "created": getCurrentTimestamp(),
256
+ "model": openAIReq.Model,
257
+ "choices": []map[string]interface{}{
258
+ {
259
+ "message": map[string]interface{}{
260
+ "role": "assistant",
261
+ "content": fullContent,
262
+ },
263
+ "finish_reason": "stop",
264
+ "index": 0,
265
+ },
266
+ },
267
+ }
268
+ json.NewEncoder(w).Encode(response)
269
+ return
270
+ }
271
+
272
+ reader := bufio.NewReader(resp.Body)
273
+ for {
274
+ line, err := reader.ReadString('\n')
275
+ if err != nil {
276
+ if err == io.EOF {
277
+ break
278
+ }
279
+ continue
280
+ }
281
+
282
+ if strings.HasPrefix(line, "event: message") {
283
+ dataLine, _ := reader.ReadString('\n')
284
+ var merlinResp MerlinResponse
285
+ json.Unmarshal([]byte(strings.TrimPrefix(dataLine, "data: ")), &merlinResp)
286
+
287
+ if merlinResp.Data.Content != "" {
288
+ openAIResp := OpenAIResponse{
289
+ Id: generateUUID(),
290
+ Object: "chat.completion.chunk",
291
+ Created: getCurrentTimestamp(),
292
+ Model: openAIReq.Model,
293
+ Choices: []struct {
294
+ Delta struct {
295
+ Content string `json:"content"`
296
+ } `json:"delta"`
297
+ Index int `json:"index"`
298
+ FinishReason string `json:"finish_reason"`
299
+ }{{
300
+ Delta: struct {
301
+ Content string `json:"content"`
302
+ }{
303
+ Content: merlinResp.Data.Content,
304
+ },
305
+ Index: 0,
306
+ FinishReason: "",
307
+ }},
308
+ }
309
+
310
+ respData, _ := json.Marshal(openAIResp)
311
+ fmt.Fprintf(w, "data: %s\n\n", string(respData))
312
+ flusher.Flush()
313
+ }
314
+ }
315
+ }
316
+
317
+ finalResp := OpenAIResponse{
318
+ Id: generateUUID(),
319
+ Object: "chat.completion.chunk",
320
+ Created: getCurrentTimestamp(),
321
+ Model: openAIReq.Model,
322
+ Choices: []struct {
323
+ Delta struct {
324
+ Content string `json:"content"`
325
+ } `json:"delta"`
326
+ Index int `json:"index"`
327
+ FinishReason string `json:"finish_reason"`
328
+ }{{
329
+ Delta: struct {
330
+ Content string `json:"content"`
331
+ }{Content: ""},
332
+ Index: 0,
333
+ FinishReason: "stop",
334
+ }},
335
+ }
336
+ respData, _ := json.Marshal(finalResp)
337
+ fmt.Fprintf(w, "data: %s\n\n", string(respData))
338
+ fmt.Fprintf(w, "data: [DONE]\n\n")
339
+ flusher.Flush()
340
+ }
341
+
342
+ func generateUUID() string {
343
+ return uuid.New().String()
344
+ }
345
+
346
+ func generateV1UUID() string {
347
+ uuidObj := uuid.Must(uuid.NewUUID())
348
+ return uuidObj.String()
349
+ }
350
+
351
+ func getCurrentTimestamp() int64 {
352
+ return time.Now().Unix()
353
+ }
354
+
355
+ func main() {
356
+ port := getEnvOrDefault("PORT", "7860")
357
+ http.HandleFunc("/", Handler)
358
+ fmt.Printf("Server starting on port %s...\n", port)
359
+ if err := http.ListenAndServe(":"+port, nil); err != nil {
360
+ fmt.Printf("Error starting server: %v\n", err)
361
+ }
362
+ }