From 550fbe516def87734d0ddeebc05a3e0c1ee28953 Mon Sep 17 00:00:00 2001 From: Seefs Date: Mon, 2 Mar 2026 15:51:55 +0800 Subject: [PATCH] fix: default empty input_json_delta arguments to {} for tool call parsing --- relay/channel/claude/relay-claude.go | 8 ++++- relay/channel/claude/relay_claude_test.go | 38 +++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 82cc5fb29..d9e522514 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -451,11 +451,17 @@ func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCo choice.Delta.Content = claudeResponse.Delta.Text switch claudeResponse.Delta.Type { case "input_json_delta": + arguments := "{}" + if claudeResponse.Delta.PartialJson != nil { + if partial := strings.TrimSpace(*claudeResponse.Delta.PartialJson); partial != "" { + arguments = partial + } + } tools = append(tools, dto.ToolCallResponse{ Type: "function", Index: common.GetPointer(fcIdx), Function: dto.FunctionResponse{ - Arguments: *claudeResponse.Delta.PartialJson, + Arguments: arguments, }, }) case "signature_delta": diff --git a/relay/channel/claude/relay_claude_test.go b/relay/channel/claude/relay_claude_test.go index 0fd890621..986788cf9 100644 --- a/relay/channel/claude/relay_claude_test.go +++ b/relay/channel/claude/relay_claude_test.go @@ -215,3 +215,41 @@ func TestRequestOpenAI2ClaudeMessage_AssistantToolCallWithMalformedArguments(t * require.True(t, ok) assert.Empty(t, inputObj) } + +func TestStreamResponseClaude2OpenAI_EmptyInputJSONDeltaFallback(t *testing.T) { + empty := "" + resp := &dto.ClaudeResponse{ + Type: "content_block_delta", + Index: func() *int { v := 1; return &v }(), + Delta: &dto.ClaudeMediaMessage{ + Type: "input_json_delta", + PartialJson: &empty, + }, + } + + chunk := StreamResponseClaude2OpenAI(resp) + require.NotNil(t, chunk) + require.Len(t, chunk.Choices, 1) + require.NotNil(t, chunk.Choices[0].Delta.ToolCalls) + require.Len(t, chunk.Choices[0].Delta.ToolCalls, 1) + assert.Equal(t, "{}", chunk.Choices[0].Delta.ToolCalls[0].Function.Arguments) +} + +func TestStreamResponseClaude2OpenAI_NonEmptyInputJSONDeltaPreserved(t *testing.T) { + partial := `{"timezone":"Asia/Shanghai"}` + resp := &dto.ClaudeResponse{ + Type: "content_block_delta", + Index: func() *int { v := 1; return &v }(), + Delta: &dto.ClaudeMediaMessage{ + Type: "input_json_delta", + PartialJson: &partial, + }, + } + + chunk := StreamResponseClaude2OpenAI(resp) + require.NotNil(t, chunk) + require.Len(t, chunk.Choices, 1) + require.NotNil(t, chunk.Choices[0].Delta.ToolCalls) + require.Len(t, chunk.Choices[0].Delta.ToolCalls, 1) + assert.Equal(t, partial, chunk.Choices[0].Delta.ToolCalls[0].Function.Arguments) +}