fix: Gemini streaming and function calling#32
Conversation
|
Thanks for the PR — fixes real Gemini compatibility issues and the test coverage is solid. Two design concerns worth discussing before merging: 1.
|
|
@cablehead thanks for trusting yoagent! |
65a463d to
f3d880d
Compare
|
Thanks for the thoughtful review -- and for building yoagent, it's a great foundation to work with. Pushed updates addressing both points: 1. Added an optional 2. Dropped the Gemini returns real function call IDs, so the synthetic fallback was unnecessary. Now it's just Re: |
|
Thanks for the iteration — Blocking: literal
|
- Fix SSE parsing to handle \r\n line endings from Google's API - Skip empty text parts sent during thinking phase - Fix stop_reason: don't let "STOP" override ToolUse (Gemini returns "STOP" even when emitting function calls) - Capture and round-trip thought signatures via provider_metadata field on Content::ToolCall (keeps provider-specific data out of tool args) - Use real function call IDs from Gemini response - Include function call ID in functionResponse - Add unit tests for parsing, thought signature round-tripping, CRLF SSE handling, and function response ID behavior - Add Gemini integration tests for simple text and tool use
f3d880d to
c7eeb37
Compare
|
Good catch on the Also addressed:
The test coverage gaps are fair points. Happy to add a |
- Replace literal provider_metadata: None with .. in all pattern match
sites (agent_loop, azure_openai, openai_compat, openai_responses,
google_vertex) -- None acted as a match guard, silently dropping
ToolCalls with Some(metadata)
- Add provider_metadata field to MockToolCall for test injection
- Add test_tool_call_with_provider_metadata_executes: verifies agent
loop executes tools even when provider_metadata is Some
- Restore synthetic google-fc-{n} ID fallback for models that omit id
- Upgrade SSE parse error log from debug to warn
|
Follow-up commit adds the MockProvider test: |
|
Re-reviewed after
Verified locally: LGTM — approving. Thanks for the careful fixup. A few non-blocking follow-ups I'll track in a separate issue (breaking-change Note for the release: adding a required field to the public |
…google.rs error surfacing Type policy (completes the non_exhaustive work coherently): - ModelConfig::custom(api, provider, base_url, id, name): the construction path for Bedrock/Vertex/Azure and future protocols — non_exhaustive had removed downstream struct literals without a replacement for those three - variant-level non_exhaustive on Message::Assistant and Content::Thinking (same PR-#32 exposure class as ToolCall); Message::assistant() + with_error_message()/with_timestamp() and Content::thinking()/ thinking_signed() constructors; external tests migrated - doc fixes: enum-level vs variant-level levers explained separately, "0.9.0" phrased as slated rather than shipped, FRU/serde(default) notes, stale enum listings in messages-events.md and CLAUDE.md updated google.rs error surfacing (pre-existing gaps in the rewired loop): - mid-stream {"error": ...} payloads now classify and return Err instead of deserializing into an empty chunk and vanishing - transport errors return Err(Network) like every other provider instead of warn + break -> fake successful turn - SAFETY/PROHIBITED_CONTENT/BLOCKLIST/SPII finish reasons map to StopReason::Refusal with an explanatory error_message Tests (closing the coverage-review gaps): - empty-text part with functionCall no longer swallowed (loop-level) - next_sse_data consumes keepalives/data-less events (Some(""), not None) - multi-event text-delta accumulation; usage mapping; in-stream error; SAFETY -> Refusal; structural thoughtSignature placement assertion; constructor field-pinning; metadata ToolCall persistence round-trip Co-Authored-By: Claude Fable 5 <[email protected]>
…ening (#55) Exhaustiveness policy: #[non_exhaustive] on Content (+ ToolCall/Thinking variants), Message::Assistant variant, and ModelConfig, with constructors (Content::tool_call*/thinking*, Message::assistant, ModelConfig::custom); StopReason deliberately exhaustive. google.rs: real tests for extracted SSE helpers (earliest-separator fix, empty-text no longer swallows functionCall), in-stream errors and transport failures now surface as Err, SAFETY finish reasons map to StopReason::Refusal. PR #32 scenarios covered in CI via wiremock. Refs #33 (items 1-3; 4-5 deferred) Co-Authored-By: Claude Fable 5 <[email protected]>
Fixes several issues with the Google Gemini provider that break streaming and function calling.
SSE parsing
\r\nline endings (Google's API uses CRLF, parser only handled LF)debug!towarn!Function calling
finishReason: "STOP"overrideStopReason::ToolUse-- Gemini returns "STOP" even when it emits function calls, so the agent loop never executed toolsprovider_metadatafield onContent::ToolCall(keeps provider-specific data out of tool arguments)idfrom response, with syntheticgoogle-fc-{n}fallback for models that omit itidinfunctionResponsewhen sending tool results backContent::ToolCallchangeprovider_metadata: Option<serde_json::Value>field for provider-specific data that shouldn't reach tool execution..to ignore the field; only construction sites set it explicitlyTests
provider_metadataisSome(...)Generated with LLM assistance (Claude Code). Changes have been manually exercised against live APIs via yoke, a headless agent harness built on yoagent.