Model Registry¶
The model registry lets transports advertise which models they support and what each model can do. This enables capability-based model selection and cost-aware routing.
ModelSpec¶
A frozen dataclass describing a single model:
from dataclasses import dataclass
from axio.models import Capability
@dataclass(frozen=True, slots=True)
class ModelSpec:
id: str
capabilities: frozenset[Capability] = frozenset()
max_output_tokens: int = 8192
context_window: int = 128000
input_cost: float = 0.0
output_cost: float = 0.0
Capability¶
Models declare their capabilities via a StrEnum:
from enum import StrEnum
class Capability(StrEnum):
# Input modalities
text = "text"
vision = "vision"
video = "video"
audio = "audio"
# Output modalities
image_generation = "image_generation"
video_generation = "video_generation"
# Processing capabilities
reasoning = "reasoning"
tool_use = "tool_use"
json_mode = "json_mode"
structured_outputs = "structured_outputs"
embedding = "embedding"
Capability |
Meaning |
|---|---|
|
Accepts text input |
|
Accepts image input |
|
Accepts video input |
|
Accepts audio input |
|
Can generate images |
|
Can generate videos |
|
Supports extended thinking / chain-of-thought |
|
Supports function / tool calling |
|
Can be forced to output JSON |
|
Supports schema-constrained structured outputs |
|
Can produce embedding vectors |
ModelRegistry¶
A dict-like container for ModelSpec values with powerful query methods:
from axio.models import ModelRegistry, ModelSpec, Capability
registry = ModelRegistry()
registry["gpt-4o"] = ModelSpec(
id="gpt-4o",
capabilities=frozenset({Capability.text, Capability.vision, Capability.tool_use}),
context_window=128000,
input_cost=2.50,
output_cost=10.00,
)
Query methods¶
All query methods return a new ModelRegistry, so they can be chained:
by_prefix(prefix)Filter models whose ID starts with a prefix.
assert "gpt-4o" in registry.by_prefix("gpt-4").ids()
by_capability(*caps)Keep only models that have all specified capabilities.
assert "gpt-4o" in registry.by_capability(Capability.vision, Capability.tool_use).ids()
search(*q)Keep models whose ID contains all query substrings.
assert "gpt-4o" in registry.search("gpt", "4o").ids()
by_cost(*, output=False, desc=False)Sort by input cost (default) or output cost, ascending or descending.
cheapest = registry.by_cost() # cheapest input first priciest = registry.by_cost(desc=True) # most expensive first assert cheapest.ids() == priciest.ids()[::-1]
ids()Return a plain list of model ID strings.
assert registry.by_capability(Capability.vision).ids() == ["gpt-4o"]
Chaining example¶
# Find the cheapest vision-capable model with tool use
model = (
registry
.by_capability(Capability.vision, Capability.tool_use)
.by_cost()
.ids()[0]
)
assert model == "gpt-4o"
Agent capability checking¶
The agent reads Capability.tool_use from the active model before each
iteration. If the model lacks this capability, no tools are passed to the
transport:
model = getattr(transport, "model", None)
model_caps = getattr(model, "capabilities", None)
if model_caps is not None and Capability.tool_use not in model_caps:
active_tools = [] # embedding or image-gen models: no tools
This means you can safely point an Agent at an embedding or image-generation
model - it will not try to send tool definitions that the API would reject.