fabro/lib/crates
Scott Werner 074f90c391
refactor(llm): consolidate the HTTP transport behind the codec seam (#491)
PR 6 of the gateway refactor series (after #481, #488, #487, #489):
collapse the four per-adapter transport copies into one `transport`
module. Net −157 lines, and every cross-adapter duplication flagged in
the #487/#488 simplify findings is resolved here.

## What moved where

**`transport.rs` (new)** — how bytes travel, dialect-blind:
- `HttpTransport` (promoted from `providers::http_api::HttpApi`):
client, auth key, base URL, timeouts
- `LineReader` + `parse_retry_after` + `parse_rate_limit_headers` (moved
from `providers::common`, re-export shims kept there for the frozen
fabro-cli imports; `LineReader::new` keeps its 2-arg signature)
- `complete_via_http` / `send_for_body`: blocking send with the shared
timeout/error/status warn logs, non-2xx mapped through
`Codec::decode_error`
- `stream_via_http` + one SSE decode loop, parameterized by
`SseFraming::{EventBlocks, DataLines}` — replaces the four verbatim
`StreamLoop` + unfold copies and the four divergent framers (anthropic's
`parse_sse_block`, openai's `parse_sse_message`, the inline data-line
handling in openai_compatible/gemini, and fabro_server's private block
parser)

**`codec/mod.rs`** — gains the dialect-neutral pure helpers
`parse_error_body` and `extract_system_prompt` (moved from
`providers::common`), so the codec layer no longer imports from the
transport-side providers module.

**Adapters** — shrink to auth + route config + codec composition.
`send_and_read_response` and its `error_code_field` parameter are
deleted: the dialect error-body key now lives only in the codecs, and
any future `decode_error` override applies to blocking and streaming
paths alike.

## Unified SSE framing semantics (deliberate decisions)

The four framers disagreed on edge cases; the shared framer picks one
behavior, stated here rather than chosen silently:
- data payloads are trimmed; multi-line `data:` payloads join with `\n`;
CRLF tolerated in both modes
- comment (`:`), blank, and non-data lines are skipped
- events with an **empty payload are dropped** rather than handed to the
decoder — previously anthropic would error the whole stream on a bare
`data:` line and openai_compatible would feed the decoder an empty
string (also an error); openai/gemini already skipped

All streaming wire snapshots pass unmodified through the shared loop,
and the framer has direct unit tests for these cases.

## Behavior notes (beyond the framing edge cases)

- **Error values are byte-identical**: `Codec::decode_error`'s default
is exactly the `parse_error_body("type")` + `error_from_status_code`
path the deleted call sites inlined; gemini's gRPC-aware override is
what its paths already used.
- **Logging only**: gemini's blocking paths gain the shared
timeout/error/status warn logs (they had none); count-tokens requests
are uniformly tagged `operation="input_token_count"` (previously only
openai's was). The openai count-tokens logging pin passes unchanged.
- gemini's timeout error message now uses the configured provider name
instead of a hardcoded `gemini:` prefix (visible only on custom-named
gemini routes).

## Verification

- `cargo nextest run --workspace`: green except the 5 pre-existing
fabro-workflow failures that fail identically on main
(environment-dependent, unrelated)
- fabro-llm: 545 passed — all PR 0 wire snapshots unmodified
- clippy `-D warnings` + pinned-nightly fmt clean
- fabro-cli compiles against the frozen `providers::common::{LineReader,
parse_retry_after}` paths

Next in the series: PR 7 (codec on the route in fabro-model) — route
vocabulary + the route-equivalence table test.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 09:12:02 -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 feat(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01:56 -04:00
fabro-api feat(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01: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 Interpolation foundation (InterpString v2) (#472) 2026-06-10 12:51:08 -04:00
fabro-cli feat(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01:56 -04:00
fabro-client Add fabro variable CLI namespace for server-managed variables (#434) 2026-05-27 13:57:25 -04:00
fabro-config feat(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01:56 -04:00
fabro-core Replace bare unwrap() with documented expect() across production runtim… (#415) 2026-05-26 17:46:39 -04:00
fabro-dev feat(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01:56 -04:00
fabro-dump Model run sandbox lifecycle explicitly (#431) 2026-05-27 12:48:56 -04:00
fabro-environment Interpolation foundation (InterpString v2) (#472) 2026-06-10 12:51:08 -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): consolidate the HTTP transport behind the codec seam (#491) 2026-06-11 09:12:02 -04:00
fabro-macros refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-manifest Interpolation foundation (InterpString v2) (#472) 2026-06-10 12:51:08 -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(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01:56 -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 Interpolation foundation (InterpString v2) (#472) 2026-06-10 12:51:08 -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 Interpolation foundation (InterpString v2) (#472) 2026-06-10 12:51:08 -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 feat(llm): add Claude Fable 5 support (#482) 2026-06-10 14:01:56 -04:00