diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts index d777c116..2fcf86fd 100644 --- a/packages/ai/src/providers/openai-completions.ts +++ b/packages/ai/src/providers/openai-completions.ts @@ -452,6 +452,17 @@ function buildParams(model: Model<"openai-completions">, context: Context, optio (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 = {}; + if (routing.only) gatewayOptions.only = routing.only; + if (routing.order) gatewayOptions.order = routing.order; + (params as any).providerOptions = { gateway: gatewayOptions }; + } + } + return params; } @@ -797,6 +808,7 @@ function detectCompat(model: Model<"openai-completions">): Required): Required { id: string; diff --git a/packages/ai/test/openai-completions-tool-result-images.test.ts b/packages/ai/test/openai-completions-tool-result-images.test.ts index 36398f26..3330e776 100644 --- a/packages/ai/test/openai-completions-tool-result-images.test.ts +++ b/packages/ai/test/openai-completions-tool-result-images.test.ts @@ -31,6 +31,7 @@ const compat: Required = { requiresMistralToolIds: false, thinkingFormat: "openai", openRouterRouting: {}, + vercelGatewayRouting: {}, }; function buildToolResult(toolCallId: string, timestamp: number): ToolResultMessage { diff --git a/packages/coding-agent/docs/models.md b/packages/coding-agent/docs/models.md index dc2391f5..cad0c06b 100644 --- a/packages/coding-agent/docs/models.md +++ b/packages/coding-agent/docs/models.md @@ -165,6 +165,7 @@ For providers with partial OpenAI compatibility, use the `compat` field: | `supportsUsageInStreaming` | Supports `stream_options: { include_usage: true }` (default: `true`) | | `maxTokensField` | Use `max_completion_tokens` or `max_tokens` | | `openRouterRouting` | OpenRouter routing config passed to OpenRouter for model/provider selection | +| `vercelGatewayRouting` | Vercel AI Gateway routing config for provider selection (`only`, `order`) | 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"] + } + } + } + ] + } + } +} +``` diff --git a/packages/coding-agent/src/core/model-registry.ts b/packages/coding-agent/src/core/model-registry.ts index 2cd2171a..50c4c6b0 100644 --- a/packages/coding-agent/src/core/model-registry.ts +++ b/packages/coding-agent/src/core/model-registry.ts @@ -31,6 +31,12 @@ const OpenRouterRoutingSchema = Type.Object({ 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 const OpenAICompletionsCompatSchema = Type.Object({ supportsStore: Type.Optional(Type.Boolean()), @@ -44,6 +50,7 @@ const OpenAICompletionsCompatSchema = Type.Object({ requiresMistralToolIds: Type.Optional(Type.Boolean()), thinkingFormat: Type.Optional(Type.Union([Type.Literal("openai"), Type.Literal("zai")])), openRouterRouting: Type.Optional(OpenRouterRoutingSchema), + vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema), }); const OpenAIResponsesCompatSchema = Type.Object({