fabro/lib/crates
Scott Werner 3985eaf1d7
refactor(llm): introduce Codec trait seam + extract openai_compatible (#481)
## 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>
2026-06-10 12:50:19 -04:00
..
build-support fix(build): refresh embedded git sha on branch commits 2026-05-09 14:10:04 -04:00
fabro-acp fix(workflow): capture configured artifacts once (#337) 2026-05-21 10:52:22 -04:00
fabro-agent fix(llm): preserve raw compatible tool arguments (#448) 2026-05-28 11:52:36 -04:00
fabro-api feat: chat-driven workflow builder at /playground (#450) 2026-06-09 11:24:56 -04:00
fabro-auth Replace vague expect/panic messages with invariant-explaining messages (#422) 2026-05-27 10:38:20 -04:00
fabro-automation fix(automation): honor schedule trigger enabled state 2026-05-30 02:19:52 -04:00
fabro-checkpoint Make git metadata sandbox-native 2026-04-27 21:43:15 -07:00
fabro-cli feat: add server-owned environment store (Task 1 & 2 foundation) (#446) 2026-05-28 17:10:59 -04:00
fabro-client Add fabro variable CLI namespace for server-managed variables (#434) 2026-05-27 13:57:25 -04:00
fabro-config fix(ci): restore Rust checks 2026-06-02 19:05:08 -04:00
fabro-core Replace bare unwrap() with documented expect() across production runtim… (#415) 2026-05-26 17:46:39 -04:00
fabro-dev ci: lock Cargo resolution and fix cancellation flake (#461) 2026-05-30 15:07:41 -04:00
fabro-dump Model run sandbox lifecycle explicitly (#431) 2026-05-27 12:48:56 -04:00
fabro-environment feat: Add Environment REST CRUD API under /api/v1/environments (#453) 2026-05-29 17:30:57 -04:00
fabro-github fix(github): refresh installation tokens during workflows 2026-05-06 07:15:18 -04:00
fabro-graphviz Replace vague expect/panic messages with invariant-explaining messages (#422) 2026-05-27 10:38:20 -04:00
fabro-hooks Add event-sourced todo tools for OpenAI and Anthropic profiles (#353) 2026-05-22 13:44:42 -04:00
fabro-http refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-install feat(install): enable only allowed sandbox providers 2026-06-02 18:39:31 -04:00
fabro-interview Replace stdin JSONL control pipe with WebSocket worker control bus (#440) 2026-05-27 20:24:25 -04:00
fabro-llm refactor(llm): introduce Codec trait seam + extract openai_compatible (#481) 2026-06-10 12:50:19 -04:00
fabro-macros refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-manifest feat: add server-owned environment store (Task 1 & 2 foundation) (#446) 2026-05-28 17:10:59 -04:00
fabro-mcp Add legacy SSE MCP transport support (#386) 2026-05-24 15:29:01 -04:00
fabro-mcp-server feat: Add approve/deny run controls to MCP and CLI (#400) 2026-05-25 15:49:57 -04:00
fabro-model feat(model): add Claude Opus 4.8 (#451) 2026-05-28 19:58:00 -04:00
fabro-oauth Replace bare unwrap() with documented expect() across production runtim… (#415) 2026-05-26 17:46:39 -04:00
fabro-options-metadata refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-proc refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-redact refactor(integrations): make chat integrations Slack-only 2026-05-09 11:43:16 -04:00
fabro-sandbox Model run sandbox lifecycle explicitly (#431) 2026-05-27 12:48:56 -04:00
fabro-server feat: chat-driven workflow builder at /playground (#450) 2026-06-09 11:24:56 -04:00
fabro-slack Replace vague expect/panic messages with invariant-explaining messages (#422) 2026-05-27 10:38:20 -04:00
fabro-spa feat(dev): gitignore embedded spa assets 2026-04-26 21:31:11 -04:00
fabro-static refactor: rationalize server secret scopes (vault-only for optional int… (#401) 2026-05-25 17:26:01 -04:00
fabro-store perf(server): cache bare GitHub clones for automation materialization 2026-05-29 08:29:58 -04:00
fabro-telemetry Replace vague expect/panic messages with invariant-explaining messages (#422) 2026-05-27 10:38:20 -04:00
fabro-template feat(template): resolve template error locations (#333) 2026-05-20 20:15:04 -04:00
fabro-test test(llm): pin provider wire behavior with per-dialect snapshot tests (#471) 2026-06-08 16:11:27 -04:00
fabro-tool feat: Add approve/deny run controls to MCP and CLI (#400) 2026-05-25 15:49:57 -04:00
fabro-tracker fix(github): refresh installation tokens during workflows 2026-05-06 07:15:18 -04:00
fabro-types feat: chat-driven workflow builder at /playground (#450) 2026-06-09 11:24:56 -04:00
fabro-util Replace vague expect/panic messages with invariant-explaining messages (#422) 2026-05-27 10:38:20 -04:00
fabro-validate fix(graph): support dotted Fabro graph attributes (#324) 2026-05-20 09:31:08 -04:00
fabro-variable feat(server): add variables API (#430) 2026-05-27 11:46:36 -04:00
fabro-vault feat(server): add variables API (#430) 2026-05-27 11:46:36 -04:00
fabro-workflow Limit DOT templates to prompt + goal (#474) 2026-06-05 11:22:38 -04:00