Layer A — model reference returned by the optional LLMDB catalog.
See spec §6.3 (lines 637-648). Carries the catalog's view of a single model:
provider, id, capability flags (tools, json_native, vision), context/output
limits, per-million-token pricing, and an opaque :metadata bag.
A %ModelRef{} is plain serializable data — no PIDs, refs, funs, or API
keys. It round-trips through :erlang.term_to_binary/1 and JSON
(ALLM.Serializer.to_json!/1) just like every other Layer A struct.
Construction is via new/1. Hydration from a JSON-decoded map is via
__from_tagged__/1 (called by ALLM.Serializer.hydrate/1); the module is
registered in ALLM.Serializer.@known_modules so tagged JSON
(%{"__type__" => "ALLM.ModelRef", "data" => ...}) decodes automatically.
Layer A nested-map JSON asymmetry (carve-out)
:capabilities, :limits, :pricing, and :metadata are opaque map
bags whose nested keys are not atomized on JSON rehydration. ETF
round-trip is byte-identical, but Jason.encode!/1 →
Serializer.from_json/1 produces:
iex> ref = ALLM.ModelRef.new(
...> provider: :openai, id: "x",
...> capabilities: %{tools: %{enabled: false}}
...> )
iex> {:ok, hydrated} = ALLM.Serializer.from_json(ALLM.Serializer.to_json!(ref))
iex> hydrated.capabilities
%{"tools" => %{"enabled" => false}}This matches the Phase 1 Engine.metadata precedent — opaque
caller-owned maps don't get atom-key restoration because the closed-set
of valid keys can't be bounded at hydration time. Consumers handle
this asymmetry directly: ALLM.Capability.preflight/2 and
ALLM.Capability.populate_costs/2 pattern-match on both atom-keyed
and string-keyed shapes, so a JSON-rehydrated %ModelRef{} pre-flights
and prices identically to an in-process one. A naive consumer that
pattern-matches only on atom-keyed shapes will silently bypass the
rehydrated ref — see ALLM.Capability @moduledoc.
Examples
iex> ref = ALLM.ModelRef.new(provider: :openai, id: "gpt-4.1-mini")
iex> ref.provider
:openai
iex> ref.id
"gpt-4.1-mini"
iex> ref.metadata
%{}
Summary
Types
@type limits() :: %{ optional(:context) => pos_integer(), optional(:output) => pos_integer(), optional(atom()) => term() }
Functions
Build a %ALLM.ModelRef{} from keyword opts.
Accepts any subset of the documented struct fields; unknown keys raise
KeyError via struct!/2 (Phase 1 convention).
Examples
iex> ref = ALLM.ModelRef.new(
...> provider: :openai,
...> id: "gpt-4.1-mini",
...> capabilities: %{tools: %{enabled: true}, json_native: true},
...> pricing: %{input: 0.15, output: 0.6}
...> )
iex> ref.pricing
%{input: 0.15, output: 0.6}