-
Notifications
You must be signed in to change notification settings - Fork 0
Registry
The registry (src/registry/) is TinyAgents' named capability catalog — the
third of the five surfaces in the Architecture. It is the
piece that makes recursion resolvable: every model, tool, graph, router, and
reducer is registered under a name, and the declarative .rag and
imperative .ragsh languages bind to those names rather than to Rust types. That
indirection is exactly what lets a model spawn a sub-agent or sub-graph it never
hardcoded — it references a name, and the registry resolves it to a real,
allow-listed capability. See Recursion and RLM.
The registry is deliberately explicit. It is not a hidden global runtime: you own one per application, test, tenant, workspace, or server. It does not execute graph nodes or call providers — it answers "which capabilities exist, under what names, and is this name allowed?"
The implemented core has three submodules:
-
registry::component— identity and discovery types. -
registry::capability— the name-addressableCapabilityRegistry. -
registry::catalog— the offline model catalog (prices, context windows, capabilities).
Three types describe what a registered capability is, independent of any live handle (they serialize, diff, and render in a UI long after the registering process exits):
-
ComponentId(String)— a stable, durable identifier. It is a newtype over the registered name (e.g."gpt-4o","lookup_user"), not a Rust type path, so a capability survives module moves, renames, and serialization. -
ComponentKind— the kind that 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 Status ModelArc<dyn ChatModel<State>>executable handle ToolArc<dyn Tool<State>>executable handle GraphBlueprint(a.raggraph)serializable value AgentArc<dyn HarnessAgent>executable handle Routername-only descriptor presence only Reducername-only descriptor presence only Storename-only descriptor reserved -
ComponentMetadata { id, kind, description, tags, aliases }— discovery / UI metadata. Every registration records one (even name-only ones, where the description and tags start empty); it is the source of truth for presence.
The broader registry spec (
docs/modules/registry/README.md) anticipates first-class stores, middleware, listeners, and an event bus. Today theStorekind is a reserved name-only kind and the rest live in the harness; document and use what exists. (Agent, by contrast, is now an executable kind — see below.)
CapabilityRegistry<State = ()> is the name-addressable catalog. It is generic
over the application State because models and tools are generic over it;
State = () is the common stateless default. Storage is partitioned by kind:
models, tools, and executable agents keep live handles, graphs keep a
serializable Blueprint, and routers/reducers (plus the reserved store kind)
are name-only descriptors. (Agents are state-decoupled — they receive a mapped
prompt, not the registry's State — so the agent map is independent of the
State generic.) The meta map — keyed by (kind, canonical name) — is the
uniform presence index behind has and names.
It is intentionally distinct from the harness' per-run ModelRegistry and
ToolRegistry (which are executable stores). The CapabilityRegistry is the
catalog that declarative source is validated against.
Registering capabilities:
let mut reg = CapabilityRegistry::<()>::new();
reg.register_model("gpt-4o", model)?; // duplicate name -> error
reg.register_tool(tool)?; // keyed by Tool::name()
reg.register_graph_blueprint("triage", blueprint)?;
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 withTinyAgentsError::DuplicateComponentif the(kind, name)pair already exists; thereplace_*variants (replace_model/replace_tool/replace_graph_blueprint/replace_agent) overwrite while preserving any richer metadata already attached. -
register_agent(agent)registers an executableArc<dyn HarnessAgent>keyed by its ownname(); aSubAgentNoderesolves it (viaagent(name), below) to delegate a graph step to a model-driven agent loop. -
register_descriptor(kind, name)backs the name-only kinds and is exposed for the reservedStorekind. -
alias(kind, alias, target)declares an alternate name; it validates the target exists and the alias is free, then records the alias on the target's metadata for discovery.
Looking things up:
model(name), tool(name), graph_blueprint(name), and agent(name) resolve a
name or one alias hop to a live handle/blueprint via resolve_name.
has(kind, name)
answers presence, names(kind) lists canonical names (sorted), and
names_including_aliases(kind) lists canonical names and aliases — the exact
set declarative source may reference. metadata(kind, name) returns the
ComponentMetadata.
Handing off to the runtime:
-
to_model_registry()builds a harnessModelRegistryfrom the registered models, binding alias names to the same handle. -
to_tool_registry()builds a harnessToolRegistry(tools are keyed by their own schemaTool::name(), so tool-level aliases are intentionally not propagated — a tool is always invoked under its canonical name). -
capability_resolver()builds the language layer'sCapabilityResolver— the bridge described below.
This is the registry's reason to exist. CapabilityResolver::from_registry(reg)
(in language::compiler) snapshots names_including_aliases for every kind into
allow-lists:
Self {
models: reg.names_including_aliases(Model),
tools: reg.names_including_aliases(Tool),
subgraphs: reg.names_including_aliases(Graph),
routers: reg.names_including_aliases(Router),
reducers: reg.names_including_aliases(Reducer),
node_kinds: DEFAULT_NODE_KINDS, // model, tool, ...
}When a .rag blueprint or a .ragsh program names a model, tool, subgraph,
router, or reducer, the compiler checks it against these allow-lists
(bind_blueprint, bind_capabilities_with_registry). An unknown reference is a
compile error — before anything runs. This is what makes agent-authored
source safe: a model can emit a .rag blueprint, but it can only wire together
capabilities a human already registered and allowed. Because graphs are
registered as Graph components, a blueprint can reference another blueprint by
name as a subgraph — the registry is where graph-runs-graph recursion gets
its names. The resolver also supports incremental allow_model / allow_tool /
allow_subgraph / allow_router / allow_reducer extensions for tests and
controlled escalation.
flowchart LR
Rust[Rust: register_* by name] --> Reg[CapabilityRegistry]
Reg -->|names_including_aliases| Res[CapabilityResolver]
Rag[".rag / .ragsh source"] -->|references names| Res
Res -->|validated| Graph[Graph + Harness runtime]
The model catalog is an offline snapshot of provider model metadata — not a
live source of truth. It gives TinyAgents deterministic behavior for cost
estimates, context-window checks, capability discovery, model-picker UIs, tests,
and request validation before a provider call. A checked-in seed snapshot
(docs/modules/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 exact id or alias;snapshot()exposes the rawModelCatalogSnapshotandmodels()the full entry slice. -
ModelCatalogSnapshotcarriesschema_version,snapshot_id,created_at,currency,unit, an optionaldescription, provenancesources(name / url /retrieved_at), and themodelslist. -
ModelCatalogEntrydescribes one model:provider,model_id,aliases,mode,max_input_tokens/max_output_tokens, optionaldeprecation_date,pricing,capabilities, asourceidentifier with optionalsource_url, and arawJSONValuethat preserves the upstream payload verbatim for fields not modeled above. -
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.
The catalog is metadata only — it never calls a provider. See
Providers for the live model surface and
docs/modules/registry/model-catalog.md for snapshot provenance rules.
Use a CapabilityRegistry whenever capabilities must be referenced by name:
to compile and validate .rag blueprints or .ragsh programs, to let one agent
or graph spawn another it did not hardcode, to power a discovery UI over
available models/tools/graphs, or to hand a curated, allow-listed set of models
and tools to a harness. Use the model catalog whenever you need offline cost
or capability facts about a model without a network call.
- Architecture — the five surfaces and how they compose.
-
Graph Runtime — what registered
Graphblueprints become. - Recursion and RLM — name-binding as the substrate for self-authoring and sub-agent/sub-graph recursion.
- Module spec:
docs/modules/registry/README.md,design.md,model-catalog.md.
Recursive language-model (RLM) harness for Rust.
Getting started
Concepts
Modules
Providers
Contributing