brazen 0.0.4

A stateless, swiss-army-knife adapter for every LLM provider and protocol.
Documentation
//! The transport seam (arch §4.1, §9.1): the ONE impure surface. `bz` wires the
//! rustls-backed `HttpTransport`; tests wire `MockTransport`. A blocking,
//! incremental body iterator keeps the pipeline a pure `Iterator`, never async.

use std::io;

use crate::canonical::CanonicalError;
use crate::protocol::WireRequest;

pub mod envelope;

/// One transport body chunk. An alias, so the framers' `Vec<u8>` and the body
/// stream speak the same type.
pub type Bytes = Vec<u8>;

/// The peeked HTTP status (read even under `--raw`, for exit-code correctness)
/// plus the blocking, incremental body stream (arch §4.1).
pub struct TransportResponse {
    pub status: u16,
    pub body: Box<dyn Iterator<Item = io::Result<Bytes>>>,
    /// The raw `Retry-After` response-header value, when present — the ONE transport
    /// fact captured beyond the body (arch §3.3): a pacing hint a caller-owned retry
    /// loop wants, which the parsed error BODY (`provider_detail`) never holds. Kept
    /// MINIMAL (the one header, verbatim; parsed to seconds later against the `Clock`
    /// seam), not a header map — widening to more headers is additive. `None` where
    /// the response carried no such header.
    pub retry_after: Option<String>,
}

/// The per-request transport budgets (config §4.3), in WHOLE SECONDS; each `None`
/// leaves that bound unset (the transport's own default). ureq's three phase
/// budgets — the seam's internal vocabulary, NOT the config surface: the resolved
/// config carries ONE `timeout` (the silence budget), and `ResolvedConfig::timeouts()`
/// FANS it onto all three here (all equal, or all `None`), so the collapse to one
/// knob is a surface fact and the fan is observable at this seam (arch §13.15).
/// Carried on the [`WireRequest`] — the one thing crossing the seam — so config-
/// sourced policy reaches the impure transport without widening the `send` signature.
/// The one number lives in config (floor `data/defaults.toml`), never as magic in the
/// bin (severability — policy in config, not core).
///
/// - `connect`: cap on establishing the connection (DNS/TCP/TLS).
/// - `response`: cap on awaiting the response headers — **not** the body.
/// - `idle`: the INTER-CHUNK bound on the streaming body, reset on every chunk.
///   It bounds a provider that sends headers then stalls mid-stream without
///   capping total stream length, so a long-but-live generation is never
///   truncated (a *total* body cap, ureq's `timeout_recv_body`, would be wrong).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Timeouts {
    pub connect: Option<u64>,
    pub response: Option<u64>,
    pub idle: Option<u64>,
}

/// The single network seam (arch §4.1). Object-safe; `Send + Sync` so an impl is
/// shareable. Exactly one round-trip per process — a caller wanting N concurrent
/// requests spawns N `bz`.
pub trait Transport: Send + Sync {
    fn send(&self, wire: WireRequest) -> Result<TransportResponse, CanonicalError>;
}