--- title: "OmniRoute Auto-Combo Engine" version: 3.8.2 lastUpdated: 2026-05-13 --- # OmniRoute Auto-Combo Engine > Self-managing model chains with adaptive scoring + zero-config auto-routing ## Zero-Config Auto-Routing (`auto/` prefix) > **NEW:** No combo creation required. Use `auto/` prefix directly in any client. ### Quick Examples | Model ID | Variant | Behavior | | -------------- | ------- | ------------------------------------------------------------------------ | | `auto` | default | All connected providers, LKGP strategy, balanced weights | | `auto/coding` | coding | Quality-first weights, suitable for code generation | | `auto/fast` | fast | Low-latency weighted selection | | `auto/cheap` | cheap | Cost-optimized routing (lowest cost first) | | `auto/offline` | offline | Favors providers with highest quota availability | | `auto/smart` | smart | Quality-first + higher exploration rate (10%) for better model discovery | | `auto/lkgp` | lkgp | Explicit LKGP (same as default `auto`) | **How to use:** ```bash # Any IDE or CLI tool that supports OpenAI format Base URL: http://localhost:20128/v1 API Key: # In your code/config, set model to: model: "auto" # balanced default model: "auto/coding" # best for coding tasks model: "auto/fast" # fastest available model: "auto/cheap" # cheapest per token ``` **What happens:** 1. OmniRoute detects `auto/` prefix in `src/sse/handlers/chat.ts` 2. Queries all **active provider connections** from the database 3. Filters to those with valid credentials (API key or OAuth token) 4. Determines the model per connection (`connection.defaultModel` or provider's first model) 5. Builds a **virtual combo** in-memory (not stored in DB) 6. Routes using the selected variant's weight profile + LKGP strategy **Key properties:** - ✅ **Always-on:** No toggle, no combo creation, no configuration needed - ✅ **Dynamic:** Reflects current connected providers automatically - ✅ **Session stickiness:** LKGP ensures last successful provider is prioritized - ✅ **Multi-account aware:** Each provider connection becomes a separate candidate - ✅ **No DB writes:** Virtual combo exists only for the request, zero persistence overhead **Behind the scenes:** ```txt Request: { model: "auto/coding" } ↓ src/sse/handlers/chat.ts detects prefix ↓ createVirtualAutoCombo('coding') → candidatePool from active connections ↓ handleComboChat (same engine as persisted combos) ↓ Auto-scoring selects best provider/model per request ``` **Implementation files:** | File | Purpose | | --------------------------------------------------------- | ----------------------------------------- | | `open-sse/services/autoCombo/autoPrefix.ts` | Prefix parser (`parseAutoPrefix`) | | `open-sse/services/autoCombo/virtualFactory.ts` | Creates virtual `AutoComboConfig` objects | | `open-sse/services/autoCombo/providerRegistryAccessor.ts` | Test hook for mocking provider registry | | `src/sse/handlers/chat.ts` | Integration: auto prefix short-circuit | | `src/shared/constants/providers.ts` | `SYSTEM_PROVIDERS.auto` system entry | ## How It Works (Persisted Auto-Combos) The Auto-Combo Engine dynamically selects the best provider/model for each request using a **9-factor scoring function** (defined in `open-sse/services/autoCombo/scoring.ts` → `DEFAULT_WEIGHTS`). All weights sum to **1.0**. ![Auto-Combo 9-factor scoring](../diagrams/exported/auto-combo-9factor.svg) > Source: [diagrams/auto-combo-9factor.mmd](../diagrams/auto-combo-9factor.mmd) | Factor | Default Weight | Description | | :----------------- | :------------- | :------------------------------------------------------------------------------------------------- | | `health` | 0.22 | Health score from circuit breaker (CLOSED=1.0, HALF_OPEN=0.5, OPEN=0.0) | | `quota` | 0.17 | Remaining quota / rate-limit headroom [0..1] | | `costInv` | 0.17 | Inverse **blended** cost (60% input + 40% output token price, normalized) — cheaper = higher score | | `latencyInv` | 0.13 | Inverse p95 latency normalized to pool — faster = higher score | | `taskFit` | 0.08 | Task-type fitness (coding, review, planning, analysis, debugging, docs) | | `specificityMatch` | 0.08 | Match between request specificity (manifest hint) and model tier | | `stability` | 0.05 | Variance-based stability (low latency stdDev / error rate) | | `tierPriority` | 0.05 | Account-tier priority — Ultra=1.0, Pro=0.67, Standard=0.33, Free=0.0 | | `tierAffinity` | 0.05 | Affinity between the candidate's tier and the manifest-recommended tier | **Sum:** `0.22 + 0.17 + 0.17 + 0.13 + 0.08 + 0.08 + 0.05 + 0.05 + 0.05 = 1.0` (validated by `validateWeights()`). ## Mode Packs Four pre-defined weight profiles in `open-sse/services/autoCombo/modePacks.ts`. Each pack overrides the default weights to bias selection toward a specific goal. Below are the **full weight tables per pack** (each row sums to 1.0). | Factor | ship-fast | cost-saver | quality-first | offline-friendly | | :----------- | :-------- | :--------- | :------------ | :--------------- | | quota | 0.15 | 0.15 | 0.10 | **0.40** | | health | 0.30 | 0.20 | 0.20 | 0.30 | | costInv | 0.05 | **0.40** | 0.05 | 0.10 | | latencyInv | **0.35** | 0.05 | 0.05 | 0.05 | | taskFit | 0.10 | 0.10 | **0.40** | 0.00 | | stability | 0.00 | 0.05 | 0.15 | 0.10 | | tierPriority | 0.05 | 0.05 | 0.05 | 0.05 | Notes: - `tierAffinity` and `specificityMatch` are not set in mode packs — `calculateScore()` treats them as `?? 0` when absent. - Each pack's emphasis at a glance: - **ship-fast** → latencyInv 0.35 + health 0.30 (low-latency, healthy connections) - **cost-saver** → costInv 0.40 (cheapest tokens win) - **quality-first** → taskFit 0.40 + stability 0.15 (best model for the task, consistent) - **offline-friendly** → quota 0.40 + health 0.30 (max headroom regardless of speed/cost) ## All Routing Strategies OmniRoute's combo engine supports **14 routing strategies** (declared in `src/shared/constants/routingStrategies.ts` → `ROUTING_STRATEGY_VALUES`). The Auto Combo engine itself is exposed under the `auto` strategy; the others are available for persisted combos. | Strategy | Description | | :------------------ | :----------------------------------------------------------------- | | `priority` | First-target ordered list with explicit priority | | `weighted` | Weighted random by per-target weight | | `round-robin` | Cycle through targets in order | | `context-relay` | Hand off context across targets (long conversations) | | `fill-first` | Fill each target's quota before moving to next | | `p2c` | Power-of-2-choices random load balancing | | `random` | Uniform random selection | | `least-used` | Pick target with lowest current load | | `cost-optimized` | Minimize $ per request given catalog pricing | | `reset-aware` ⭐ | Prioritize by quota reset time — short reset windows ranked higher | | `strict-random` | Random without deduplication of repeats | | `auto` | Use Auto Combo scoring (9-factor) — **recommended** | | `lkgp` | Last-Known-Good Path (sticky route to last successful target) | | `context-optimized` | Pick target with best fit for current context size | ⭐ = New in v3.8.0 ## Virtual Auto-Combo Factory The Auto Combo engine doesn't require pre-defined combos. Instead, `open-sse/services/autoCombo/virtualFactory.ts` builds candidates on-the-fly: 1. Pulls `getProviderConnections({ isActive: true })` (all enabled connections) 2. Filters to those with valid credentials (API key or non-expired OAuth token via `hasUsableOAuthToken()`) 3. Cross-references with `getProviderRegistry()` for model availability + pricing 4. For each tuple `(provider, model, connection)`, builds a `VirtualAutoComboCandidate` 5. Picks `connection.defaultModel` (or the registry's first model) as the dispatch target 6. Scores each candidate using the 9-factor `scorePool()` and the variant's weight pack 7. Returns the resulting in-memory `AutoComboConfig` for `handleComboChat()` — never persisted to DB This means **adding a new provider with `auto/*` enabled automatically expands the candidate pool** — no manual combo editing needed. The virtual combo is rebuilt per request, so newly-added or newly-healthy connections are picked up immediately. ## Self-Healing - **Temporary exclusion**: Score < 0.2 → excluded for 5 min (progressive backoff, max 30 min) - **Circuit breaker awareness**: OPEN → auto-excluded; HALF_OPEN → probe requests - **Incident mode**: >50% OPEN → disable exploration, maximize stability - **Cooldown recovery**: After exclusion, first request is a "probe" with reduced timeout ## Bandit Exploration 5% of requests (configurable) are routed to random providers for exploration. Disabled in incident mode. ## API There is **no dedicated `POST /api/combos/auto` endpoint** — Auto-Combo is consumed in two ways: 1. **Zero-config (recommended):** Send any chat completion request with `model: "auto"` or `model: "auto/"`. The virtual factory builds the combo per request — no persistence, no API calls needed. 2. **Persisted combo with `strategy: "auto"`:** Create a regular combo via `POST /api/combos` and set `strategy: "auto"` plus `config.auto.weights` / `config.auto.candidatePool`. The same scoring engine is used; the combo is stored in `combos` and reusable by ID. ```bash # Zero-config usage (no combo creation) curl -X POST http://localhost:20128/v1/chat/completions \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"auto/coding","messages":[{"role":"user","content":"Hello"}]}' # Persisted auto combo via the regular combos endpoint curl -X POST http://localhost:20128/api/combos \ -H "Content-Type: application/json" \ -d '{"id":"my-auto","name":"Auto Coder","strategy":"auto","config":{"auto":{"candidatePool":["anthropic","google","openai"],"weights":{"quota":0.15,"health":0.3,"costInv":0.05,"latencyInv":0.35,"taskFit":0.1,"stability":0,"tierPriority":0.05}}}}' ``` ### Auto router strategies Persisted `strategy: "auto"` combos can set `config.routerStrategy` (or legacy `config.auto.routerStrategy`) to one of: - `rules` — default weighted scoring - `cost` / `eco` — cheapest healthy provider - `latency` / `fast` — lowest p95 latency with reliability penalty - `sla-aware` / `sla` — prefer candidates that satisfy p95 latency, error-rate, and optional cost SLOs - `lkgp` — last known good provider first SLA-aware fields: ```json { "strategy": "auto", "config": { "routerStrategy": "sla-aware", "slaTargetP95Ms": 1500, "slaMaxErrorRate": 0.05, "slaMaxCostPer1MTokens": 5, "slaHardConstraints": true } } ``` ## Task Fitness 30+ models scored across 6 task types (`coding`, `review`, `planning`, `analysis`, `debugging`, `documentation`). Supports wildcard patterns (e.g., `*-coder` → high coding score). ## Auto Variants Recap Including the bare `auto` (default) plus the 6 `AutoVariant` values declared in `autoPrefix.ts`, there are **7 invokable model IDs**: `auto`, `auto/coding`, `auto/fast`, `auto/cheap`, `auto/offline`, `auto/smart`, `auto/lkgp` (`AutoVariant` itself enumerates 6 values; the 7th option is "no variant" — bare `auto` — handled by `parseAutoPrefix()` as `variant: undefined`.) ## How tiers fit Auto-Combo The 9-factor scoring function (`open-sse/services/autoCombo/scoring.ts`) treats tier membership as one signal via the `tierPriority` weight. Default weights (from `DEFAULT_WEIGHTS`): | Factor | Default weight | Notes | | ------------------------ | -------------- | -------------------------------------------------------------- | | Tier priority | 0.05 | Tier 1 premium → higher score | | Latency (p50 inverse) | 0.35 | Fastest wins | | Cost ($/1M inverse) | 0.20 | Cheapest **blended** price wins (60% input + 40% output ratio) | | Recent health/error rate | 0.15 | Unhealthy deprioritized | | Quota remaining | 0.10 | Near-exhausted deprioritized | | Context window match | 0.08 | Penalizes short windows | | Task fitness | 0.10 | Coding → coding-specialist models | | Stability | 0.00 | Disabled by default | Tier alone does **not** force Tier 1 first — if Tier 1 latency is bad or cost-vs-quality is suboptimal, Tier 2 wins. To force tier ordering, use combo strategy `priority` and arrange providers by tier. To strongly favor Tier 1 (subscription), increase `tierPriority` weight: ```json { "strategy": "auto", "config": { "auto": { "weights": { "tierPriority": 0.3, "costInv": 0.05 } } } } ``` See `docs/marketing/TIERS.md` for tier definitions and provider classification. ## Files | File | Purpose | | :-------------------------------------------------------- | :------------------------------------------------------------------------- | | `open-sse/services/autoCombo/scoring.ts` | 9-factor scoring function, `DEFAULT_WEIGHTS`, pool norm | | `open-sse/services/autoCombo/taskFitness.ts` | Model × task fitness lookup | | `open-sse/services/autoCombo/engine.ts` | Selection logic, bandit, budget cap | | `open-sse/services/autoCombo/selfHealing.ts` | Exclusion, probes, incident mode | | `open-sse/services/autoCombo/modePacks.ts` | 4 weight profiles (ship-fast, cost-saver, quality-first, offline-friendly) | | `open-sse/services/autoCombo/autoPrefix.ts` | `auto/` prefix parser + 6 variants | | `open-sse/services/autoCombo/virtualFactory.ts` | Builds in-memory `AutoComboConfig` from live connections | | `open-sse/services/autoCombo/providerRegistryAccessor.ts` | Test hook for mocking provider registry | | `src/shared/constants/routingStrategies.ts` | `ROUTING_STRATEGY_VALUES` (14 strategies) | | `src/sse/handlers/chat.ts` | Integration: auto-prefix short-circuit |