Skip to content

Model routing

Every pipeline node routes independently to a model of your choice (config/models.yaml; the shipped defaults are DeepSeek slugs, but the host is yours to pick with /provider). Swap any node to any slug your host serves, live and persisted:

/model critic openai/gpt-4o # one node
/model deepseek/deepseek-v4-pro # everything
NodeModelWhy
planner, writer, consolidationDeepSeek V4 ProHighest prose quality; global reasoning across the book
criticDeepSeek V4 ProInsight scoring and thesis checks need real judgment
judgeDeepSeek V4 ProRanks divergent drafts side by side (best-of-N) - route cross-family for an independent eye
verifierDeepSeek V4 ProChecks each cited claim against its source text - cross-family routing helps here too
diagramDeepSeek V4 ProAuthors a structured diagram spec (nodes/edges/labels); layout is done deterministically, not by the model
diagram_fallbackDeepSeek V4 FlashRe-authors the spec if pro returns nothing - a placeholder never ships
toc, summarizer, production, learner, researcher, humanizer, chatDeepSeek V4 FlashThe bulk of calls by volume - fast and cost-efficient

Explicit per node where it matters:

NodeTempWhy
writer0.9Base; divergent first drafts sample 0.7 / 1.0 / 1.2
toc0.4Structured but not rigid
humanizer0.3Surgical line edits - must not get creative
critic, judge, verifier0.2Consistent, repeatable judgment
summarizer0.0Deterministic extraction

config/models.yaml carries a per-node max_tokens: block, read via ModelConfig.max_tokens_for. The shipped config sets it for the reasoning judgment nodes - critic, judge, verifier = 8000 - so the model has room to emit its JSON after it finishes reasoning (the same headroom the diagram node’s 16k budget relies on). It also ships repetition penalties (frequency_penalty: writer 0.3, presence_penalty: writer 0.1) that attack token-level repetition at the source. Add or raise a max_tokens: entry for any node you want to cap or give more room.

With cost_mode: budget (the shipped default), the spend-heavy judgment nodes - critic, judge, verifier, consolidation, and diagram - are rerouted to the global fallback: tier (DeepSeek V4 Flash) for the run, and the drafting knobs are pinned lean (divergent_drafts 1, max_revisions 1, table_read off, max_context_chars 12000). The writer and planner stay on Pro. Set cost_mode: standard to keep every node on its routed model. The run log lists exactly what was pinned. See Cost.

  • Classified retry with exponential backoff + jitter; honors Retry-After; fails fast on 4xx.
  • Structured outputs are validated against Pydantic schemas with a repair retry (the model sees its own invalid output + the error).
  • Truncation recovery: a reasoning model can spend its whole budget thinking and come back empty or cut off mid-JSON (finish_reason=length). Structured calls raise max_tokens and retry the same model instead of degrading - so the call stays on its stronger routed tier rather than falling back to flash.
  • Model fallback: after a node’s primary model exhausts its retries (outage, 5xx, content filter), the call retries once on the global fallback: slug (default DeepSeek V4 Flash) - one node’s failure degrades the run instead of killing an unattended book.
  • Context-overflow recovery: an over-long prompt is shrunk and retried once instead of failing the node.
  • Per-request timeout (request_timeout), and the run-level token kill-switch (max_run_tokens) checks before every call.

Real cost (OpenRouter’s usage.cost) is captured per call, shown live in the run dashboard, summarized on the post-run card, and aggregated in /dashboard. A minimal 2-section article run lands roughly at ~$0.15, ~15 calls, ~84k tokens. A full agentic article (6 sections, divergent drafts, diagrams, claim-checks) measured ~$0.52, ~108 calls, ~606k tokens - agentic mode and deep research multiply the call count; the prompt cache offsets some of it (a measured run served 36% of prompt tokens from cache). See Cost.