diff --git a/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge.go b/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge.go index a14e0ca202..b434180573 100644 --- a/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge.go +++ b/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge.go @@ -362,10 +362,8 @@ func ChatCompletionsResponseToAnthropic(resp *ChatCompletionsResponse, model str choice := resp.Choices[0] out.Content = chatMessageToAnthropicBlocks(choice.Message) out.StopReason = chatFinishReasonToAnthropicStopReason(choice.FinishReason, out.Content) - if choice.FinishReason == "length" { - // Anthropic conveys max-tokens via stop_reason only; no separate - // incomplete_details field. stop_sequence stays nil. - } + // "length" → "max_tokens" is handled by chatFinishReasonToAnthropicStopReason; + // Anthropic conveys max-tokens via stop_reason only, no incomplete_details field. } if resp.Usage != nil { out.Usage = chatUsageToAnthropicUsage(resp.Usage) @@ -483,13 +481,13 @@ type ChatCompletionsToAnthropicStreamState struct { MessageStopSent bool // Current content block lifecycle. - ContentBlockIndex int - ContentBlockOpen bool - CurrentBlockType string // "text" | "thinking" | "tool_use" - CurrentToolName string - CurrentToolArgs string + ContentBlockIndex int + ContentBlockOpen bool + CurrentBlockType string // "text" | "thinking" | "tool_use" + CurrentToolName string + CurrentToolArgs string CurrentToolHadDelta bool - HasToolCall bool + HasToolCall bool // Tool calls keyed by the upstream tool_call index. The Anthropic block // index assigned at content_block_start time is stored so later argument @@ -519,9 +517,9 @@ type ChatCompletionsToAnthropicStreamState struct { // NewChatCompletionsToAnthropicStreamState returns an initialized stream state. func NewChatCompletionsToAnthropicStreamState(model string) *ChatCompletionsToAnthropicStreamState { return &ChatCompletionsToAnthropicStreamState{ - ResponseID: generateResponsesID(), - Model: model, - Created: time.Now().Unix(), + ResponseID: generateResponsesID(), + Model: model, + Created: time.Now().Unix(), toolBlockIndex: make(map[int]int), toolAnnounced: make(map[int]bool), toolName: make(map[int]string), diff --git a/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge_test.go b/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge_test.go index e4a2c6d8e8..c569d818a2 100644 --- a/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge_test.go +++ b/backend/internal/pkg/apicompat/chatcompletions_anthropic_bridge_test.go @@ -12,9 +12,6 @@ import ( // Helpers // --------------------------------------------------------------------------- -func strPtr(s string) *string { return &s } -func intPtr(v int) *int { return &v } - // collectAnthropicStreamEvents feeds CC chunks through the direct bridge and // appends finalize events, returning the full Anthropic event sequence. func collectAnthropicStreamEvents(t *testing.T, chunks []string) []AnthropicStreamEvent { @@ -199,7 +196,8 @@ func TestAnthropicToChatCompletionsRequest_ToolChoiceSpecificTool(t *testing.T) var tc map[string]any require.NoError(t, json.Unmarshal(out.ToolChoice, &tc)) require.Equal(t, "function", tc["type"]) - fn := tc["function"].(map[string]any) + fn, ok := tc["function"].(map[string]any) + require.True(t, ok, "tool_choice function should be a map") require.Equal(t, "get_weather", fn["name"]) } @@ -254,10 +252,10 @@ func TestAnthropicToChatCompletionsRequest_MaxTokensFloor(t *testing.T) { func TestAnthropicToChatCompletionsRequest_ReasoningEffortMapping(t *testing.T) { req := &AnthropicRequest{ - Model: "gpt-5.4", - MaxTokens: 100, - OutputConfig: &AnthropicOutputConfig{Effort: "max"}, - Messages: []AnthropicMessage{{Role: "user", Content: json.RawMessage(`"hi"`)}}, + Model: "gpt-5.4", + MaxTokens: 100, + OutputConfig: &AnthropicOutputConfig{Effort: "max"}, + Messages: []AnthropicMessage{{Role: "user", Content: json.RawMessage(`"hi"`)}}, } out, err := AnthropicToChatCompletionsRequest(req)