diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/bedrock_thinking_test.go b/plugins/wasm-go/extensions/ai-proxy/provider/bedrock_thinking_test.go index a9504ed06..c72813bef 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/bedrock_thinking_test.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/bedrock_thinking_test.go @@ -115,6 +115,28 @@ func TestBedrockStreamPreservesClaudeNativeIndexesAndStops(t *testing.T) { assert.Equal(t, 2, *events[0].Payload.Index) } +func TestBedrockStreamToolCallArgumentDeltaIncludesFunctionType(t *testing.T) { + provider := &bedrockProvider{} + ctx := newMockMultipartHttpContext() + + chunk, err := provider.convertEventFromBedrockToOpenAI(ctx, ConverseStreamEvent{ + ContentBlockIndex: 0, + Delta: &converseStreamEventContentBlockDelta{ + ToolUse: &toolUseBlockDelta{Input: `{"path":"/tmp/example"}`}, + }, + }) + require.NoError(t, err) + + body := strings.TrimPrefix(strings.TrimSpace(string(chunk)), ssePrefix) + var event chatCompletionResponse + require.NoError(t, json.Unmarshal([]byte(body), &event)) + require.Len(t, event.Choices, 1) + require.NotNil(t, event.Choices[0].Delta) + require.Len(t, event.Choices[0].Delta.ToolCalls, 1) + assert.Equal(t, "function", event.Choices[0].Delta.ToolCalls[0].Type) + assert.Equal(t, `{"path":"/tmp/example"}`, event.Choices[0].Delta.ToolCalls[0].Function.Arguments) +} + func TestBedrockResponsePreservesClaudeNativeRedactedThinking(t *testing.T) { provider := &bedrockProvider{} ctx := newMockMultipartHttpContext() diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/claude.go b/plugins/wasm-go/extensions/ai-proxy/provider/claude.go index b79f56b83..7842feb2a 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/claude.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/claude.go @@ -925,6 +925,7 @@ func (c *claudeProvider) streamResponseClaude2OpenAI(ctx wrapper.HttpContext, or ToolCalls: []toolCall{ { Index: index, + Type: "function", Function: functionCall{ Arguments: origResponse.Delta.PartialJson, }, diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/claude_test.go b/plugins/wasm-go/extensions/ai-proxy/provider/claude_test.go index d1ec356e4..d02ee166c 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/claude_test.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/claude_test.go @@ -443,6 +443,28 @@ func TestClaudeProvider_StreamPreservesNativeSignatureAndStopsForClaudeConversio assert.Equal(t, "content_block_stop", events[0].Name) } +func TestClaudeProvider_StreamToolCallArgumentDeltaIncludesFunctionType(t *testing.T) { + provider := &claudeProvider{} + ctx := newMockMultipartHttpContext() + index := 0 + + response := provider.streamResponseClaude2OpenAI(ctx, &claudeTextGenStreamResponse{ + Type: "content_block_delta", + Index: &index, + Delta: &claudeTextGenDelta{ + Type: "input_json_delta", + PartialJson: `{"path":"/tmp/example"}`, + }, + }) + + require.NotNil(t, response) + require.Len(t, response.Choices, 1) + require.NotNil(t, response.Choices[0].Delta) + require.Len(t, response.Choices[0].Delta.ToolCalls, 1) + assert.Equal(t, "function", response.Choices[0].Delta.ToolCalls[0].Type) + assert.Equal(t, `{"path":"/tmp/example"}`, response.Choices[0].Delta.ToolCalls[0].Function.Arguments) +} + func TestClaudeProvider_BuildClaudeTextGenRequest_ClaudeCodeMode(t *testing.T) { provider := &claudeProvider{ config: ProviderConfig{