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:
| Strategy | Reads from models[i] | Extra strategy-specific config |
|---|---|---|
priority, fill-first | array order only | — |
weighted | weight (weighted draw) | stickyWeightedLimit |
round-robin | array order (rotation) | concurrencyPerModel, queueTimeoutMs, queueDepth, stickyRoundRobinLimit |
random, strict-random | order ignored (shuffled/deck-drawn) | — |
least-used, cost-optimized, context-optimized, p2c, lkgp, reset-aware | re-sorted by its own metric | reset-aware: resetAwareSessionWeight, resetAwareWeeklyWeight, resetAwareTieBandPercent, resetAwareExhaustionGuardPercent |
context-relay | order + handoff eligibility | handoffThreshold, handoffModel, handoffProviders, maxMessagesForSummary |
auto | scored via 9-factor engine | config.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’smodels[]is expanded inline into the parent’s flat list, in the child’s declared order. The child’s ownstrategyis not honored — it behaves like aprioritylist 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¶
import os
import subprocess as sp
import ipywidgets as widgets
from IPython.display import display, clear_output
from omniroute import OmniRouteTrust the Notebook¶
You have to trust the notebook so that the widget shows up.
Open the Command Palette by pressing
Ctrl + Shift + C(orCmd + Shift + Con macOS), or by navigating to View > Command Palette.Type Trust Notebook into the search bar.
Click or press Enter on the Trust Notebook command.
password_box = widgets.Password(
description="Enter gopass password:",
disabled=False,
style={"description_width": "initial"},
)
output_area = widgets.Output()
def on_value_change(change):
with output_area:
clear_output()
password = change["new"]
if password:
print(f"Password captured! (Length: {len(password)} characters)")
password_box.observe(on_value_change, names="value")
display(password_box, output_area)env = os.environ.copy()
env["GOPASS_AGE_PASSWORD"] = password_box.value
token = sp.run(
["gopass", "show", "-o", "omniroute/token"],
# check=True,
capture_output=True,
text=True,
env=env,
).stdoutomni = OmniRoute(token=token)combos = [
{
"name": "user/llm-flash-free",
"strategy": "auto",
"models": [
"gemini/gemini-3.6-flash",
"gemini/gemini-3.7-flash",
"oc/deepseek-v4-flash-free",
],
},
{
"name": "user/llm-paid-cheap",
"strategy": "auto",
"models": [
"ds/deepseek-v4-flash",
],
},
{
"name": "user/vcs-commit-message",
"strategy": "priority",
"config": {
"nestedComboMode": "execute",
},
"models": [
{
"kind": "combo-ref",
"comboName": "user/llm-flash-free",
},
{
"kind": "combo-ref",
"comboName": "user/llm-paid-cheap",
},
],
},
]omni.add_combos(combos)[{'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}]