Unified Config Contract v0.3
Issue: #1505
Before
Before v0.3, the repo had several partially overlapping config contracts:
- router runtime consumed a flat Go config
- Python CLI used its own nested YAML plus merge/default logic
- dashboard and onboarding imported YAML but still assumed legacy top-level
signalsanddecisions - Helm and operator each translated config differently
- DSL mixed routing semantics with legacy
BACKENDandGLOBALexpectations
This caused three persistent problems:
- The same concept had to be edited in multiple schema layers.
- Endpoint, API key, and model semantics were mixed together.
- Runtime defaults depended on external template files such as
router-defaults.yaml, which made defaults harder to reason about and replace.
Problems with the old model
CLI and router drifted
The Python CLI and Go router did not share one schema owner. A user could build config through the CLI, the dashboard, or Kubernetes and still hit structural mismatches.
Model semantics and deployment bindings were entangled
Logical models were carrying:
- semantic routing identity
- endpoint binding
- API key
- provider model ID
That made reuse hard. If several logical models pointed at the same backend, config still repeated backend details.
DSL scope was too broad
DSL was useful for routing semantics, but legacy BACKEND and GLOBAL blocks made it look like the right place to author deployment and runtime state too. That was not sustainable across local, dashboard, and Kubernetes workflows.
v0.3 contract
v0.3 defines one canonical config:
version:
listeners:
providers:
routing:
global:
What each section means
providers: deployment bindings and provider defaultsrouting: semantic routing graphglobal: sparse router-wide runtime overrides
DSL boundary
DSL now owns only:
routing.modelCardsrouting.signalsrouting.decisions
It no longer owns endpoints, API keys, listeners, or router-global runtime settings.
Deployment binding split
Model semantics and deployment bindings are now separated explicitly:
routing.modelCardscarries semantic catalog data such as size, context window, description, and capabilitiesrouting.modelCards[].lorascarries the canonical LoRA adapter catalog for each logical modelproviders.defaultscarries provider-wide defaults such asdefault_model,reasoning_families, anddefault_reasoning_effortproviders.modelscarries per-model access bindings directly- each
providers.models[].backend_refs[]item carries its own transport and auth fields such asendpoint,base_url,protocol,auth_header,auth_prefix,api_key, andapi_key_env routing.decisions[].modelRefs[].lora_nameresolves against the matchingrouting.modelCards[].lorasentry, solora_nameis now part of the supported routing contract instead of a runtime-only escape hatch
Global defaults
Router-global defaults are now owned by the router itself, not by a second user-maintained defaults file.
- the router provides typed built-in defaults
global:only overrides what you need to changeglobal.routergroups router-engine control knobs, includingconfig_sourceglobal.servicesgroups shared APIs and runtime servicesglobal.storesgroups storage-backed servicesglobal.integrationsgroups helper runtime integrationsglobal.model_cataloggroups router-owned model assets underembeddings,system,external, andmodulesglobal.model_catalog.modulesis the home for router-owned module settings such asprompt_compression,prompt_guard,classifier,hallucination_mitigation,feedback_detector, andmodality_detector- omitted fields keep the built-in default
This makes local, dashboard, Helm, and operator behavior converge on the same baseline.