Repository navigation
Registry
The registry (crates/tinyagents-registry/) is the named capability catalog.
Models, tools, agents, and host-provided capability descriptors are registered
under names so a host can inspect and select them without relying on hidden
process globals.
The registry is explicit, not a hidden global — you own one per application, test, tenant, or workspace. It does not execute tools or call providers; it answers "which capabilities exist, under what names, and is this name allowed?"
Cargo feature: tracing only (no default features).
Three submodules:
-
component— identity and discovery types. -
capability— the name-addressableCapabilityRegistry. -
catalog— the offline model catalog (prices, context windows, capabilities). -
router—ModelRouter, a workload-alias layer over named models.
-
ComponentId(String)— a stable identifier, a newtype over the registered name (e.g."gpt-4o","lookup_user"), not a Rust type path. -
ComponentKind— partitions the namespace: lookups, duplicate detection, aliasing, and discovery are all scoped by kind, so(Model, "x")and(Tool, "x")are independent entries. Variants:Kind Stores Executable? ModelArc<dyn ChatModel<State>>yes ToolArc<dyn Tool<State>>yes Graphhost-provided graph descriptor no AgentArc<dyn HarnessAgent>yes Routername-only descriptor no Reducername-only descriptor no Storename-only descriptor no Scriptname-only descriptor for a host-provided scripted node no Middlewarename-only descriptor no Checkpointername-only descriptor no TaskStorename-only descriptor no Listenername-only descriptor no -
ComponentMetadata { id, kind, description, tags, aliases }— discovery metadata. Every registration records one, even the name-only kinds (where description and tags start empty); it is the source of truth for presence.
CapabilityRegistry<State = ()> is the name-addressable catalog, generic over
the application State because models and tools are generic over it.
Registered agents are state-decoupled — a HarnessAgent receives a mapped
prompt, not the registry's State — so the agent map does not depend on the
State generic.
It is distinct from the harness's per-run ModelRegistry and ToolRegistry
(executable stores for a single harness). CapabilityRegistry is the catalog
that declarative source is validated against; to_model_registry() /
to_tool_registry() build a harness registry out of it.
let mut reg = CapabilityRegistry::<()>::new();
reg.register_model("gpt-4o", model)?; // duplicate name -> error
reg.register_tool(tool)?; // keyed by Tool::name()
reg.register_agent(agent)?; // keyed by HarnessAgent::name()
reg.register_router("route_by_intent")?; // name-only descriptor
reg.register_reducer("append_messages")?; // name-only descriptor
reg.alias(ComponentKind::Model, "fast", "gpt-4o")?;-
register_*fails with a duplicate-component error if the(kind, name)pair already exists;replace_model/replace_tool/replace_agentoverwrite while preserving any richer metadata already attached. -
register_descriptor(kind, name)backs the name-only kinds. -
alias(kind, alias, target)declares an alternate name; it validates the target exists and the alias is free.
Looking things up: model(name), tool(name), and agent(name) resolve a
name (or one alias hop) to a live handle. has(kind, name) answers presence,
names(kind) lists canonical names, and names_including_aliases(kind) lists
canonical names and aliases. metadata(kind, name) returns the
ComponentMetadata.
Handing off to the runtime: to_model_registry() builds a harness
ModelRegistry from the registered models, binding alias names to the same
handle. to_tool_registry() builds a harness ToolRegistry (tools are keyed
by their own Tool::name(), so tool-level aliases are not propagated — a
tool is always invoked under its canonical name).
CapabilityRegistry<State> also implements tinyagents_registry::AgentRegistry
for resolving registered agents by name.
snapshot() returns a serializable RegistrySnapshot, sorted by
(kind, name). It enumerates both components and every alias as
AliasBinding { kind, alias, canonical }.
let mut reg = CapabilityRegistry::<()>::new();
reg.register_model("gpt-4o", model)?;
reg.alias(ComponentKind::Model, "default", "gpt-4o")?;
let snapshot = reg.snapshot();
assert_eq!(snapshot.aliases[0].alias, "default");
assert_eq!(snapshot.aliases[0].canonical, "gpt-4o");
assert_eq!(snapshot.aliases[0].kind, ComponentKind::Model);diagnostics() returns RegistryDiagnostics the registration-time duplicate
check cannot catch alone: an alias that shadows a registered component's
name, a dangling alias pointing at an unregistered name, and a name reused
across different kinds (legal, since kinds partition the namespace, but
flagged for audits — the diagnostic message names the offending kinds).
let mut reg = CapabilityRegistry::<()>::new();
reg.register_model("shared", model)?;
reg.register_router("shared")?; // legal: a different kind
let diags = reg.diagnostics();
assert_eq!(diags[0].name, "shared");
assert!(diags[0].message.contains("multiple kinds"));ModelRouter maps a workload alias (chat-v1, vision-v1, ...) onto a
WorkloadRoute { alias, model, requires: CapabilitySet, fallbacks }. It is a
declarative layer above the named model registry: route(alias) /
target_model(alias) resolve an alias to a concrete registered model name,
required_capabilities(alias) returns the capability gate, and
fallback_policy(alias) returns an ordered same-family fallback chain. A host
uses this to let application code ask for "the model for this workload" while
keeping the concrete model swap in one place.
The model catalog is an offline snapshot of provider model metadata, not a
live source of truth. It gives deterministic behavior for cost estimates,
context-window checks, and capability gating without a network call. A
checked-in snapshot (crates/tinyagents-registry/model-catalog.snapshot.json)
is compiled into the crate.
-
ModelCatalog::seed()loads the bundled snapshot;from_json/from_snapshotload a custom one.get(provider, model_id)andget_by_model_id(model_id)resolve an entry by id or alias;snapshot()exposes the rawModelCatalogSnapshotandmodels()the full entry slice. -
ModelCatalogSnapshotcarriesschema_version,snapshot_id, provenance, and themodelslist. -
ModelCatalogEntrydescribes one model:provider,model_id,aliases, token limits,pricing, andcapabilities. -
ModelPricinggives per-token input/output prices plus cache-read, cache-creation, audio, and reasoning rates. -
ModelCapabilitiesis the feature matrix:streaming,tool_calling,parallel_tool_calling,json_schema,system_messages,vision,audio_input/audio_output,pdf_input,prompt_caching,reasoning.
Use a CapabilityRegistry to power a discovery UI over available
models/tools/agents, to resolve agents by name, or to hand a curated,
allow-listed set of models and tools to a harness. Use the model catalog for
offline cost or capability facts about a model.
- Architecture — how the crates fit together.
- Harness — the agent loop that consumes registered models and tools.
Provider-neutral agent harness and tools for Rust.
Getting started
Concepts
Modules
Providers
Contributing