How Provider Executors Work in OmniRoute: Implementation Guide and Custom Use Cases

OmniRoute routes every request through an executor class that inherits from BaseExecutor, and you only need to build a custom executor when a provider diverges from the OpenAI-compatible format in its URL structure, authentication scheme, or request body requirements.

OmniRoute uses a Strategy pattern to abstract provider-specific logic into reusable executor classes. Every request flowing through the system is handled by an executor that knows how to communicate with a specific upstream LLM provider. Understanding how these provider executors are implemented—and when to extend them—is essential for integrating non-standard models or optimizing routing behavior.

The BaseExecutor Architecture

All executors in OmniRoute inherit from the core BaseExecutor class defined in open-sse/executors/base.ts. This base class implements the Strategy pattern and provides the common workflow that every provider follows:

  • buildUrl – Constructs the request endpoint, defaulting to the provider’s static baseUrl or falling back to an OpenAI-compatible endpoint
  • buildHeaders – Assembles request headers, adding Content-Type, provider-specific defaults, and handling extra API-key rotation
  • transformRequest – Normalizes the request body by stripping empty fields, removing unsupported parameters, and applying provider-specific transformations like Claude-Code tool-name cloaking
  • refreshCredentials – Handles token refresh for OAuth or short-lived tokens (default is a no-op)
  • execute – Performs the HTTP call with intra-URL retries, timeout handling, optional fingerprinting, and request signing for Claude-compatible providers

The base class spans lines 36-90 in open-sse/executors/base.ts and encapsulates the generic request lifecycle, allowing specific providers to override only the methods that differ from the standard OpenAI contract.

When to Implement Custom Provider Executors

Custom executors are required only when a provider diverges from the OpenAI-compatible JSON schema. The following scenarios necessitate a custom implementation:

Non-Standard Endpoint URLs

Providers like Google Vertex AI require complex URL paths such as v1/projects/<project>/locations/<region>/publishers/google/models/<model>:generateContent. In open-sse/executors/vertex.ts, the executor overrides buildUrl to assemble the Google-specific path and uses resolveBaseUrl to handle per-connection providerSpecificData.baseUrl.

Azure-Specific Deployment Routing

Azure OpenAI requires URLs containing the deployment name and API version: /openai/deployments/<deployment>/chat/completions?api-version=2023-05-15. The azure-openai.ts executor overrides buildUrl to inject the deployment and modifies buildHeaders to use the api-key header instead of Bearer tokens.

Authentication and Token Refresh

Providers using OAuth or short-lived tokens must override refreshCredentials. The Vertex executor (vertex.ts) calls the Google token endpoint to fetch fresh OAuth access tokens before each request, then adds Authorization: Bearer … headers in buildHeaders.

Request Signing and Body Transformation

Anthropic Claude-Code requires signed request bodies and mandatory billing headers. The claude.ts executor overrides transformRequest for tool-name cloaking and context editing, and extends execute to add billing headers and call signRequestBody.

Groq rejects certain fields like prompt_cache_retention. The groq.ts executor overrides transformRequest to call stripGroqUnsupportedFields, ensuring compatibility without modifying the base class.

Using the DefaultExecutor for OpenAI-Compatible Providers

If a provider already conforms to the OpenAI-compatible JSON schema, use the generic DefaultExecutor located in open-sse/executors/default.ts. This class inherits all behavior from BaseExecutor without any overrides, making it suitable for standard OpenAI-compliant endpoints.

The executor selection logic in getExecutor() (open-sse/executors/index.ts) automatically returns DefaultExecutor for any provider ID not explicitly mapped to a custom class.

Implementing a Custom Executor

Follow these steps to add support for a new provider that requires custom handling.

Creating the Executor Class

Extend BaseExecutor and override only the methods that differ from the OpenAI standard. Here is a minimal example for a fictional "my-cloud" provider:

import { BaseExecutor, ProviderCredentials, ExecuteInput } from '@/open-sse/executors/base.ts';

export class MyCloudExecutor extends BaseExecutor {
  // Custom URL composition for Azure-style endpoints
  buildUrl(model: string, stream: boolean, urlIndex = 0, credentials: ProviderCredentials | null) {
    const base = this.getBaseUrls()[urlIndex];
    return `${base}/v1/models/${model}:predict`;
  }

  // Custom header for my-cloud authentication
  buildHeaders(credentials: ProviderCredentials, stream = true) {
    const { headers } = super.buildHeadersPreamble(credentials, stream);
    if (credentials.apiKey) {
      headers['x-my-token'] = credentials.apiKey;
    }
    return headers;
  }

  // No body transformation needed – payload forwards as-is
}

Registering the Executor

Add your executor to the registry in open-sse/executors/index.ts:

import { MyCloudExecutor } from '@/open-sse/executors/my-cloud.ts';
import { executorRegistry } from '@/open-sse/executors/index.ts';

executorRegistry.set('my-cloud', (config) => new MyCloudExecutor('my-cloud', config));

Selecting Executors at Runtime

Use the getExecutor() factory function to retrieve the appropriate executor for a provider:

import { getExecutor } from '@/open-sse/executors/index.ts';

const executor = getExecutor('my-cloud', {
  baseUrl: 'https://api.mycloud.com/v1',
  timeoutMs: 30_000,
});

await executor.execute({
  model: 'llama-3-70b',
  body: { messages: [{ role: 'user', content: 'Hello' }] },
  stream: true,
  credentials: {
    apiKey: process.env.MYCLOUD_API_KEY,
    providerSpecificData: {},
  },
  signal: abortCtrl.signal,
});

Summary

  • All executors inherit from BaseExecutor (open-sse/executors/base.ts), which implements the Strategy pattern for the request lifecycle
  • Override specific methods (buildUrl, buildHeaders, transformRequest, refreshCredentials) only when the provider diverges from OpenAI standards
  • Use DefaultExecutor for any OpenAI-compatible provider without modification
  • Register custom executors in open-sse/executors/index.ts to make them available to the routing layer via getExecutor()

Frequently Asked Questions

What is the difference between BaseExecutor and DefaultExecutor in OmniRoute?

BaseExecutor is the abstract class in open-sse/executors/base.ts that defines the request lifecycle and common functionality for all providers. DefaultExecutor is a concrete implementation in open-sse/executors/default.ts that inherits from BaseExecutor without adding any overrides, making it suitable for standard OpenAI-compatible providers.

How do I add support for a new LLM provider that uses OAuth2 authentication?

Extend BaseExecutor and override the refreshCredentials method to fetch fresh tokens from the OAuth endpoint, as demonstrated in open-sse/executors/vertex.ts. Then override buildHeaders to inject the Authorization: Bearer … header using the refreshed token.

Can I use the same executor for multiple providers if they are both OpenAI-compatible?

Yes. For any provider that follows the OpenAI JSON schema, you can use DefaultExecutor without modification. The getExecutor() function in open-sse/executors/index.ts automatically falls back to DefaultExecutor for any provider ID not explicitly registered to a custom class.

Where should I register a new custom executor in the codebase?

Register custom executors in open-sse/executors/index.ts by importing the class and adding it to the executorRegistry Map with the provider ID as the key. This allows getExecutor() to instantiate the correct class when routing requests to that provider.

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 →