Skip to content

feat: issue #33 follow-ups — non_exhaustive policy and google.rs test quality#55

Merged
yuanhao merged 2 commits into
mainfrom
feat/issue-33-followups
Jul 5, 2026
Merged

feat: issue #33 follow-ups — non_exhaustive policy and google.rs test quality#55
yuanhao merged 2 commits into
mainfrom
feat/issue-33-followups

Conversation

@yuanhao

@yuanhao yuanhao commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Implements items 1–3 of #33; items 4–5 are deferred with rationale (see issue comment). A 5-agent review pass ran on this branch; all findings addressed in the second commit.

Item 1 — exhaustiveness policy (breaking, must land in 0.9.0)

  • #[non_exhaustive] on Content (enum level: downstream matches need a wildcard) and on the ToolCall, Thinking, and Message::Assistant variants — the field-growth exposure class that PR fix: Gemini streaming and function calling #32 hit. Constructors: Content::tool_call() / tool_call_with_metadata() / thinking() / thinking_signed(), Message::assistant() + with_error_message() / with_timestamp().
  • #[non_exhaustive] on ModelConfig, plus ModelConfig::custom(api, provider, base_url, id, name) so Bedrock/Vertex/Azure users (no preset) keep a construction path.
  • Deliberately not on StopReason (control-flow enum — a new variant should be a compile error downstream) or AgentMessage (untagged serde + context-inclusion semantics). Policy documented on the types.

Item 2 — real tests for google.rs SSE handling

next_sse_data() / part_text() extracted and driven by the production loop; the two previously-vacuous tests now exercise them. Behavior fixes that fell out: earliest-separator splitting (the old logic could merge a CRLF event into a later LF event), and an empty text part no longer skips a functionCall in the same part.

Item 3 — PR #32 scenarios now run in CI

tests/google_stream_test.rs (wiremock): thought-signature capture into provider_metadata, structural round-trip assertion on the outbound body (signature on the functionCall part, synthetic google-fc- id stripped), empty-text+functionCall, multi-event text accumulation, and usage mapping.

Bonus (from the review pass) — google.rs error surfacing

  • Mid-stream {"error": ...} payloads now classify and return Err (previously deserialized into an empty chunk and vanished — fake successful turn, no retry).
  • Transport errors return Err(Network) (retryable) instead of warn! + breakOk(Stop).
  • SAFETY / PROHIBITED_CONTENT / BLOCKLIST / SPII finish reasons → StopReason::Refusal with an explanatory error_message.

Breaking-change notes for 0.9.0

Downstream must: add wildcard arms to exhaustive Content matches; construct ToolCall/Thinking/Message::Assistant via the new constructors (patterns need ..); construct ModelConfig via presets or ModelConfig::custom() (field mutation still works; literals/FRU do not).

🤖 Generated with Claude Code

yuanhao and others added 2 commits July 4, 2026 23:22
…ality

Item 1 — exhaustiveness policy (breaking, batched into 0.9.0):
- #[non_exhaustive] on Content (enum) and Content::ToolCall (variant):
  new content kinds and ToolCall fields become warn-only for downstream.
  Add Content::tool_call()/tool_call_with_metadata() constructors.
- #[non_exhaustive] on ModelConfig (0.9.0 added a field; future ones
  should not break downstream literals).
- StopReason stays deliberately exhaustive (control-flow enum: a new
  variant should be a compile error downstream, not a silent wildcard).

Item 2 — fix vacuous google.rs tests:
- extract next_sse_data() and part_text() and drive them from the
  production loop; rewrite test_parse_chunk_with_crlf_sse and
  test_parse_chunk_with_empty_text to exercise the real helpers
- drive-by fix: split at the EARLIEST separator when both \n\n and
  \r\n\r\n are buffered (the old inline logic preferred the LF match
  and could merge two events); regression test added
- empty-text parts no longer skip a functionCall in the same part

Item 3 — CI coverage for the PR #32 scenarios:
- tests/google_stream_test.rs: wiremock-based thought-signature capture
  and round-trip (signature echoed, synthetic google-fc- id stripped),
  running on every PR instead of only under GEMINI_API_KEY

Refs #33 (items 4 and 5 deferred — see issue comment)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…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 <noreply@anthropic.com>
@yuanhao yuanhao merged commit 2f6f12d into main Jul 5, 2026
1 check passed
@yuanhao yuanhao deleted the feat/issue-33-followups branch July 5, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant