mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-12 03:51:01 +00:00
## What Introduces the `Codec` / `StreamDecoder` trait seam in `fabro-llm` and extracts the OpenAI Chat Completions wire logic behind it as the first conforming codec. Two commits: 1. **`codec/mod.rs`** — the pure translation contract (`encode` / `decode_response` / `stream_decoder`, plus defaulted `encode_count_tokens` / `decode_count_tokens` / `decode_error`) and its data types (`CodecCtx`, `CodecParams`, `EncodedRequest`, `RawEvent`). A codec knows *what the bytes say*; it owns no HTTP, auth, or base URL. 2. **`codec/openai_compatible/`** — the Chat Completions codec split into `wire` / `translate` / `request` / `response` / `stream`. `providers/openai_compatible.rs` shrinks from 1,608 → ~330 lines: a thin transport shell that keeps the public struct/builders/auth/`validate_request`, owns the streaming byte loop + SSE `data:` framing, and delegates all translation to the codec. The two hand-rolled stream unfolds collapse into one. This is the first step of a gateway refactor that separates codec (wire dialect) from transport/auth/route, so later work (Bedrock, OpenRouter) becomes mostly config rather than parallel adapters. ## Behavior No behavior change. The public adapter API (`OpenAiCompatibleAdapter::new` / `with_name` / `with_catalog` / …) is unchanged, and **all 126 wire snapshots pass without edits** — the parity proof that the extracted codec produces byte-identical output. The 29 in-module unit tests move into the codec submodules alongside the code they exercise. ## On the trait `openai_compatible` is the simplest dialect, so its `impl Codec` is just three methods — count-tokens and error mapping inherit the defaults. The contract is defined in full now (a scoped `dead_code` allow on `codec/mod.rs` covers the seams the anthropic/openai/gemini codecs will exercise in follow-up PRs) so those extractions only *override* methods, never extend the trait. Extracting a real codec refined two trait signatures vs. the initial sketch: the canonical `Request` lives in `CodecCtx` (decoders need it for tool-argument parsing and the stream model fallback), and the header-parsed `rate_limit` threads into `decode_response` / `stream_decoder`. `on_event` returns `Result` so dialect error events propagate as stream errors. ## Tests - `cargo nextest run -p fabro-llm` — 515 passed (incl. 126 wire snapshots, unmodified) - `cargo nextest run --workspace` — green - fabro-agent `parity_matrix` (the frozen `OpenAiCompatibleAdapter` contract) — green - `cargo +nightly fmt --check` / `clippy --all-targets -- -D warnings` — clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| crates | ||
| packages/fabro-api-client | ||