11package chat
22
33import (
4+ "bytes"
45 "context"
6+ "encoding/json"
57 "fmt"
8+ "net/http"
9+ "strings"
610
711 "github.com/Tencent/WeKnora/internal/types"
812 "github.com/sashabaranov/go-openai"
@@ -13,6 +17,14 @@ type RemoteAPIChat struct {
1317 modelName string
1418 client * openai.Client
1519 modelID string
20+ baseURL string
21+ apiKey string
22+ }
23+
24+ // QwenChatCompletionRequest 用于 qwen 模型的自定义请求结构体
25+ type QwenChatCompletionRequest struct {
26+ openai.ChatCompletionRequest
27+ EnableThinking * bool `json:"enable_thinking,omitempty"` // qwen 模型专用字段
1628}
1729
1830// NewRemoteAPIChat 调用远程API 聊天实例
@@ -26,6 +38,8 @@ func NewRemoteAPIChat(chatConfig *ChatConfig) (*RemoteAPIChat, error) {
2638 modelName : chatConfig .ModelName ,
2739 client : openai .NewClientWithConfig (config ),
2840 modelID : chatConfig .ModelID ,
41+ baseURL : chatConfig .BaseURL ,
42+ apiKey : apiKey ,
2943 }, nil
3044}
3145
@@ -41,6 +55,27 @@ func (c *RemoteAPIChat) convertMessages(messages []Message) []openai.ChatComplet
4155 return openaiMessages
4256}
4357
58+ // isQwenModel 检查是否为 qwen 模型
59+ func (c * RemoteAPIChat ) isAliyunQwen3Model () bool {
60+ return strings .HasPrefix (c .modelName , "qwen3-" ) && c .baseURL == "https://dashscope.aliyuncs.com/compatible-mode/v1"
61+ }
62+
63+ // buildQwenChatCompletionRequest 构建 qwen 模型的聊天请求参数
64+ func (c * RemoteAPIChat ) buildQwenChatCompletionRequest (messages []Message ,
65+ opts * ChatOptions , isStream bool ,
66+ ) QwenChatCompletionRequest {
67+ req := QwenChatCompletionRequest {
68+ ChatCompletionRequest : c .buildChatCompletionRequest (messages , opts , isStream ),
69+ }
70+
71+ // 对于 qwen 模型,在非流式调用中强制设置 enable_thinking: false
72+ if ! isStream {
73+ enableThinking := false
74+ req .EnableThinking = & enableThinking
75+ }
76+ return req
77+ }
78+
4479// buildChatCompletionRequest 构建聊天请求参数
4580func (c * RemoteAPIChat ) buildChatCompletionRequest (messages []Message ,
4681 opts * ChatOptions , isStream bool ,
@@ -71,18 +106,18 @@ func (c *RemoteAPIChat) buildChatCompletionRequest(messages []Message,
71106 if opts .PresencePenalty > 0 {
72107 req .PresencePenalty = float32 (opts .PresencePenalty )
73108 }
74- if opts .Thinking != nil {
75- req .ChatTemplateKwargs = map [string ]any {
76- "enable_thinking" : * opts .Thinking ,
77- }
78- }
79109 }
80110
81111 return req
82112}
83113
84114// Chat 进行非流式聊天
85115func (c * RemoteAPIChat ) Chat (ctx context.Context , messages []Message , opts * ChatOptions ) (* types.ChatResponse , error ) {
116+ // 如果是 qwen 模型,使用自定义请求
117+ if c .isAliyunQwen3Model () {
118+ return c .chatWithQwen (ctx , messages , opts )
119+ }
120+
86121 // 构建请求参数
87122 req := c .buildChatCompletionRequest (messages , opts , false )
88123
@@ -111,6 +146,68 @@ func (c *RemoteAPIChat) Chat(ctx context.Context, messages []Message, opts *Chat
111146 }, nil
112147}
113148
149+ // chatWithQwen 使用自定义请求处理 qwen 模型
150+ func (c * RemoteAPIChat ) chatWithQwen (ctx context.Context , messages []Message , opts * ChatOptions ) (* types.ChatResponse , error ) {
151+ // 构建 qwen 请求参数
152+ req := c .buildQwenChatCompletionRequest (messages , opts , false )
153+
154+ // 序列化请求
155+ jsonData , err := json .Marshal (req )
156+ if err != nil {
157+ return nil , fmt .Errorf ("marshal request: %w" , err )
158+ }
159+
160+ // 构建 URL
161+ endpoint := c .baseURL + "/chat/completions"
162+
163+ // 创建 HTTP 请求
164+ httpReq , err := http .NewRequestWithContext (ctx , "POST" , endpoint , bytes .NewBuffer (jsonData ))
165+ if err != nil {
166+ return nil , fmt .Errorf ("create request: %w" , err )
167+ }
168+
169+ // 设置请求头
170+ httpReq .Header .Set ("Content-Type" , "application/json" )
171+ httpReq .Header .Set ("Authorization" , "Bearer " + c .apiKey )
172+
173+ // 发送请求
174+ client := & http.Client {}
175+ resp , err := client .Do (httpReq )
176+ if err != nil {
177+ return nil , fmt .Errorf ("send request: %w" , err )
178+ }
179+ defer resp .Body .Close ()
180+
181+ // 检查响应状态
182+ if resp .StatusCode != http .StatusOK {
183+ return nil , fmt .Errorf ("API request failed with status: %d" , resp .StatusCode )
184+ }
185+
186+ // 解析响应
187+ var chatResp openai.ChatCompletionResponse
188+ if err := json .NewDecoder (resp .Body ).Decode (& chatResp ); err != nil {
189+ return nil , fmt .Errorf ("decode response: %w" , err )
190+ }
191+
192+ if len (chatResp .Choices ) == 0 {
193+ return nil , fmt .Errorf ("no response from API" )
194+ }
195+
196+ // 转换响应格式
197+ return & types.ChatResponse {
198+ Content : chatResp .Choices [0 ].Message .Content ,
199+ Usage : struct {
200+ PromptTokens int `json:"prompt_tokens"`
201+ CompletionTokens int `json:"completion_tokens"`
202+ TotalTokens int `json:"total_tokens"`
203+ }{
204+ PromptTokens : chatResp .Usage .PromptTokens ,
205+ CompletionTokens : chatResp .Usage .CompletionTokens ,
206+ TotalTokens : chatResp .Usage .TotalTokens ,
207+ },
208+ }, nil
209+ }
210+
114211// ChatStream 进行流式聊天
115212func (c * RemoteAPIChat ) ChatStream (ctx context.Context ,
116213 messages []Message , opts * ChatOptions ,
0 commit comments