mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 09:01:14 +00:00
feat(ai): add OpenRouter provider routing support
Allows custom models to specify which upstream providers OpenRouter should route requests to via the `openRouterRouting` field in model definitions. Supported fields: - `only`: list of provider slugs to exclusively use - `order`: list of provider slugs to try in order
This commit is contained in:
parent
a6d878e804
commit
dac7474da2
5 changed files with 30 additions and 0 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added OpenRouter provider routing support for custom models via `openRouterRouting` compat field ([#859](https://github.com/badlogic/pi-mono/pull/859) by [@v01dpr1mr0s3](https://github.com/v01dpr1mr0s3))
|
||||||
- Added `azure-openai-responses` provider support for Azure OpenAI Responses API. ([#890](https://github.com/badlogic/pi-mono/pull/890) by [@markusylisiurunen](https://github.com/markusylisiurunen))
|
- Added `azure-openai-responses` provider support for Azure OpenAI Responses API. ([#890](https://github.com/badlogic/pi-mono/pull/890) by [@markusylisiurunen](https://github.com/markusylisiurunen))
|
||||||
- Added `createAssistantMessageEventStream()` factory function for use in extensions.
|
- Added `createAssistantMessageEventStream()` factory function for use in extensions.
|
||||||
- Added `resetApiProviders()` to clear and re-register built-in API providers.
|
- Added `resetApiProviders()` to clear and re-register built-in API providers.
|
||||||
|
|
|
||||||
|
|
@ -445,6 +445,11 @@ function buildParams(model: Model<"openai-completions">, context: Context, optio
|
||||||
params.reasoning_effort = options.reasoningEffort;
|
params.reasoning_effort = options.reasoningEffort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenRouter provider routing preferences
|
||||||
|
if (model.baseUrl.includes("openrouter.ai") && model.compat?.openRouterRouting) {
|
||||||
|
(params as any).provider = model.compat.openRouterRouting;
|
||||||
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -777,6 +782,7 @@ function detectCompat(model: Model<"openai-completions">): Required<OpenAIComple
|
||||||
requiresThinkingAsText: isMistral,
|
requiresThinkingAsText: isMistral,
|
||||||
requiresMistralToolIds: isMistral,
|
requiresMistralToolIds: isMistral,
|
||||||
thinkingFormat: isZai ? "zai" : "openai",
|
thinkingFormat: isZai ? "zai" : "openai",
|
||||||
|
openRouterRouting: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -800,5 +806,6 @@ function getCompat(model: Model<"openai-completions">): Required<OpenAICompletio
|
||||||
requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
|
requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
|
||||||
requiresMistralToolIds: model.compat.requiresMistralToolIds ?? detected.requiresMistralToolIds,
|
requiresMistralToolIds: model.compat.requiresMistralToolIds ?? detected.requiresMistralToolIds,
|
||||||
thinkingFormat: model.compat.thinkingFormat ?? detected.thinkingFormat,
|
thinkingFormat: model.compat.thinkingFormat ?? detected.thinkingFormat,
|
||||||
|
openRouterRouting: model.compat.openRouterRouting ?? {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,8 @@ export interface OpenAICompletionsCompat {
|
||||||
requiresMistralToolIds?: boolean;
|
requiresMistralToolIds?: boolean;
|
||||||
/** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "zai" uses thinking: { type: "enabled" }. Default: "openai". */
|
/** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "zai" uses thinking: { type: "enabled" }. Default: "openai". */
|
||||||
thinkingFormat?: "openai" | "zai";
|
thinkingFormat?: "openai" | "zai";
|
||||||
|
/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */
|
||||||
|
openRouterRouting?: OpenRouterRouting;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compatibility settings for OpenAI Responses APIs. */
|
/** Compatibility settings for OpenAI Responses APIs. */
|
||||||
|
|
@ -221,6 +223,18 @@ export interface OpenAIResponsesCompat {
|
||||||
// Reserved for future use
|
// Reserved for future use
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenRouter provider routing preferences.
|
||||||
|
* Controls which upstream providers OpenRouter routes requests to.
|
||||||
|
* @see https://openrouter.ai/docs/provider-routing
|
||||||
|
*/
|
||||||
|
export interface OpenRouterRouting {
|
||||||
|
/** List of provider slugs to exclusively use for this request (e.g., ["amazon-bedrock", "anthropic"]). */
|
||||||
|
only?: string[];
|
||||||
|
/** List of provider slugs to try in order (e.g., ["anthropic", "openai"]). */
|
||||||
|
order?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
// Model interface for the unified model system
|
// Model interface for the unified model system
|
||||||
export interface Model<TApi extends Api> {
|
export interface Model<TApi extends Api> {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ const compat: Required<OpenAICompletionsCompat> = {
|
||||||
requiresThinkingAsText: false,
|
requiresThinkingAsText: false,
|
||||||
requiresMistralToolIds: false,
|
requiresMistralToolIds: false,
|
||||||
thinkingFormat: "openai",
|
thinkingFormat: "openai",
|
||||||
|
openRouterRouting: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildToolResult(toolCallId: string, timestamp: number): ToolResultMessage {
|
function buildToolResult(toolCallId: string, timestamp: number): ToolResultMessage {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,12 @@ import type { AuthStorage } from "./auth-storage.js";
|
||||||
|
|
||||||
const Ajv = (AjvModule as any).default || AjvModule;
|
const Ajv = (AjvModule as any).default || AjvModule;
|
||||||
|
|
||||||
|
// Schema for OpenRouter routing preferences
|
||||||
|
const OpenRouterRoutingSchema = Type.Object({
|
||||||
|
only: Type.Optional(Type.Array(Type.String())),
|
||||||
|
order: Type.Optional(Type.Array(Type.String())),
|
||||||
|
});
|
||||||
|
|
||||||
// Schema for OpenAI compatibility settings
|
// Schema for OpenAI compatibility settings
|
||||||
const OpenAICompletionsCompatSchema = Type.Object({
|
const OpenAICompletionsCompatSchema = Type.Object({
|
||||||
supportsStore: Type.Optional(Type.Boolean()),
|
supportsStore: Type.Optional(Type.Boolean()),
|
||||||
|
|
@ -37,6 +43,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
|
||||||
requiresThinkingAsText: Type.Optional(Type.Boolean()),
|
requiresThinkingAsText: Type.Optional(Type.Boolean()),
|
||||||
requiresMistralToolIds: Type.Optional(Type.Boolean()),
|
requiresMistralToolIds: Type.Optional(Type.Boolean()),
|
||||||
thinkingFormat: Type.Optional(Type.Union([Type.Literal("openai"), Type.Literal("zai")])),
|
thinkingFormat: Type.Optional(Type.Union([Type.Literal("openai"), Type.Literal("zai")])),
|
||||||
|
openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
const OpenAIResponsesCompatSchema = Type.Object({
|
const OpenAIResponsesCompatSchema = Type.Object({
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue