Chimera — Dynamic Multi-Model Deliberation Gateway
One API call. A team of models. One answer.
Chimera takes your prompt, dispatches it to a hand-picked team of LLMs (each with a custom subtask scoped to their strengths), and an aggregator merges their outputs using dispatcher-written instructions. One dispatcher model call designs the entire deliberation at once.
Quickstart
# Install (choose one)
pip install chimera-deliberation[full] # full CLI + server in a venv
pipx install chimera-deliberation[full] # same, isolated in its own env
pip install chimera-deliberation[server] # API server only
# Configure — the repo ships NO chimera.yaml. Your live config is local-only
# (gitignored, never committed), so a fresh clone has none — create it:
chimera config init # copies chimera.yaml.example → chimera.yaml
# (from a repo checkout: cp chimera.yaml.example chimera.yaml works too)
# Add your API keys (at minimum: DEEPSEEK_API_KEY)
# Run
chimera "What is the capital of France?" # CLI deliberation
chimera run "Compare React and Vue" # explicit `run` subcommand (same as above)
chimera --quiet "Compare React and Vue" # stdout = the raw answer only
chimera --json "Compare React and Vue" # stdout = one JSON object (answer + trace)
chimera --formation simple "Compare …" # pick a formation (an unknown name exits 2)
chimera --version # print the package version
chimera serve # REST API + web UI (see Server & MCP)
chimera-mcp # MCP tools for agents
python -m chimera --version # fallback when the `chimera` script is not on PATH (fresh clone, non-activated venv, bare wheel)
Machine-readable output
--quiet and --json are mutually exclusive group flags — place them
BEFORE the subcommand. Both work for the implicit-prompt form and the
explicit run form, and in both modes stdout carries exactly ONE payload
(diagnostics and logs stay on stderr):
chimera --quiet "Summarize this changelog" # stdout: the answer + "\n"
chimera --quiet run "Summarize this changelog" # same, explicit run form
ANSWER=$(chimera --quiet "Name one HTTP status code for 'not found'")
chimera --json "Compare React and Vue" | jq .answer
chimera --json run "Compare React and Vue" | jq '.trace.total_tokens'
--json writes one object per invocation (span fields abridged here for
readability — the real trace is the complete DeliberationTrace dump):
{
"answer": "merged output from multiple models",
"trace": {
"request_id": "a71b3f2c...",
"formation": "auto",
"source": "auto",
"dispatch": {"stage_id": "dispatch", "kind": "dispatch", "model": "deepseek/deepseek-v4-flash", "latency_ms": 1234},
"stages": [{"stage_id": "worker_1", "kind": "worker", "model": "anthropic/claude-sonnet-4.6", "tokens_input": 300, "tokens_output": 1200}],
"aggregator": {"stage_id": "aggregator", "kind": "aggregator", "model": "deepseek/deepseek-v4-flash"},
"answer_stage_id": "aggregator",
"total_tokens": 12345,
"total_cost": 0.012,
"total_duration_ms": 15234,
"worker_failures": [],
"dispatch_note": null,
"dispatch_repairs": []
}
}
The trace value is the COMPLETE trace serialization (model_dump(mode="json"))
— the same object the REST API returns. Unicode answers are preserved
(ensure_ascii=False), so café stays café.
When the dispatcher had to repair a malformed auto DAG (an edges entry
pointing at a stage missing from stages, or a worker-only DAG with no
aggregator), trace.dispatch_note carries the human-readable summary and
trace.dispatch_repairs carries the same fact in machine-readable form — one
record per repair with kind, action, stage_ids, depends_on and
reason. Clean dispatches and fallbacks serialize [].
Operational warnings are never suppressed: dropped-worker and
dispatch-degradation/repair warnings (which the human mode prints next to the
panel) move to stderr under --quiet / --json, so
chimera --json "..." > out.json cannot be corrupted. Combining both flags is
a usage error (exit code 2); --verbose is ignored in the machine modes.
The flags cover deliberation output — chimera models / chimera formations
tables are unchanged (use GET /v1/models / GET /v1/formations for a
machine-readable catalog).
Open http://localhost:8765/web/ for the web UI with live DAG visualization.
Python:
# If chimera.yaml is missing, create it before calling load_config():
chimera config init # finds the template: local copy, wheel copy, or repo checkout
# Missing template? Copy chimera.yaml.example from a repo checkout or reinstall:
# error: chimera.yaml.example not found — copy the template from a chimera-v2 repo checkout (chimera.yaml.example at the repo root) or reinstall chimera-deliberation to restore the packaged template.
load_config() finds the config in this order: explicit path argument, then the CHIMERA_CONFIG env var, then a walk-up from the current directory.
import asyncio
from chimera import Engine, LiteLLMGateway, load_config
config = load_config()
engine = Engine(config, LiteLLMGateway(config))
result = asyncio.run(engine.deliberate("Explain quantum computing."))
print(result.answer) # merged output from multiple models
OpenAI-compatible:
curl -X POST http://localhost:8765/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "auto", "messages": [{"role": "user", "content": "Hello"}]}'
Configuration
chimera.yaml is found in this order: an explicit path argument, then the
CHIMERA_CONFIG env var, then a walk-up from the current directory.
${VAR} environment-variable substitution
Any string in chimera.yaml may reference an environment variable as
${VAR}. Tokens are substituted at load time from the process
environment, so no API key has to be written into the YAML:
api_keys:
deepseek: "${DEEPSEEK_API_KEY}"
openrouter: "${OPENROUTER_API_KEY}"
A token is resolved from, in order:
- the process environment;
~/.hermes/.env(Hermes' dotenv file), as a fallback for variables that are not in the process environment — so cron/agent sessions resolve keys they never exported.
A variable that resolves nowhere is replaced with the empty string: the config still loads, but every call to that provider fails auth. Substitution is recursive — it applies to nested mappings and list items, not just top-level scalars.
.env auto-load
Before substitution, load_config() loads a .env file sitting next to
chimera.yaml into the process environment. It is best-effort: only
variables that are not already set are populated (the real environment keeps
precedence), and a missing file, a missing python-dotenv, or a malformed
file is tolerated (logged, never fatal). Full key resolution order:
process environment > repo .env (next to chimera.yaml) > ~/.hermes/.env
Environment-variable overrides
Common knobs can be set from the environment instead of editing the YAML (env vars win):
CHIMERA_CONFIG— config file pathCHIMERA_HOST,CHIMERA_PORT— server bind addressCHIMERA_DISPATCHER,CHIMERA_WORKER,CHIMERA_AGGREGATOR— model defaultsCHIMERA_LOG_LEVEL— observability log levelCHIMERA_AUTH_ENABLED,CHIMERA_RATE_LIMIT_ENABLED,CHIMERA_AUTO_ALLOW_ALL_CATALOG—"true"/"1"to enableDEEPSEEK_KEY/DEEPSEEK_API_KEY,OPENROUTER_KEY/OPENROUTER_API_KEY,OPENAI_*,XAI_*,ZAI_*,ANTHROPIC_*,GEMINI_*— populateapi_keys.*for that provider
First-run remedies
| Symptom | The message names | Do this |
|---|---|---|
error: No chimera.yaml found. … (one line, exit 2, no traceback) |
chimera config init |
Run chimera config init — it bootstraps chimera.yaml from the shipped chimera.yaml.example (local copy, repo checkout, or the copy inside the installed wheel, so a bare pip install works). --force overwrites an existing file. |
A provider rejects the call (401, Missing credentials, invalid api key) |
the provider and its own env var, e.g. provider 'deepseek' rejected the credentials or none were found: set DEEPSEEK_API_KEY |
Set that variable (or your providers.<name>.api_key_env) and re-run. |
The env var is resolved from the provider that actually served the model —
providers.<name>.api_key_env if set, else the canonical name in the table
above (google → GEMINI_API_KEY), else <PROVIDER>_API_KEY. LiteLLM's own
error prose is provider-blind (a DeepSeek call is served through the OpenAI
SDK and mentions OPENAI_API_KEY), so Chimera puts its accurate remedy in
front of it on every surface: CLI, REST body, MCP tool result, trace and logs.
Keyless loopback providers (lmstudio, ollama) keep their raw error text —
Chimera never names a key variable for an endpoint that needs none. A custom
base_url provider follows the same rule: a failure from the Hermes gateway
names API_SERVER_KEY, because that is what providers.hermes.api_key_env
declares — not OPENAI_API_KEY, even though the call itself is served over
the OpenAI SDK.
See docs/CONFIG.md for the full chain.
Documentation
Detailed guides live in docs/:
| Guide | Contents |
|---|---|
| docs/CONFIG.md | Full configuration reference (chimera.yaml, categories, model selection) |
| docs/USAGE.md | CLI usage and examples |
| docs/INTEGRATION.md | Integrate Chimera into your app (deployment, clients, auth, errors) |
| docs/OPENAI_API.md | OpenAI-compatible API contract |
| docs/SECURITY.md | Security model and credential handling |
| docs/EDGE_CASES.md | Edge cases and behavior notes |
| docs/FAILURE_RESILIENCE.md | Failure handling and resilience design |
| docs/RESILIENCE.md | Resilience guarantees and degradation semantics |
| docs/LOCAL_CI.md | Run the CI suite locally with act (ruff + tests, same workflow as CI) |
Full docs index, including historical artifacts and dogfood logs: docs/README.md.
Architecture
flowchart TB
subgraph Client
A[User Prompt]
end
subgraph Dispatcher["Dispatcher (1 call)"]
B[Designs DAG<br/>Picks models by category<br/>Writes custom prompts<br/>Writes merge instructions]
end
subgraph Workers["Workers (parallel)"]
C1[Worker A<br/>domain-scoped task]
C2[Worker B<br/>domain-scoped task]
C3[Worker C<br/>domain-scoped task]
end
subgraph Aggregation
D[Aggregator<br/>merges with dispatcher instructions]
end
A --> B
B --> C1
B --> C2
B --> C3
C1 --> D
C2 --> D
C3 --> D
D --> E[Final Answer]
Formation Types
flowchart LR
subgraph Simple["Simple (2 workers)"]
S1[W1] --> SA[Aggregator]
S2[W2] --> SA
end
subgraph Debate["Debate (3 workers + merge)"]
D1[W1] --> DA1[Agg 1]
D2[W2] --> DA1
D2 --> DA2[Agg 2]
D3[W3] --> DA2
DA1 --> DM[Merge]
DA2 --> DM
end
subgraph Custom["Custom DAG (client-defined)"]
C1[Researcher] --> C2[Critic]
C2 --> C3[Polisher]
C3 --> C4[Final]
end
Flow: Request to Answer
sequenceDiagram
participant Client
participant Engine
participant Dispatcher
participant Workers
participant Aggregator
Client->>Engine: POST /v1/chat/completions
Engine->>Dispatcher: Design formation
Dispatcher->>Dispatcher: Pick models by category weights
Dispatcher->>Dispatcher: Write per-worker prompts + merge instructions
Dispatcher-->>Engine: DAG + prompts + instructions
par Workers (parallel)
Engine->>Workers: Worker A (custom prompt)
Engine->>Workers: Worker B (custom prompt)
Engine->>Workers: Worker C (custom prompt)
Workers-->>Engine: responses
end
Engine->>Aggregator: Merge with dispatcher instructions
Aggregator-->>Engine: Final answer
Engine-->>Client: JSON response + trace
Server & MCP
chimera serve needs the [server] extra (fastapi/uvicorn) — it is part
of the [full] extra too. On a bare pip install chimera-deliberation the
command does not crash with a traceback: it exits 2 with the exact remedy
(pip install chimera-deliberation[server], or [full] for the web UI and MCP
extras). chimera serve --help works either way.
chimera serve starts the REST API plus the web UI:
- http://localhost:8765/v1/chat/completions — OpenAI-compatible endpoint
- http://localhost:8765/docs — OpenAPI docs
- http://localhost:8765/web/ — web UI with live DAG visualization
Health probes (all return JSON):
- http://localhost:8765/health — alias of the liveness probe (always 200 when the process is alive)
- http://localhost:8765/v1/health — health check (
healthy/degraded); see below - http://localhost:8765/v1/health/ready — readiness probe (200 when ≥1 provider reachable, 503 otherwise)
- http://localhost:8765/v1/health/live — liveness probe (always 200 when alive)
/v1/health status semantics. healthy means every configured provider's
live probe succeeded. degraded means at least one provider probe failed —
missing credentials, an auth error (401/403), a quota/billing failure
(429, insufficient balance, "no resource package"), an API error, a probe
timeout, or an exception inside the check itself. The endpoint always answers
HTTP 200 and reports the verdict in the status field (plus a details
object with per-provider healthy / error / model_tested), so monitoring
must read status, not the HTTP code. Any probe failure — including "all
providers failed" — reports degraded; an unhealthy status value is not
emitted.
Machine-readable degraded reason (DF-CHIMERA-V2-14). status alone tells
you that something is wrong, not what:
- the top-level
unhealthy_providerslist names the providers whose probe did not succeed, sorted. It is present in every response and is[]exactly whenstatusishealthy, so a client can branch on it instead of walkingdetails.providersand string-matchingerror. - every failed entry under
details.providers.<name>carries anerror_class:missing_credentials(no resolvable key — no live call was made),slow(the probe never answered insideserver.health_timeout_s— see below),timeout(a probe-side timeout: the call itself failed with a timeout),auth(401/403or auth prose),quota(429, insufficient balance, "no resource package"), orapi(anything else). A healthy provider has noerror_class; treat an unrecognized value as opaque. - top-level
slow_providersnames the providers whose probe never answered inside the budget. Such a provider is UNMEASURED, not proven broken, so it does NOT makestatusdegradedand is NOT inunhealthy_providers—slow_providers: ["hermes"]withstatus: "healthy"is the honest reading for a gateway that is alive but slower than the probe budget (DF-CHIMERA-V2-27). A provider that fails a different way (connection refused, 5xx, auth, quota) still degradesstatusexactly as before. - if the probe itself raises, the response is still
degradedwith every configured provider named inunhealthy_providers— none of them was proven healthy — and the underlying reason indetails.error.
A probe is one upstream attempt per model (DF-CHIMERA-V2-16). The health
probe does not use the retry ladder a normal completion uses: it makes
exactly one call per model and reports whatever the provider answers. A
provider that replies with a fast 429/401 is therefore reported as its real
class (quota / auth) carrying the provider's own message — a quota reset
timestamp, say — instead of being retried with backoff until the shared
server.health_timeout_s budget expires and the verdict turns into a
fabricated timeout. Retrying an answer that is already final buys nothing and
destroys the signal; the non-probe retry policy (config.retry) is unchanged.
Both are additive: status, details.config_loaded,
details.models_configured, details.providers_configured,
details.providers_discovered, details.commit,
and the per-provider healthy / error / model_tested / note fields keep
their names and meanings, and the HTTP status stays 200. GET /v1/health/ready carries the same unhealthy_providers list in its 200 body.
Configured vs discovered providers (CH-GAP-053). details.providers_configured
counts only the providers explicitly declared in chimera.yaml — not the
full provider map. Provider auto-discovery (on by default, provider_discovery: true) merges extra providers from models.dev into config.providers at load
time, and providers_configured used to silently include them. The
discovery-added names are now reported separately in the additive
details.providers_discovered list (sorted, names only — no credentials). The
per-provider map under details.providers still lists every provider, both
declared and discovered; a config built programmatically (no YAML load) keeps
the old behavior where everything in the map counts as configured and
providers_discovered is [].
The provider probe costs money. Each /v1/health (and /v1/health/ready)
call runs a real completion per configured provider — gateway.complete(model, "ping", max_tokens=1) — so it consumes a (small) number of tokens on every
provider it can reach, and takes real latency. Details worth knowing before you
poll it in a tight loop:
- providers with no resolvable credentials are reported immediately
(
missing-credentials) with no live call; - on a non-timeout failure up to 3 models of that provider are tried before it is marked unhealthy (one blocked model does not condemn the provider); a timeout is terminal for that provider after one model;
- probes run concurrently, bounded by
server.health_timeout_s(default10.0s); providers still pending at that bound are reported unhealthy with atimeout:error — after one boundedserver.health_probe_grace_swindow (default1.0s, DF-CHIMERA-V2-17) that lets a merely-cold probe land and report its real verdict instead; set it to0for cancel-at-deadline. Slow tail latency is therefore indistinguishable from a dead provider — raiseserver.health_timeout_sif your provider is merely slow. - each of those probes is one upstream attempt per model — the retry ladder
is not used (DF-CHIMERA-V2-16), so a provider that answers with a fast
429/401is reported asquota/authwith the provider's own message (including a quota reset time) rather than retried into the timeout budget.
chimera-mcp runs the MCP server over stdio so AI agents (Hermes, Claude
Code, etc.) can call Chimera directly. Both read the same chimera.yaml
configured in Quickstart.
Live smoke test
/v1/health reporting healthy does not prove a deliberation works (billing,
auth and prompt-format regressions only surface on a real call). Run the live
smoke test after a deploy:
python scripts/smoke_live.py --base-url http://localhost:8765 # default base URL
python scripts/smoke_live.py --base-url http://myhost:8765 --formation auto
CHIMERA_API_KEY=... python scripts/smoke_live.py --base-url http://myhost:8765 # if auth is enabled
The flag is --base-url (there is no --port); CHIMERA_BASE_URL is the env
default. Other flags: --formation (default simple), --prompt,
--api-key, --timeout. The script checks liveness + running commit (warning
when the deployed commit diverges from local HEAD), probes /v1/health, then
POSTs a real /v1/deliberate and prints the merged answer. Exit 0 = merged
answer received, 1 = failure with an actionable message, 2 = usage/config
error. Stdlib-only.
The provider-health block is class-aware (DF-CHIMERA-V2-4), because a fresh install and a real degradation used to print the same words:
providers: <healthy>/<total> healthy— counted from the payload's provider map, never fromproviders_configured(that counts providers explicitly declared in the config, excluding auto-discovery additions — seeproviders_discoveredfor those — and says nothing about whether any of them answered);INFO: providers without a configured API key (expected on a fresh install): …— the providers whoseerror_classismissing_credentials. Expected on a fresh install, so it is information rather than a warning: the deliberation below is the real proof that the deployment works;WARNING: providers reported unhealthy by /v1/health: <name> [<class>] …— every other class (timeout,auth,quota,api,unknown), naming each provider with its class and surfacing the provider's own message (a quota reset time, for example);INFO: providers slower than the probe budget (not a failure — the probe never landed; a real call may still work): <name> [slow]: …— the providers the server classifiedslow(DF-CHIMERA-V2-27), with the measured wait. Information, not a warning: catching a standing slow condition is useful, but it is not a degradation the operator must act on, and the server does not report it as one either;INFO: providers whose live health probe is disabled (health_probe: false): …— the providers explicitly opted out of probing. Nothing was measured, so the line exists to keep the omission visible.
None of these lines changes the exit code: the provider block never fails the run — only the deliberation decides pass/fail.
Model Selection
The dispatcher picks models using category-weighted scoring:
| Category | What it measures |
|---|---|
code |
Programming, debugging, software engineering |
analysis |
Data analysis, research, evaluation |
reasoning |
Logic, math, complex problem-solving |
design |
Creative work, UX, content generation |
audit |
Fact-checking, safety, correctness review |
Each model in the catalog has a percent score (0–100) per category, on the same
scale as the shipped chimera.yaml.example catalog and the categories map in
GET /v1/models. A catalog written on the historical 0.0–1.0 scale is accepted
and rescaled ×100 at load (with a warning); a score outside 0–100 or a
non-numeric score is rejected at load. The dispatcher matches
task domains to model strengths. You can override any model choice per request:
{
"model": "auto",
"messages": [{"role": "user", "content": "..."}],
"dispatcher_model": "deepseek/deepseek-v4-flash",
"aggregator_model": "zai-coding-plan/glm-5.2",
"worker_model": "deepseek/deepseek-v4-pro",
"allowed_models": ["deepseek/deepseek-v4-pro", "zai-coding-plan/glm-5.2"],
"stage_models": {"worker_1": "anthropic/claude-sonnet-4.6"}
}
dispatcher_model— model for the planning/dispatch call itself.aggregator_model— forces every aggregator/merge/audit stage.worker_model— forces every worker stage.stage_models— per-stage overrides; these win over the global ones above.- Config locks (
defaults.lock_aggregator: true/defaults.lock_dispatcher: true) force the configured default for that role and discard the request override. A discarded override is never silent: it is recorded in the trace'sdispatch_note(e.g.override discarded: aggregator_model='X' ignored (lock_aggregator=true)).
Custom DAGs
Send your own formation structure at request time. The example below is the
complete request body for POST /v1/chat/completions with
"model": "custom" — that endpoint is the custom-DAG contract
(POST /v1/deliberate does not accept formation custom):
{
"model": "custom",
"allow_custom_dag": true,
"dag": {
"stages": [
{"id": "researcher", "kind": "worker", "model": "anthropic/claude-sonnet-4.6"},
{"id": "critic", "kind": "aggregator", "model": "zai-coding-plan/glm-5.2", "depends_on": ["researcher"]},
{"id": "polisher", "kind": "worker", "model": "deepseek/deepseek-v4-pro", "depends_on": ["critic"]},
{"id": "final", "kind": "aggregator", "model": "anthropic/claude-sonnet-4.6", "depends_on": ["polisher"]}
],
"edges": [["researcher","critic"], ["critic","polisher"], ["polisher","final"]]
},
"messages": [{"role": "user", "content": "..."}]
}
The dispatcher writes custom prompts for each stage but uses YOUR structure exactly.
Interfaces
| Interface | Endpoint | Use |
|---|---|---|
| REST API | POST /v1/chat/completions |
OpenAI-compatible drop-in |
| REST API | POST /v1/deliberate |
Full control (DAG, overrides, trace) |
| REST API | GET /v1/models |
OpenAI-shaped model list (data) + chimera catalog map |
| REST API | GET /v1/formations |
Available formation presets |
| CLI | chimera run |
Command-line usage (add --quiet / --json for machine-readable output, --version for the package version) |
| MCP | chimera_deliberate |
Run a deliberation from an agent |
| MCP | chimera_formations |
List formation presets |
| MCP | chimera_models |
List models with weights |
Response Trace
Every deliberation returns a full trace:
request_id: a71b3f2c...
formation: auto
source: auto ← not "fallback" — dispatcher designed it
total_tokens: 12345
total_cost: $0.012
total_duration_ms: 15234
dispatch: V4 Flash (1,234ms, 800+420 tok)
workers:
worker_rust: Claude Sonnet 4 (4,500ms, 300+1,200 tok)
worker_go: Kimi K2.7 (8,200ms, 250+800 tok)
aggregator: V4 Flash (2,100ms, 3,000+500 tok)
Providers
Chimera uses LiteLLM under the hood. Supported providers:
| Provider | Direct API | Via OpenRouter | Models |
|---|---|---|---|
| DeepSeek | ✅ | ✅ | v4-flash, v4-pro, r1 |
| Anthropic | — | ✅ | Sonnet 4, Opus 4.7/4.8, Haiku 4.5 |
| OpenAI | — | ✅ | GPT-5.5, GPT-5.1 |
| xAI | — | ✅ | Grok 4.20 |
| — | ✅ | Gemini 3.5 Flash, 3.1 Pro, 2.5 Flash | |
| Z.AI | ✅ (direct) | ✅ | GLM-5.2 |
| MoonshotAI | — | ✅ | Kimi K2.7 Code, K2.6 |
| MiniMax | — | ✅ | M3 |
| Meta | — | ✅ | Llama 4 Maverick |
| Hermes gateway | ✅ (local) | — | any model the running Hermes serves, addressed as hermes/<model> |
| 9router fleet gateway | ✅ (tailnet) | — | ~269 models across 15 upstream prefixes, addressed as router9/<upstream id> |
Custom OpenAI-compatible endpoints
Any provider Chimera does not route natively is called through its own
base_url, over the OpenAI-compatible SDK, using the provider's resolved key.
Exactly one leading <provider>/ segment is stripped before the request goes
out, so hermes/glm-5.3-flash reaches the endpoint as the bare
glm-5.3-flash — the same convention the zai provider uses.
Endpoints that serve namespaced model ids of their own keep the inner
slashes: the catalog id router9/ds/deepseek-v4-flash reaches 9router as
ds/deepseek-v4-flash (not deepseek-v4-flash), and
router9/openrouter/x-ai/grok-4.6 as openrouter/x-ai/grok-4.6.
That is how the local Hermes gateway (:8642, OpenAI-compatible) plugs in
as a first-class provider:
providers:
hermes:
base_url: http://127.0.0.1:8642/v1
api_key_env: API_SERVER_KEY # the var name in ~/.hermes/.env — never inline the key
models:
hermes/glm-5.3-flash:
provider: hermes
cost_tier: budget
Two things to plan for with the Hermes gateway: it adds its own large system
prompt to every request (~41k tokens of input), so the effective context budget
is the model's window minus that; and /v1/health probes providers with a real
completion under server.health_timeout_s (default 10.0s), so raise that
bound if your gateway is slower than that on a 1-token call. (The 9router
gateway adds no prompt of its own — its lanes are the upstream providers'
own endpoints.)
…and the same seam carries the 9router fleet gateway — one
OpenAI-compatible endpoint (http://master001:20128/v1, reachable on the
tailnet as master001) in front of ~269 models across 15 upstream prefixes
(ds/, mmx/, minimax/, kimi/, openrouter/, xai/, ollama/, …):
providers:
router9:
base_url: http://master001:20128/v1
api_key_env: ROUTER9_API_KEY # the var name in ~/.hermes/.env — never inline the key
models:
router9/ds/deepseek-v4-flash:
provider: router9
cost_tier: budget
Each upstream lane is an account of its own, so an exhausted lane answers
429/403 for every model behind it while the gateway itself is healthy —
a provider-side quota condition, not a Chimera defect. Point a formation at
another prefix's model rather than re-dispatching at the same one.
See docs/CONFIG.md for the full contract.
Metadata
Release files for chimera-deliberation 0.2.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chimera_deliberation-0.2.7.tar.gz | 2.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chimera_deliberation-0.2.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.1 MB
Release files / chimera_deliberation-0.2.7.tar.gz
| Download URL | chimera_deliberation-0.2.7.tar.gz |
|---|---|
| Size | 2.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ccf90715cf4bfe84ff58399bfb0f1ba16dc80bb7d8047433f569f4242b143ad
|
|
BLAKE2b-256 checksum How to use checksums |
eb5d67ade9ab9c44a8331a503d1703fe9948a6f2aa4f6298d61136dcdbcce325
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / chimera_deliberation-0.2.7-py3-none-any.whl
| Download URL | chimera_deliberation-0.2.7-py3-none-any.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
607c5988e11e47aaff97ddbc381986d39455057c4c16b3640ae50fef9f8efaa6
|
|
BLAKE2b-256 checksum How to use checksums |
43365d06a734580f38b895ca3f073c518823a1a4475a525103a866c3362196b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|