Spaces:
Paused
Paused
| package main | |
| import ( | |
| "bufio" | |
| "encoding/json" | |
| "fmt" | |
| "io" | |
| "net/http" | |
| "os" | |
| "strings" | |
| "time" | |
| "github.com/google/uuid" | |
| ) | |
| type OpenAIRequest struct { | |
| Messages []Message `json:"messages"` | |
| Stream bool `json:"stream"` | |
| Model string `json:"model"` | |
| } | |
| type Message struct { | |
| Role string `json:"role"` | |
| Content string `json:"content"` | |
| } | |
| type MerlinRequest struct { | |
| Attachments []interface{} `json:"attachments"` | |
| ChatId string `json:"chatId"` | |
| Language string `json:"language"` | |
| Message struct { | |
| Content string `json:"content"` | |
| Context string `json:"context"` | |
| ChildId string `json:"childId"` | |
| Id string `json:"id"` | |
| ParentId string `json:"parentId"` | |
| } `json:"message"` | |
| Metadata struct { | |
| LargeContext bool `json:"largeContext"` | |
| MerlinMagic bool `json:"merlinMagic"` | |
| ProFinderMode bool `json:"proFinderMode"` | |
| WebAccess bool `json:"webAccess"` | |
| } `json:"metadata"` | |
| Mode string `json:"mode"` | |
| Model string `json:"model"` | |
| } | |
| type MerlinResponse struct { | |
| Data struct { | |
| Content string `json:"content"` | |
| } `json:"data"` | |
| } | |
| type OpenAIResponse struct { | |
| Id string `json:"id"` | |
| Object string `json:"object"` | |
| Created int64 `json:"created"` | |
| Model string `json:"model"` | |
| Choices []struct { | |
| Delta struct { | |
| Content string `json:"content"` | |
| } `json:"delta"` | |
| Index int `json:"index"` | |
| FinishReason string `json:"finish_reason"` | |
| } `json:"choices"` | |
| } | |
| type TokenResponse struct { | |
| IdToken string `json:"idToken"` | |
| } | |
| func getEnvOrDefault(key, defaultValue string) string { | |
| if value := os.Getenv(key); value != "" { | |
| return value | |
| } | |
| return defaultValue | |
| } | |
| func getToken() (string, error) { | |
| tokenReq := struct { | |
| UUID string `json:"uuid"` | |
| }{ | |
| UUID: getEnvOrDefault("UUID", ""), | |
| } | |
| tokenReqBody, _ := json.Marshal(tokenReq) | |
| resp, err := http.Post( | |
| "https://getmerlin-main-server.vercel.app/generate", | |
| "application/json", | |
| strings.NewReader(string(tokenReqBody)), | |
| ) | |
| if err != nil { | |
| return "", err | |
| } | |
| defer resp.Body.Close() | |
| var tokenResp TokenResponse | |
| if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { | |
| return "", err | |
| } | |
| return tokenResp.IdToken, nil | |
| } | |
| func Handler(w http.ResponseWriter, r *http.Request) { | |
| authToken := r.Header.Get("Authorization") | |
| envToken := getEnvOrDefault("AUTH_TOKEN", "") | |
| if envToken != "" && authToken != "Bearer "+envToken { | |
| http.Error(w, "Unauthorized", http.StatusUnauthorized) | |
| return | |
| } | |
| if r.URL.Path != "/hf/v1/chat/completions" { | |
| w.Header().Set("Content-Type", "application/json") | |
| w.WriteHeader(http.StatusOK) | |
| fmt.Fprintf(w, `{"status":"GetMerlin2Api Service Running...","message":"MoLoveSze..."}`) | |
| return | |
| } | |
| if r.Method != http.MethodPost { | |
| http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| return | |
| } | |
| var openAIReq OpenAIRequest | |
| if err := json.NewDecoder(r.Body).Decode(&openAIReq); err != nil { | |
| http.Error(w, err.Error(), http.StatusBadRequest) | |
| return | |
| } | |
| var contextMessages []string | |
| for i := 0; i < len(openAIReq.Messages)-1; i++ { | |
| msg := openAIReq.Messages[i] | |
| contextMessages = append(contextMessages, fmt.Sprintf("%s: %s", msg.Role, msg.Content)) | |
| } | |
| context := strings.Join(contextMessages, "\n") | |
| merlinReq := MerlinRequest{ | |
| Attachments: make([]interface{}, 0), | |
| ChatId: generateV1UUID(), | |
| Language: "AUTO", | |
| Message: struct { | |
| Content string `json:"content"` | |
| Context string `json:"context"` | |
| ChildId string `json:"childId"` | |
| Id string `json:"id"` | |
| ParentId string `json:"parentId"` | |
| }{ | |
| Content: openAIReq.Messages[len(openAIReq.Messages)-1].Content, | |
| Context: context, | |
| ChildId: generateUUID(), | |
| Id: generateUUID(), | |
| ParentId: "root", | |
| }, | |
| Mode: "UNIFIED_CHAT", | |
| Model: openAIReq.Model, | |
| Metadata: struct { | |
| LargeContext bool `json:"largeContext"` | |
| MerlinMagic bool `json:"merlinMagic"` | |
| ProFinderMode bool `json:"proFinderMode"` | |
| WebAccess bool `json:"webAccess"` | |
| }{ | |
| LargeContext: false, | |
| MerlinMagic: false, | |
| ProFinderMode: false, | |
| WebAccess: false, | |
| }, | |
| } | |
| token, err := getToken() | |
| if err != nil { | |
| http.Error(w, "Failed to get token: "+err.Error(), http.StatusInternalServerError) | |
| return | |
| } | |
| client := &http.Client{} | |
| merlinReqBody, _ := json.Marshal(merlinReq) | |
| req, _ := http.NewRequest("POST", "https://arcane.getmerlin.in/v1/thread/unified", strings.NewReader(string(merlinReqBody))) | |
| req.Header.Set("Content-Type", "application/json") | |
| req.Header.Set("Accept", "text/event-stream, text/event-stream") | |
| req.Header.Set("Authorization", "Bearer "+token) | |
| req.Header.Set("x-merlin-version", "web-merlin") | |
| 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") | |
| req.Header.Set("sec-ch-ua", `"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"`) | |
| req.Header.Set("sec-ch-ua-mobile", "?0") | |
| req.Header.Set("sec-ch-ua-platform", "Windows") | |
| req.Header.Set("Sec-Fetch-Site", "same-site") | |
| req.Header.Set("Sec-Fetch-Mode", "cors") | |
| req.Header.Set("Sec-Fetch-Dest", "empty") | |
| req.Header.Set("host", "arcane.getmerlin.in") | |
| var flusher http.Flusher | |
| if openAIReq.Stream { | |
| var ok bool | |
| flusher, ok = w.(http.Flusher) | |
| if !ok { | |
| http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) | |
| return | |
| } | |
| w.Header().Set("Content-Type", "text/event-stream") | |
| w.Header().Set("Cache-Control", "no-cache") | |
| w.Header().Set("Connection", "keep-alive") | |
| w.Header().Set("X-Accel-Buffering", "no") | |
| w.Header().Set("Transfer-Encoding", "chunked") | |
| defer func() { | |
| if flusher != nil { | |
| flusher.Flush() | |
| } | |
| }() | |
| } else { | |
| w.Header().Set("Content-Type", "application/json") | |
| } | |
| resp, err := client.Do(req) | |
| if err != nil { | |
| http.Error(w, err.Error(), http.StatusInternalServerError) | |
| return | |
| } | |
| defer resp.Body.Close() | |
| if !openAIReq.Stream { | |
| var fullContent string | |
| reader := bufio.NewReader(resp.Body) | |
| for { | |
| line, err := reader.ReadString('\n') | |
| if err != nil { | |
| if err == io.EOF { | |
| break | |
| } | |
| continue | |
| } | |
| line = strings.TrimSpace(line) | |
| if strings.HasPrefix(line, "event: message") { | |
| dataLine, err := reader.ReadString('\n') | |
| if err != nil { | |
| continue | |
| } | |
| dataLine = strings.TrimSpace(dataLine) | |
| if strings.HasPrefix(dataLine, "data: ") { | |
| dataStr := strings.TrimPrefix(dataLine, "data: ") | |
| var merlinResp MerlinResponse | |
| if err := json.Unmarshal([]byte(dataStr), &merlinResp); err != nil { | |
| continue | |
| } | |
| if merlinResp.Data.Content != " " { | |
| fullContent += merlinResp.Data.Content | |
| } | |
| } | |
| } | |
| } | |
| response := map[string]interface{}{ | |
| "id": generateUUID(), | |
| "object": "chat.completion", | |
| "created": getCurrentTimestamp(), | |
| "model": openAIReq.Model, | |
| "choices": []map[string]interface{}{ | |
| { | |
| "message": map[string]interface{}{ | |
| "role": "assistant", | |
| "content": fullContent, | |
| }, | |
| "finish_reason": "stop", | |
| "index": 0, | |
| }, | |
| }, | |
| } | |
| json.NewEncoder(w).Encode(response) | |
| return | |
| } | |
| reader := bufio.NewReader(resp.Body) | |
| for { | |
| line, err := reader.ReadString('\n') | |
| if err != nil { | |
| if err == io.EOF { | |
| break | |
| } | |
| continue | |
| } | |
| if strings.HasPrefix(line, "event: message") { | |
| dataLine, _ := reader.ReadString('\n') | |
| var merlinResp MerlinResponse | |
| json.Unmarshal([]byte(strings.TrimPrefix(dataLine, "data: ")), &merlinResp) | |
| if merlinResp.Data.Content != "" { | |
| openAIResp := OpenAIResponse{ | |
| Id: generateUUID(), | |
| Object: "chat.completion.chunk", | |
| Created: getCurrentTimestamp(), | |
| Model: openAIReq.Model, | |
| Choices: []struct { | |
| Delta struct { | |
| Content string `json:"content"` | |
| } `json:"delta"` | |
| Index int `json:"index"` | |
| FinishReason string `json:"finish_reason"` | |
| }{{ | |
| Delta: struct { | |
| Content string `json:"content"` | |
| }{ | |
| Content: merlinResp.Data.Content, | |
| }, | |
| Index: 0, | |
| FinishReason: "", | |
| }}, | |
| } | |
| respData, _ := json.Marshal(openAIResp) | |
| fmt.Fprintf(w, "data: %s\n\n", string(respData)) | |
| flusher.Flush() | |
| } | |
| } | |
| } | |
| finalResp := OpenAIResponse{ | |
| Id: generateUUID(), | |
| Object: "chat.completion.chunk", | |
| Created: getCurrentTimestamp(), | |
| Model: openAIReq.Model, | |
| Choices: []struct { | |
| Delta struct { | |
| Content string `json:"content"` | |
| } `json:"delta"` | |
| Index int `json:"index"` | |
| FinishReason string `json:"finish_reason"` | |
| }{{ | |
| Delta: struct { | |
| Content string `json:"content"` | |
| }{Content: ""}, | |
| Index: 0, | |
| FinishReason: "stop", | |
| }}, | |
| } | |
| respData, _ := json.Marshal(finalResp) | |
| fmt.Fprintf(w, "data: %s\n\n", string(respData)) | |
| fmt.Fprintf(w, "data: [DONE]\n\n") | |
| flusher.Flush() | |
| } | |
| func generateUUID() string { | |
| return uuid.New().String() | |
| } | |
| func generateV1UUID() string { | |
| uuidObj := uuid.Must(uuid.NewUUID()) | |
| return uuidObj.String() | |
| } | |
| func getCurrentTimestamp() int64 { | |
| return time.Now().Unix() | |
| } | |
| func main() { | |
| port := getEnvOrDefault("PORT", "7860") | |
| http.HandleFunc("/", Handler) | |
| fmt.Printf("Server starting on port %s...\n", port) | |
| if err := http.ListenAndServe(":"+port, nil); err != nil { | |
| fmt.Printf("Error starting server: %v\n", err) | |
| } | |
| } |