# How to Create Custom Routing Strategies in OmniRoute Beyond the 19 Built-In Options

> Learn how to create custom routing strategies in OmniRoute beyond the 19 built-in options. Implement the RouterStrategy interface, register your class, and define custom names effortlessly.

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

---

**You can create custom routing strategies in OmniRoute by implementing the `RouterStrategy` interface in [`open-sse/services/autoCombo/routerStrategy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/routerStrategy.ts), registering your class with `registerStrategy()`, and referencing the custom name in any combo definition—no changes to core request-handling code are required.**

OmniRoute’s auto-combo engine delegates every provider-model decision to a pluggable `RouterStrategy` implementation. While the platform ships with 19 built-in strategies enumerated in [`src/shared/constants/routingStrategies.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/routingStrategies.ts), the TypeScript class hierarchy lets you create custom routing strategies tailored to domain-specific metrics like latency, cost, or custom load-balancing without modifying the core engine.

## How the OmniRoute Strategy Engine Works

The routing pipeline is implemented entirely inside [`open-sse/services/autoCombo/combo.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/combo.ts). When a request arrives, `resolveComboTargets()` resolves a combo’s provider-model targets into an ordered array of `ResolvedComboTarget` objects. Next, `handleComboChat()` calls `getStrategy(combo.strategy)` from [`open-sse/services/autoCombo/routerStrategy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/routerStrategy.ts) to obtain a `RouterStrategy` instance. The strategy’s `pickTargets(targets, comboContext)` method returns the ordered list the engine iterates, calling `handleSingleModel()` for each target until one succeeds or all are exhausted.

Because the engine only interacts with the strategy through the `RouterStrategy` interface, the internals of how targets are ordered remain fully encapsulated. As long as the class implements `pickTargets` and is registered in `strategyRegistry`, the combo engine will delegate to it automatically.

## Implementing a Custom Routing Strategy

### 1. Implement the RouterStrategy Interface

Create a TypeScript class that satisfies the `RouterStrategy` contract. The interface defines a single method: `pickTargets(targets: ResolvedComboTarget[], ctx: ComboContext): ResolvedComboTarget[]`. The `ComboContext` object contains per-target latency, cost, health, and quota maps populated upstream by the combo engine.

```ts
// src/custom/router/MyLatencyAwareStrategy.ts
import type {
  RouterStrategy,
  ResolvedComboTarget,
  ComboContext,
} from '@/open-sse/services/autoCombo/routerStrategy';

export class MyLatencyAwareStrategy implements RouterStrategy {
  pickTargets(
    targets: ResolvedComboTarget[],
    ctx: ComboContext,
  ): ResolvedComboTarget[] {
    return [...targets].sort((a, b) => {
      const latA = ctx.latencyByTarget.get(a.id) ?? Infinity;
      const latB = ctx.latencyByTarget.get(b.id) ?? Infinity;
      if (latA !== latB) return latA - latB;

      const costA = ctx.costByTarget.get(a.id) ?? Infinity;
      const costB = ctx.costByTarget.get(b.id) ?? Infinity;
      return costA - costB;
    });
  }
}

```

This example sorts targets by recorded latency ascending, then by cost ascending, using the runtime data already gathered by the engine.

### 2. Register Your Class with registerStrategy()

Add a registration call early in the server boot sequence, such as in [`src/server/init.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/init.ts) or a dedicated plugin module. The global `strategyRegistry` (a `Map<string, RouterStrategy>`) lives alongside the interface in [`routerStrategy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/routerStrategy.ts).

```ts
// src/server/init.ts
import { registerStrategy } from '@/open-sse/services/autoCombo/routerStrategy';
import { MyLatencyAwareStrategy } from '@/custom/router/MyLatencyAwareStrategy';

registerStrategy('my-latency-aware', new MyLatencyAwareStrategy());

```

The `registerStrategy` function overwrites or adds a mapping in `strategyRegistry`. If the same name is registered twice, a console warning is emitted, as seen around lines 354–362 of [`routerStrategy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/routerStrategy.ts).

### 3. Configure a Combo to Reference the Custom Name

Create or edit a combo via the API, UI, or database. The Zod schema in [`src/shared/validation/schemas/combo.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/validation/schemas/combo.ts) validates the `strategy` field. Because non-built-in names are accepted as custom strategies, you can reference your registered name directly.

```json
{
  "name": "my-fast-combo",
  "strategy": "my-latency-aware",
  "targets": [
    { "providerId": "openai", "modelId": "gpt-4o-mini" },
    { "providerId": "anthropic", "modelId": "claude-3-5-sonnet" },
    { "providerId": "gemini", "modelId": "gemini-1.5-flash" }
  ]
}

```

At runtime, when `handleComboChat()` calls `getStrategy('my-latency-aware')`, the engine resolves the custom implementation from `strategyRegistry` rather than the built-in set.

### 4. Verify Runtime Behavior

After deployment, confirm your strategy is active using the following steps:

1. Run `omniroute combo list` from the CLI and verify the combo shows strategy `my-latency-aware`.
2. Call `POST /api/v1/combo/create` with the payload above and note the returned combo ID.
3. Dispatch a chat request that uses the combo, for example via `POST /api/v1/chat/completions` with the header `x-omniroute-combo: my-fast-combo`.
4. Enable debug-level server logs to observe the ordered target list chosen by your strategy after the `pickTargets` call.

## Full Code Examples for Custom Routing Strategies

### Latency-Aware Failover Strategy

The following file implements a strategy that prefers the target with the lowest recent latency and falls back to cost when latencies are equal.

```ts
// src/custom/router/MyLatencyAwareStrategy.ts
import type {
  RouterStrategy,
  ResolvedComboTarget,
  ComboContext,
} from '@/open-sse/services/autoCombo/routerStrategy';

export class MyLatencyAwareStrategy implements RouterStrategy {
  pickTargets(
    targets: ResolvedComboTarget[],
    ctx: ComboContext,
  ): ResolvedComboTarget[] {
    return [...targets].sort((a, b) => {
      const latA = ctx.latencyByTarget.get(a.id) ?? Infinity;
      const latB = ctx.latencyByTarget.get(b.id) ?? Infinity;
      if (latA !== latB) return latA - latB;

      const costA = ctx.costByTarget.get(a.id) ?? Infinity;
      const costB = ctx.costByTarget.get(b.id) ?? Infinity;
      return costA - costB;
    });
  }
}

```

### Round-Robin Load-Balancing Strategy

This example rotates the target list on every request to distribute load evenly across providers.

```ts
// src/custom/router/MyRoundRobinStrategy.ts
import type {
  RouterStrategy,
  ResolvedComboTarget,
  ComboContext,
} from '@/open-sse/services/autoCombo/routerStrategy';

export class MyRoundRobinStrategy implements RouterStrategy {
  private cursor = 0;

  pickTargets(targets: ResolvedComboTarget[], _ctx: ComboContext) {
    if (targets.length === 0) return [];
    const start = this.cursor % targets.length;
    this.cursor = (this.cursor + 1) % targets.length;
    return [
      ...targets.slice(start),
      ...targets.slice(0, start),
    ];
  }
}

```

Register it exactly like the latency-aware variant:

```ts
// src/server/init.ts
import { registerStrategy } from '@/open-sse/services/autoCombo/routerStrategy';
import { MyRoundRobinStrategy } from '@/custom/router/MyRoundRobinStrategy';

registerStrategy('my-rr', new MyRoundRobinStrategy());

```

Then reference `"strategy": "my-rr"` in any combo definition to dispatch requests using your round-robin logic.

## Key Source Files for Custom Routing Strategies

Study the following files to understand the exact contract and extension points:

- **[`src/shared/constants/routingStrategies.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/routingStrategies.ts)** – Canonical list of built-in identifiers in `ROUTING_STRATEGY_VALUES`.
- **[`open-sse/services/autoCombo/routerStrategy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/routerStrategy.ts)** – Definition of `RouterStrategy`, built-in implementations, `strategyRegistry`, and the `getStrategy` / `registerStrategy` helpers.
- **[`open-sse/services/autoCombo/combo.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/combo.ts)** – Core combo engine containing `resolveComboTargets`, `handleComboChat`, and the retry loop over ordered targets.
- **[`src/shared/validation/schemas/combo.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/validation/schemas/combo.ts)** – Zod schema that permits custom strategy names in combo payloads.
- **[`docs/routing/AUTO-COMBO.md`](https://github.com/diegosouzapw/OmniRoute/blob/main/docs/routing/AUTO-COMBO.md)** – User-facing documentation with additional strategy skeletons.

## Summary

- OmniRoute routing is **pluggable** through a single-method `RouterStrategy` interface.
- You create custom routing strategies by implementing `pickTargets()` in a new class and registering it via `registerStrategy()` before the server accepts traffic.
- The combo engine in [`open-sse/services/autoCombo/combo.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/combo.ts) delegates ordering to your implementation at runtime without core code changes.
- Custom strategy names are accepted by the combo validation schema and resolved through `strategyRegistry`, giving you full control over latency, cost, health, or custom load-balancing logic beyond the 19 built-in options.

## Frequently Asked Questions

### Do I need to modify OmniRoute core files to add a custom routing strategy?

No. The router is a pure TypeScript class hierarchy, so you implement the `RouterStrategy` interface in your own file and register it with the global `strategyRegistry` using `registerStrategy()`. The combo engine automatically picks up the custom implementation at runtime.

### What runtime data is available inside the `pickTargets` method?

The `pickTargets` method receives the full array of `ResolvedComboTarget` objects plus a `ComboContext` parameter that contains per-target latency, cost, health, and quota maps populated upstream by the combo engine. You can inspect fields like `ctx.latencyByTarget` and `ctx.costByTarget` to build your ordering logic.

### Can a custom strategy override one of the 19 built-in OmniRoute strategies?

Yes. Calling `registerStrategy(name, impl)` overwrites any existing mapping in `strategyRegistry` if the same name is supplied. The framework emits a console warning when this happens, as implemented around lines 354–362 of [`open-sse/services/autoCombo/routerStrategy.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/routerStrategy.ts).

### Where should I call `registerStrategy()` so it loads before requests arrive?

Call it early in the server boot sequence, such as in [`src/server/init.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/init.ts) or a dedicated plugin file that runs at startup. The registration must complete before the first combo request reaches `handleComboChat()` in [`open-sse/services/autoCombo/combo.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/services/autoCombo/combo.ts).