Model Catalogs
Selecting a model needs an exact, provider-visible id. Anything that lets a
person choose one — a picker, a --model flag, a settings page — needs the
catalog behind it: which ids this provider serves, what they are called, and
what each one supports.
That is the same provider edge an agent or a direct call uses, asked a different question:
use everruns::{OpenAI, models};
for model in models::list(OpenAI::from_env()?).await? { println!("{} — {}", model.id(), model.display_name().unwrap_or("?"));}Ids come back exactly as chat calls expect them, newest first, merged with the model profile registry so a provider that returns bare ids still renders human-readable names and descriptions.
list is a provider call: it costs a round trip and catalogs change rarely, so
cache the result instead of asking per keystroke.
What each entry carries
Section titled “What each entry carries”ModelInfo separates the id from everything around it. The id is the
provider’s own; the rest is display and capability metadata, absent when
neither the provider nor the registry knows it.
use everruns::ModelInfo;
fn describe(model: &ModelInfo) -> String { let name = model.display_name().unwrap_or(model.id()); let window = model.context_window().unwrap_or_default(); let tools = if model.supports_tools() { "tools" } else { "no tools" }; format!("{name}: {window} tokens, {tools}")}id— pass back unchanged.display_name,description— for rendering.vendor— who trained the model, which is not always who serves it: an aggregator offers many vendors’ models.context_window,supports_tools,supports_reasoning— the common checks, from the profile registry.profile— the fullModelProfilebehind those: limits, per-million-token prices, modalities, and capability flags.
Capability answers come from curated data, so false also covers “not in the
registry”. Treat them as display hints rather than guarantees.
From a selection to a run
Section titled “From a selection to a run”A selection converts straight back into the Model the rest of the API takes,
bundled with the provider it was discovered through:
use everruns::{Agent, OpenAI, models};
let catalog = models::list(OpenAI::from_env()?).await?;let picked = catalog .into_iter() .find(|model| model.supports_tools()) .ok_or("no tool-calling model")?;
let agent = Agent::builder() .instructions("Be concise.") .model(picked.model()) .build()?;No string handling, and nothing to reconfigure: the model already knows how to be reached.
Providers without a catalog
Section titled “Providers without a catalog”Not every provider can enumerate its models. That is not a failure of the request, so it is a distinct variant rather than an error to log:
use everruns::{Provider, models};
let ids = match models::list(provider).await { Ok(catalog) => catalog.iter().map(|model| model.id().to_string()).collect(), // Keep the application's curated suggestions. Err(models::CatalogError::NoCatalog) => curated, Err(error) => return Err(error.into()),};CatalogError::Call carries the provider failure verbatim, with the full
LlmError classification intact.
Metadata without a provider call
Section titled “Metadata without a provider call”The profile registry is static data, so a model’s identity can be read offline:
use everruns::{DriverId, models};
let profile = models::profile(&DriverId::OpenAI, "gpt-5.6-terra");assert!(profile.is_some());A Model that bundles its provider answers the same question directly with
model.profile(). A bare model id has no profile: nothing says which vendor’s
registry to consult.
Drivers and the vendor behind a provider
Section titled “Drivers and the vendor behind a provider”A provider’s runtime key is the application’s own name for it, so
Provider::new("my-gateway", ...) says nothing about which vendor’s models it
serves. Declare the driver kind when they differ, and profile lookups resolve
against the vendor:
use everruns::{ChatDriver, DriverId, Provider};
let provider = Provider::new("my-gateway", driver) .base_url("https://gateway.example/v1") .with_driver_id(DriverId::OpenAI);Unset, the driver kind falls back to the runtime key, which is the conventional
case (OpenAI::from_env() and every driver crate’s from_env already declare
it). A custom driver joins in by implementing ChatDriver::list_models and
returning DiscoveredModel values; returning None — the default — is how a
driver says it has no catalog. See
Custom providers.
The runnable version of this page is
model_catalog.rs,
which runs offline without an API key.