Skip to main content

brazen/
lib.rs

1#![forbid(unsafe_code)]
2#![cfg_attr(
3    not(test),
4    deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)
5)]
6// The compiler half of the §9.8 interface-parity invariant: a public signature may not
7// expose a private type. `tests/interface_parity.rs` enforces the other half (no public
8// type is unreachable from an entry point); together they pin the surface exactly.
9#![deny(private_interfaces, private_bounds)]
10//! `brazen` — the engine behind the `bz` command, and the pure, fully-tested core
11//! of a stateless LLM adapter.
12//!
13//! `cargo install brazen` builds the `bz` binary (this crate's `[[bin]]`); the
14//! library is that binary's engine, published alongside it in case it is useful as
15//! a dependency. **Its API is not yet a stability contract** — pin an exact version
16//! if you build on it.
17//!
18//! This crate holds the canonical model (the single source of truth every
19//! provider/protocol projects to and from) and the traits behind which all
20//! impurity (network, clock, credentials, browser) is injected. The `bz` binary
21//! and `src/native/` own the native impls; the library reaches 100% coverage on its
22//! own because nothing here touches IO — a boundary `tests/purity.rs` keeps real now
23//! that the bin and the library share one crate.
24//!
25//! Beyond the canonical model and error model (the dependency root), this crate
26//! defines the *seams* the rest of the pipeline plugs into: the `Protocol`,
27//! `Auth`, `Transport`, `CredStore`, and `Clock` traits, the data records they
28//! exchange (`WireRequest`, `Provider`, `Cred`, …), and the `Registry` that
29//! dispatches by id without ever matching a vendor name (§4 of the architecture).
30//! It also holds the pure pipeline — input resolution, canonical-in parsing, and
31//! the output projections + pump loop (§5). Concrete protocol/auth/transport
32//! impls land via their own tasks; the shared test doubles live in [`testing`].
33
34// Modules are PRIVATE (crate-visible, never `pub mod`): the public surface is
35// re-exported below by hand, so nothing leaks via a module path. See arch §9.8 — the
36// public lib API is *exactly* the capability set the `bz` CLI exposes (bidirectional
37// exclusive parity), enforced by `tests/interface_parity.rs`.
38mod auth;
39mod canonical;
40mod cli;
41mod config;
42mod ingress;
43mod os;
44mod pipeline;
45mod protocol;
46mod registry;
47mod run;
48mod store;
49mod transport;
50
51// ---- The native host (feature `native-host`, DEFAULT OFF; yog DESIGN §16.7 U-brazen) ----
52// The impure impls behind brazen's seams — the same `src/native/` shim the `bz` bin owns
53// (the rustls `ureq` `HttpTransport`, the XDG `XdgCredStore`/`XdgModelCache`, the loopback
54// `LoopbackReceiver`, the `SystemClock`/`RealPacer`/`SystemBrowserLauncher`, `TcpBind`,
55// `random_token`, `stash_root`). OFF by default, so the pure library never links them; ON,
56// an embedding host (yog, lernie) that links this crate reaches `bz`'s native capability
57// in process, with no system `bz`. This is the ONLY place `native` becomes lib surface, and
58// it is feature-gated — the feature is the purity boundary, which `tests/purity.rs` pins.
59//
60// Native modules import the crate's own public seams as `brazen::…`; inside the lib crate
61// that name is not otherwise in scope, so alias self under the same gate (harmless, and
62// only compiled when the shim is).
63#[cfg(feature = "native-host")]
64extern crate self as brazen;
65
66/// Native impure impls behind brazen's seams, for embedding the `bz` binary's capability
67/// (arch §6.5, §9.5, §10; yog DESIGN §16.7 U-brazen). Present ONLY under `--features
68/// native-host`.
69///
70/// **Stability: none.** These are the `bz` shim, re-exposed as-is. Their shapes track the
71/// bin's needs and carry NO semver contract — pin an exact `brazen` version if you build
72/// on them. They are coverage-excluded (`make cov` ignores `src/native`), so unlike the
73/// pure core they are not held to 100% line coverage.
74///
75/// **What an embedding host must do.** These types implement the lib's seam traits
76/// (`Transport`, `CredStore`, `ModelCache`, `Clock`, plus the login seams `BrowserLauncher`
77/// / `CodeReceiver` / `Pacer`, and `Bind` for `--serve`). To drive brazen in process:
78/// construct the impls (`HttpTransport::new()`, `XdgCredStore::new()`, `XdgModelCache::new()`,
79/// `SystemClock`, `ReplayStash::new(native::stash_root())`), assemble a [`Host`] over them,
80/// and call [`run`]/[`generate`]/[`serve`]/[`list_models`]/[`count_tokens`]/[`login`] — the
81/// same wiring `src/main.rs` performs. Config and credentials use the same XDG paths as `bz`:
82/// `XdgCredStore` writes one atomic 0600 JSON file per provider under the platform data dir;
83/// `XdgModelCache` caches under `$XDG_CACHE_HOME/brazen/models`; a host wanting isolation can
84/// supply its own seam impls instead and never touch these.
85///
86/// **What stays bin-side (the host must replicate it).** The process-global effects in
87/// `src/main.rs` do NOT lift here: restoring `SIGPIPE` to `SIG_DFL`, the stdin/stdout
88/// `isatty` probes that populate `Args.tty`/`Args.stdout_tty`, and snapshotting the real
89/// `argv`/env into [`Args`]. A host must build [`Args`] itself (via [`EnvSnapshot`]) and, on
90/// Unix, decide the SIGPIPE disposition for its own process.
91#[cfg(feature = "native-host")]
92pub mod native;
93
94// The in-lib test doubles + the relocated unit/integration suite. Both are
95// `#[cfg(test)]`: they exist only in the test build, so they never widen the
96// published surface and never become dead code in the release binary (§9.8).
97#[cfg(test)]
98mod testing;
99#[cfg(test)]
100mod tests;
101
102// ---- The public library surface: the typed interface + what drives it ----
103// The interface is the typed I/O — a `CanonicalRequest` in, an `Event` stream out (the
104// canonical model, §3) — exposed through the `generate` entry point, plus the seams and
105// config that drive it; the byte `bz` CLI is one serialization of exactly this (§9.8,
106// bl-b4a9). Modules are private; this `pub use` block is the whole surface, and it is
107// EXACTLY the type-closure of the entry points below — `tests/interface_parity.rs`
108// derives that closure mechanically and asserts equality, so no type leaks or orphans.
109pub use auth::login::{login, BrowserLauncher, CodeReceiver, LoginIo, Pacer};
110pub use auth::{query_from_request_line, OAuthConfig, RedirectSpec};
111pub use canonical::{
112    CachedModels, CanonicalError, CanonicalRequest, Content, ContentKind, Delta, DocumentSource,
113    ErrorKind, Event, FinishReason, ImageSource, Message, Model, OutputFormat, ReasoningEffort,
114    Role, Tool, ToolChoice, Usage, EVENT_SCHEMA_VERSION,
115};
116pub use cli::{route, Args, Route};
117pub use config::provider::{
118    AuthId, HeaderScheme, HeaderSpec, ModelsOverride, ProtocolId, Provider, TransportSpec,
119};
120pub use config::{EnvSnapshot, OutMode, ResolvedConfig};
121pub use ingress::{decode_request, IngressError, IngressId};
122pub use os::browser_argv;
123pub use protocol::{Envelope, ExecSpec, Method, WireRequest};
124pub use run::{
125    count_tokens, generate, list_models, list_providers, run, serve, Bind, CountIo, Host, ListIo,
126    Listener, ProvidersIo, ServeConn, ServeIo, VERSION,
127};
128pub use store::{
129    parse_ambient, AmbientFormat, AmbientSpec, Clock, Cred, CredStore, ModelCache, ReplayStash,
130    Secret,
131};
132pub use transport::envelope::{envelope_error, envelope_head, envelope_request, EnvelopeHead};
133pub use transport::{Bytes, Timeouts, Transport, TransportResponse};
134
135// ---- Test-only internal prelude (NOT part of the public surface) ----
136// The relocated in-crate tests (`src/tests/`) exercise internals NOT on the interface:
137// the pure parsers/encoders, the config fold, the OAuth wire builders, the registry, the
138// sinks. Re-exporting them at the crate root as `pub(crate)` under `#[cfg(test)]` keeps
139// the tests ergonomic (`crate::Foo`) WITHOUT publishing them — `pub(crate)` is invisible
140// to `cargo public-api`/external consumers and `#[cfg(test)]` strips it from every
141// non-test build. So test layout never drives the surface (§9.8).
142#[cfg(test)]
143pub(crate) use auth::{
144    build_authorize_url, build_token_exchange_request, is_expired, parse_callback,
145    parse_token_response, Auth, AuthCtx, AuthError, Grant, NoAuth, OAuth2Auth, Pkce,
146    StaticSecretAuth, TokenResponse,
147};
148#[cfg(test)]
149pub(crate) use canonical::{select_model, ExitClass, Provenance};
150#[cfg(test)]
151pub(crate) use cli::parse_args;
152#[cfg(test)]
153pub(crate) use config::{
154    config_path, defaults, dump_config, fill_absent, lead_with_preamble, parse_config,
155    partial_from_env, redact, strip_unsupported, ConfigError, IngressConfig, LossyMode,
156    PartialConfig, PartialIngress, PartialProvider,
157};
158#[cfg(test)]
159pub(crate) use ingress::{encode_response, IngressState};
160#[cfg(test)]
161pub(crate) use pipeline::{
162    open_input, parse, pump, read_files, read_request, Glyph, NdjsonSink, PrettySink, RawSink, Sgr,
163    Sink, Style, TextSink,
164};
165#[cfg(test)]
166pub(crate) use protocol::{DecodeState, Frame, Framing, OpenBlock, Protocol, ProviderCtx};
167#[cfg(test)]
168pub(crate) use registry::Registry;
169#[cfg(test)]
170pub(crate) use run::append_query;