Configuration¶
See the example configs; humpyard check-config <file> validates one. Entries:
[providers.<name>]: an OpenAI-compatible endpoint. The API key is read from the environment variable named byapi_key_env; an inline key is rejected. Optionalheaders = { ... }adds static HTTP headers to every call (authentication headers are rejected).[targets.<name>]: a model served by an ordered list of{ provider, model }endpoints. Clients may request a target by name; endpoints are tried in order, failing over on connection errors, timeouts, HTTP 429 and 5xx (not on other 4xx, and not once a stream has begun). Responses carryx-humpyard-targetandx-humpyard-providerheaders.[routes.<name>]: a built-in Switchyard algorithm over targets, requested by clients as themodel:passthrough,random(weights, seed),pool = { strength, min_context_window }in place oftargetsonpassthrough,fastestandrandom(every target with that strength, resolved at load),fastest(eligible targets ordered by observed speed, withexplore,min_samples,max_age_secs,seed; see Fastest routes),stage_router(tool-result signals pick efficient vs capable), orllm_classifier(capabilityjudges task difficulty,escalationlatches to capable after repeated judge verdicts; both need ajudgetarget). Sendx-switchyard-session-idso per-session state (latches, holds) persists across requests. If the selected target fails entirely, the next target the algorithm returned is tried; a route'sfallback_on = ["overflow", ...]limits which failures may do that (default: all exceptunavailable, which is on by default only forfastestand pool routes; see Routing).price_source = "models-dev:<provider id>"on a provider (optional): wherehumpyard pricesreads its prices; serving never uses it. See Keeping prices current.price = { input, output, cached_input, tiers }on an endpoint (USD per million tokens;tiersraise the rates above a prompt size): see Prices and cost.quality = 0.74on a target (optional): how good the model is, 0 to 1, as you state it (a benchmark score you trust; never fetched). A pool'smin_qualitykeeps only targets that declare enough, and afastestroute'squality_weightblends it with speed; see Fastest routes.strengths = ["general", "fast"]on a target (optional): soft labels (what the model is good at) that decide which pool routes include it; they never remove it from a request.class = "private"on a target (optional): a label (private,public,paid, or your own) that decisions andexplainuse to flag a fallback that crosses classes; see Target classes.allowed_classes = ["private"]on a route (optional): the route may only use targets of those classes; the others are removed before routing, so a private route can never reach a public target. Every target the route names must have a class. See Strict routes.[[select]]: ordered rules that choose the route from the key, the requested model, headers (x-humpyard-profile,x-humpyard-tag-<name>, or any named header), agent metadata, and what the request contains (estimated prompt size, tools, images). First match wins; a rule is skipped when the key may not use its route, so clients can narrow but never widen. Responses carryx-humpyard-routeandx-humpyard-rule. See Routing.- Auto routing (one
automodel, pool chosen by intent): a tested recipe in examples/auto-routing.toml and Routing. quota_source = "opencode-go"on a provider (optional), withquota_poll_secs(top level, default 60, 1 to 3600) andquota_stale_secs(default 300, at least the poll interval): the gateway reads that provider's published usage windows while serving; see Provider quota.[metrics](optional, off by default): the Prometheus/metricsendpoint.enabled,listen(a separate address; recommended),bearer_sha256(the credential for serving it on the main listener),key_labelsandkey_label_limit. See Metrics and alerts.decision_retention_days(top level, default 90): how long routing decisions and provider quota samples are kept; see Keys, budgets and the ledger.context_window,toolsandvisionon an endpoint (all optional): what the model can take. A target that cannot serve a request (tools, images or an oversized prompt) is removed before routing, only when that is certain, and a request no target can serve is refused with 400no_capable_target. Undeclared means capable. See Capabilities.[health]: optional endpoint circuit breaker (failure_threshold,cooldown_secs,max_cooldown_secs,max_retry_after_secs: the cap on a cooldown taken from a retry time the error states, default 300) andskip_cold_targets(default true: leave a target whose endpoints are all cooling down out of a request's plan when another target remains, never refusing a request for it);GET /v1/healthshows which endpoints are being skipped. See Endpoint health.-
[overrides]: optional limits for runtime overrides, an operator's expiring instruction to stop using a target or provider (OpenSpec changeadd-runtime-overrides).default_secs(default 14400, four hours) is how long one lasts when its creator names no time, andmax_secs(default 86400, a day; at most 604800) is the longest one may last. Every override expires, anddefault_secsmay not exceedmax_secs. The settings take effect through/v1/overrides; see Runtime overrides. -
[discovery]anddiscoveron a provider (optional): the settings for model discovery (ADR 0022), which keeps free pools current from a generated file; see Keeping pools current, including how to schedule it. It is off until opted in at two levels:[discovery] enabled = true(the master switch, default false, needsfile) and, per provider,discover = { strengths = ["general"] }(it also needsprice_source, which names the models.dev provider;enabled = falseinside it opts that provider out again).[discovery]also hasfile(the generated file, an absolute path;humpyard models syncwrites it andservereads it),class(defaultpublic, applied to every generated target),max_added_per_run(default 10),max_removed_fraction(default 0.5) andexclude(provider/modelids never to generate).serveonly reads that file, at load and on every reload (the reload poll watches it too), and never fetches anything. It is accepted or ignored as a whole: it may contain nothing but[targets.auto-*]on opted-in providers, every endpoint priced at zero, no model a hand-written target already uses, and never a class other than the configured one; otherwise it is ignored with a log line,humpyard check-configsays so, and the gateway serves with the main configuration. Target names starting withauto-are reserved whenfileis set.humpyard_discovery_file_stateandhumpyard_discovery_targetsshow its state.
Logging is controlled by RUST_LOG (default info).
Reloading. Edit the config file and send SIGHUP (or set reload_poll_secs so the gateway
notices by itself, which is how to do it under Kubernetes). The new configuration replaces the old
one atomically; a file that fails to load is rejected and the old one keeps serving, with the
reason in GET /v1/health. Running requests finish on the old configuration. listen, the ledger
path and shutdown_grace_secs need a restart, as does rotating a provider API key. Details in
Deployment.
Shutdown. SIGTERM or Ctrl-C starts a graceful stop: new connections are refused, requests
already running may finish for up to shutdown_grace_secs (default 30; a second signal stops
waiting at once), anything still running is then cancelled (streams end with an error event), and
queued usage entries are written to the ledger before the process exits 0. A kill -9 or crash can
still lose entries that were queued but not yet written. check-config validates a file and reports
dangling provider/target references and missing key variables.
Not supported yet: database-managed keys, an admin API,
previous_response_id (returns 400).