diff --git a/internal/adapter/openai/tool_sieve_core.go b/internal/adapter/openai/tool_sieve_core.go index 23bfdff..5d96503 100644 --- a/internal/adapter/openai/tool_sieve_core.go +++ b/internal/adapter/openai/tool_sieve_core.go @@ -183,7 +183,7 @@ func findToolSegmentStart(s string) int { return -1 } lower := strings.ToLower(s) - keywords := []string{"tool_calls", "\"function\"", "function.name:", "functionCall", "\"tool_use\""} + keywords := []string{"tool_calls", "\"function\"", "function.name:", "functioncall", "\"tool_use\""} bestKeyIdx := -1 for _, kw := range keywords { idx := strings.Index(lower, kw) @@ -240,7 +240,7 @@ func consumeToolCapture(state *toolStreamSieveState, toolNames []string) (prefix lower := strings.ToLower(captured) keyIdx := -1 - keywords := []string{"tool_calls", "\"function\"", "function.name:", "functionCall", "\"tool_use\""} + keywords := []string{"tool_calls", "\"function\"", "function.name:", "functioncall", "\"tool_use\""} for _, kw := range keywords { idx := strings.Index(lower, kw) if idx >= 0 && (keyIdx < 0 || idx < keyIdx) { diff --git a/internal/adapter/openai/tool_sieve_xml_test.go b/internal/adapter/openai/tool_sieve_xml_test.go index 9201189..0cf98e4 100644 --- a/internal/adapter/openai/tool_sieve_xml_test.go +++ b/internal/adapter/openai/tool_sieve_xml_test.go @@ -104,6 +104,7 @@ func TestFindToolSegmentStartDetectsXMLToolCalls(t *testing.T) { want int }{ {"tool_calls_tag", "some text \n", 10}, + {"gemini_function_call_json", `some text {"functionCall":{"name":"search","args":{"q":"latest"}}}`, 10}, {"tool_call_tag", "prefix \n", 7}, {"invoke_tag", "text body", 5}, {"function_call_tag", "body", 0}, @@ -119,6 +120,27 @@ func TestFindToolSegmentStartDetectsXMLToolCalls(t *testing.T) { } } +func TestProcessToolSieveDetectsGeminiFunctionCallPayload(t *testing.T) { + var state toolStreamSieveState + events := processToolSieveChunk(&state, `{"functionCall":{"name":"search_web","args":{"query":"latest"}}}`, []string{"search_web"}) + events = append(events, flushToolSieve(&state, []string{"search_web"})...) + + var textContent string + var toolCalls int + for _, evt := range events { + if evt.Content != "" { + textContent += evt.Content + } + toolCalls += len(evt.ToolCalls) + } + if toolCalls != 1 { + t.Fatalf("expected one tool call from functionCall payload, got events=%#v", events) + } + if strings.Contains(strings.ToLower(textContent), "functioncall") { + t.Fatalf("functionCall json leaked into text content: %q", textContent) + } +} + func TestFindPartialXMLToolTagStart(t *testing.T) { cases := []struct { name string