mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: Add Vercel AI Gateway provider integration
This commit is contained in:
parent
a79c3d04a6
commit
3169f25a5c
22 changed files with 384 additions and 0 deletions
|
|
@ -198,6 +198,7 @@ export const SECRET_STATE_KEYS = [
|
|||
"fireworksApiKey",
|
||||
"featherlessApiKey",
|
||||
"ioIntelligenceApiKey",
|
||||
"vercelAiGatewayApiKey",
|
||||
] as const satisfies readonly (keyof ProviderSettings)[]
|
||||
export type SecretState = Pick<ProviderSettings, (typeof SECRET_STATE_KEYS)[number]>
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export const providerNames = [
|
|||
"featherless",
|
||||
"io-intelligence",
|
||||
"roo",
|
||||
"vercel-ai-gateway",
|
||||
] as const
|
||||
|
||||
export const providerNamesSchema = z.enum(providerNames)
|
||||
|
|
@ -321,6 +322,11 @@ const rooSchema = apiModelIdProviderModelSchema.extend({
|
|||
// No additional fields needed - uses cloud authentication
|
||||
})
|
||||
|
||||
const vercelAiGatewaySchema = baseProviderSettingsSchema.extend({
|
||||
vercelAiGatewayApiKey: z.string().optional(),
|
||||
vercelAiGatewayModelId: z.string().optional(),
|
||||
})
|
||||
|
||||
const defaultSchema = z.object({
|
||||
apiProvider: z.undefined(),
|
||||
})
|
||||
|
|
@ -360,6 +366,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
|
|||
ioIntelligenceSchema.merge(z.object({ apiProvider: z.literal("io-intelligence") })),
|
||||
qwenCodeSchema.merge(z.object({ apiProvider: z.literal("qwen-code") })),
|
||||
rooSchema.merge(z.object({ apiProvider: z.literal("roo") })),
|
||||
vercelAiGatewaySchema.merge(z.object({ apiProvider: z.literal("vercel-ai-gateway") })),
|
||||
defaultSchema,
|
||||
])
|
||||
|
||||
|
|
@ -399,6 +406,7 @@ export const providerSettingsSchema = z.object({
|
|||
...ioIntelligenceSchema.shape,
|
||||
...qwenCodeSchema.shape,
|
||||
...rooSchema.shape,
|
||||
...vercelAiGatewaySchema.shape,
|
||||
...codebaseIndexProviderSchema.shape,
|
||||
})
|
||||
|
||||
|
|
@ -425,6 +433,7 @@ export const MODEL_ID_KEYS: Partial<keyof ProviderSettings>[] = [
|
|||
"litellmModelId",
|
||||
"huggingFaceModelId",
|
||||
"ioIntelligenceModelId",
|
||||
"vercelAiGatewayModelId",
|
||||
]
|
||||
|
||||
export const getModelId = (settings: ProviderSettings): string | undefined => {
|
||||
|
|
|
|||
|
|
@ -27,4 +27,5 @@ export * from "./unbound.js"
|
|||
export * from "./vertex.js"
|
||||
export * from "./vscode-llm.js"
|
||||
export * from "./xai.js"
|
||||
export * from "./vercel-ai-gateway.js"
|
||||
export * from "./zai.js"
|
||||
|
|
|
|||
19
packages/types/src/providers/vercel-ai-gateway.ts
Normal file
19
packages/types/src/providers/vercel-ai-gateway.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import type { ModelInfo } from "../model.js"
|
||||
|
||||
// https://ai-gateway.vercel.sh/v1/
|
||||
export const vercelAiGatewayDefaultModelId = "anthropic/claude-sonnet-4"
|
||||
|
||||
export const vercelAiGatewayDefaultModelInfo: ModelInfo = {
|
||||
maxTokens: 64000,
|
||||
contextWindow: 128000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3,
|
||||
outputPrice: 15,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
description:
|
||||
"Claude Sonnet 4 significantly improves on Sonnet 3.7's industry-leading capabilities, excelling in coding with a state-of-the-art 72.7% on SWE-bench. The model balances performance and efficiency for internal and external use cases, with enhanced steerability for greater control over implementations. While not matching Opus 4 in most domains, it delivers an optimal mix of capability and practicality.",
|
||||
}
|
||||
|
||||
export const VERCEL_AI_GATEWAY_DEFAULT_TEMPERATURE = 0
|
||||
|
|
@ -38,6 +38,7 @@ import {
|
|||
FireworksHandler,
|
||||
RooHandler,
|
||||
FeatherlessHandler,
|
||||
VercelAiGatewayHandler,
|
||||
} from "./providers"
|
||||
import { NativeOllamaHandler } from "./providers/native-ollama"
|
||||
|
||||
|
|
@ -151,6 +152,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
return new RooHandler(options)
|
||||
case "featherless":
|
||||
return new FeatherlessHandler(options)
|
||||
case "vercel-ai-gateway":
|
||||
return new VercelAiGatewayHandler(options)
|
||||
default:
|
||||
apiProvider satisfies "gemini-cli" | undefined
|
||||
return new AnthropicHandler(options)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { RouterName, ModelRecord } from "../../../shared/api"
|
|||
import { fileExistsAtPath } from "../../../utils/fs"
|
||||
|
||||
import { getOpenRouterModels } from "./openrouter"
|
||||
import { getVercelAiGatewayModels } from "./vercel-ai-gateway"
|
||||
import { getRequestyModels } from "./requesty"
|
||||
import { getGlamaModels } from "./glama"
|
||||
import { getUnboundModels } from "./unbound"
|
||||
|
|
@ -81,6 +82,9 @@ export const getModels = async (options: GetModelsOptions): Promise<ModelRecord>
|
|||
case "io-intelligence":
|
||||
models = await getIOIntelligenceModels(options.apiKey)
|
||||
break
|
||||
case "vercel-ai-gateway":
|
||||
models = await getVercelAiGatewayModels()
|
||||
break
|
||||
default: {
|
||||
// Ensures router is exhaustively checked if RouterName is a strict union
|
||||
const exhaustiveCheck: never = provider
|
||||
|
|
|
|||
115
src/api/providers/fetchers/vercel-ai-gateway.ts
Normal file
115
src/api/providers/fetchers/vercel-ai-gateway.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import axios from "axios"
|
||||
import { z } from "zod"
|
||||
|
||||
import type { ModelInfo } from "@roo-code/types"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../../shared/api"
|
||||
import { parseApiPrice } from "../../../shared/cost"
|
||||
|
||||
/**
|
||||
* VercelAiGatewayPricing
|
||||
*/
|
||||
|
||||
const vercelAiGatewayPricingSchema = z.object({
|
||||
input: z.string(),
|
||||
output: z.string(),
|
||||
input_cache_write: z.string().optional(),
|
||||
input_cache_read: z.string().optional(),
|
||||
})
|
||||
|
||||
/**
|
||||
* VercelAiGatewayModel
|
||||
*/
|
||||
|
||||
const vercelAiGatewayModelSchema = z.object({
|
||||
id: z.string(),
|
||||
object: z.string(),
|
||||
created: z.number(),
|
||||
owned_by: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
context_window: z.number(),
|
||||
max_tokens: z.number(),
|
||||
type: z.string(),
|
||||
pricing: vercelAiGatewayPricingSchema,
|
||||
})
|
||||
|
||||
export type VercelAiGatewayModel = z.infer<typeof vercelAiGatewayModelSchema>
|
||||
|
||||
/**
|
||||
* VercelAiGatewayModelsResponse
|
||||
*/
|
||||
|
||||
const vercelAiGatewayModelsResponseSchema = z.object({
|
||||
object: z.string(),
|
||||
data: z.array(vercelAiGatewayModelSchema),
|
||||
})
|
||||
|
||||
type VercelAiGatewayModelsResponse = z.infer<typeof vercelAiGatewayModelsResponseSchema>
|
||||
|
||||
/**
|
||||
* getVercelAiGatewayModels
|
||||
*/
|
||||
|
||||
export async function getVercelAiGatewayModels(options?: ApiHandlerOptions): Promise<Record<string, ModelInfo>> {
|
||||
const models: Record<string, ModelInfo> = {}
|
||||
const baseURL = "https://ai-gateway.vercel.sh/v1"
|
||||
|
||||
try {
|
||||
const response = await axios.get<VercelAiGatewayModelsResponse>(`${baseURL}/models`)
|
||||
const result = vercelAiGatewayModelsResponseSchema.safeParse(response.data)
|
||||
const data = result.success ? result.data.data : response.data.data
|
||||
|
||||
if (!result.success) {
|
||||
console.error("Vercel AI Gateway models response is invalid", result.error.format())
|
||||
}
|
||||
|
||||
for (const model of data) {
|
||||
const { id } = model
|
||||
|
||||
// Filter out embedding models (models with "embedding" in name)
|
||||
if (id.toLowerCase().includes("embed")) {
|
||||
continue
|
||||
}
|
||||
|
||||
models[id] = parseVercelAiGatewayModel({
|
||||
id,
|
||||
model,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error fetching Vercel AI Gateway models: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
return models
|
||||
}
|
||||
|
||||
/**
|
||||
* parseVercelAiGatewayModel
|
||||
*/
|
||||
|
||||
export const parseVercelAiGatewayModel = ({ id, model }: { id: string; model: VercelAiGatewayModel }): ModelInfo => {
|
||||
const cacheWritesPrice = model.pricing?.input_cache_write
|
||||
? parseApiPrice(model.pricing?.input_cache_write)
|
||||
: undefined
|
||||
|
||||
const cacheReadsPrice = model.pricing?.input_cache_read ? parseApiPrice(model.pricing?.input_cache_read) : undefined
|
||||
|
||||
const supportsPromptCache = typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined"
|
||||
|
||||
const modelInfo: ModelInfo = {
|
||||
maxTokens: model.max_tokens,
|
||||
contextWindow: model.context_window,
|
||||
supportsImages: false,
|
||||
supportsPromptCache,
|
||||
inputPrice: parseApiPrice(model.pricing?.input),
|
||||
outputPrice: parseApiPrice(model.pricing?.output),
|
||||
cacheWritesPrice,
|
||||
cacheReadsPrice,
|
||||
description: model.description,
|
||||
}
|
||||
|
||||
return modelInfo
|
||||
}
|
||||
|
|
@ -32,3 +32,4 @@ export { ZAiHandler } from "./zai"
|
|||
export { FireworksHandler } from "./fireworks"
|
||||
export { RooHandler } from "./roo"
|
||||
export { FeatherlessHandler } from "./featherless"
|
||||
export { VercelAiGatewayHandler } from "./vercel-ai-gateway"
|
||||
|
|
|
|||
115
src/api/providers/vercel-ai-gateway.ts
Normal file
115
src/api/providers/vercel-ai-gateway.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import OpenAI from "openai"
|
||||
|
||||
import {
|
||||
vercelAiGatewayDefaultModelId,
|
||||
vercelAiGatewayDefaultModelInfo,
|
||||
VERCEL_AI_GATEWAY_DEFAULT_TEMPERATURE,
|
||||
} from "@roo-code/types"
|
||||
|
||||
import { ApiHandlerOptions } from "../../shared/api"
|
||||
|
||||
import { ApiStream } from "../transform/stream"
|
||||
import { convertToOpenAiMessages } from "../transform/openai-format"
|
||||
import { addCacheBreakpoints } from "../transform/caching/anthropic"
|
||||
|
||||
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
|
||||
import { RouterProvider } from "./router-provider"
|
||||
|
||||
// Extend OpenAI's CompletionUsage to include Vercel AI Gateway specific fields
|
||||
interface VercelAiGatewayUsage extends OpenAI.CompletionUsage {
|
||||
cache_creation_input_tokens?: number
|
||||
prompt_tokens_details?: {
|
||||
cached_tokens?: number
|
||||
}
|
||||
}
|
||||
|
||||
export class VercelAiGatewayHandler extends RouterProvider implements SingleCompletionHandler {
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
super({
|
||||
options,
|
||||
name: "vercel-ai-gateway",
|
||||
baseURL: "https://ai-gateway.vercel.sh/v1",
|
||||
apiKey: options.vercelAiGatewayApiKey,
|
||||
modelId: options.vercelAiGatewayModelId,
|
||||
defaultModelId: vercelAiGatewayDefaultModelId,
|
||||
defaultModelInfo: vercelAiGatewayDefaultModelInfo,
|
||||
})
|
||||
}
|
||||
|
||||
override async *createMessage(
|
||||
systemPrompt: string,
|
||||
messages: Anthropic.Messages.MessageParam[],
|
||||
metadata?: ApiHandlerCreateMessageMetadata,
|
||||
): ApiStream {
|
||||
const { id: modelId, info } = await this.fetchModel()
|
||||
|
||||
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...convertToOpenAiMessages(messages),
|
||||
]
|
||||
|
||||
if (modelId.startsWith("anthropic/claude-3")) {
|
||||
addCacheBreakpoints(systemPrompt, openAiMessages)
|
||||
} //TODO: add cache breakpoints for other models
|
||||
|
||||
const body: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: modelId,
|
||||
messages: openAiMessages,
|
||||
temperature: this.supportsTemperature(modelId)
|
||||
? (this.options.modelTemperature ?? VERCEL_AI_GATEWAY_DEFAULT_TEMPERATURE)
|
||||
: undefined,
|
||||
max_tokens: info.maxTokens,
|
||||
stream: true,
|
||||
}
|
||||
|
||||
const completion = await this.client.chat.completions.create(body)
|
||||
|
||||
for await (const chunk of completion) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
const usage = chunk.usage as VercelAiGatewayUsage
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: usage.prompt_tokens || 0,
|
||||
outputTokens: usage.completion_tokens || 0,
|
||||
cacheWriteTokens: usage.cache_creation_input_tokens || undefined,
|
||||
cacheReadTokens: usage.prompt_tokens_details?.cached_tokens || undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async completePrompt(prompt: string): Promise<string> {
|
||||
const { id: modelId, info } = await this.fetchModel()
|
||||
|
||||
try {
|
||||
const requestOptions: OpenAI.Chat.ChatCompletionCreateParams = {
|
||||
model: modelId,
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
stream: false,
|
||||
}
|
||||
|
||||
if (this.supportsTemperature(modelId)) {
|
||||
requestOptions.temperature = this.options.modelTemperature ?? VERCEL_AI_GATEWAY_DEFAULT_TEMPERATURE
|
||||
}
|
||||
|
||||
requestOptions.max_tokens = info.maxTokens
|
||||
|
||||
const response = await this.client.chat.completions.create(requestOptions)
|
||||
return response.choices[0]?.message.content || ""
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Vercel AI Gateway completion error: ${error.message}`)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/assets/images/vercel-ai-gateway.png
Normal file
BIN
src/assets/images/vercel-ai-gateway.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 875 B |
|
|
@ -575,6 +575,7 @@ export const webviewMessageHandler = async (
|
|||
},
|
||||
{ key: "glama", options: { provider: "glama" } },
|
||||
{ key: "unbound", options: { provider: "unbound", apiKey: apiConfiguration.unboundApiKey } },
|
||||
{ key: "vercel-ai-gateway", options: { provider: "vercel-ai-gateway" } },
|
||||
]
|
||||
|
||||
// Add IO Intelligence if API key is provided
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ const routerNames = [
|
|||
"ollama",
|
||||
"lmstudio",
|
||||
"io-intelligence",
|
||||
"vercel-ai-gateway",
|
||||
] as const
|
||||
|
||||
export type RouterName = (typeof routerNames)[number]
|
||||
|
|
@ -151,3 +152,4 @@ export type GetModelsOptions =
|
|||
| { provider: "ollama"; baseUrl?: string }
|
||||
| { provider: "lmstudio"; baseUrl?: string }
|
||||
| { provider: "io-intelligence"; apiKey: string }
|
||||
| { provider: "vercel-ai-gateway" }
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import {
|
|||
featherlessDefaultModelId,
|
||||
ioIntelligenceDefaultModelId,
|
||||
rooDefaultModelId,
|
||||
vercelAiGatewayDefaultModelId,
|
||||
} from "@roo-code/types"
|
||||
|
||||
import { vscode } from "@src/utils/vscode"
|
||||
|
|
@ -91,6 +92,7 @@ import {
|
|||
ZAi,
|
||||
Fireworks,
|
||||
Featherless,
|
||||
VercelAiGateway,
|
||||
} from "./providers"
|
||||
|
||||
import { MODELS_BY_PROVIDER, PROVIDERS } from "./constants"
|
||||
|
|
@ -335,6 +337,7 @@ const ApiOptions = ({
|
|||
featherless: { field: "apiModelId", default: featherlessDefaultModelId },
|
||||
"io-intelligence": { field: "ioIntelligenceModelId", default: ioIntelligenceDefaultModelId },
|
||||
roo: { field: "apiModelId", default: rooDefaultModelId },
|
||||
"vercel-ai-gateway": { field: "vercelAiGatewayModelId", default: vercelAiGatewayDefaultModelId },
|
||||
openai: { field: "openAiModelId" },
|
||||
ollama: { field: "ollamaModelId" },
|
||||
lmstudio: { field: "lmStudioModelId" },
|
||||
|
|
@ -607,6 +610,16 @@ const ApiOptions = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "vercel-ai-gateway" && (
|
||||
<VercelAiGateway
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
routerModels={routerModels}
|
||||
organizationAllowList={organizationAllowList}
|
||||
modelValidationError={modelValidationError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedProvider === "human-relay" && (
|
||||
<>
|
||||
<div className="text-sm text-vscode-descriptionForeground">
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ type ModelIdKey = keyof Pick<
|
|||
| "openAiModelId"
|
||||
| "litellmModelId"
|
||||
| "ioIntelligenceModelId"
|
||||
| "vercelAiGatewayModelId"
|
||||
>
|
||||
|
||||
interface ModelPickerProps {
|
||||
|
|
|
|||
|
|
@ -79,4 +79,5 @@ export const PROVIDERS = [
|
|||
{ value: "featherless", label: "Featherless AI" },
|
||||
{ value: "io-intelligence", label: "IO Intelligence" },
|
||||
{ value: "roo", label: "Roo Code Cloud" },
|
||||
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway" },
|
||||
].sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
import { useCallback } from "react"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import { type ProviderSettings, vercelAiGatewayDefaultModelId } from "@roo-code/types"
|
||||
|
||||
import type { OrganizationAllowList } from "@roo/cloud"
|
||||
import type { RouterModels } from "@roo/api"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
|
||||
import { inputEventTransform } from "../transforms"
|
||||
import { ModelPicker } from "../ModelPicker"
|
||||
|
||||
type VercelAiGatewayProps = {
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
|
||||
routerModels?: RouterModels
|
||||
organizationAllowList: OrganizationAllowList
|
||||
modelValidationError?: string
|
||||
}
|
||||
|
||||
export const VercelAiGateway = ({
|
||||
apiConfiguration,
|
||||
setApiConfigurationField,
|
||||
routerModels,
|
||||
organizationAllowList,
|
||||
modelValidationError,
|
||||
}: VercelAiGatewayProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
<K extends keyof ProviderSettings, E>(
|
||||
field: K,
|
||||
transform: (event: E) => ProviderSettings[K] = inputEventTransform,
|
||||
) =>
|
||||
(event: E | Event) => {
|
||||
setApiConfigurationField(field, transform(event as E))
|
||||
},
|
||||
[setApiConfigurationField],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.vercelAiGatewayApiKey || ""}
|
||||
type="password"
|
||||
onInput={handleInputChange("vercelAiGatewayApiKey")}
|
||||
placeholder={t("settings:placeholders.apiKey")}
|
||||
className="w-full">
|
||||
<label className="block font-medium mb-1">{t("settings:providers.vercelAiGatewayApiKey")}</label>
|
||||
</VSCodeTextField>
|
||||
<div className="text-sm text-vscode-descriptionForeground -mt-2">
|
||||
{t("settings:providers.apiKeyStorageNotice")}
|
||||
</div>
|
||||
<ModelPicker
|
||||
apiConfiguration={apiConfiguration}
|
||||
setApiConfigurationField={setApiConfigurationField}
|
||||
defaultModelId={vercelAiGatewayDefaultModelId}
|
||||
models={routerModels?.["vercel-ai-gateway"] ?? {}}
|
||||
modelIdKey="vercelAiGatewayModelId"
|
||||
serviceName="Vercel AI Gateway"
|
||||
serviceUrl="https://vercel.com/ai-gateway/models"
|
||||
organizationAllowList={organizationAllowList}
|
||||
errorMessage={modelValidationError}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -28,3 +28,4 @@ export { ZAi } from "./ZAi"
|
|||
export { LiteLLM } from "./LiteLLM"
|
||||
export { Fireworks } from "./Fireworks"
|
||||
export { Featherless } from "./Featherless"
|
||||
export { VercelAiGateway } from "./VercelAiGateway"
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import {
|
|||
rooModels,
|
||||
qwenCodeDefaultModelId,
|
||||
qwenCodeModels,
|
||||
vercelAiGatewayDefaultModelId,
|
||||
BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
|
||||
} from "@roo-code/types"
|
||||
|
||||
|
|
@ -317,6 +318,11 @@ function getSelectedModel({
|
|||
const info = qwenCodeModels[id as keyof typeof qwenCodeModels]
|
||||
return { id, info }
|
||||
}
|
||||
case "vercel-ai-gateway": {
|
||||
const id = apiConfiguration.vercelAiGatewayModelId ?? vercelAiGatewayDefaultModelId
|
||||
const info = routerModels["vercel-ai-gateway"]?.[id]
|
||||
return { id, info }
|
||||
}
|
||||
// case "anthropic":
|
||||
// case "human-relay":
|
||||
// case "fake-ai":
|
||||
|
|
|
|||
|
|
@ -83,6 +83,14 @@ const WelcomeView = () => {
|
|||
description: t("welcome:routers.openrouter.description"),
|
||||
authUrl: getOpenRouterAuthUrl(uriScheme),
|
||||
},
|
||||
{
|
||||
slug: "vercel-ai-gateway",
|
||||
name: "Vercel AI Gateway",
|
||||
description: t("welcome:routers.vercelAiGateway.description"),
|
||||
incentive: t("welcome:routers.vercelAiGateway.incentive"),
|
||||
authUrl:
|
||||
"https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys&title=AI+Gateway+API+Key",
|
||||
},
|
||||
]
|
||||
|
||||
// Shuffle providers based on machine ID (will be consistent for the same machine)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@
|
|||
},
|
||||
"openrouter": {
|
||||
"description": "A unified interface for LLMs"
|
||||
},
|
||||
"vercelAiGateway": {
|
||||
"description": "The AI Gateway for Developers",
|
||||
"incentive": "$5 free credits to use with any model"
|
||||
}
|
||||
},
|
||||
"chooseProvider": "To do its magic, Roo needs an API key.",
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ describe("Model Validation Functions", () => {
|
|||
ollama: {},
|
||||
lmstudio: {},
|
||||
"io-intelligence": {},
|
||||
"vercel-ai-gateway": {},
|
||||
}
|
||||
|
||||
const allowAllOrganization: OrganizationAllowList = {
|
||||
|
|
|
|||
|
|
@ -136,6 +136,11 @@ function validateModelsAndKeysProvided(apiConfiguration: ProviderSettings): stri
|
|||
return i18next.t("settings:validation.qwenCodeOauthPath")
|
||||
}
|
||||
break
|
||||
case "vercel-ai-gateway":
|
||||
if (!apiConfiguration.vercelAiGatewayApiKey) {
|
||||
return i18next.t("settings:validation.apiKey")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
|
@ -204,6 +209,8 @@ function getModelIdForProvider(apiConfiguration: ProviderSettings, provider: str
|
|||
return apiConfiguration.huggingFaceModelId
|
||||
case "io-intelligence":
|
||||
return apiConfiguration.ioIntelligenceModelId
|
||||
case "vercel-ai-gateway":
|
||||
return apiConfiguration.vercelAiGatewayModelId
|
||||
default:
|
||||
return apiConfiguration.apiModelId
|
||||
}
|
||||
|
|
@ -277,6 +284,9 @@ export function validateModelId(apiConfiguration: ProviderSettings, routerModels
|
|||
case "io-intelligence":
|
||||
modelId = apiConfiguration.ioIntelligenceModelId
|
||||
break
|
||||
case "vercel-ai-gateway":
|
||||
modelId = apiConfiguration.vercelAiGatewayModelId
|
||||
break
|
||||
}
|
||||
|
||||
if (!modelId) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue