From 8b9188c5845da0d4e4eb0d771a2cf882c8133b9a Mon Sep 17 00:00:00 2001 From: yanggh Date: Mon, 13 Oct 2025 19:32:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(convert):=20=E4=BF=AE=E5=A4=8D=20OpenAI=20?= =?UTF-8?q?=E8=BD=AC=20Claude=20=E6=B5=81=E6=97=B6=20thinking=20=E5=9D=97?= =?UTF-8?q?=E7=9A=84=E6=A0=BC=E5=BC=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 `ClaudeMediaMessage.Thinking` 的类型从 `string` 修改为 `*string`,以解决 `omitempty` 导致 `"thinking": ""` 字段在 JSON 序列化时被忽略的问题。 同时更新了 `service/convert.go` 和 `relay/channel/claude/relay-claude.go` 中的相关逻辑,以兼容新的指针类型,确保生成的 Claude 事件流符合官方规范。 --- dto/claude.go | 2 +- relay/channel/claude/relay-claude.go | 15 +++++++++------ service/convert.go | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/dto/claude.go b/dto/claude.go index d66ec2e0a..3ad04bc23 100644 --- a/dto/claude.go +++ b/dto/claude.go @@ -24,7 +24,7 @@ type ClaudeMediaMessage struct { StopReason *string `json:"stop_reason,omitempty"` PartialJson *string `json:"partial_json,omitempty"` Role string `json:"role,omitempty"` - Thinking string `json:"thinking,omitempty"` + Thinking *string `json:"thinking,omitempty"` Signature string `json:"signature,omitempty"` Delta string `json:"delta,omitempty"` CacheControl json.RawMessage `json:"cache_control,omitempty"` diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 79d68bb4b..fe523deff 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -477,8 +477,7 @@ func StreamResponseClaude2OpenAI(reqMode int, claudeResponse *dto.ClaudeResponse signatureContent := "\n" choice.Delta.ReasoningContent = &signatureContent case "thinking_delta": - thinkingContent := claudeResponse.Delta.Thinking - choice.Delta.ReasoningContent = &thinkingContent + choice.Delta.ReasoningContent = claudeResponse.Delta.Thinking } } } else if claudeResponse.Type == "message_delta" { @@ -513,7 +512,9 @@ func ResponseClaude2OpenAI(reqMode int, claudeResponse *dto.ClaudeResponse) *dto var responseThinking string if len(claudeResponse.Content) > 0 { responseText = claudeResponse.Content[0].GetText() - responseThinking = claudeResponse.Content[0].Thinking + if claudeResponse.Content[0].Thinking != nil { + responseThinking = *claudeResponse.Content[0].Thinking + } } tools := make([]dto.ToolCallResponse, 0) thinkingContent := "" @@ -545,7 +546,9 @@ func ResponseClaude2OpenAI(reqMode int, claudeResponse *dto.ClaudeResponse) *dto }) case "thinking": // 加密的不管, 只输出明文的推理过程 - thinkingContent = message.Thinking + if message.Thinking != nil { + thinkingContent = *message.Thinking + } case "text": responseText = message.GetText() } @@ -598,8 +601,8 @@ func FormatClaudeResponseInfo(requestMode int, claudeResponse *dto.ClaudeRespons if claudeResponse.Delta.Text != nil { claudeInfo.ResponseText.WriteString(*claudeResponse.Delta.Text) } - if claudeResponse.Delta.Thinking != "" { - claudeInfo.ResponseText.WriteString(claudeResponse.Delta.Thinking) + if claudeResponse.Delta.Thinking != nil { + claudeInfo.ResponseText.WriteString(*claudeResponse.Delta.Thinking) } } else if claudeResponse.Type == "message_delta" { // 最终的usage获取 diff --git a/service/convert.go b/service/convert.go index 1dc26c0fb..975ab2d0b 100644 --- a/service/convert.go +++ b/service/convert.go @@ -352,7 +352,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "content_block_start", ContentBlock: &dto.ClaudeMediaMessage{ Type: "thinking", - Thinking: "", + Thinking: common.GetPointer[string](""), }, }) } @@ -360,7 +360,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon // text delta claudeResponse.Delta = &dto.ClaudeMediaMessage{ Type: "thinking_delta", - Thinking: reasoning, + Thinking: &reasoning, } } else { if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeText {