mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +00:00
PR 7 of the gateway refactor series (after #481, #488, #487, #489, #491) — the series capstone: the wire dialect becomes route vocabulary in fabro-model config instead of a structural implication of the adapter type. ## What's here **`fabro-model/src/codec.rs` (new)** — `CodecKind` (`anthropic_messages`, `openai_responses`, `openai_compatible`, `gemini_generate`; strum per house style). `CodecKind::default_for(AdapterKind)` reproduces the historical adapter→dialect fusion exactly. **Catalog schema** — optional `codec` on provider rows and model rows (the multiplexer case), sparse-merged with the existing `.or()` pattern. Omitted everywhere in the built-in catalog, so **all defaults reproduce today's routes**. Explicit pairings outside the adapter's default are rejected at catalog build (`UnsupportedProviderCodec` / `UnsupportedModelCodec`) so no new route combination is silently enabled by configuration — the field is vocabulary for the OpenRouter/Bedrock feature PRs, not a new capability. `Catalog::effective_codec` mirrors `effective_agent_profile`. fabro-config mirrors the field through `LlmLayer` (`ProviderSettings.codec`, `ModelSettings.codec`) and the catalog-settings conversion. **Route resolution** — `adapter_registry::resolve_route(catalog, model)` assembles `(provider row, model row)` into `Route { provider, transport, codec, deployment_id, billing_policy, agent_profile }`. **Route-equivalence table test** — every built-in model row pinned to its resolved tuple as an executable table (23 rows), with a coverage assert so a new built-in model can't land without a deliberate table edit. This is the "compat mapping as an executable table, not a comment" test from the plan. **`AdapterConfig` cleanup** — the OpenAI-only fields (`codex_mode`, `org_id`, `project_id`) move out of the shared struct into `AdapterKindOptions::OpenAi(OpenAiAdapterOptions)`; the client populates them only for OpenAi-kind routes, which is the only factory that ever read them. ## Deliberate scope cuts - **No per-model `billing_policy`** — that schema change exists solely for the OpenRouter redo, which owns it. - **`codec_params` and `supports_count_tokens` stay adapter-internal** — the registry `Route` carries what the catalog defines; the per-route knobs in the adapters' `RouteConfig` move out when a second codec/transport pairing actually exists (OpenRouter's anthropic skin / Bedrock). Wiring `resolve_route` into `Client` request dispatch is the optional PR 8 and is likewise deferred. - **No user-facing docs for `codec`** — every accepted value equals the default, so there is nothing actionable to document yet; docs land with the first feature PR that enables a non-default pairing. ## Verification - `cargo nextest run --workspace --no-fail-fast` (re-run post-rebase onto #491's merge): 6701 passed; the only failures are the same 5 pre-existing environment-dependent fabro-workflow failures noted in #491, identical on main - fabro-llm: 548 passed — all wire snapshots unmodified - clippy `-D warnings` + pinned-nightly fmt clean This ends the refactor series: the seams exist. Next up are a standalone cost PR (`cost.rs` + `Response.cost_usd`/`CostSource`, pulled forward from the #438 triage as its own pre-OpenRouter step) and then the feature redos — OpenRouter (#438: one TOML + typed codec params) and Bedrock (#459: sigv4/eventstream transport + config, private codec layer deleted). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| crates | ||
| packages/fabro-api-client | ||