mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 10:02:23 +00:00
feat(ai): add Vercel AI Gateway routing support (#1051)
* feat(ai): add Vercel AI Gateway routing support Add vercelGatewayRouting to OpenAICompletionsCompat, parallel to openRouterRouting. When a model targets ai-gateway.vercel.sh and has vercelGatewayRouting configured, the openai-completions provider passes providerOptions.gateway with only/order in the request body. Changes: - types.ts: VercelGatewayRouting interface + field on OpenAICompletionsCompat - openai-completions.ts: buildParams passes providerOptions.gateway, detectCompat/getCompat include the new field - model-registry.ts: VercelGatewayRoutingSchema for models.json validation - test: updated Required<OpenAICompletionsCompat> in test fixture * docs(coding-agent): add vercelGatewayRouting to custom models documentation
This commit is contained in:
parent
d1560a9640
commit
e045a9f142
5 changed files with 67 additions and 0 deletions
|
|
@ -452,6 +452,17 @@ function buildParams(model: Model<"openai-completions">, context: Context, optio
|
||||||
(params as any).provider = model.compat.openRouterRouting;
|
(params as any).provider = model.compat.openRouterRouting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vercel AI Gateway provider routing preferences
|
||||||
|
if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
|
||||||
|
const routing = model.compat.vercelGatewayRouting;
|
||||||
|
if (routing.only || routing.order) {
|
||||||
|
const gatewayOptions: Record<string, string[]> = {};
|
||||||
|
if (routing.only) gatewayOptions.only = routing.only;
|
||||||
|
if (routing.order) gatewayOptions.order = routing.order;
|
||||||
|
(params as any).providerOptions = { gateway: gatewayOptions };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -797,6 +808,7 @@ function detectCompat(model: Model<"openai-completions">): Required<OpenAIComple
|
||||||
requiresMistralToolIds: isMistral,
|
requiresMistralToolIds: isMistral,
|
||||||
thinkingFormat: isZai ? "zai" : "openai",
|
thinkingFormat: isZai ? "zai" : "openai",
|
||||||
openRouterRouting: {},
|
openRouterRouting: {},
|
||||||
|
vercelGatewayRouting: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -821,5 +833,6 @@ function getCompat(model: Model<"openai-completions">): Required<OpenAICompletio
|
||||||
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 ?? {},
|
openRouterRouting: model.compat.openRouterRouting ?? {},
|
||||||
|
vercelGatewayRouting: model.compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,8 @@ export interface OpenAICompletionsCompat {
|
||||||
thinkingFormat?: "openai" | "zai";
|
thinkingFormat?: "openai" | "zai";
|
||||||
/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */
|
/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */
|
||||||
openRouterRouting?: OpenRouterRouting;
|
openRouterRouting?: OpenRouterRouting;
|
||||||
|
/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */
|
||||||
|
vercelGatewayRouting?: VercelGatewayRouting;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compatibility settings for OpenAI Responses APIs. */
|
/** Compatibility settings for OpenAI Responses APIs. */
|
||||||
|
|
@ -237,6 +239,18 @@ export interface OpenRouterRouting {
|
||||||
order?: string[];
|
order?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vercel AI Gateway routing preferences.
|
||||||
|
* Controls which upstream providers the gateway routes requests to.
|
||||||
|
* @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options
|
||||||
|
*/
|
||||||
|
export interface VercelGatewayRouting {
|
||||||
|
/** List of provider slugs to exclusively use for this request (e.g., ["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;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ const compat: Required<OpenAICompletionsCompat> = {
|
||||||
requiresMistralToolIds: false,
|
requiresMistralToolIds: false,
|
||||||
thinkingFormat: "openai",
|
thinkingFormat: "openai",
|
||||||
openRouterRouting: {},
|
openRouterRouting: {},
|
||||||
|
vercelGatewayRouting: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildToolResult(toolCallId: string, timestamp: number): ToolResultMessage {
|
function buildToolResult(toolCallId: string, timestamp: number): ToolResultMessage {
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ For providers with partial OpenAI compatibility, use the `compat` field:
|
||||||
| `supportsUsageInStreaming` | Supports `stream_options: { include_usage: true }` (default: `true`) |
|
| `supportsUsageInStreaming` | Supports `stream_options: { include_usage: true }` (default: `true`) |
|
||||||
| `maxTokensField` | Use `max_completion_tokens` or `max_tokens` |
|
| `maxTokensField` | Use `max_completion_tokens` or `max_tokens` |
|
||||||
| `openRouterRouting` | OpenRouter routing config passed to OpenRouter for model/provider selection |
|
| `openRouterRouting` | OpenRouter routing config passed to OpenRouter for model/provider selection |
|
||||||
|
| `vercelGatewayRouting` | Vercel AI Gateway routing config for provider selection (`only`, `order`) |
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
@ -191,3 +192,34 @@ Example:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Vercel AI Gateway example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"providers": {
|
||||||
|
"vercel-ai-gateway": {
|
||||||
|
"baseUrl": "https://ai-gateway.vercel.sh/v1",
|
||||||
|
"apiKey": "AI_GATEWAY_API_KEY",
|
||||||
|
"api": "openai-completions",
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"id": "moonshotai/kimi-k2.5",
|
||||||
|
"name": "Kimi K2.5 (Fireworks via Vercel)",
|
||||||
|
"reasoning": true,
|
||||||
|
"input": ["text", "image"],
|
||||||
|
"cost": { "input": 0.6, "output": 3, "cacheRead": 0, "cacheWrite": 0 },
|
||||||
|
"contextWindow": 262144,
|
||||||
|
"maxTokens": 262144,
|
||||||
|
"compat": {
|
||||||
|
"vercelGatewayRouting": {
|
||||||
|
"only": ["fireworks", "novita"],
|
||||||
|
"order": ["fireworks", "novita"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ const OpenRouterRoutingSchema = Type.Object({
|
||||||
order: Type.Optional(Type.Array(Type.String())),
|
order: Type.Optional(Type.Array(Type.String())),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Schema for Vercel AI Gateway routing preferences
|
||||||
|
const VercelGatewayRoutingSchema = 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()),
|
||||||
|
|
@ -44,6 +50,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
|
||||||
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),
|
openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
|
||||||
|
vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
const OpenAIResponsesCompatSchema = Type.Object({
|
const OpenAIResponsesCompatSchema = Type.Object({
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue