Spaces:
Sleeping
Sleeping
| package main | |
| import ( | |
| "bufio" | |
| "bytes" | |
| "encoding/json" | |
| "fmt" | |
| "io" | |
| "net/http" | |
| "os" | |
| "strings" | |
| "time" | |
| ) | |
| // UserRequest, Message, SiderResponse, OpenAIResponse, OpenAIStreamResponse structs remain the same... | |
| type UserRequest struct { | |
| Messages []Message `json:"messages"` | |
| Model string `json:"model"` | |
| Stream bool `json:"stream"` | |
| MaxTokens int `json:"max_tokens"` | |
| } | |
| type Message struct { | |
| Role string `json:"role"` | |
| Content string `json:"content"` | |
| } | |
| var defaultJsonTemplate = []byte(`{ | |
| "app_name": "ChitChat_Edge_Ext", | |
| "app_version": "4.40.0", | |
| "tz_name": "Asia/Shanghai", | |
| "cid": "", | |
| "search": false, | |
| "auto_search": false, | |
| "filter_search_history": false, | |
| "from": "chat", | |
| "group_id": "default", | |
| "chat_models": [], | |
| "files": [], | |
| "prompt_templates": [ | |
| {"key": "artifacts", "attributes": {"lang": "original"}}, | |
| {"key": "thinking_mode", "attributes": {}} | |
| ], | |
| "tools": { | |
| "auto": ["search", "text_to_image", "data_analysis"] | |
| }, | |
| "extra_info": { | |
| "origin_url": "chrome-extension://dhoenijjpgpeimemopealfcbiecgceod/standalone.html?from=sidebar", | |
| "origin_title": "Sider" | |
| }, | |
| "branch": true | |
| }`) | |
| // Sider响应结构 | |
| type SiderResponse struct { | |
| Code int `json:"code"` | |
| Msg string `json:"msg"` | |
| Data struct { | |
| Type string `json:"type"` | |
| Text string `json:"text"` | |
| ChatModel string `json:"chat_model"` | |
| } `json:"data"` | |
| } | |
| // OpenAI响应结构 | |
| type OpenAIResponse struct { | |
| ID string `json:"id"` | |
| Object string `json:"object"` | |
| Created int64 `json:"created"` | |
| Model string `json:"model"` | |
| Choices []struct { | |
| Message struct { | |
| Role string `json:"role"` | |
| Content string `json:"content"` | |
| } `json:"message"` | |
| FinishReason string `json:"finish_reason"` | |
| Index int `json:"index"` | |
| } `json:"choices"` | |
| Usage struct { | |
| PromptTokens int `json:"prompt_tokens"` | |
| CompletionTokens int `json:"completion_tokens"` | |
| TotalTokens int `json:"total_tokens"` | |
| } `json:"usage"` | |
| } | |
| // OpenAI流式响应结构 | |
| type OpenAIStreamResponse 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"` | |
| FinishReason string `json:"finish_reason"` | |
| Index int `json:"index"` | |
| } `json:"choices"` | |
| } | |
| func handleOptions(w http.ResponseWriter, r *http.Request) { | |
| w.Header().Set("Access-Control-Allow-Origin", "*") | |
| w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") | |
| w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") | |
| w.WriteHeader(http.StatusOK) | |
| } | |
| // authMiddleware 认证中间件 | |
| func authMiddleware(next http.HandlerFunc) http.HandlerFunc { | |
| return func(w http.ResponseWriter, r *http.Request) { | |
| authToken := os.Getenv("AUTH_TOKEN") | |
| if authToken == "" { | |
| http.Error(w, "Authentication token not configured", http.StatusUnauthorized) | |
| return | |
| } | |
| authHeader := r.Header.Get("Authorization") | |
| if authHeader == "" { | |
| http.Error(w, "Authorization header is required", http.StatusUnauthorized) | |
| return | |
| } | |
| parts := strings.Split(authHeader, " ") | |
| if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" || parts[1] != authToken { | |
| http.Error(w, "Invalid authorization token", http.StatusUnauthorized) | |
| return | |
| } | |
| next(w, r) | |
| } | |
| } | |
| func forwardToSider(w http.ResponseWriter, r *http.Request) { | |
| fmt.Printf("收到新请求: %s %s\n", r.Method, r.URL.Path) | |
| // 设置CORS头 | |
| w.Header().Set("Access-Control-Allow-Origin", "*") | |
| w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") | |
| w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") | |
| // 读取请求体 | |
| body, err := io.ReadAll(r.Body) | |
| if err != nil { | |
| fmt.Printf("读取请求体失败: %v\n", err) | |
| http.Error(w, "读取请求失败", http.StatusBadRequest) | |
| return | |
| } | |
| defer r.Body.Close() | |
| fmt.Printf("收到请求体: %s\n", string(body)) | |
| // 解析用户请求 | |
| var userReq UserRequest | |
| if err := json.Unmarshal(body, &userReq); err != nil { | |
| fmt.Printf("解析请求体失败: %v\n", err) | |
| http.Error(w, "解析请求失败", http.StatusBadRequest) | |
| return | |
| } | |
| // 解析默认模板 | |
| var defaultConfig map[string]interface{} | |
| if err := json.Unmarshal(defaultJsonTemplate, &defaultConfig); err != nil { | |
| fmt.Printf("解析默认配置失败: %v\n", err) | |
| http.Error(w, "服务器配置错误", http.StatusInternalServerError) | |
| return | |
| } | |
| // 获取用户消息内容 | |
| prompt := "你好" // 默认提示词 | |
| if len(userReq.Messages) > 0 { | |
| prompt = userReq.Messages[len(userReq.Messages)-1].Content | |
| } | |
| fmt.Printf("处理的prompt: %s\n", prompt) | |
| // 添加prompt到配置中 | |
| defaultConfig["prompt"] = prompt | |
| // 添加model到配置中 | |
| if userReq.Model != "" { | |
| defaultConfig["model"] = userReq.Model | |
| } else { | |
| defaultConfig["model"] = "gpt-4o" // 默认模型 | |
| } | |
| // 设置stream参数 | |
| defaultConfig["stream"] = userReq.Stream | |
| fmt.Printf("使用的模型: %s\n", defaultConfig["model"]) | |
| // 转换回JSON | |
| finalBody, err := json.Marshal(defaultConfig) | |
| if err != nil { | |
| fmt.Printf("生成最终请求体失败: %v\n", err) | |
| http.Error(w, "处理请求失败", http.StatusInternalServerError) | |
| return | |
| } | |
| fmt.Printf("发送到Sider的请求体: %s\n", string(finalBody)) | |
| // Get Sider API URL from environment variable | |
| siderAPIURL := os.Getenv("SIDER_API_URL") | |
| if siderAPIURL == "" { | |
| siderAPIURL = "https://api2.sider.ai/api/v3/completion/text" // Default value | |
| } | |
| // 创建转发到Sider的请求 | |
| req, err := http.NewRequest("POST", siderAPIURL, bytes.NewBuffer(finalBody)) | |
| if err != nil { | |
| fmt.Printf("创建Sider请求失败: %v\n", err) | |
| http.Error(w, "创建请求失败", http.StatusInternalServerError) | |
| return | |
| } | |
| // 设置请求头 | |
| req.Header.Set("accept", "*/*") | |
| req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6") | |
| // Get authorization token from environment variable | |
| authToken := os.Getenv("SIDER_AUTH_TOKEN") | |
| if authToken == "" { | |
| fmt.Println("Error: SIDER_AUTH_TOKEN environment variable not set.") | |
| http.Error(w, "Authorization token not configured", http.StatusUnauthorized) | |
| return | |
| } | |
| req.Header.Set("authorization", "Bearer "+authToken) | |
| req.Header.Set("content-type", "application/json") | |
| req.Header.Set("origin", "chrome-extension://dhoenijjpgpeimemopealfcbiecgceod") | |
| req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0") | |
| // 发送请求到Sider | |
| client := &http.Client{} | |
| resp, err := client.Do(req) | |
| if err != nil { | |
| fmt.Printf("发送到Sider请求失败: %v\n", err) | |
| http.Error(w, "发送请求失败", http.StatusInternalServerError) | |
| return | |
| } | |
| defer resp.Body.Close() | |
| fmt.Printf("Sider响应状态码: %d\n", resp.StatusCode) | |
| if !userReq.Stream { | |
| // 非流式响应 | |
| w.Header().Set("Content-Type", "application/json") | |
| fullResponse := "" | |
| reader := bufio.NewReader(resp.Body) | |
| for { | |
| line, err := reader.ReadString('\n') | |
| if err != nil { | |
| if err == io.EOF { | |
| break | |
| } | |
| http.Error(w, "读取响应失败", http.StatusInternalServerError) | |
| return | |
| } | |
| line = strings.TrimSpace(line) | |
| line = strings.TrimPrefix(line, "data:") | |
| if line == "" || line == "[DONE]" { | |
| continue | |
| } | |
| var siderResp SiderResponse | |
| if err := json.Unmarshal([]byte(line), &siderResp); err != nil { | |
| continue | |
| } | |
| fullResponse += siderResp.Data.Text | |
| } | |
| openAIResp := OpenAIResponse{ | |
| ID: "chatcmpl-" + time.Now().Format("20060102150405"), | |
| Object: "chat.completion", | |
| Created: time.Now().Unix(), | |
| Model: userReq.Model, | |
| Choices: []struct { | |
| Message struct { | |
| Role string `json:"role"` | |
| Content string `json:"content"` | |
| } `json:"message"` | |
| FinishReason string `json:"finish_reason"` | |
| Index int `json:"index"` | |
| }{ | |
| { | |
| Message: struct { | |
| Role string `json:"role"` | |
| Content string `json:"content"` | |
| }{ | |
| Role: "assistant", | |
| Content: fullResponse, | |
| }, | |
| FinishReason: "stop", | |
| Index: 0, | |
| }, | |
| }, | |
| Usage: struct { | |
| PromptTokens int `json:"prompt_tokens"` | |
| CompletionTokens int `json:"completion_tokens"` | |
| TotalTokens int `json:"total_tokens"` | |
| }{ | |
| PromptTokens: len(prompt), | |
| CompletionTokens: len(fullResponse), | |
| TotalTokens: len(prompt) + len(fullResponse), | |
| }, | |
| } | |
| json.NewEncoder(w).Encode(openAIResp) | |
| return | |
| } | |
| // 流式响应 | |
| w.Header().Set("Content-Type", "text/event-stream") | |
| w.Header().Set("Cache-Control", "no-cache") | |
| w.Header().Set("Connection", "keep-alive") | |
| w.WriteHeader(http.StatusOK) | |
| // 使用bufio.Reader来读取流式响应 | |
| reader := bufio.NewReader(resp.Body) | |
| for { | |
| line, err := reader.ReadString('\n') | |
| if err != nil { | |
| if err == io.EOF { | |
| fmt.Println("响应结束") | |
| break | |
| } | |
| fmt.Printf("读取响应失败: %v\n", err) | |
| return | |
| } | |
| // 去除前缀和空白字符 | |
| line = strings.TrimSpace(line) | |
| line = strings.TrimPrefix(line, "data:") | |
| // 跳过空行 | |
| if line == "" { | |
| continue | |
| } | |
| // 如果是[DONE],发送OpenAI格式的[DONE] | |
| if line == "[DONE]" { | |
| _, err = w.Write([]byte("data: [DONE]\n\n")) | |
| if err != nil { | |
| fmt.Printf("写入DONE失败: %v\n", err) | |
| } | |
| w.(http.Flusher).Flush() | |
| break | |
| } | |
| // 解析Sider响应 | |
| var siderResp SiderResponse | |
| if err := json.Unmarshal([]byte(line), &siderResp); err != nil { | |
| fmt.Printf("解析Sider响应失败: %v\n", err) | |
| continue | |
| } | |
| // 转换为OpenAI格式 | |
| openAIResp := OpenAIStreamResponse{ | |
| ID: "chatcmpl-" + siderResp.Data.ChatModel, | |
| Object: "chat.completion.chunk", | |
| Created: time.Now().Unix(), | |
| Model: siderResp.Data.ChatModel, | |
| Choices: []struct { | |
| Delta struct { | |
| Content string `json:"content"` | |
| } `json:"delta"` | |
| FinishReason string `json:"finish_reason"` | |
| Index int `json:"index"` | |
| }{ | |
| { | |
| Delta: struct { | |
| Content string `json:"content"` | |
| }{ | |
| Content: siderResp.Data.Text, | |
| }, | |
| FinishReason: "", | |
| Index: 0, | |
| }, | |
| }, | |
| } | |
| // 转换为JSON | |
| openAIJSON, err := json.Marshal(openAIResp) | |
| if err != nil { | |
| fmt.Printf("转换OpenAI格式失败: %v\n", err) | |
| continue | |
| } | |
| // 发送OpenAI格式的响应 | |
| _, err = w.Write([]byte("data: " + string(openAIJSON) + "\n\n")) | |
| if err != nil { | |
| fmt.Printf("写入响应失败: %v\n", err) | |
| return | |
| } | |
| w.(http.Flusher).Flush() | |
| } | |
| } | |
| func indexHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Header().Set("Content-Type", "text/html; charset=utf-8") | |
| fmt.Fprintf(w, "🚀服务已启动!") | |
| } | |
| type Model struct { | |
| ID string `json:"id"` | |
| Object string `json:"object"` | |
| OwnedBy string `json:"owned_by"` | |
| Permission []string `json:"permission"` | |
| } | |
| type ModelListResponse struct { | |
| Object string `json:"object"` | |
| Data []Model `json:"data"` | |
| } | |
| func listModelsHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Header().Set("Access-Control-Allow-Origin", "*") | |
| w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") | |
| w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") | |
| if r.Method == "OPTIONS" { | |
| w.WriteHeader(http.StatusOK) | |
| return | |
| } | |
| authMiddleware(func(w http.ResponseWriter, r *http.Request){ | |
| models := []Model{ | |
| {ID: "gpt-4o", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| {ID: "claude-3.5-sonnet", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| {ID: "deepseek-reasoner", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| {ID: "o3-mini", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| {ID: "o1", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| {ID: "llama-3.1-405b", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| {ID: "gemini-2.0-pro", Object: "model", OwnedBy: "sider", Permission: []string{"read"}}, | |
| } | |
| response := ModelListResponse{ | |
| Object: "list", | |
| Data: models, | |
| } | |
| w.Header().Set("Content-Type", "application/json") | |
| json.NewEncoder(w).Encode(response) | |
| })(w,r) | |
| } | |
| func main() { | |
| // 注册路由处理函数 | |
| http.HandleFunc("/", indexHandler) // 添加主页路由 | |
| http.HandleFunc("/hf/v1/chat/completions", func(w http.ResponseWriter, r *http.Request) { | |
| if r.Method == "OPTIONS" { | |
| handleOptions(w, r) | |
| return | |
| } | |
| // 添加认证中间件 | |
| authMiddleware(forwardToSider)(w, r) | |
| }) | |
| http.HandleFunc("/hf/v1/models", listModelsHandler) | |
| fmt.Println("服务已启动!") | |
| fmt.Println("支持的模型: gpt-4o, claude-3.5-sonnet, deepseek-reasoner,o3-mini,o1,llama-3.1-405b,gemini-2.0-pro") | |
| // Use 0.0.0.0 to listen on all interfaces | |
| if err := http.ListenAndServe("0.0.0.0:7055", nil); err != nil { | |
| fmt.Printf("服务器启动失败: %v\n", err) | |
| } | |
| } | |