Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Register a model

Add one model registration in crates/harn-vm/src/llm/catalog_sources/60-models/, then generate the runtime catalog, capabilities, bindings, and provider documentation:

make gen-provider-catalog
make check-provider-catalog check-provider-matrix check-provider-support

Use the owning provider's live API to verify tools, tool results, reasoning controls, structured output, streaming, and any other capability the registration claims. Record failures as well as successes. A successful HTTP response does not prove that a provider honored a parameter.

Write one source entry#

Keep ordinary model facts in the model table. Place request policy, aliases, gateway routes, and picker placement beneath its source-only registration:

[models."example-current"]
name = "Example current"
provider = "example"
context_window = 200000
operations = ["text_generation"]
capabilities = ["tools", "streaming"]

[models."example-current".registration]
aliases = ["current"]

[models."example-current".registration.request]
native_tools = true
preferred_tool_format = "native"

[models."example-current".registration.routes."gateway/example/current"]
provider = "gateway"
model = { name = "Example current via gateway", wire_model = "example/current" }
request = { reasoning_none_supported = false }

The generator derives exact request matches from the catalog and wire IDs. Those IDs must be literal names; glob operators and escaping are rejected using the same syntax predicate as the capability matcher. Do not author model_match or extends inside a registration. Route requests inherit the base request fields and override individual fields. Route model fields also inherit the base; each override replaces the entire field, including a complete pricing card or serving-tier list. Clear direct-only modes with serving_tiers = [] and reasoning_modes = [] on gateway routes. Provider names must already exist in the catalog.

For an existing model family, add registration.selection with family, dimension, optional before, and a value containing its label and tradeoffs. The dimension must have kind model; the generator supplies model_id. Use registration.supersedes or a route's supersedes to name model rows that this registration replaces. A successor may reuse its predecessor's picker value; generation replaces that cell in place. This also works when the predecessor and its routes were generated by another registration, regardless of ID sort order. An unrelated model cannot take over a picker value. Remove any separately authored replacement pointer so the replacement has one owner.

A successor can also list a predecessor's aliases to move those names to the new model on the same provider. Unlisted aliases keep their original targets; versioned aliases therefore do not move merely because a successor exists.

Unknown registration or request fields, duplicate aliases/routes, and missing picker dimensions fail generation. Runtime artifacts contain no registration metadata. Installed runtimes consume the generated typed catalog.

Reuse measured facts#

Use Models.dev bindings for suitable pinned facts. Retain complete local pricing cards when upstream data cannot express their conditions. Keep measured transport behavior in the registration. Generation reads the edited source rather than the old embedded catalog. Rebuild before exercising a runtime whose embedded catalog changed.