How the OmniRoute Auto-Combo 12-Factor Scoring Engine Evaluates Candidate Providers

The OmniRoute Auto-Combo engine uses a 12-factor weighted scoring system in open-sse/services/autoCombo/scoring.ts that normalizes infrastructure signals—quota, health, cost, latency, task fit, and more—into a 0-to-1 score to rank LLM provider candidates.

The Auto-Combo engine is OmniRoute's intelligent routing layer that dynamically selects the optimal LLM provider, model, and connection-account combination for each incoming request. Rather than relying on static rules, it computes a quantitative fitness score for every candidate using twelve normalized factors and a configurable weight vector. This article breaks down exactly how factor extraction and weighted aggregation work inside the OmniRoute codebase.


The Two-Stage Scoring Pipeline

Provider evaluation happens in two distinct phases:

  1. Factor extractioncalculateFactors() ingests raw telemetry and metadata from each ProviderCandidate, producing normalized values between 0 and 1.
  2. Weighted aggregationcalculateScore() multiplies each factor by its corresponding weight and sums the results, returning a final score clamped to [0, 1].

The top-scoring candidate wins the request. If multiple candidates tie, secondary sort keys (latency, then randomization) break the deadlock.


The 12 I²-Factors Explained

All twelve factors are normalized by clamp01() to prevent malformed telemetry from poisoning rankings.

Factor Definition Data Source Normalization Formula
quota Remaining quota percentage candidate.quotaRemaining quotaRemaining / 100
health Circuit-breaker health state candidate.circuitBreakerState 1 = CLOSED, 0.5 = HALF_OPEN, 0 = OPEN
costInv Inverse cost (cheaper → higher) candidate.costPer1MTokens vs pool max 1 - (cost / maxCost)
latencyInv Inverse p95 latency (faster → higher) candidate.p95LatencyMs vs pool max 1 - (latency / maxLatency)
taskFit Model-task compatibility getTaskFitness(model, taskType) Static table lookup, clamped 0-1
stability Latency consistency (lower jitter → higher) candidate.latencyStdDev vs pool max 1 - (stdDev / maxStdDev)
tierPriority Account tier + reset window bonus accountTier, quotaResetIntervalSecs calculateTierScore()
tierAffinity Alignment with minimum tier hint manifestHint?.recommendedMinTier calculateTierAffinity()
specificityMatch Match to manifest specificity hint manifestHint?.specificity.score calculateSpecificityMatch()
contextAffinity Session provider reuse preference candidate.contextAffinity Clamped input or default 0.5
cacheAffinity Prompt cache key affinity candidate.cacheAffinity Clamped input or default 0
resetWindowAffinity Preference for soon-resetting quotas candidate.resetWindowAffinity Clamped input or default 0.5
connectionDensity Inverse connection pool saturation candidate.connectionPoolSize ((size - 1) / 10)

The "I²" designation refers to infrastructure-squared: quota and health are the dominant first-order signals that together receive 35% of default weight allocation.


Core Scoring Implementation

Factor Extraction

// open-sse/services/autoCombo/scoring.ts
export interface ScoringFactors {
  quota: number;              // [0, 1]
  health: number;             // [0, 1]
  costInv: number;            // [0, 1]
  latencyInv: number;         // [0, 1]
  taskFit: number;            // [0, 1]
  stability: number;          // [0, 1]
  tierPriority: number;       // [0, 1]
  tierAffinity: number;       // [0, 1]
  specificityMatch: number;   // [0, 1]
  contextAffinity: number;    // [0, 1]
  cacheAffinity: number;      // [0, 1]
  resetWindowAffinity: number; // [0, 1]
  connectionDensity: number;  // [0, 1]
}

calculateFactors() computes each value by comparing the candidate against pool-wide maximums (for cost, latency, stability) or direct metadata lookups (for quota, health, tier).

Weighted Score Calculation

// open-sse/services/autoCombo/scoring.ts
export function calculateScore(
  factors: ScoringFactors,
  weights: ScoringWeights,
): number {
  return clamp01(
    weights.quota * factors.quota +
    weights.health * factors.health +
    weights.costInv * factors.costInv +
    weights.latencyInv * factors.latencyInv +
    weights.taskFit * factors.taskFit +
    weights.stability * factors.stability +
    weights.tierPriority * factors.tierPriority +
    (weights.tierAffinity ?? 0) * factors.tierAffinity +
    (weights.specificityMatch ?? 0) * factors.specificityMatch +
    (weights.contextAffinity ?? 0) * factors.contextAffinity +
    (weights.cacheAffinity ?? 0) * (factors.cacheAffinity ?? 0) +
    (weights.resetWindowAffinity ?? 0) * factors.resetWindowAffinity +
    (weights.connectionDensity ?? 0) * factors.connectionDensity,
  );
}

Optional weights (marked with ?? 0) activate only when the operator configures them or when a RoutingHint supplies manifest-level guidance.


Default Weight Distribution

The DEFAULT_WEIGHTS constant in scoring.ts establishes the following allocation:

Weight Value Purpose
quota 0.15 Prevent quota exhaustion
health 0.20 Avoid unhealthy providers
costInv 0.15 Prefer cheaper options
latencyInv 0.12 Prefer faster options
taskFit 0.10 Match model to task
stability 0.08 Reduce jitter
tierPriority 0.12 Respect account tiers
tierAffinity 0.05 Honor minimum tier hints
specificityMatch 0.03 Honor specificity hints
contextAffinity 0.00 Disabled by default
cacheAffinity 0.00 Disabled by default
resetWindowAffinity 0.00 Disabled by default
connectionDensity 0.00 Disabled by default

Infrastructure factors (quota, health, costInv, latencyInv) collectively control 62% of the score, giving the engine its "I²" character.


Practical Code Examples

Basic Pool Scoring

import { scorePool, DEFAULT_WEIGHTS } from '@/open-sse/services/autoCombo/scoring';
import { getTaskFitness } from '@/open-sse/services/autoCombo/taskFitness';

// Build candidates from provider catalog
const rawCandidates = await virtualFactory.createVirtualAutoCombo();

// Score for "coding" task using defaults
const ranked = scorePool(rawCandidates, 'coding', DEFAULT_WEIGHTS, getTaskFitness);

console.log(`Top provider: ${ranked[0].provider}/${ranked[0].model}`);
console.log(`Score: ${ranked[0].score.toFixed(4)}`);

Custom Weight Override (Cost-Sensitive Mode)

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

const costSensitiveWeights = normalizeScoringWeights({
  quota: 0.20,
  health: 0.15,
  costInv: 0.35,      // Boost cost importance
  latencyInv: 0.10,   // Deprioritize speed
  taskFit: 0.15,
  stability: 0.05,
  tierPriority: 0.10,
});

const ranked = scorePool(rawCandidates, 'coding', costSensitiveWeights, getTaskFitness);

Single Candidate Evaluation (Testing)

import { calculateFactors, calculateScore } from '@/open-sse/services/autoCombo/scoring';

const candidate: ProviderCandidate = {
  provider: 'openai',
  model: 'gpt-4-turbo',
  quotaRemaining: 78,
  circuitBreakerState: 'CLOSED',
  costPer1MTokens: 10.00,
  p95LatencyMs: 450,
  // ... other fields
};

const factors = calculateFactors(candidate, allCandidates, 'coding', getTaskFitness);
const score = calculateScore(factors, DEFAULT_WEIGHTS);

console.log(`Factors:`, factors);
console.log(`Final score: ${score.toFixed(4)}`);

Manifest Hint Integration

import type { RoutingHint } from '@/open-sse/services/autoCombo/manifestAdapter';

const hint: RoutingHint = {
  recommendedMinTier: 'ultra',      // Activates tierAffinity
  specificity: {
    score: 85,                       // Activates specificityMatch
    dimensions: ['reasoning', 'code']
  }
};

const ranked = scorePool(
  rawCandidates,
  'coding',
  DEFAULT_WEIGHTS,
  getTaskFitness,
  hint  // Optional 4th parameter
);

Entry Point and Integration

The scorePool() function is invoked by autoCombo strategy implementation in open-sse/services/autoCombo/index.ts. This is the public API consumed by OmniRoute's routing layer:

// open-sse/services/autoCombo/index.ts (simplified)
export async function autoComboRoute(
  request: LLMRequest,
  context: RoutingContext,
): Promise<ProviderSelection> {
  const candidates = await virtualFactory.createVirtualAutoCombo();
  const taskType = classifyTask(request);
  
  const scored = scorePool(
    candidates,
    taskType,
    context.weights ?? DEFAULT_WEIGHTS,
    getTaskFitness,
    context.manifestHint,
  );
  
  return {
    provider: scored[0].provider,
    model: scored[0].model,
    connectionAccount: scored[0].connectionAccount,
    score: scored[0].score,
  };
}

Key Source Files

File Role
open-sse/services/autoCombo/scoring.ts Core factor extraction, calculateScore(), scorePool(), weight normalization
open-sse/services/autoCombo/index.ts Public entry point integrating with routing layer
open-sse/services/autoCombo/virtualFactory.ts Candidate pool construction via createVirtualAutoCombo()
open-sse/services/autoCombo/taskFitness.ts Static model-task compatibility matrix
open-sse/services/autoCombo/manifestAdapter.ts RoutingHint type definitions and parsing
open-sse/handlers/autoComboCandidates.ts Read-only API decorating candidates with reachability state

Summary

  • The OmniRoute Auto-Combo 12-factor scoring engine evaluates every provider candidate across quota, health, cost, latency, task fit, stability, tier priority, tier affinity, specificity match, context affinity, cache affinity, reset window affinity, and connection density.
  • Factors are normalized to [0, 1] and combined via weighted summation in calculateScore().
  • Default weights prioritize infrastructure signals (62% combined), with quota (15%) and health (20%) as the dominant "I²" factors.
  • The engine supports runtime weight customization and manifest-driven hints for fine-grained routing control.
  • All scoring logic lives in open-sse/services/autoCombo/scoring.ts and is invoked through scorePool() by the routing layer.

Frequently Asked Questions

What does "I²-factor" mean in OmniRoute's scoring engine?

"I²-factor" refers to the two infrastructure-level factors that dominate the default weight distribution: quota availability (15%) and health/circuit-breaker state (20%). Together these "infrastructure-squared" signals control 35% of the scoring decision, ensuring the engine prioritizes providers that are both well-capitalized and operationally healthy before considering cost or latency optimizations.

How can I customize which factors matter most for my workload?

Override the DEFAULT_WEIGHTS by calling normalizeScoringWeights() with a partial weight object. Pass your custom weights as the third argument to scorePool(). The function automatically renormalizes so weights sum to 1.0. For UI-driven changes, store weights in your deployment configuration and inject them via the RoutingContext.

Why are some factors like cacheAffinity and contextAffinity zero by default?

These factors represent optional affinity optimizations that make sense only in specific deployment patterns. cacheAffinity requires prompt-key caching infrastructure; contextAffinity assumes sticky session routing. Keeping them at 0.00 in DEFAULT_WEIGHTS ensures safe, stateless behavior out of the box while allowing operators to opt into affinity-based routing when their architecture supports it.

How does the engine handle missing or malformed candidate telemetry?

All factor calculations pass through clamp01(), which bounds every intermediate result to [0, 1]. For pool-relative factors like costInv and latencyInv, the engine computes maximums across the candidate set, so a single outlier cannot collapse the scale. Missing optional fields (e.g., connectionPoolSize) receive sensible defaults that neutralize their impact on the final score.

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 →