# How the Auto Combo Routing Strategy Works in OmniRoute

> Discover how OmniRoute's auto combo routing strategy dynamically selects the best LLM provider using a nine-step pipeline including intent classification, self-healing, and budget enforcement.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-25

---

**The auto combo routing strategy in OmniRoute dynamically selects the best LLM provider per request through a nine-step pipeline that combines intent classification, self-healing health checks, tiered scoring, and budget enforcement.**

OmniRoute's `auto` combo is a self-optimizing routing mode designed to eliminate static provider lists. Instead of hard-coded priorities, the engine evaluates candidates at runtime using a multi-factor scoring system. This article breaks down the complete implementation as found in the OmniRoute source code.

## What Is the Auto Combo Routing Strategy?

The **auto combo** is one of OmniRoute's configurable routing strategies. Unlike fixed-provider combos, it builds a **candidate pool** at request time and applies a sophisticated selection pipeline. The strategy lives primarily in [`open-sse/services/autoCombo/engine.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/engine.ts) and draws on supporting modules for scoring, health checks, and task fitness evaluation.

The core goal: deliver cost-aware, latency-optimized, and task-appropriate provider selection without manual tuning.

## The 9-Step Auto Combo Pipeline

### Step 1: Intent Classification

When callers don't specify a concrete `taskType`, the engine infers one from the conversation context. It extracts text from the last user message and runs `classifyPromptIntent` to categorize the request into built-in intents: `code`, `reasoning`, `simple`, or `medium`.

This inferred intent feeds into downstream task-fitness scoring. Implementation spans **lines 30-45** of [`open-sse/services/autoCombo/engine.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/engine.ts).

### Step 2: Mode Pack and Weight Resolution

Combos can specify a **mode pack**—predefined weight profiles like `"coding"` or `"fast"`—that override raw `config.weights`. The engine normalizes final weights through `normalizeScoringWeights`.

This allows operational profiles without per-request configuration. See **lines 48-55**.

### Step 3: Candidate Filtering

The engine applies two filters to build the initial candidate set:

- **Pool restriction**: Only providers in `config.candidatePool` are considered (empty pool = all providers eligible)
- **Health exclusion**: The **Self-Healing Manager** (`getSelfHealingManager`) evaluates each provider; unhealthy providers are excluded and tracked in the `excluded` list

This filtering occurs at **lines 56-68**.

### Step 4: Provider Scoring

Remaining candidates pass through `scorePool`, which combines:

| Factor | Source |
|--------|--------|
| Static attributes | Cost, latency, reliability from provider metadata |
| Task fitness | `getTaskFitness` result using the Step 1 intent to bias toward appropriate model families |

The scoring implementation resides at **lines 77-79**.

### Step 5: Second-Pass Self-Healing

After scoring, the Self-Healing manager re-evaluates providers using their actual scores. Any provider still deemed unhealthy is removed from consideration. This double-check prevents borderline-healthy providers from winning on score alone (**lines 80-88**).

### Step 6: Exploration vs. Exploitation

The engine implements a **configurable exploration rate** (default 5%) via `explorationRate`. A random draw determines whether to:

- **Explore**: Select any candidate randomly to gather performance data on underutilized providers
- **Exploit**: Use deterministic **Score-Tier Rotator** selection

This trade-off between discovery and optimization appears at **lines 96-104 and 98-106**.

### Step 7: Tiered Round-Robin Selection

When exploiting, providers are grouped into three tiers via `groupIntoTiers`:

- **Top tier**: Highest-scoring providers
- **Mid tier**: Moderate performers
- **Rest tier**: Lower-scoring options

If top-tier scores exceed the rest by `CLEAR_WINNER_THRESHOLD`, that tier wins outright. Otherwise, the combo name itself—`"smart"`, `"fast"`, `"cheap"`—drives weighted tier selection through `chooseTierWeighted`.

The tier logic spans **lines 52-75, 78-84, 86-94, and 108-124**.

### Step 8: Budget Cap Enforcement

When `config.budgetCap` is set, the engine:

1. Estimates USD cost for the selected provider
2. If over cap, seeks a cheaper alternative within budget
3. If no compliant candidate exists, applies `config.budgetFallback`:
   - **`"cheapest"`**: Fall back to the cheapest provider (legacy behavior)
   - **`"strict"`**: Throw `BudgetExceededError`, failing the request

Budget handling occupies **lines 108-136 and 139-149**.

### Step 9: Result Construction

The final `SelectionResult` encapsulates:

- Selected provider and model
- Computed score
- Whether exploration was used
- Scoring factors breakdown
- Excluded providers list
- Connection ID (when applicable)

Assembly occurs at **lines 138-147**.

## Configuration and Usage Example

```typescript
import { selectProvider, type AutoComboConfig } from '@/open-sse/services/autoCombo/engine';
import { getProviderCandidates } from '@/open-sse/services/autoCombo/providerRegistryAccessor';

// Example auto-combo configuration
const config: AutoComboConfig = {
  id: 'auto-chat',
  name: 'smart',
  type: 'auto',
  candidatePool: [],                // empty → consider all providers
  weights: { cost: 0.2, latency: 0.3, reliability: 0.5 },
  modePack: 'coding',               // optional – overrides weights
  budgetCap: 0.02,                  // $0.02 per request
  budgetFallback: 'strict',
  explorationRate: 0.05,
  routerStrategy: 'cost',
};

// Pull the current provider candidates from the registry
const candidates = await getProviderCandidates();

// Optional: raw messages for intent classification
const promptMessages = [
  { role: 'user', content: 'Write a function to merge two sorted arrays in JavaScript.' },
];

// Perform the auto selection
try {
  const result = selectProvider(config, candidates, 'default', promptMessages);
  console.log('Selected provider:', result.provider);
  console.log('Model:', result.model);
  console.log('Score:', result.score);
  console.log('Exploration:', result.isExploration);
} catch (e) {
  if (e instanceof BudgetExceededError) {
    console.error('Request exceeds budget cap:', e.message);
  } else {
    throw e;
  }
}

```

## Key Source Files in OmniRoute

| File | Purpose |
|------|---------|
| [`open-sse/services/autoCombo/engine.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/engine.ts) | Core implementation: selection, tier rotation, budget handling, exploration logic |
| [`open-sse/services/autoCombo/scoring.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/scoring.ts) | `scorePool` and weight normalization utilities |
| [`open-sse/services/autoCombo/taskFitness.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/taskFitness.ts) | Task-specific fitness computation |
| [`open-sse/services/autoCombo/selfHealing.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/selfHealing.ts) | Health-checking and provider exclusion |
| [`open-sse/services/autoCombo/modePacks.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/modePacks.ts) | Predefined weight profiles (`coding`, `fast`, etc.) |
| [`open-sse/services/autoCombo/providerRegistryAccessor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/providerRegistryAccessor.ts) | Live provider candidate retrieval |
| [`open-sse/services/autoCombo/__tests__/autoCombo.test.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/__tests__/autoCombo.test.ts) | End-to-end validation suite |

## Summary

- The **auto combo routing strategy** eliminates static provider ordering through runtime evaluation
- **Nine pipeline steps** handle intent detection, health filtering, scoring, exploration/exploitation trade-offs, tiered selection, and budget enforcement
- **Self-healing health checks** run twice to ensure reliability
- **Mode packs** and **combo names** provide operational tuning without code changes
- **Strict budget fallback** enables cost-guaranteed workloads

## Frequently Asked Questions

### How does OmniRoute's auto combo handle provider failures?

The auto combo uses a **Self-Healing Manager** that evaluates provider health during two separate phases. First, it filters out unhealthy providers before scoring. Second, after scoring, it re-checks remaining candidates and removes any that fail health validation. Unhealthy providers are tracked in the `excluded` list returned with the selection result.

### What is the exploration rate in OmniRoute auto routing?

The `explorationRate` parameter (default 5%) controls probabilistic random selection. When triggered, the engine ignores scores and picks any candidate randomly. This gathers performance data on underutilized providers and prevents the system from over-optimizing to stale benchmarks. When not triggered, deterministic tier-based selection takes over.

### Can the auto combo enforce strict budget limits?

Yes. Setting `budgetCap` enables cost estimation before final selection. If the chosen provider exceeds the cap, the engine seeks alternatives. With `budgetFallback: 'strict'`, no valid candidate triggers a `BudgetExceededError`, failing the request rather than violating the budget. Use `'cheapest'` for legacy fallback behavior instead.

### How does OmniRoute determine which model fits a task?

If no `taskType` is provided, the engine runs `classifyPromptIntent` on the last user message to infer intent (`code`, `reasoning`, `simple`, `medium`). This intent feeds `getTaskFitness`, which biases the composite score toward providers with appropriate model families. Mode packs like `"coding"` further tune weight distributions for common workload patterns.