How OmniRoute Auto-Combo Scoring Works: Understanding the I2 (Inverse-Cost & Inverse-Latency) Factors

OmniRoute’s auto-combo engine assigns each provider-model candidate a weighted score from 0 to 1, where the I2 factors (costInv and latencyInv) calculate inverse preferences that boost cheaper and faster providers according to configurable weights.

OmniRoute’s intelligent routing system, implemented in the diegosouzapw/OmniRoute repository, uses a sophisticated auto-combo scoring mechanism to dynamically select optimal AI providers from a candidate pool. The scoring pipeline evaluates providers across 13 distinct factors, with particular emphasis on the I2 factors—inverse-cost and inverse-latency metrics that penalize expensive or slow options. This article explains how these inverse factors are calculated, weighted, and integrated into routing decisions at the source code level.

The Auto-Combo Scoring Pipeline

When a request reaches the auto-combo router, the system gathers viable ProviderCandidate objects and passes them to scorePool() ([scoring.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/services/autoCombo/scoring.ts), lines 41-48). This function orchestrates a three-stage evaluation:

  1. calculateFactors() (lines 20-38) gathers raw telemetry—including quota, health, cost, and latency—and normalizes every metric to the [0..1] range.
  2. calculateScore() (lines 22-41) multiplies each normalized factor by its configurable weight and sums the products, clamping the final result to [0..1].
  3. The router sorts candidates by descending score and selects the top-ranked provider.

The I2 factors are calculated during the factor normalization stage to ensure higher values represent better performance (lower cost and latency).

Breaking Down the I2 Factors

The I2 abbreviation refers to the two inverse metrics in the scoring matrix: Inverse-Cost and Inverse-Latency. These are derived from raw telemetry to ensure the scoring algorithm maximizes preference for cheaper and faster providers.

Inverse-Cost (costInv)

The costInv factor measures preference for cheaper providers using the formula:


1 - candidate.costPer1MTokens / maxCost

Where maxCost is the highest cost per 1M tokens found in the current candidate pool (see line 27 of calculateFactors). The result is passed through clamp01() (lines 24-27 of [scoring.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/services/autoCombo/scoring.ts)) to guarantee a [0..1] output and protect against malformed telemetry. A provider with the lowest cost in the pool receives a costInv score of approximately 1, while the most expensive receives 0.

Inverse-Latency (latencyInv)

Similarly, latencyInv measures preference for faster providers:


1 - candidate.p95LatencyMs / maxLatency

Here, maxLatency is the highest P95 latency in the current pool (see line 28 of calculateFactors). This normalization ensures that providers with the lowest latency (best performance) score near 1, while slower providers approach 0.

Weight Configuration and Normalization

By default, the system assigns specific weights to the I2 factors via DEFAULT_WEIGHTS (lines 43-48):

  • costInv = 0.15
  • latencyInv = 0.12

These weights can be customized per combo via the UI or API. When custom weights are provided, the engine calls normalizeScoringWeights() to ensure the total sum approximates 1, maintaining proportional scoring balance across all 13 factors.

Impact on Routing Decisions

Because the final score is a weighted sum, the I2 factors directly influence provider selection:

  • A cheap and fast provider will have both costInv ≈ 1 and latencyInv ≈ 1, significantly boosting its total score.
  • An expensive or slow provider will have costInv ≈ 0 or latencyInv ≈ 0, dragging down the score even if other factors (quota, health, task-fit) are strong.

The auto-combo engine ([engine.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/services/autoCombo/engine.ts)) orchestrates this flow, while the pipeline router ([pipelineRouter.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/services/autoCombo/pipelineRouter.ts)) handles final selection and fallback logic.

Code Implementation Example

The following TypeScript example demonstrates how to score a candidate pool and inspect I2 contributions:

// 1️⃣ Build a candidate (normally fetched from DB / live telemetry)
const candidate: ProviderCandidate = {
  provider: "openai",
  model: "gpt-4o",
  quotaRemaining: 85,
  circuitBreakerState: "CLOSED",
  costPer1MTokens: 0.028,
  p95LatencyMs: 180,
  // …other optional fields omitted for brevity
};

// 2️⃣ Score a pool of candidates
import { scorePool, DEFAULT_WEIGHTS } from "@omniroute/open-sse/services/autoCombo/scoring";
import { getTaskFitness } from "@omniroute/open-sse/services/autoCombo/taskFitness";

const pool = [candidate /*, …more candidates */];
const scored = scorePool(pool, "chat", DEFAULT_WEIGHTS, getTaskFitness);

// 3️⃣ Inspect the I2 contributions
const { costInv, latencyInv } = scored[0].factors;
console.log(`Inverse-cost: ${costInv.toFixed(2)}, Inverse-latency: ${latencyInv.toFixed(2)}`);

To prioritize latency over cost for a specific combo, override the weights before normalization:

import { normalizeScoringWeights } from "@omniroute/open-sse/services/autoCombo/scoring";

const customWeights = {
  ...DEFAULT_WEIGHTS,
  costInv: 0.08,      // lower weight for cost
  latencyInv: 0.20,   // higher weight for latency
};
const normalized = normalizeScoringWeights(customWeights);

// Use `normalized` when calling `scorePool` for this combo
const scoredForCombo = scorePool(pool, "chat", normalized, getTaskFitness);

Summary

  • OmniRoute’s auto-combo scoring evaluates providers using 13 normalized factors, including the critical I2 inverse metrics.
  • costInv and latencyInv are calculated as 1 - (value/max) to ensure higher scores represent cheaper and faster providers.
  • Default weights are 0.15 for cost and 0.12 for latency, customizable via normalizeScoringWeights().
  • The scoring logic resides in open-sse/services/autoCombo/scoring.ts, with utilities like clamp01 in open-sse/utils/number.ts.
  • Final routing decisions combine these weighted scores with task-fitness and health checks to optimize provider selection.

Frequently Asked Questions

What does I2 stand for in OmniRoute scoring?

I2 stands for the Inverse-Cost and Inverse-Latency factors. These are the second pair of inverse values in the scoring methodology, designed to reward providers with lower costs and lower P95 latency by inverting their raw metrics relative to the pool maximum.

How are the I2 factor weights configured?

The default weights (0.15 for costInv and 0.12 for latencyInv) are defined in DEFAULT_WEIGHTS within scoring.ts. Administrators can override these per combo via the UI or API; the system automatically normalizes custom weight sets using normalizeScoringWeights() to ensure the total sums to approximately 1.

Why does OmniRoute use inverse metrics instead of raw cost and latency?

The scoring system maximizes the final score (higher is better). By calculating inverse values (1 - normalized), cheaper providers receive costInv scores near 1 and expensive providers near 0, allowing the weighted sum to correctly prioritize cost efficiency without requiring inverted logic in the aggregation function.

Which source files handle the I2 factor calculations?

The core logic resides in open-sse/services/autoCombo/scoring.ts, specifically within calculateFactors() (lines 20-38) for metric normalization and calculateScore() (lines 22-41) for weighted aggregation. The clamp01() safety guard is implemented in open-sse/utils/number.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →