pub trait Codec:
Send
+ Sync
+ 'static {
// Required methods
fn name(&self) -> &'static str;
fn capabilities(&self, model: &str) -> Capabilities;
fn encode(&self, request: &ModelRequest) -> Result<EncodedRequest>;
fn decode(
&self,
body: &[u8],
warnings_in: Vec<ModelWarning>,
) -> Result<ModelResponse>;
// Provided methods
fn auto_output_strategy(&self, _model: &str) -> OutputStrategy { ... }
fn extract_rate_limit(
&self,
_headers: &HeaderMap,
) -> Option<RateLimitSnapshot> { ... }
fn encode_streaming(&self, request: &ModelRequest) -> Result<EncodedRequest> { ... }
fn decode_stream<'a>(
&'a self,
bytes: BoxByteStream<'a>,
warnings_in: Vec<ModelWarning>,
) -> BoxDeltaStream<'a> { ... }
}Expand description
Stateless encoder/decoder for ONE provider wire format.
A Codec knows nothing about HTTP, auth, or retries. It turns IR into
bytes and bytes into IR. Streaming uses the same trait — decode_stream
owns the codec’s parser state machine.
Required Methods§
Sourcefn name(&self) -> &'static str
fn name(&self) -> &'static str
Stable codec identifier — "anthropic-messages",
"openai-chat", etc. Used in logs and metrics tags.
Sourcefn capabilities(&self, model: &str) -> Capabilities
fn capabilities(&self, model: &str) -> Capabilities
Capability surface the codec advertises for the given model. Codecs vary by model (small models lacking vision, etc.).
Sourcefn encode(&self, request: &ModelRequest) -> Result<EncodedRequest>
fn encode(&self, request: &ModelRequest) -> Result<EncodedRequest>
Encode IR → wire body for a one-shot (non-streaming) call.
Implementors push warnings onto the returned
EncodedRequest::warnings for any IR field they had to drop or
coerce.
Sourcefn decode(
&self,
body: &[u8],
warnings_in: Vec<ModelWarning>,
) -> Result<ModelResponse>
fn decode( &self, body: &[u8], warnings_in: Vec<ModelWarning>, ) -> Result<ModelResponse>
Decode wire body → IR. warnings_in are the encode-time warnings
that should be carried forward into ModelResponse::warnings so the
caller sees the full advisory list in one place.
Provided Methods§
Sourcefn auto_output_strategy(&self, _model: &str) -> OutputStrategy
fn auto_output_strategy(&self, _model: &str) -> OutputStrategy
Resolve OutputStrategy::Auto to the codec’s preferred
dispatch shape for model. Called once at codec-construction
time per request — never per-delta or per-retry, so the
resolved strategy is part of the SessionGraph event log’s
deterministic-replay surface.
Default returns OutputStrategy::Native — most codecs ship
vendor-native structured output (OpenAI Responses
text.format=json_schema, Gemini responseJsonSchema,
Bedrock Anthropic-passthrough). Codecs whose native channel
is newer / less mature than the tool-call surface
(Anthropic Messages today — output_config ships without
a strict toggle) override to OutputStrategy::Tool.
Sourcefn extract_rate_limit(&self, _headers: &HeaderMap) -> Option<RateLimitSnapshot>
fn extract_rate_limit(&self, _headers: &HeaderMap) -> Option<RateLimitSnapshot>
Extract a RateLimitSnapshot from response headers, if the
vendor exposes rate-limit state in headers. Default returns
None — codecs whose providers publish rate-limit headers
override this and parse them.
Sourcefn encode_streaming(&self, request: &ModelRequest) -> Result<EncodedRequest>
fn encode_streaming(&self, request: &ModelRequest) -> Result<EncodedRequest>
Encode IR → wire body for a streaming call. Default impl delegates
to encode and marks the request as streaming; codecs that need a
different body shape (e.g. stream: true field) or extra headers
(e.g. Accept: text/event-stream) override.
Sourcefn decode_stream<'a>(
&'a self,
bytes: BoxByteStream<'a>,
warnings_in: Vec<ModelWarning>,
) -> BoxDeltaStream<'a>
fn decode_stream<'a>( &'a self, bytes: BoxByteStream<'a>, warnings_in: Vec<ModelWarning>, ) -> BoxDeltaStream<'a>
Decode an incremental byte stream → IR StreamDelta stream.
Implementors own their parser state machine — Anthropic walks SSE
events, OpenAI splits data: lines, Gemini reads NDJSON, Bedrock
parses AWS event-stream frames. Default impl is a graceful
fallback: collects every chunk, runs decode once at the end, and
emits the resulting ModelResponse as a single
StreamDelta::Stop. Concrete codecs replace it as soon as they
support real token-level streaming.