Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions dto/claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ type ClaudeMessageSource struct {
}

type ClaudeMessage struct {
Role string `json:"role"`
Content any `json:"content"`
Role string `json:"role"`
Content any `json:"content"`
ReasoningContent json.RawMessage `json:"reasoning_content,omitempty"`
}

func (c *ClaudeMessage) IsStringContent() bool {
Expand Down
10 changes: 10 additions & 0 deletions relay/channel/claude/relay-claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe
Role: message.Role,
Content: message.Content,
}
if message.ReasoningContent != "" {
fmtMessage.ReasoningContent = message.ReasoningContent
}
if message.Role == "tool" {
fmtMessage.ToolCallId = message.ToolCallId
}
Expand Down Expand Up @@ -331,6 +334,13 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe
claudeMessage := dto.ClaudeMessage{
Role: message.Role,
}
if message.ReasoningContent != "" {
reasoningContent, err := common.Marshal(message.ReasoningContent)
if err != nil {
return nil, err
}
claudeMessage.ReasoningContent = reasoningContent
}
if message.Role == "tool" {
if len(claudeMessages) > 0 && claudeMessages[len(claudeMessages)-1].Role == "user" {
lastMessage := claudeMessages[len(claudeMessages)-1]
Expand Down
32 changes: 32 additions & 0 deletions relay/channel/claude/relay_claude_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,35 @@ func TestRequestOpenAI2ClaudeMessage_ConvertsTextFileContentToText(t *testing.T)
require.NotNil(t, content[0].Text)
require.Equal(t, "alpha\nbeta", *content[0].Text)
}

func TestRequestOpenAI2ClaudeMessagePreservesReasoningContent(t *testing.T) {
message := dto.Message{
Role: "assistant",
Content: "",
ReasoningContent: " ",
}
message.SetToolCalls([]dto.ToolCallRequest{
{
ID: "call_123",
Type: "function",
Function: dto.FunctionRequest{
Name: "lookup",
Arguments: `{"query":"moonshot"}`,
},
},
})
request := dto.GeneralOpenAIRequest{
Model: "kimi-k2-thinking",
Messages: []dto.Message{message},
}

claudeRequest, err := RequestOpenAI2ClaudeMessage(nil, request)
require.NoError(t, err)
require.Len(t, claudeRequest.Messages, 2)
require.JSONEq(t, `" "`, string(claudeRequest.Messages[1].ReasoningContent))

content, ok := claudeRequest.Messages[1].Content.([]dto.ClaudeMediaMessage)
require.True(t, ok)
require.NotEmpty(t, content)
require.Equal(t, "tool_use", content[len(content)-1].Type)
}
3 changes: 3 additions & 0 deletions service/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ func ClaudeToOpenAIRequest(claudeRequest dto.ClaudeRequest, info *relaycommon.Re
openAIMessage := dto.Message{
Role: claudeMessage.Role,
}
if len(claudeMessage.ReasoningContent) > 0 {
openAIMessage.ReasoningContent = common.JsonRawMessageToString(claudeMessage.ReasoningContent)
}

//log.Printf("claudeMessage.Content: %v", claudeMessage.Content)
if claudeMessage.IsStringContent() {
Expand Down