sider2api / main.go
ppasd000's picture
Update main.go
8bd5853 verified
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)
}
}