Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Customize Routing Strategies in OmniRoute

Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!

Built-in Routing Strategies

OmniRoute’s combo engine ships 19 built-in routing strategies (see 🎯 Combos — The Flagship) . OmniRoute also provides zero-config auto routing combos . More docs on routing strategies can be found at OmniRoute Docs - Routing .

Define Customized Routing Strategies

In OmniRoute, customized routing strategies are configured through Combos (chains of upstream providers/models with specific rules) or by registering a custom JavaScript/TypeScript strategy implementation. We focus on defining customized routing strategies through Combos in this article.

The Combo schema

Every combo — whatever built-in strategy it uses — shares one unified top-level schema:

{ name: string; strategy: RoutingStrategy; models: ComboStep[]; config?: {...} }

models[] is an array of ComboSteps (src/lib/combos/steps.ts) — the same union is used by every strategy, and a plain "provider/model" string is also accepted as shorthand (it auto-normalizes to the kind: "model" shape):

type ComboStep =
  | { kind: "model"; model; providerId?; connectionId?; allowedConnectionIds?; weight; label?; tags?;
      prompt?; fallbackOnlyOnQuotaExhaustion? }
  | { kind: "combo-ref"; comboName; weight; label?; fallbackOnlyOnQuotaExhaustion? }
  | { kind: "provider-wildcard"; providerId; modelPattern; connectionId?; allowedConnectionIds?; weight; label? }

What differs between strategies is only which of these fields they actually read, and which extra strategy-specific keys live under config:

StrategyReads from models[i]Extra strategy-specific config
priority, fill-firstarray order only—
weightedweight (weighted draw)stickyWeightedLimit
round-robinarray order (rotation)concurrencyPerModel, queueTimeoutMs, queueDepth, stickyRoundRobinLimit
random, strict-randomorder ignored (shuffled/deck-drawn)—
least-used, cost-optimized, context-optimized, p2c, lkgp, reset-awarere-sorted by its own metricreset-aware: resetAwareSessionWeight, resetAwareWeeklyWeight, resetAwareTieBandPercent, resetAwareExhaustionGuardPercent
context-relayorder + handoff eligibilityhandoffThreshold, handoffModel, handoffProviders, maxMessagesForSummary
autoscored via 9-factor engineconfig.auto.{weights,explorationRate,candidatePool,routerStrategy,modePack,budgetCap,...}

(reset-window, headroom, cache-optimized, fusion, pipeline are the remaining strategies from the full list in the previous section and aren’t detailed here.)


Nesting: a models[] entry can itself be another Combo

A models[] entry isn’t limited to a single provider/model — it can also be {"kind": "combo-ref", "comboName": "..."}, referencing another, already-created combo by name. This is the entire nesting mechanism: there’s no inline/anonymous {strategy, models} sub-object, and no nodes/targets key — comboName is always a lookup against existing combos, so the inner combo has to be created first, then referenced.

config.nestedComboMode (default "flatten") controls what happens to that reference:

  • "flatten" — the referenced combo’s models[] is expanded inline into the parent’s flat list, in the child’s declared order. The child’s own strategy is not honored — it behaves like a priority list once flattened.

  • "execute" — the combo-ref becomes a black-box unit: the parent strategy picks it as one opaque target, and if selected, the child combo actually runs its own strategy, retries, and sticky state (open-sse/services/combo/runtimeUnits.ts::executeRuntimeUnitCombo). This is what genuinely enables hierarchical/nested strategies. Only honored when the parent’s strategy is one of: priority, round-robin, random, strict-random, weighted, fill-first — scoring strategies (cost-optimized, context-optimized, reset-aware, auto) and legacy ones (lkgp, p2c, least-used) still flatten.

Safety rails: maxComboDepth (default 3, hard cap 10), cycle detection (validateComboDAG, visitedComboNames; diamond graphs allowed, back-references rejected), and a shared attemptBudget across the whole tree so nested retries can’t blow up request cost.

For example, a combo that performs weighted load balancing between two primary providers, and if both fail, falls back via priority to a cheap local model — first create the inner combo, then reference it from the outer one:

// 1. create the inner combo first
{
  "name": "weighted-primary-pool",
  "strategy": "weighted",
  "models": [
    { "kind": "model", "model": "anthropic/claude-3-5-sonnet", "weight": 70 },
    { "kind": "model", "model": "openai/gpt-4o", "weight": 30 }
  ]
}
// 2. reference it from the outer combo, with execute mode so it runs its own strategy
{
  "name": "my-resilient-route",
  "strategy": "priority",
  "config": { "nestedComboMode": "execute" },
  "models": [
    { "kind": "combo-ref", "comboName": "weighted-primary-pool" },
    "groq/llama-3.3-70b"
  ]
}

Add/Update Customized Combo Route Via the REST API

Trust the Notebook

You have to trust the notebook so that the widget shows up.

  1. Open the Command Palette by pressing Ctrl + Shift + C (or Cmd + Shift + C on macOS), or by navigating to View > Command Palette.

  2. Type Trust Notebook into the search bar.

  3. Click or press Enter on the Trust Notebook command.

Loading...
Loading...
[{'name': 'user/llm-flash-free', 'models': [{'id': 'user-llm-flash-free-model-1-gemini-gemini-3-6-flash', 'kind': 'model', 'model': 'gemini/gemini-3.6-flash', 'providerId': 'gemini', 'weight': 0}, {'id': 'user-llm-flash-free-model-2-gemini-gemini-3-7-flash', 'kind': 'model', 'model': 'gemini/gemini-3.7-flash', 'providerId': 'gemini', 'weight': 0}, {'id': 'user-llm-flash-free-model-3-oc-deepseek-v4-flash-free', 'kind': 'model', 'model': 'oc/deepseek-v4-flash-free', 'providerId': 'oc', 'weight': 0}], 'strategy': 'auto', 'id': 'c7aea490-6b27-4dc1-9d23-70674c776c9a', 'config': {}, 'isHidden': False, 'sortOrder': 5, 'createdAt': '2026-08-28T06:54:33.749Z', 'updatedAt': '2026-08-28T06:54:33.749Z', 'version': 2}, {'name': 'user/llm-paid-cheap', 'models': [{'id': 'user-llm-paid-cheap-model-1-ds-deepseek-v4-flash', 'kind': 'model', 'model': 'ds/deepseek-v4-flash', 'providerId': 'ds', 'weight': 0}], 'strategy': 'auto', 'id': '8970a814-36db-41a2-b964-cfd31c341cb1', 'config': {}, 'isHidden': False, 'sortOrder': 6, 'createdAt': '2026-08-28T06:54:33.757Z', 'updatedAt': '2026-08-28T06:54:33.757Z', 'version': 2}, {'name': 'user/vcs-commit-message', 'models': [{'id': 'user-vcs-commit-message-ref-1-user-llm-flash-free', 'kind': 'combo-ref', 'comboName': 'user/llm-flash-free', 'weight': 0}, {'id': 'user-vcs-commit-message-ref-2-user-llm-paid-cheap', 'kind': 'combo-ref', 'comboName': 'user/llm-paid-cheap', 'weight': 0}], 'strategy': 'priority', 'config': {'nestedComboMode': 'execute'}, 'id': '10fe0652-831b-4291-a96b-31f44af37942', 'isHidden': False, 'sortOrder': 7, 'createdAt': '2026-08-28T06:54:33.767Z', 'updatedAt': '2026-08-28T06:54:33.767Z', 'version': 2}]