Spaces:
Paused
Paused
| package model | |
| import "encoding/json" | |
| // OpenAI 格式的消息内容项 | |
| type ContentPart struct { | |
| Type string `json:"type"` | |
| Text string `json:"text,omitempty"` | |
| ImageURL *ImageURL `json:"image_url,omitempty"` | |
| } | |
| type ImageURL struct { | |
| URL string `json:"url"` | |
| } | |
| // Tool 工具定义(OpenAI 兼容) | |
| type Tool struct { | |
| Type string `json:"type"` | |
| Function ToolFunction `json:"function"` | |
| } | |
| // ToolFunction 函数定义 | |
| type ToolFunction struct { | |
| Name string `json:"name"` | |
| Description string `json:"description,omitempty"` | |
| Parameters interface{} `json:"parameters,omitempty"` | |
| } | |
| // ToolCall 模型返回的工具调用 | |
| type ToolCall struct { | |
| ID string `json:"id"` | |
| Type string `json:"type"` | |
| Function FunctionCall `json:"function"` | |
| Index int `json:"index"` | |
| } | |
| // FunctionCall 函数调用(名称 + 参数 JSON 字符串) | |
| type FunctionCall struct { | |
| Name string `json:"name"` | |
| Arguments string `json:"arguments"` | |
| } | |
| // Message 支持纯文本和多模态内容 | |
| type Message struct { | |
| Role string `json:"role"` | |
| Content interface{} `json:"content"` // string 或 []ContentPart | |
| ToolCallID string `json:"tool_call_id,omitempty"` // role: "tool" 时使用 | |
| ToolCalls []ToolCall `json:"tool_calls,omitempty"` // role: "assistant" 时使用 | |
| } | |
| // 解析消息内容,返回文本和图片URL列表 | |
| func (m *Message) ParseContent() (text string, imageURLs []string) { | |
| switch content := m.Content.(type) { | |
| case string: | |
| return content, nil | |
| case []interface{}: | |
| for _, item := range content { | |
| if part, ok := item.(map[string]interface{}); ok { | |
| partType, _ := part["type"].(string) | |
| if partType == "text" { | |
| if t, ok := part["text"].(string); ok { | |
| text += t | |
| } | |
| } else if partType == "image_url" { | |
| if imgURL, ok := part["image_url"].(map[string]interface{}); ok { | |
| if url, ok := imgURL["url"].(string); ok { | |
| imageURLs = append(imageURLs, url) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return text, imageURLs | |
| } | |
| // 转换为上游消息格式,支持多模态 | |
| func (m *Message) ToUpstreamMessage(urlToFileID map[string]string) map[string]interface{} { | |
| // tool 消息:包含 tool_call_id | |
| if m.Role == "tool" { | |
| msg := map[string]interface{}{ | |
| "role": m.Role, | |
| "content": m.Content, | |
| "tool_call_id": m.ToolCallID, | |
| } | |
| return msg | |
| } | |
| // assistant 消息带 tool_calls | |
| if m.Role == "assistant" && len(m.ToolCalls) > 0 { | |
| msg := map[string]interface{}{ | |
| "role": m.Role, | |
| "content": m.Content, | |
| } | |
| var toolCalls []map[string]interface{} | |
| for _, tc := range m.ToolCalls { | |
| toolCalls = append(toolCalls, map[string]interface{}{ | |
| "id": tc.ID, | |
| "type": tc.Type, | |
| "function": map[string]interface{}{ | |
| "name": tc.Function.Name, | |
| "arguments": tc.Function.Arguments, | |
| }, | |
| }) | |
| } | |
| msg["tool_calls"] = toolCalls | |
| return msg | |
| } | |
| text, imageURLs := m.ParseContent() | |
| // 无图片,返回纯文本 | |
| if len(imageURLs) == 0 { | |
| return map[string]interface{}{ | |
| "role": m.Role, | |
| "content": text, | |
| } | |
| } | |
| // 有图片,构建多模态内容 | |
| var content []interface{} | |
| if text != "" { | |
| content = append(content, map[string]interface{}{ | |
| "type": "text", | |
| "text": text, | |
| }) | |
| } | |
| for _, imgURL := range imageURLs { | |
| if fileID, ok := urlToFileID[imgURL]; ok { | |
| content = append(content, map[string]interface{}{ | |
| "type": "image_url", | |
| "image_url": map[string]interface{}{ | |
| "url": fileID, | |
| }, | |
| }) | |
| } | |
| } | |
| return map[string]interface{}{ | |
| "role": m.Role, | |
| "content": content, | |
| } | |
| } | |
| type ChatRequest struct { | |
| Model string `json:"model"` | |
| Messages []Message `json:"messages"` | |
| Stream bool `json:"stream"` | |
| Tools []Tool `json:"tools,omitempty"` | |
| ToolChoice interface{} `json:"tool_choice,omitempty"` | |
| } | |
| type ChatCompletionChunk struct { | |
| ID string `json:"id"` | |
| Object string `json:"object"` | |
| Created int64 `json:"created"` | |
| Model string `json:"model"` | |
| Choices []Choice `json:"choices"` | |
| } | |
| type Choice struct { | |
| Index int `json:"index"` | |
| Delta *Delta `json:"delta,omitempty"` | |
| Message *MessageResp `json:"message,omitempty"` | |
| FinishReason *string `json:"finish_reason"` | |
| } | |
| type Delta struct { | |
| Content string `json:"content,omitempty"` | |
| ReasoningContent string `json:"reasoning_content,omitempty"` | |
| ToolCalls []ToolCall `json:"tool_calls,omitempty"` | |
| } | |
| type MessageResp struct { | |
| Role string `json:"role"` | |
| Content string `json:"content"` | |
| ReasoningContent string `json:"reasoning_content,omitempty"` | |
| ToolCalls []ToolCall `json:"tool_calls,omitempty"` | |
| } | |
| type ChatCompletionResponse struct { | |
| ID string `json:"id"` | |
| Object string `json:"object"` | |
| Created int64 `json:"created"` | |
| Model string `json:"model"` | |
| Choices []Choice `json:"choices"` | |
| } | |
| type ModelsResponse struct { | |
| Object string `json:"object"` | |
| Data []ModelInfo `json:"data"` | |
| } | |
| type ModelInfo struct { | |
| ID string `json:"id"` | |
| Object string `json:"object"` | |
| OwnedBy string `json:"owned_by"` | |
| } | |
| // SearchResult 搜索结果 | |
| type SearchResult struct { | |
| Title string `json:"title"` | |
| URL string `json:"url"` | |
| Index int `json:"index"` | |
| RefID string `json:"ref_id"` | |
| } | |
| // ImageSearchResult 图片搜索结果 | |
| type ImageSearchResult struct { | |
| Title string `json:"title"` | |
| Link string `json:"link"` | |
| Thumbnail string `json:"thumbnail"` | |
| } | |
| // UpstreamData 上游返回的数据结构 | |
| type UpstreamData struct { | |
| Type string `json:"type"` | |
| Data struct { | |
| DeltaContent string `json:"delta_content"` | |
| EditContent string `json:"edit_content"` | |
| Phase string `json:"phase"` | |
| Done bool `json:"done"` | |
| } `json:"data"` | |
| } | |
| func (u *UpstreamData) GetEditContent() string { | |
| editContent := u.Data.EditContent | |
| if editContent == "" { | |
| return "" | |
| } | |
| if len(editContent) > 0 && editContent[0] == '"' { | |
| var unescaped string | |
| if err := json.Unmarshal([]byte(editContent), &unescaped); err == nil { | |
| return unescaped | |
| } | |
| } | |
| return editContent | |
| } | |