mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-17 23:52:34 +00:00
|
Some checks failed
Rust / Format (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Generated Docs (push) Has been cancelled
Rust / Test (Linux) (push) Has been cancelled
Rust / Test (macOS) (push) Has been cancelled
TypeScript / Typecheck (push) Has been cancelled
TypeScript / Test (push) Has been cancelled
TypeScript / Build (push) Has been cancelled
## Summary Final dialect extraction in the gateway refactor series (after #481 / #485, sibling of #487 and #488): the Gemini `generateContent` wire translation moves out of `providers/gemini.rs` into `codec/gemini_generate/`, behind the `Codec` / `StreamDecoder` traits. The adapter becomes a thin transport shell (1,607 → ~400 lines) owning auth (`x-goog-api-key`), base URL, and the streaming byte loop; all translation is in the codec. Two commits, each independently green: 1. **Add the codec** (`wire`/`encode`/`decode`/`stream`/`mod`) — compiling but unused behind a scoped `dead_code` allow. 2. **Rewire the adapter** to it, migrate the ~22 unit tests, and add the previously missing stream-decoder tests. Gemini is the simplest route story in the series — no provider-name branching, no mode flags, count-tokens always available, no forced streaming — so there is no route config and **no `CodecParams` changes** (this PR is conflict-free with #487/#488 apart from one `mod` line; if it lands after them, the unit-struct `CodecParams` literals become `::default()` on rebase, mechanical). It does exercise two trait seams the other codecs don't: - **Fully-formed endpoints from the codec**: model-in-path `:generateContent` / `:streamGenerateContent?alt=sse` / `:countTokens` ride on `EncodedRequest.endpoint` (the count body wraps the request in `generateContentRequest`). - **The first `decode_error` override**: Gemini's gRPC-status mapping (`error_from_grpc_status` with HTTP-status fallback) moves behind the codec; the adapter feeds it status + body + retry-after. The send-side timeout mapping stays transport-side. Other moves, wholesale and already pure: synthetic-UUID tool-call/response ids, the id→name recovery map for `functionResponse`, usage arithmetic (cache subtraction + tool-use addition + thoughts→reasoning), default `safety_settings` injection (flagged profile-ish in a comment, unchanged), `thoughtSignature` round-trip, and the `provider_options.gemini` merge. `translate_messages` goes sync: file-backed Image/Audio/Document attachments resolve via the shared `attachments::resolve` (#485) before encode. The streaming decoder preserves Gemini's distinct stream-end contract exactly: data-only SSE (no event types, no `[DONE]`), and `finish()` synthesizes the `Finish` from accumulated state unconditionally at byte-stream end — there is no terminal wire event. ## Behavior preservation No behavior change. The 32 gemini wire snapshots from #471 (encode round-trips, attachments, response_format, provider_options merges, streaming happy path / tool deltas / reasoning deltas / the unconditional-Finish stream-end pin) pass unmodified, and the full fabro-llm suite is green at 525: all 22 migrated tests plus 10 new ones — 9 stream-decoder unit tests (gemini previously had **zero**: text/thought deltas, reasoning→text transition, single-chunk function calls, finish-reason handling, Finish synthesis with and without a wire finish reason, ToolCalls inference, malformed-chunk errors) and 1 pinning the three model-in-path endpoints. ## Testing - `cargo nextest run -p fabro-llm` — 525 passed (126 wire snapshots included) - `cargo check --workspace` - `cargo +nightly-2026-04-14 clippy -p fabro-llm --all-targets -- -D warnings` - `cargo +nightly-2026-04-14 fmt --check` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| crates | ||
| packages/fabro-api-client | ||