| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| export const OMNIROUTE_PROVIDER_KEY = "omniroute" as const;
|
| export const OMNIROUTE_PROVIDER_NPM = "@ai-sdk/openai-compatible" as const;
|
| export const OPENCODE_CONFIG_SCHEMA = "https://opencode.ai/config.json" as const;
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export const OMNIROUTE_DEFAULT_OPENCODE_MODELS = [
|
| "cc/claude-opus-4-8",
|
| "cc/claude-opus-4-7",
|
| "cc/claude-sonnet-4-6",
|
| "cc/claude-haiku-4-5-20251001",
|
| "claude-opus-4-5-thinking",
|
| "claude-sonnet-4-5-thinking",
|
| "gemini-3.1-pro-high",
|
| "gemini-3-flash",
|
| ] as const;
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export interface ModelCapabilities {
|
|
|
| label?: string;
|
|
|
| attachment?: boolean;
|
|
|
| reasoning?: boolean;
|
|
|
| temperature?: boolean;
|
|
|
| tool_call?: boolean;
|
| }
|
|
|
| |
| |
| |
|
|
| export const OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS: Record<string, number> = {
|
| "cc/claude-opus-4-8": 1_000_000,
|
| "cc/claude-opus-4-7": 1_000_000,
|
| "cc/claude-sonnet-4-6": 200_000,
|
| "cc/claude-haiku-4-5-20251001": 200_000,
|
| "claude-opus-4-5-thinking": 200_000,
|
| "claude-sonnet-4-5-thinking": 200_000,
|
| "gemini-3.1-pro-high": 1_000_000,
|
| "gemini-3-flash": 1_000_000,
|
| };
|
|
|
| |
| |
| |
| |
| |
| |
|
|
| export const OMNIROUTE_DEFAULT_MODEL_CAPABILITIES: Record<string, ModelCapabilities> = {
|
| "cc/claude-opus-4-8": { attachment: true, reasoning: true, temperature: true, tool_call: true },
|
| "cc/claude-opus-4-7": { attachment: true, reasoning: true, temperature: true, tool_call: true },
|
| "cc/claude-sonnet-4-6": { attachment: true, reasoning: true, temperature: true, tool_call: true },
|
| "cc/claude-haiku-4-5-20251001": { attachment: true, temperature: true, tool_call: true },
|
| "claude-opus-4-5-thinking": {
|
| attachment: true,
|
| reasoning: true,
|
| temperature: true,
|
| tool_call: true,
|
| },
|
| "claude-sonnet-4-5-thinking": {
|
| attachment: true,
|
| reasoning: true,
|
| temperature: true,
|
| tool_call: true,
|
| },
|
| "gemini-3.1-pro-high": { attachment: true, reasoning: true, temperature: true, tool_call: true },
|
| "gemini-3-flash": { attachment: true, temperature: true, tool_call: true },
|
| };
|
|
|
| export interface OmniRouteProviderOptions {
|
|
|
| baseURL: string;
|
|
|
| apiKey: string;
|
|
|
| displayName?: string;
|
|
|
| models?: readonly (string | { id: string; contextLength?: number })[];
|
|
|
| modelLabels?: Record<string, string>;
|
| |
| |
| |
| |
|
|
| modelCapabilities?: Record<string, ModelCapabilities>;
|
| |
| |
| |
| |
|
|
| modelContextLengths?: Record<string, string | number>;
|
| |
| |
| |
|
|
| model?: string;
|
| |
| |
| |
|
|
| smallModel?: string;
|
| }
|
|
|
|
|
| export interface OpenCodeModelEntry {
|
| name: string;
|
| attachment?: boolean;
|
| reasoning?: boolean;
|
| temperature?: boolean;
|
| tool_call?: boolean;
|
| |
| |
| |
| |
|
|
| limit?: {
|
|
|
| context: number;
|
|
|
| input?: number;
|
|
|
| output?: number;
|
| };
|
| }
|
|
|
| export interface OpenCodeProviderEntry {
|
|
|
| npm: typeof OMNIROUTE_PROVIDER_NPM;
|
|
|
| name: string;
|
|
|
| options: {
|
| baseURL: string;
|
| apiKey: string;
|
| };
|
|
|
| models: Record<string, OpenCodeModelEntry>;
|
| }
|
|
|
| export interface OpenCodeConfigDocument {
|
| $schema: typeof OPENCODE_CONFIG_SCHEMA;
|
|
|
| model?: string;
|
|
|
| small_model?: string;
|
| provider: {
|
| [OMNIROUTE_PROVIDER_KEY]: OpenCodeProviderEntry;
|
| };
|
| }
|
|
|
| function requireNonEmpty(value: unknown, field: string): string {
|
| if (typeof value !== "string") {
|
| throw new TypeError(`@omniroute/opencode-provider: ${field} must be a string`);
|
| }
|
| const trimmed = value.trim();
|
| if (!trimmed) {
|
| throw new Error(`@omniroute/opencode-provider: ${field} is required and cannot be empty`);
|
| }
|
| return trimmed;
|
| }
|
|
|
| |
| |
| |
|
|
| export function normalizeBaseURL(rawBaseURL: string): string {
|
| const trimmed = requireNonEmpty(rawBaseURL, "baseURL");
|
| try {
|
| new URL(trimmed);
|
| } catch {
|
| throw new Error(
|
| `@omniroute/opencode-provider: baseURL is not a valid URL: ${JSON.stringify(rawBaseURL)}`
|
| );
|
| }
|
| let base = trimmed;
|
| let end = base.length;
|
| while (end > 0 && base[end - 1] === "/") end--;
|
| base = end < base.length ? base.slice(0, end) : base;
|
| if (base.endsWith("/v1")) base = base.slice(0, -3);
|
| return base + "/v1";
|
| }
|
|
|
| |
| |
| |
|
|
| export function createOmniRouteProvider(options: OmniRouteProviderOptions): OpenCodeProviderEntry {
|
| const baseURL = normalizeBaseURL(options.baseURL);
|
| const apiKey = requireNonEmpty(options.apiKey, "apiKey");
|
|
|
| const modelList =
|
| options.models && options.models.length > 0
|
| ? [...options.models]
|
| : [...OMNIROUTE_DEFAULT_OPENCODE_MODELS];
|
|
|
| const labels = options.modelLabels ?? {};
|
| const overrides = options.modelCapabilities ?? {};
|
| const models: Record<string, OpenCodeModelEntry> = {};
|
| const seen = new Set<string>();
|
| for (const raw of modelList) {
|
| const id =
|
| typeof raw === "object" && raw !== null && "id" in raw && typeof (raw as any).id === "string"
|
| ? (raw as { id: string }).id.trim()
|
| : typeof raw === "string"
|
| ? raw.trim()
|
| : "";
|
| if (!id || seen.has(id)) continue;
|
| seen.add(id);
|
| const defaults = OMNIROUTE_DEFAULT_MODEL_CAPABILITIES[id] ?? {};
|
| const override = overrides[id] ?? {};
|
| const merged: ModelCapabilities = { ...defaults, ...override };
|
| const explicitLabel =
|
| typeof merged.label === "string" && merged.label.trim()
|
| ? merged.label.trim()
|
| : typeof labels[id] === "string" && labels[id].trim()
|
| ? labels[id].trim()
|
| : id;
|
| const entry: OpenCodeModelEntry = { name: explicitLabel };
|
| if (typeof merged.attachment === "boolean") entry.attachment = merged.attachment;
|
| if (typeof merged.reasoning === "boolean") entry.reasoning = merged.reasoning;
|
| if (typeof merged.temperature === "boolean") entry.temperature = merged.temperature;
|
| if (typeof merged.tool_call === "boolean") entry.tool_call = merged.tool_call;
|
|
|
|
|
| const liveContext =
|
| typeof raw === "object" && raw !== null
|
| ? (raw as { contextLength?: number }).contextLength
|
| : undefined;
|
| const rawContextLength =
|
| liveContext ??
|
| options.modelContextLengths?.[id] ??
|
| OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS[id];
|
| const contextLength =
|
| typeof rawContextLength === "string" ? parseInt(rawContextLength, 10) : rawContextLength;
|
| if (typeof contextLength === "number" && !isNaN(contextLength) && contextLength > 0) {
|
| entry.limit = { context: contextLength };
|
| }
|
|
|
| models[id] = entry;
|
| }
|
|
|
| return {
|
| npm: OMNIROUTE_PROVIDER_NPM,
|
| name: options.displayName?.trim() || "OmniRoute",
|
| options: { baseURL, apiKey },
|
| models,
|
| };
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| export function buildOmniRouteOpenCodeConfig(
|
| options: OmniRouteProviderOptions
|
| ): OpenCodeConfigDocument {
|
| const doc: OpenCodeConfigDocument = {
|
| $schema: OPENCODE_CONFIG_SCHEMA,
|
| provider: {
|
| [OMNIROUTE_PROVIDER_KEY]: createOmniRouteProvider(options),
|
| },
|
| };
|
|
|
| if (options.model !== undefined) {
|
| const id = options.model.trim();
|
| if (id) doc.model = `${OMNIROUTE_PROVIDER_KEY}/${id}`;
|
| }
|
|
|
| if (options.smallModel !== undefined) {
|
| const id = options.smallModel.trim();
|
| if (id) doc.small_model = `${OMNIROUTE_PROVIDER_KEY}/${id}`;
|
| }
|
|
|
| return doc;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export function mergeIntoExistingConfig(
|
| existing: Record<string, unknown>,
|
| options: OmniRouteProviderOptions
|
| ): Record<string, unknown> {
|
| const partial = buildOmniRouteOpenCodeConfig(options);
|
|
|
| const merged: Record<string, unknown> = { ...existing };
|
|
|
| if (partial.model !== undefined) merged.model = partial.model;
|
| if (partial.small_model !== undefined) merged.small_model = partial.small_model;
|
|
|
| const existingProvider =
|
| typeof existing.provider === "object" && existing.provider !== null
|
| ? (existing.provider as Record<string, unknown>)
|
| : {};
|
|
|
| merged.provider = {
|
| ...existingProvider,
|
| [OMNIROUTE_PROVIDER_KEY]: partial.provider[OMNIROUTE_PROVIDER_KEY],
|
| };
|
|
|
| return merged;
|
| }
|
|
|
| |
| |
| |
|
|
| export const OMNIROUTE_MCP_DEFAULT_SCOPES = [
|
| "read:health",
|
| "read:combos",
|
| "read:quota",
|
| "read:usage",
|
| "read:models",
|
| "read:cache",
|
| "read:compression",
|
| ] as const;
|
|
|
| export type OmniRouteMCPScope = (typeof OMNIROUTE_MCP_DEFAULT_SCOPES)[number] | string;
|
|
|
| export interface OmniRouteMCPOptions {
|
|
|
| serverPath: string;
|
|
|
| apiKey: string;
|
| |
| |
| |
|
|
| managementApiKey?: string;
|
| |
| |
| |
| |
|
|
| scopes?: OmniRouteMCPScope[];
|
| |
| |
| |
| |
| |
|
|
| runtime?: "tsx" | "node";
|
| }
|
|
|
| export interface OpenCodeMCPServerEntry {
|
| command: string;
|
| args: string[];
|
| env: Record<string, string>;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export function createOmniRouteMCPEntry(options: OmniRouteMCPOptions): OpenCodeMCPServerEntry {
|
| const serverPath = requireNonEmpty(options.serverPath, "serverPath");
|
| const apiKey = requireNonEmpty(options.apiKey, "apiKey");
|
|
|
| const runtime = options.runtime ?? "tsx";
|
|
|
| const command = runtime === "tsx" ? "npx" : "node";
|
| const args = runtime === "tsx" ? ["tsx", serverPath] : [serverPath];
|
|
|
| const env: Record<string, string> = {
|
| OMNIROUTE_API_KEY: apiKey,
|
| };
|
|
|
| if (options.managementApiKey !== undefined) {
|
| const mgmtKey = options.managementApiKey.trim();
|
| if (mgmtKey) env.OMNIROUTE_MANAGEMENT_API_KEY = mgmtKey;
|
| }
|
|
|
| if (options.scopes !== undefined && options.scopes.length > 0) {
|
| env.OMNIROUTE_MCP_ENFORCE_SCOPES = "true";
|
| env.OMNIROUTE_MCP_SCOPES = options.scopes.join(",");
|
| }
|
|
|
| return { command, args, env };
|
| }
|
|
|
| async function fetchJSON<T>(url: string, apiKey: string, timeoutMs: number): Promise<T> {
|
| const controller = new AbortController();
|
| const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
|
| try {
|
| const response = await fetch(url, {
|
| headers: { Authorization: `Bearer ${apiKey}` },
|
| signal: controller.signal,
|
| });
|
|
|
| if (!response.ok) {
|
| throw new Error(`received HTTP ${response.status}`);
|
| }
|
|
|
| return (await response.json()) as T;
|
| } catch (err) {
|
| const message = err instanceof Error ? err.message : String(err);
|
| throw new Error(`@omniroute/opencode-provider: request to ${url} failed: ${message}`);
|
| } finally {
|
| clearTimeout(timer);
|
| }
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export interface OmniRouteLiveModel {
|
| id: string;
|
| name: string;
|
|
|
| contextLength?: number;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export async function fetchLiveModels(
|
| baseURL: string,
|
| apiKey: string,
|
| timeoutMs = 5_000
|
| ): Promise<OmniRouteLiveModel[]> {
|
| const key = requireNonEmpty(apiKey, "apiKey");
|
| const url = `${normalizeBaseURL(baseURL)}/models`;
|
|
|
| const body = await fetchJSON<unknown>(url, key, timeoutMs);
|
|
|
| const rawList: unknown[] = Array.isArray(body)
|
| ? body
|
| : body && typeof body === "object" && Array.isArray((body as { data?: unknown[] }).data)
|
| ? ((body as { data: unknown[] }).data as unknown[])
|
| : [];
|
|
|
| const models: OmniRouteLiveModel[] = [];
|
| for (const raw of rawList) {
|
| if (typeof raw !== "object" || raw === null) continue;
|
| const r = raw as Record<string, unknown>;
|
|
|
| const id =
|
| typeof r.id === "string"
|
| ? r.id.trim()
|
| : typeof r.modelId === "string"
|
| ? r.modelId.trim()
|
| : typeof r.model_id === "string"
|
| ? r.model_id.trim()
|
| : "";
|
|
|
| if (!id) continue;
|
|
|
| const name =
|
| typeof r.name === "string"
|
| ? r.name.trim()
|
| : typeof r.displayName === "string"
|
| ? r.displayName.trim()
|
| : typeof r.display_name === "string"
|
| ? r.display_name.trim()
|
| : id;
|
|
|
|
|
|
|
|
|
|
|
| const contextLength =
|
| typeof r.context_length === "number" && r.context_length > 0
|
| ? r.context_length
|
| : typeof r.max_context_window_tokens === "number" && r.max_context_window_tokens > 0
|
| ? r.max_context_window_tokens
|
| : undefined;
|
|
|
| models.push({ id, name: name || id, ...(contextLength ? { contextLength } : {}) });
|
| }
|
|
|
| return models;
|
| }
|
|
|
| |
| |
| |
|
|
| export type OmniRouteCompressionOverride =
|
| | ""
|
| | "off"
|
| | "lite"
|
| | "standard"
|
| | "aggressive"
|
| | "ultra"
|
| | "rtk"
|
| | "stacked";
|
|
|
| const VALID_COMPRESSION_OVERRIDES = new Set<string>([
|
| "",
|
| "off",
|
| "lite",
|
| "standard",
|
| "aggressive",
|
| "ultra",
|
| "rtk",
|
| "stacked",
|
| ]);
|
|
|
|
|
| export interface OmniRouteCombo {
|
| id: string;
|
| name: string;
|
| strategy: string;
|
| active: boolean;
|
| compressionOverride: OmniRouteCompressionOverride;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export async function listCombos(
|
| baseURL: string,
|
| managementApiKey: string,
|
| timeoutMs = 5_000
|
| ): Promise<OmniRouteCombo[]> {
|
| const key = requireNonEmpty(managementApiKey, "managementApiKey");
|
| const base = normalizeBaseURL(baseURL).replace(/\/v1$/, "");
|
| const url = `${base}/api/combos`;
|
|
|
| const body = await fetchJSON<unknown>(url, key, timeoutMs);
|
| const rawList: unknown[] = Array.isArray(body)
|
| ? body
|
| : body && typeof body === "object" && Array.isArray((body as { combos?: unknown[] }).combos)
|
| ? ((body as { combos: unknown[] }).combos as unknown[])
|
| : [];
|
|
|
| const combos: OmniRouteCombo[] = [];
|
| for (const raw of rawList) {
|
| if (typeof raw !== "object" || raw === null) continue;
|
| const r = raw as Record<string, unknown>;
|
|
|
| const id = typeof r.id === "string" ? r.id.trim() : "";
|
| if (!id) continue;
|
|
|
| const name = typeof r.name === "string" ? r.name.trim() : id;
|
| const strategy = typeof r.strategy === "string" ? r.strategy : "";
|
| const active = typeof r.active === "boolean" ? r.active : false;
|
|
|
| const rawOverride = typeof r.compressionOverride === "string" ? r.compressionOverride : "";
|
| const compressionOverride = VALID_COMPRESSION_OVERRIDES.has(rawOverride)
|
| ? (rawOverride as OmniRouteCompressionOverride)
|
| : "";
|
|
|
| combos.push({ id, name, strategy, active, compressionOverride });
|
| }
|
|
|
| return combos;
|
| }
|
|
|
| |
| |
| |
| |
|
|
| export interface OmniRouteComboConfigOptions {
|
|
|
| name: string;
|
|
|
| strategy: string;
|
| |
| |
| |
|
|
| compressionOverride?: OmniRouteCompressionOverride;
|
|
|
| active?: boolean;
|
| |
| |
| |
|
|
| providers?: string[];
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export function createOmniRouteComboConfig(
|
| options: OmniRouteComboConfigOptions
|
| ): Record<string, unknown> {
|
| const name = requireNonEmpty(options.name, "name");
|
| const strategy = requireNonEmpty(options.strategy, "strategy");
|
|
|
| const payload: Record<string, unknown> = {
|
| name,
|
| strategy,
|
| active: options.active ?? true,
|
| };
|
|
|
| if (options.compressionOverride !== undefined) {
|
| payload.compressionOverride = options.compressionOverride;
|
| }
|
|
|
| if (options.providers !== undefined) {
|
| const providers = options.providers.filter((p) => typeof p === "string" && p.trim());
|
| if (providers.length > 0) {
|
| payload.providers = providers;
|
| }
|
| }
|
|
|
| return payload;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
| export interface OmniRouteRoleOverrides {
|
|
|
| temperature?: number;
|
|
|
| top_p?: number;
|
| }
|
|
|
|
|
| export interface OmniRouteAgentRole extends OmniRouteRoleOverrides {
|
|
|
| modelId: string;
|
|
|
| tools?: Record<string, boolean>;
|
|
|
| prompt?: string;
|
| }
|
|
|
|
|
| export interface OmniRouteAgentBlockOptions {
|
|
|
| roles: Record<string, OmniRouteAgentRole>;
|
| }
|
|
|
|
|
| export interface OpenCodeAgentEntry extends OmniRouteRoleOverrides {
|
|
|
| model: string;
|
|
|
| tools?: Record<string, boolean>;
|
|
|
| prompt?: string;
|
| }
|
|
|
| function buildAgentEntry(role: OmniRouteAgentRole): OpenCodeAgentEntry | undefined {
|
| if (!role || typeof role.modelId !== "string") return undefined;
|
| const modelId = role.modelId.trim();
|
| if (!modelId) return undefined;
|
| const entry: OpenCodeAgentEntry = { model: `${OMNIROUTE_PROVIDER_KEY}/${modelId}` };
|
| if (typeof role.temperature === "number") entry.temperature = role.temperature;
|
| if (typeof role.top_p === "number") entry.top_p = role.top_p;
|
| if (role.tools && typeof role.tools === "object" && !Array.isArray(role.tools)) {
|
| const tools: Record<string, boolean> = {};
|
| for (const [name, enabled] of Object.entries(role.tools)) {
|
| if (typeof name !== "string" || !name.trim()) continue;
|
| if (typeof enabled !== "boolean") continue;
|
| tools[name] = enabled;
|
| }
|
| if (Object.keys(tools).length > 0) entry.tools = tools;
|
| }
|
| if (typeof role.prompt === "string" && role.prompt.trim()) {
|
| entry.prompt = role.prompt;
|
| }
|
| return entry;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export function createOmniRouteAgentBlock(
|
| options: OmniRouteAgentBlockOptions
|
| ): Record<string, OpenCodeAgentEntry> {
|
| const out: Record<string, OpenCodeAgentEntry> = {};
|
| const roles = options.roles ?? {};
|
| for (const [roleName, role] of Object.entries(roles)) {
|
| const entry = buildAgentEntry(role);
|
| if (entry) out[roleName] = entry;
|
| }
|
| return out;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| export interface OmniRouteMode extends OmniRouteAgentRole {}
|
|
|
| |
| |
| |
| |
|
|
| export interface OmniRouteModesBlockOptions {
|
|
|
| modes: Record<string, OmniRouteMode>;
|
| }
|
|
|
| |
| |
| |
| |
|
|
| export interface OpenCodeModeEntry extends OpenCodeAgentEntry {}
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export function createOmniRouteModesBlock(
|
| options: OmniRouteModesBlockOptions
|
| ): Record<string, OpenCodeModeEntry> {
|
| const out: Record<string, OpenCodeModeEntry> = {};
|
| const modes = options.modes ?? {};
|
| for (const [modeName, mode] of Object.entries(modes)) {
|
| const entry = buildAgentEntry(mode);
|
| if (entry) out[modeName] = entry;
|
| }
|
| return out;
|
| }
|
|
|
| export default createOmniRouteProvider;
|
|
|