# Model-routing config (the real structure, with illustrative values)
# --------------------------------------------------------------------
# Each agent ROLE resolves to a model via an internal alias, and a cost-aware
# router (applied before each call) can swap to a cheaper model for routine work.
# On failure (rate-limit / 5xx / timeout) the request falls through an ordered
# chain so one provider's outage degrades gracefully instead of dropping work.
#
# This file shows the real config STRUCTURE. The model IDs and dollar figures
# are illustrative placeholders: the roster rotates as better options ship,
# and the architecture is model-agnostic on purpose (see README, "Model
# independence"). Swap in your own models; the shape is the point.
#
# Model IDs use the internal "openrouter/<provider>/<model>" prefix the agent
# layer routes on. The fallback chain is shown flat at the router level for
# brevity; the real config defines a per-role primary + ordered chain, and the
# terminals are deliberately DIFFERENT models per role, so no two roles share
# a last resort (see README §3, Reliability and cost). Any role can instead be pinned to a model
# served locally on the box.

router:
  # A cheaper "downgrade" tier handles routine/low-stakes calls when budget
  # pressure warrants it; each primary maps to its own downgrade path:
  downgrade_map:
    openrouter/provider-a/long-context-tool-caller: openrouter/provider-d/fast-cheap-generalist
    openrouter/provider-b/code-specialist-large-ctx: openrouter/provider-d/fast-cheap-generalist
  # Ordered fallback applied on a failed call, after retries on the primary:
  fallback_chain:
    - openrouter/provider-e/cross-vendor-backstop    # different provider than any primary
    - openrouter/provider-f/second-backstop          # distinct terminal per role in the real config
  local_pin:
    # Escape hatch, not a chain tier: any workload can be pinned to a model
    # served on the box itself (no external provider in the path).
    model: local/self-hosted-open-weight
  retries_per_model: 2
  # Two different timeouts, deliberately ordered (ttft < outer):
  ttft_timeout_seconds: 90    # bounds only the FIRST token of each attempt
  outer_timeout_seconds: 240  # backstops a rare mid-stream stall

roles:                       # 4 of 8 shown
  operator:                  # plans + routes work; latency-sensitive, deterministic
    model: openrouter/provider-a/long-context-tool-caller
    temperature: 0.15
    max_output_tokens: 8192

  coder:                     # writes/debugs code
    model: openrouter/provider-b/code-specialist-large-ctx
    temperature: 0.25
    max_output_tokens: 16384

  researcher:                # retrieval-heavy synthesis
    model: openrouter/provider-c/fast-long-context
    temperature: 0.70
    max_output_tokens: 8192

  analyst:                   # structured analysis over data
    model: openrouter/provider-b/large-moe-reasoner
    temperature: 0.40
    max_output_tokens: 16384

budget:
  # Every call is metered before dispatch. The hard ceiling is MONTHLY; the
  # daily check is a rate alarm only and never blocks (interactive work must
  # not stall mid-month).
  monthly_usd_cap: 200            # illustrative; set your own ceiling
  track_per_request: true
  daily_rate_alarm: 3x_trailing_average   # telemetry only; logs an alarm, does NOT block
  # Tiered, complexity-aware degradation as monthly spend climbs:
  thresholds:
    optimize: 0.80    # >=80%: downgrade only trivial tasks
    downgrade: 0.95   # >=95%: downgrade all but essential, high-complexity work
    hard_cap: 1.00    # 100% : block non-essential calls
  # Every interactive role is essential and is never hard-blocked; it degrades
  # to a cheaper model instead. Only the background evaluator is blockable.
  essential_roles: [operator, coder, researcher, analyst, productivity, reviewer, coder_researcher]
  on_exceed:
    essential: degrade        # fall through to progressively cheaper models
    non_essential: block      # background evaluator only
