Skip to main content

One identity and one source

Polyglot identifies an offering by the exact pair (driver, wire model). A profile owns the offering’s support status, context and output limits, modalities, capabilities, source, and catalog version. LLMConfig and connection presets deliberately do not duplicate those facts.
find() never guesses by provider or model-family pattern. A missing pair returns a ModelProfile whose status and feature facts are unknown and whose numeric facts are null. Custom and newly released models therefore remain executable without being falsely advertised as supported or unsupported. Reasoning uses the same exact record. capabilities.reasoning declares the supported selection kinds, exact provider effort values, effective effort, mapping quality, budget range, default behavior, and whether reasoning content and token counts are visible. Omitting that object means reasoning support is unknown. A quality value of lossy says the provider value would produce a different effective effort; typed requests reject that mapping by default.

Optional request-scoped resolution

Ordinary inference does not discover, load, or consult a model catalog. Compose the runtime once per service or worker and reuse it across isolated inference objects:
Pass a catalog only when the application wants local capability enforcement, metadata, or facts needed by a selected processor:
With this explicit composition, InferenceRuntime resolves the exact profile after applying a request-level model override. The transient profile travels with the in-process request so local policy, translation, and telemetry describe the model actually executed. It is excluded from request serialization. discover() resolves model directories through the same application, monorepo, and vendor locations as LLM presets. It creates a reusable catalog without reading any model records. find() reads only the exact offering’s YAML file, using the first matching directory. Application config/llm/models takes precedence over packaged records. Overlays replace whole records; omitted facts in a replacement become unknown rather than inheriting packaged values. Discovered catalogs are memoized by application root. discover($projectRoot) selects a project explicitly without changing the global base path; fromPaths($projectModels, $packageModels) creates an independent catalog with explicit ordered roots. Each catalog caches hydrated profiles and missing keys. Reusing it across hundreds of inference objects avoids repeated file reads and hydration. count(), iteration, forDriver(), and toArray() explicitly enumerate records. Profiles are immutable. A catalog instance has no automatic reload; create a new instance to change its configured scope. The shared config reader caches parsed source by path and mtime; tests or development tools rewriting files within one timestamp tick can explicitly call Config::flushSourceCache(). Deploy records with the application’s immutable release directory, not by rebuilding a directory active requests are reading. Runtime performs no network lookup, model-family matching, catalog writes, database query, or background refresh. Before a provider body is rendered, an in-process preflight step reads only facts explicitly attached to that request. It does not make an LLM or network request. Explicitly unsupported streaming, tools, tool choice, JSON Object, and reasoning selections are rejected. Missing or unknown facts make no local support assertion, so custom models remain executable. A semantic change such as JSON Schema to JSON Object requires both an exact supported fallback fact and LLMConfig::$allowLossyFallback === true. The default is false. Approved changes are recorded on the effective request and included in the InferenceRequested event; adapters never silently remove or weaken requested behavior. An adapter can still reject an operation it cannot encode, which is a protocol limitation rather than a catalog capability decision.

Catalog files

A runtime offering is one named YAML record. For example, config/llm/models/openai-compatible/acme-1.yaml:
The path is <encoded-driver>/<encoded-model>.yaml, with each component encoded using rawurlencode(); the special components . and .. have their dots percent-encoded as well. For example, model openai/gpt-oss-120b under driver openrouter is stored as openrouter/openai%2Fgpt-oss-120b.yaml. The profile retains the exact unencoded identity, which must match its path. ModelRecordDirectory::relativePath(new ModelKey($driver, $model)) provides the canonical relative path. Model facts are loaded literally, without environment or secret substitution. schemaVersion must be integer 1; version is a non-empty data revision. Parsers reject unknown fields, invalid status types, malformed nested records, and duplicate effort mappings. Explicit null is permitted for numeric limits and the optional maximum reasoning budget; other present fields must have their declared type. Missing facts remain unknown. A malformed or unreadable selected record raises an error; it does not fall back to a lower-priority record. Every support field is tri-state: supported, unsupported, or unknown. Omitted support and numeric fields are unknown. Catalog serialization omits unknown and null facts. Overall status: supported means only that the exact (driver, wire model) route is intentionally maintained and the named driver is executable. It does not imply that every modality or feature is supported. Each nested fact stands on its own; a sparse supported record is valid. A partially supported distinction that the current schema cannot express stays unknown. For example, Qwen’s model record does not claim generic tool-choice support because some choices depend on whether thinking is enabled, while drivers that cannot render any explicit choice use unsupported. Packaged source values describe how the checked-in record was authored:
  • upstream-reviewed means selected upstream facts were reviewed before being committed;
  • hand-authored means the maintainer owns the exact assertions directly;
  • project records choose their own source label, commonly project.
These labels are evidence categories, not automatic freshness guarantees. Capability evidence is kept deliberately small and tied to a real consumer: Generic file input is intentionally absent. A future document or PDF capability needs a concrete message abstraction, at least one provider renderer, and final wire-payload tests before it can be advertised. The packaged record directory is generated offline from source and override files:
These commands do not fetch provider APIs. Updating upstream facts is an explicit source-review workflow, while application runtime reads only the selected deterministic record. --target names a generated record directory. Build updates its YAML records and removes obsolete generated records; do not store unrelated hand-authored files in that output directory. The source and override JSON catalogs remain offline maintenance inputs. ModelCatalog::fromArray() and fromFile() support explicit bulk data loading for tools and callers supplying in-memory catalogs; runtime discovery never loads a monolithic models.json file. Bulk envelopes accept schemaVersion: 1 (omission also selects schema 1); generated runtime records require it explicitly.

Maintaining the packaged catalog

Edit resources/catalog/models-source.json for reviewed base facts or resources/catalog/models-overrides.json for hand-authored whole-record replacements. Review the source diff directly in Git, then rebuild the deterministic runtime records:
All three commands are offline. Build validates both inputs before changing output, writes readable individual records, and removes obsolete generated records. Check reports stale output without changing it. Validate parses both inputs and verifies the complete generated record set. There is no fetch, staging, apply, mapping, or provenance subsystem; upstream research and review happen outside this package workflow.