ALLM.ModelRef (allm v0.3.0)

Copy Markdown View Source

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!/1Serializer.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

Functions

Build a %ALLM.ModelRef{} from keyword opts.

Types

capabilities()

@type capabilities() :: %{
  optional(:tools) => %{enabled: boolean()},
  optional(:json_native) => boolean(),
  optional(atom()) => term()
}

limits()

@type limits() :: %{
  optional(:context) => pos_integer(),
  optional(:output) => pos_integer(),
  optional(atom()) => term()
}

pricing()

@type pricing() :: %{input: number(), output: number()} | nil

t()

@type t() :: %ALLM.ModelRef{
  capabilities: capabilities() | nil,
  id: String.t() | nil,
  limits: limits() | nil,
  metadata: map(),
  pricing: pricing(),
  provider: atom() | nil
}

Functions

new(opts)

@spec new(keyword()) :: t()

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}