fix(convert): 修复 OpenAI 转 Claude 流时 thinking 块的格式问题

将 `ClaudeMediaMessage.Thinking` 的类型从 `string` 修改为 `*string`,以解决 `omitempty` 导致 `"thinking": ""` 字段在 JSON 序列化时被忽略的问题。

同时更新了 `service/convert.go` 和 `relay/channel/claude/relay-claude.go` 中的相关逻辑,以兼容新的指针类型,确保生成的 Claude 事件流符合官方规范。
This commit is contained in:
yanggh
2025-10-13 19:32:17 +08:00
parent 826ef2e5a6
commit 8b9188c584
3 changed files with 12 additions and 9 deletions

View File

@@ -24,7 +24,7 @@ type ClaudeMediaMessage struct {
StopReason *string `json:"stop_reason,omitempty"` StopReason *string `json:"stop_reason,omitempty"`
PartialJson *string `json:"partial_json,omitempty"` PartialJson *string `json:"partial_json,omitempty"`
Role string `json:"role,omitempty"` Role string `json:"role,omitempty"`
Thinking string `json:"thinking,omitempty"` Thinking *string `json:"thinking,omitempty"`
Signature string `json:"signature,omitempty"` Signature string `json:"signature,omitempty"`
Delta string `json:"delta,omitempty"` Delta string `json:"delta,omitempty"`
CacheControl json.RawMessage `json:"cache_control,omitempty"` CacheControl json.RawMessage `json:"cache_control,omitempty"`

View File

@@ -477,8 +477,7 @@ func StreamResponseClaude2OpenAI(reqMode int, claudeResponse *dto.ClaudeResponse
signatureContent := "\n" signatureContent := "\n"
choice.Delta.ReasoningContent = &signatureContent choice.Delta.ReasoningContent = &signatureContent
case "thinking_delta": case "thinking_delta":
thinkingContent := claudeResponse.Delta.Thinking choice.Delta.ReasoningContent = claudeResponse.Delta.Thinking
choice.Delta.ReasoningContent = &thinkingContent
} }
} }
} else if claudeResponse.Type == "message_delta" { } else if claudeResponse.Type == "message_delta" {
@@ -513,7 +512,9 @@ func ResponseClaude2OpenAI(reqMode int, claudeResponse *dto.ClaudeResponse) *dto
var responseThinking string var responseThinking string
if len(claudeResponse.Content) > 0 { if len(claudeResponse.Content) > 0 {
responseText = claudeResponse.Content[0].GetText() 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) tools := make([]dto.ToolCallResponse, 0)
thinkingContent := "" thinkingContent := ""
@@ -545,7 +546,9 @@ func ResponseClaude2OpenAI(reqMode int, claudeResponse *dto.ClaudeResponse) *dto
}) })
case "thinking": case "thinking":
// 加密的不管, 只输出明文的推理过程 // 加密的不管, 只输出明文的推理过程
thinkingContent = message.Thinking if message.Thinking != nil {
thinkingContent = *message.Thinking
}
case "text": case "text":
responseText = message.GetText() responseText = message.GetText()
} }
@@ -598,8 +601,8 @@ func FormatClaudeResponseInfo(requestMode int, claudeResponse *dto.ClaudeRespons
if claudeResponse.Delta.Text != nil { if claudeResponse.Delta.Text != nil {
claudeInfo.ResponseText.WriteString(*claudeResponse.Delta.Text) claudeInfo.ResponseText.WriteString(*claudeResponse.Delta.Text)
} }
if claudeResponse.Delta.Thinking != "" { if claudeResponse.Delta.Thinking != nil {
claudeInfo.ResponseText.WriteString(claudeResponse.Delta.Thinking) claudeInfo.ResponseText.WriteString(*claudeResponse.Delta.Thinking)
} }
} else if claudeResponse.Type == "message_delta" { } else if claudeResponse.Type == "message_delta" {
// 最终的usage获取 // 最终的usage获取

View File

@@ -352,7 +352,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon
Type: "content_block_start", Type: "content_block_start",
ContentBlock: &dto.ClaudeMediaMessage{ ContentBlock: &dto.ClaudeMediaMessage{
Type: "thinking", Type: "thinking",
Thinking: "", Thinking: common.GetPointer[string](""),
}, },
}) })
} }
@@ -360,7 +360,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon
// text delta // text delta
claudeResponse.Delta = &dto.ClaudeMediaMessage{ claudeResponse.Delta = &dto.ClaudeMediaMessage{
Type: "thinking_delta", Type: "thinking_delta",
Thinking: reasoning, Thinking: &reasoning,
} }
} else { } else {
if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeText { if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeText {