mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +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> |
||
|---|---|---|
| .. | ||
| build-support | ||
| fabro-acp | ||
| fabro-agent | ||
| fabro-api | ||
| fabro-auth | ||
| fabro-automation | ||
| fabro-checkpoint | ||
| fabro-cli | ||
| fabro-client | ||
| fabro-config | ||
| fabro-core | ||
| fabro-dev | ||
| fabro-dump | ||
| fabro-environment | ||
| fabro-github | ||
| fabro-graphviz | ||
| fabro-hooks | ||
| fabro-http | ||
| fabro-install | ||
| fabro-interview | ||
| fabro-llm | ||
| fabro-macros | ||
| fabro-manifest | ||
| fabro-mcp | ||
| fabro-mcp-server | ||
| fabro-model | ||
| fabro-oauth | ||
| fabro-options-metadata | ||
| fabro-proc | ||
| fabro-redact | ||
| fabro-sandbox | ||
| fabro-server | ||
| fabro-slack | ||
| fabro-spa | ||
| fabro-static | ||
| fabro-store | ||
| fabro-telemetry | ||
| fabro-template | ||
| fabro-test | ||
| fabro-tool | ||
| fabro-tracker | ||
| fabro-types | ||
| fabro-util | ||
| fabro-validate | ||
| fabro-variable | ||
| fabro-vault | ||
| fabro-workflow | ||