diff --git a/packages/app/server/src/providers/ProviderFactory.ts b/packages/app/server/src/providers/ProviderFactory.ts index ffa6e96ca..1b25dc2c8 100644 --- a/packages/app/server/src/providers/ProviderFactory.ts +++ b/packages/app/server/src/providers/ProviderFactory.ts @@ -26,6 +26,7 @@ import { VertexAIProvider, PROXY_PASSTHROUGH_ONLY_MODEL as VertexAIProxyPassthroughOnlyModel, } from './VertexAIProvider'; +import { VercelAIGatewayProvider } from './VercelAIGatewayProvider'; /** * Creates model-to-provider mapping from the model_prices_and_context_window.json file. @@ -58,6 +59,9 @@ const createChatModelToProviderMapping = (): Record => { case 'Xai': mapping[modelConfig.model_id] = ProviderType.XAI; break; + case 'VercelAIGateway': + mapping[modelConfig.model_id] = ProviderType.VERCEL_AI_GATEWAY; + break; // Add other providers as needed default: // Skip models with unsupported providers @@ -192,6 +196,8 @@ export const getProvider = ( return new GroqProvider(stream, model); case ProviderType.XAI: return new XAIProvider(stream, model); + case ProviderType.VERCEL_AI_GATEWAY: + return new VercelAIGatewayProvider(stream, model); default: throw new Error(`Unknown provider type: ${type}`); } diff --git a/packages/app/server/src/providers/ProviderType.ts b/packages/app/server/src/providers/ProviderType.ts index b2514ac80..41ded7703 100644 --- a/packages/app/server/src/providers/ProviderType.ts +++ b/packages/app/server/src/providers/ProviderType.ts @@ -12,4 +12,5 @@ export enum ProviderType { OPENAI_VIDEOS = 'OPENAI_VIDEOS', GROQ = 'GROQ', XAI = 'XAI', + VERCEL_AI_GATEWAY = 'VERCEL_AI_GATEWAY', } diff --git a/packages/app/server/src/providers/VercelAIGatewayProvider.ts b/packages/app/server/src/providers/VercelAIGatewayProvider.ts new file mode 100644 index 000000000..60a697396 --- /dev/null +++ b/packages/app/server/src/providers/VercelAIGatewayProvider.ts @@ -0,0 +1,79 @@ +import { LlmTransactionMetadata, Transaction } from '../types'; +import { BaseProvider } from './BaseProvider'; +import { ProviderType } from './ProviderType'; +import { getCostPerToken } from '../services/AccountingService'; +import logger from '../logger'; +import { parseSSEGPTFormat, CompletionStateBody } from './GPTProvider'; + + +export class VercelAIGatewayProvider extends BaseProvider { + protected readonly VERCEL_AI_GATEWAY_BASE_URL = 'https://ai-gateway.vercel.sh/v1'; + + getType(): ProviderType { + return ProviderType.VERCEL_AI_GATEWAY; + } + + getBaseUrl(): string { + return this.VERCEL_AI_GATEWAY_BASE_URL; + } + + getApiKey(): string | undefined { + return process.env.AI_GATEWAY_API_KEY; + } + + async handleBody(data: string): Promise { + try { + let prompt_tokens = 0; + let completion_tokens = 0; + let total_tokens = 0; + let providerId = 'null'; + + if (this.getIsStream()) { + // Parse Server-Sent Events (SSE) format for streaming responses + const chunks = parseSSEGPTFormat(data); + + for (const chunk of chunks) { + if (chunk.usage && chunk.usage !== null) { + prompt_tokens += chunk.usage.prompt_tokens; + completion_tokens += chunk.usage.completion_tokens; + total_tokens += chunk.usage.total_tokens; + } + providerId = chunk.id || 'null'; + } + } else { + // Parse non-streaming response + const parsed = JSON.parse(data) as CompletionStateBody; + prompt_tokens += parsed.usage.prompt_tokens; + completion_tokens += parsed.usage.completion_tokens; + total_tokens += parsed.usage.total_tokens; + providerId = parsed.id || 'null'; + } + + // Calculate cost based on token usage + const cost = getCostPerToken( + this.getModel(), + prompt_tokens, + completion_tokens + ); + + const metadata: LlmTransactionMetadata = { + providerId: providerId, + provider: this.getType(), + model: this.getModel(), + inputTokens: prompt_tokens, + outputTokens: completion_tokens, + totalTokens: total_tokens, + }; + + return { + metadata: metadata, + rawTransactionCost: cost, + status: 'success', + }; + } catch (error) { + logger.error(`Error processing Vercel AI Gateway response: ${error}`); + throw error; + } + } +} + diff --git a/packages/app/server/src/services/AccountingService.ts b/packages/app/server/src/services/AccountingService.ts index 02e51e14e..8ba3b92d7 100644 --- a/packages/app/server/src/services/AccountingService.ts +++ b/packages/app/server/src/services/AccountingService.ts @@ -9,7 +9,12 @@ import { SupportedModel, SupportedImageModel, SupportedVideoModel, + SupportedSpeechModel, + SupportedTranscriptionModel, XAIModels, + VercelAIGatewayModels, + OpenAISpeechModels, + OpenAITranscriptionModels, } from '@merit-systems/echo-typescript-sdk'; import { Decimal } from '@prisma/client/runtime/library'; @@ -30,6 +35,7 @@ export const ALL_SUPPORTED_MODELS: SupportedModel[] = [ ...OpenRouterModels, ...GroqModels, ...XAIModels, + ...VercelAIGatewayModels, ]; // Handle image models separately since they have different pricing structure @@ -42,6 +48,14 @@ export const ALL_SUPPORTED_VIDEO_MODELS: SupportedVideoModel[] = [ ...OpenAIVideoModels, ]; +export const ALL_SUPPORTED_SPEECH_MODELS: SupportedSpeechModel[] = [ + ...OpenAISpeechModels, +]; + +export const ALL_SUPPORTED_TRANSCRIPTION_MODELS: SupportedTranscriptionModel[] = [ + ...OpenAITranscriptionModels, +]; + // Create a lookup map for O(1) model price retrieval const MODEL_PRICE_MAP = new Map(); ALL_SUPPORTED_MODELS.forEach(model => { @@ -60,6 +74,18 @@ ALL_SUPPORTED_VIDEO_MODELS.forEach(model => { VIDEO_MODEL_MAP.set(model.model_id, model); }); +// Create a separate map for speech models +const SPEECH_MODEL_MAP = new Map(); +ALL_SUPPORTED_SPEECH_MODELS.forEach(model => { + SPEECH_MODEL_MAP.set(model.model_id, model); +}); + +// Create a separate map for transcription models +const TRANSCRIPTION_MODEL_MAP = new Map(); +ALL_SUPPORTED_TRANSCRIPTION_MODELS.forEach(model => { + TRANSCRIPTION_MODEL_MAP.set(model.model_id, model); +}); + export const getModelPrice = (model: string) => { const supportedModel = MODEL_PRICE_MAP.get(model); @@ -114,6 +140,92 @@ export const isValidVideoModel = (model: string) => { return VIDEO_MODEL_MAP.has(model); }; +export const isValidSpeechModel = (model: string) => { + return SPEECH_MODEL_MAP.has(model); +}; + +export const isValidTranscriptionModel = (model: string) => { + return TRANSCRIPTION_MODEL_MAP.has(model); +}; + +export const getSpeechModelPrice = ( + model: string +): SupportedSpeechModel | null => { + const speechModel = SPEECH_MODEL_MAP.get(model); + if (speechModel) { + return speechModel; + } + return null; +}; + +export const getTranscriptionModelPrice = ( + model: string +): SupportedTranscriptionModel | null => { + const transcriptionModel = TRANSCRIPTION_MODEL_MAP.get(model); + if (transcriptionModel) { + return transcriptionModel; + } + return null; +}; + +/** + * Calculate cost for speech generation (text-to-speech) + * @param model - Speech model ID + * @param characterCount - Number of characters in the input text + * @returns Cost in dollars + */ +export const getSpeechCost = ( + model: string, + characterCount: number +): Decimal => { + if (!isValidSpeechModel(model)) { + throw new UnknownModelError(`Invalid speech model: ${model}`); + } + + const modelPrice = getSpeechModelPrice(model); + if (!modelPrice) { + throw new Error(`Pricing information not found for speech model: ${model}`); + } + + const cost = new Decimal(modelPrice.cost_per_character).mul(characterCount); + + if (cost.lessThan(0)) { + throw new Error(`Invalid cost for speech model: ${model}`); + } + + return cost; +}; + +/** + * Calculate cost for transcription (speech-to-text) + * @param model - Transcription model ID + * @param audioSeconds - Duration of audio in seconds + * @returns Cost in dollars + */ +export const getTranscriptionCost = ( + model: string, + audioSeconds: number +): Decimal => { + if (!isValidTranscriptionModel(model)) { + throw new UnknownModelError(`Invalid transcription model: ${model}`); + } + + const modelPrice = getTranscriptionModelPrice(model); + if (!modelPrice) { + throw new Error( + `Pricing information not found for transcription model: ${model}` + ); + } + + const cost = new Decimal(modelPrice.cost_per_second).mul(audioSeconds); + + if (cost.lessThan(0)) { + throw new Error(`Invalid cost for transcription model: ${model}`); + } + + return cost; +}; + export const getCostPerToken = ( model: string, inputTokens: number, diff --git a/packages/app/server/src/services/EchoControlService.ts b/packages/app/server/src/services/EchoControlService.ts index dca4e6558..95021cced 100644 --- a/packages/app/server/src/services/EchoControlService.ts +++ b/packages/app/server/src/services/EchoControlService.ts @@ -32,7 +32,8 @@ export class EchoControlService { constructor(db: PrismaClient, apiKey: string) { // Check if the generated Prisma client exists - const generatedPrismaPath = join(__dirname, 'generated', 'prisma'); + // Note: __dirname is /src/services/, so we need to go up one level to /src/ + const generatedPrismaPath = join(__dirname, '..', 'generated', 'prisma'); if (!existsSync(generatedPrismaPath)) { throw new Error( `Generated Prisma client not found at ${generatedPrismaPath}. ` + diff --git a/packages/sdk/next/src/ai-providers/index.ts b/packages/sdk/next/src/ai-providers/index.ts index 4ea2555df..70041b4e1 100644 --- a/packages/sdk/next/src/ai-providers/index.ts +++ b/packages/sdk/next/src/ai-providers/index.ts @@ -3,3 +3,4 @@ export * from './google'; export * from './xai'; export * from './groq'; export * from './openai'; +export * from './vercel-ai-gateway'; diff --git a/packages/sdk/next/src/ai-providers/vercel-ai-gateway.ts b/packages/sdk/next/src/ai-providers/vercel-ai-gateway.ts new file mode 100644 index 000000000..f96a5dfac --- /dev/null +++ b/packages/sdk/next/src/ai-providers/vercel-ai-gateway.ts @@ -0,0 +1,11 @@ +import { + createEchoVercelAIGateway as createEchoVercelAIGatewayBase, + EchoConfig, +} from '@merit-systems/echo-typescript-sdk'; +import { getEchoToken } from '../auth/token-manager'; + + +export function createEchoVercelAIGateway(config: EchoConfig) { + return createEchoVercelAIGatewayBase(config, async () => getEchoToken(config)); +} + diff --git a/packages/sdk/ts/package.json b/packages/sdk/ts/package.json index 50878110a..b26f7791d 100644 --- a/packages/sdk/ts/package.json +++ b/packages/sdk/ts/package.json @@ -29,7 +29,8 @@ "update-models:gemini": "tsx scripts/update-gemini-models.ts", "update-models:openrouter": "tsx scripts/update-openrouter-models.ts", "update-models:groq": "tsx scripts/update-groq-models.ts", - "update-all-models": "pnpm run update-models:openai && pnpm run update-models:anthropic && pnpm run update-models:gemini && pnpm run update-models:openrouter && pnpm run update-models:groq", + "update-models:vercel-ai-gateway": "tsx scripts/update-vercel-ai-gateway-models.ts", + "update-all-models": "pnpm run update-models:openai && pnpm run update-models:anthropic && pnpm run update-models:gemini && pnpm run update-models:openrouter && pnpm run update-models:groq && pnpm run update-models:vercel-ai-gateway", "prepublishOnly": "pnpm run build" }, "keywords": [ @@ -58,6 +59,7 @@ ], "dependencies": { "@ai-sdk/anthropic": "2.0.17", + "@ai-sdk/gateway": "^1.0.12", "@ai-sdk/google": "2.0.14", "@ai-sdk/groq": "2.0.17", "@ai-sdk/openai": "2.0.32", diff --git a/packages/sdk/ts/scripts/update-vercel-ai-gateway-models.ts b/packages/sdk/ts/scripts/update-vercel-ai-gateway-models.ts new file mode 100644 index 000000000..7b4ccaf44 --- /dev/null +++ b/packages/sdk/ts/scripts/update-vercel-ai-gateway-models.ts @@ -0,0 +1,130 @@ +import { config } from 'dotenv'; +import { gateway } from '@ai-sdk/gateway'; +import { + generateModelFile, + SupportedModel, + updateProviderModels, + ProviderConfig, +} from './update-models'; + +config(); + +async function fetchVercelAIGatewayModels(): Promise { + console.log('šŸ“” Fetching models from Vercel AI Gateway...'); + + const availableModels = await gateway.getAvailableModels(); + + const modelIds = availableModels.models + .filter(model => model.modelType === 'language') + .map(model => model.id); + + console.log(`Found ${modelIds.length} language models in gateway`); + + return modelIds; +} + +function normalizeVercelAIGatewayModelId(modelId: string): string { + return modelId; // Keep full format: provider/model +} + +async function fetchGatewayModelsWithPricing(): Promise { + console.log('šŸ’° Fetching models and pricing from Vercel AI Gateway...'); + + const availableModels = await gateway.getAvailableModels(); + + // Process all language models + const models = availableModels.models + .filter(model => model.modelType === 'language') + .map(model => { + console.log(`Processing: ${model.id}`); + + // Validate pricing information + if (!model.pricing) { + console.warn(`āš ļø Model ${model.id} missing pricing - skipping`); + return null; + } + + if ( + model.pricing.input === undefined || + model.pricing.input === null || + model.pricing.output === undefined || + model.pricing.output === null + ) { + console.warn(`āš ļø Model ${model.id} has incomplete pricing - skipping`); + return null; + } + + const inputCost = Number(model.pricing.input); + const outputCost = Number(model.pricing.output); + + // Validate costs are valid numbers + if (isNaN(inputCost) || isNaN(outputCost)) { + console.warn(`āš ļø Model ${model.id} has invalid pricing - skipping`); + return null; + } + + return { + model_id: model.id, // Keep full format: provider/model + input_cost_per_token: inputCost, + output_cost_per_token: outputCost, + provider: 'VercelAIGateway', + }; + }) + .filter((model): model is SupportedModel => model !== null); + + console.log(`āœ… Successfully fetched ${models.length} models with pricing`); + + return models; +} + +async function updateVercelAIGatewayModels(): Promise { + try { + console.log('šŸ”„ Starting Vercel AI Gateway model update process...\n'); + + // Check for API key + const apiKey = process.env.AI_GATEWAY_API_KEY; + if (!apiKey) { + throw new Error('AI_GATEWAY_API_KEY environment variable is required'); + } + + // Fetch models with pricing directly from gateway + const models = await fetchGatewayModelsWithPricing(); + + if (models.length === 0) { + console.warn('āš ļø No models found in gateway'); + return; + } + + // Generate the file content + const fileContent = generateModelFile( + models, + 'VercelAIGateway', + 'VercelAIGateway' + ); + + // Write the file + const { writeFileSync } = await import('fs'); + const { join } = await import('path'); + const outputPath = 'src/supported-models/chat/vercel-ai-gateway.ts'; + const fullPath = join(process.cwd(), outputPath); + writeFileSync(fullPath, fileContent, 'utf8'); + + console.log( + `\nāœ… Successfully updated vercel-ai-gateway.ts with ${models.length} models` + ); + console.log(`šŸ“Š Models included:`); + models.forEach(model => { + console.log(` - ${model.model_id}`); + }); + } catch (error) { + console.error('āŒ Error updating Vercel AI Gateway models:', error); + process.exit(1); + } +} + +export { updateVercelAIGatewayModels, fetchVercelAIGatewayModels }; + +if (import.meta.url === `file://${process.argv[1]}`) { + updateVercelAIGatewayModels(); +} + diff --git a/packages/sdk/ts/src/index.ts b/packages/sdk/ts/src/index.ts index 521a30f93..1b1af325a 100644 --- a/packages/sdk/ts/src/index.ts +++ b/packages/sdk/ts/src/index.ts @@ -30,6 +30,8 @@ export type { WebSearchModelPricing, WebSearchPricing, SupportedVideoModel, + SupportedSpeechModel, + SupportedTranscriptionModel, } from './supported-models/types'; export { @@ -49,6 +51,8 @@ export { GroqModels } from './supported-models/chat/groq'; export type { GroqModel } from './supported-models/chat/groq'; export { XAIModels } from './supported-models/chat/xai'; export type { XAIModel } from './supported-models/chat/xai'; +export { VercelAIGatewayModels } from './supported-models/chat/vercel-ai-gateway'; +export type { VercelAIGatewayModel } from './supported-models/chat/vercel-ai-gateway'; export { OpenAIImageModels } from './supported-models/image/openai'; export type { OpenAIImageModel } from './supported-models/image/openai'; export { GeminiVideoModels } from './supported-models/video/gemini'; @@ -57,3 +61,7 @@ export { VertexAIVideoModels } from './supported-models/video/vertex-ai'; export type { VertexAIVideoModel } from './supported-models/video/vertex-ai'; export { OpenAIVideoModels } from './supported-models/video/open-ai'; export type { OpenAIVideoModel } from './supported-models/video/open-ai'; +export { OpenAISpeechModels } from './supported-models/speech/openai'; +export type { OpenAISpeechModel } from './supported-models/speech/openai'; +export { OpenAITranscriptionModels } from './supported-models/transcription/openai'; +export type { OpenAITranscriptionModel } from './supported-models/transcription/openai'; diff --git a/packages/sdk/ts/src/providers/index.ts b/packages/sdk/ts/src/providers/index.ts index 62f54fac8..a9024c7f0 100644 --- a/packages/sdk/ts/src/providers/index.ts +++ b/packages/sdk/ts/src/providers/index.ts @@ -4,6 +4,7 @@ export * from './groq'; export * from './xai'; export * from './openai'; export * from './openrouter'; +export * from './vercel-ai-gateway'; export function echoFetch( originalFetch: typeof fetch, diff --git a/packages/sdk/ts/src/providers/vercel-ai-gateway.ts b/packages/sdk/ts/src/providers/vercel-ai-gateway.ts new file mode 100644 index 000000000..9cb3923df --- /dev/null +++ b/packages/sdk/ts/src/providers/vercel-ai-gateway.ts @@ -0,0 +1,23 @@ +import { createOpenAI } from '@ai-sdk/openai'; +import { ROUTER_BASE_URL } from '../config'; +import { EchoConfig } from '../types'; +import { validateAppId } from '../utils/validation'; +import { echoFetch } from './index'; + +export function createEchoVercelAIGateway( + { appId, baseRouterUrl = ROUTER_BASE_URL }: EchoConfig, + getTokenFn: (appId: string) => Promise, + onInsufficientFunds?: () => void +) { + validateAppId(appId, 'createEchoVercelAIGateway'); + + return createOpenAI({ + baseURL: baseRouterUrl, + apiKey: 'placeholder_replaced_by_echoFetch', + fetch: echoFetch( + fetch, + async () => await getTokenFn(appId), + onInsufficientFunds + ), + }); +} diff --git a/packages/sdk/ts/src/resources/models.ts b/packages/sdk/ts/src/resources/models.ts index ee4064d98..0dfdad80d 100644 --- a/packages/sdk/ts/src/resources/models.ts +++ b/packages/sdk/ts/src/resources/models.ts @@ -6,10 +6,15 @@ import { GeminiModels, OpenRouterModels, OpenAIImageModels, + VercelAIGatewayModels, SupportedModel, SupportedImageModel, SupportedVideoModel, + SupportedSpeechModel, + SupportedTranscriptionModel, GeminiVideoModels, + OpenAISpeechModels, + OpenAITranscriptionModels, } from '../supported-models'; export class ModelsResource extends BaseResource { @@ -18,7 +23,7 @@ export class ModelsResource extends BaseResource { } /** - * Get supported models as a flat array of model names + * Get supported chat models as a flat array */ async listSupportedChatModels(): Promise { const allModels = [ @@ -26,16 +31,39 @@ export class ModelsResource extends BaseResource { ...AnthropicModels, ...GeminiModels, ...OpenRouterModels, + ...VercelAIGatewayModels, ]; return allModels; } + /** + * Get supported image models + */ async listSupportedImageModels(): Promise { return OpenAIImageModels; } + /** + * Get supported video models + */ async listSupportedVideoModels(): Promise { return GeminiVideoModels; } + + /** + * Get supported speech (text-to-speech) models + */ + async listSupportedSpeechModels(): Promise { + return OpenAISpeechModels; + } + + /** + * Get supported transcription (speech-to-text) models + */ + async listSupportedTranscriptionModels(): Promise< + SupportedTranscriptionModel[] + > { + return OpenAITranscriptionModels; + } } diff --git a/packages/sdk/ts/src/supported-models/chat/vercel-ai-gateway.ts b/packages/sdk/ts/src/supported-models/chat/vercel-ai-gateway.ts new file mode 100644 index 000000000..2bddaeeb7 --- /dev/null +++ b/packages/sdk/ts/src/supported-models/chat/vercel-ai-gateway.ts @@ -0,0 +1,868 @@ +import { SupportedModel } from '../types'; + +// Union type of all valid VercelAIGateway model IDs +export type VercelAIGatewayModel = + | 'alibaba/qwen-3-14b' + | 'alibaba/qwen-3-235b' + | 'alibaba/qwen-3-30b' + | 'alibaba/qwen-3-32b' + | 'alibaba/qwen3-coder' + | 'alibaba/qwen3-coder-30b-a3b' + | 'alibaba/qwen3-coder-plus' + | 'alibaba/qwen3-max' + | 'alibaba/qwen3-max-preview' + | 'alibaba/qwen3-next-80b-a3b-instruct' + | 'alibaba/qwen3-next-80b-a3b-thinking' + | 'alibaba/qwen3-vl-instruct' + | 'alibaba/qwen3-vl-thinking' + | 'amazon/nova-lite' + | 'amazon/nova-micro' + | 'amazon/nova-pro' + | 'anthropic/claude-3-haiku' + | 'anthropic/claude-3-opus' + | 'anthropic/claude-3.5-haiku' + | 'anthropic/claude-3.5-sonnet' + | 'anthropic/claude-3.5-sonnet-20240620' + | 'anthropic/claude-3.7-sonnet' + | 'anthropic/claude-haiku-4.5' + | 'anthropic/claude-opus-4' + | 'anthropic/claude-opus-4.1' + | 'anthropic/claude-sonnet-4' + | 'anthropic/claude-sonnet-4.5' + | 'cohere/command-a' + | 'cohere/command-r' + | 'cohere/command-r-plus' + | 'deepseek/deepseek-r1' + | 'deepseek/deepseek-r1-distill-llama-70b' + | 'deepseek/deepseek-v3' + | 'deepseek/deepseek-v3.1' + | 'deepseek/deepseek-v3.1-base' + | 'deepseek/deepseek-v3.1-terminus' + | 'deepseek/deepseek-v3.2-exp' + | 'deepseek/deepseek-v3.2-exp-thinking' + | 'google/gemini-2.0-flash' + | 'google/gemini-2.0-flash-lite' + | 'google/gemini-2.5-flash' + | 'google/gemini-2.5-flash-image' + | 'google/gemini-2.5-flash-image-preview' + | 'google/gemini-2.5-flash-lite' + | 'google/gemini-2.5-flash-lite-preview-09-2025' + | 'google/gemini-2.5-flash-preview-09-2025' + | 'google/gemini-2.5-pro' + | 'google/gemma-2-9b' + | 'inception/mercury-coder-small' + | 'meituan/longcat-flash-chat' + | 'meituan/longcat-flash-thinking' + | 'meta/llama-3-70b' + | 'meta/llama-3-8b' + | 'meta/llama-3.1-70b' + | 'meta/llama-3.1-8b' + | 'meta/llama-3.2-11b' + | 'meta/llama-3.2-1b' + | 'meta/llama-3.2-3b' + | 'meta/llama-3.2-90b' + | 'meta/llama-3.3-70b' + | 'meta/llama-4-maverick' + | 'meta/llama-4-scout' + | 'minimax/minimax-m2' + | 'mistral/codestral' + | 'mistral/devstral-small' + | 'mistral/magistral-medium' + | 'mistral/magistral-medium-2506' + | 'mistral/magistral-small' + | 'mistral/magistral-small-2506' + | 'mistral/ministral-3b' + | 'mistral/ministral-8b' + | 'mistral/mistral-large' + | 'mistral/mistral-medium' + | 'mistral/mistral-small' + | 'mistral/mixtral-8x22b-instruct' + | 'mistral/pixtral-12b' + | 'mistral/pixtral-large' + | 'moonshotai/kimi-k2' + | 'moonshotai/kimi-k2-0905' + | 'moonshotai/kimi-k2-turbo' + | 'morph/morph-v3-fast' + | 'morph/morph-v3-large' + | 'openai/gpt-3.5-turbo' + | 'openai/gpt-3.5-turbo-instruct' + | 'openai/gpt-4-turbo' + | 'openai/gpt-4.1' + | 'openai/gpt-4.1-mini' + | 'openai/gpt-4.1-nano' + | 'openai/gpt-4o' + | 'openai/gpt-4o-mini' + | 'openai/gpt-5' + | 'openai/gpt-5-codex' + | 'openai/gpt-5-mini' + | 'openai/gpt-5-nano' + | 'openai/gpt-5-pro' + | 'openai/gpt-oss-120b' + | 'openai/gpt-oss-20b' + | 'openai/o1' + | 'openai/o3' + | 'openai/o3-mini' + | 'openai/o4-mini' + | 'perplexity/sonar' + | 'perplexity/sonar-pro' + | 'perplexity/sonar-reasoning' + | 'perplexity/sonar-reasoning-pro' + | 'stealth/sonoma-dusk-alpha' + | 'stealth/sonoma-sky-alpha' + | 'vercel/v0-1.0-md' + | 'vercel/v0-1.5-md' + | 'xai/grok-2' + | 'xai/grok-2-vision' + | 'xai/grok-3' + | 'xai/grok-3-fast' + | 'xai/grok-3-mini' + | 'xai/grok-3-mini-fast' + | 'xai/grok-4' + | 'xai/grok-4-fast-non-reasoning' + | 'xai/grok-4-fast-reasoning' + | 'xai/grok-code-fast-1' + | 'zai/glm-4.5' + | 'zai/glm-4.5-air' + | 'zai/glm-4.5v' + | 'zai/glm-4.6'; + +export const VercelAIGatewayModels: SupportedModel[] = [ + { + model_id: 'alibaba/qwen-3-14b', + input_cost_per_token: 6e-8, + output_cost_per_token: 2.4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen-3-235b', + input_cost_per_token: 1.3e-7, + output_cost_per_token: 6e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen-3-30b', + input_cost_per_token: 8e-8, + output_cost_per_token: 2.9e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen-3-32b', + input_cost_per_token: 1e-7, + output_cost_per_token: 3e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-coder', + input_cost_per_token: 4e-7, + output_cost_per_token: 0.0000016, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-coder-30b-a3b', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 6e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-coder-plus', + input_cost_per_token: 0.000001, + output_cost_per_token: 0.000005, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-max', + input_cost_per_token: 0.0000012, + output_cost_per_token: 0.000006, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-max-preview', + input_cost_per_token: 0.0000012, + output_cost_per_token: 0.000006, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-next-80b-a3b-instruct', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 0.0000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-next-80b-a3b-thinking', + input_cost_per_token: 1.4e-7, + output_cost_per_token: 0.0000014, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-vl-instruct', + input_cost_per_token: 7e-7, + output_cost_per_token: 0.0000028, + provider: 'VercelAIGateway', + }, + { + model_id: 'alibaba/qwen3-vl-thinking', + input_cost_per_token: 7e-7, + output_cost_per_token: 0.0000084, + provider: 'VercelAIGateway', + }, + { + model_id: 'amazon/nova-lite', + input_cost_per_token: 6e-8, + output_cost_per_token: 2.4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'amazon/nova-micro', + input_cost_per_token: 3.5e-8, + output_cost_per_token: 1.4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'amazon/nova-pro', + input_cost_per_token: 8e-7, + output_cost_per_token: 0.0000032, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-3-haiku', + input_cost_per_token: 2.5e-7, + output_cost_per_token: 0.00000125, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-3-opus', + input_cost_per_token: 0.000015, + output_cost_per_token: 0.000075, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-3.5-haiku', + input_cost_per_token: 8e-7, + output_cost_per_token: 0.000004, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-3.5-sonnet', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-3.5-sonnet-20240620', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-3.7-sonnet', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-haiku-4.5', + input_cost_per_token: 0.000001, + output_cost_per_token: 0.000005, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-opus-4', + input_cost_per_token: 0.000015, + output_cost_per_token: 0.000075, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-opus-4.1', + input_cost_per_token: 0.000015, + output_cost_per_token: 0.000075, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-sonnet-4', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'anthropic/claude-sonnet-4.5', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'cohere/command-a', + input_cost_per_token: 0.0000025, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'cohere/command-r', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 6e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'cohere/command-r-plus', + input_cost_per_token: 0.0000025, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-r1', + input_cost_per_token: 7.9e-7, + output_cost_per_token: 0.000004, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-r1-distill-llama-70b', + input_cost_per_token: 7.5e-7, + output_cost_per_token: 9.9e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-v3', + input_cost_per_token: 7.7e-7, + output_cost_per_token: 7.7e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-v3.1', + input_cost_per_token: 1.999e-7, + output_cost_per_token: 8.001e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-v3.1-base', + input_cost_per_token: 1.999e-7, + output_cost_per_token: 8.001e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-v3.1-terminus', + input_cost_per_token: 2.7e-7, + output_cost_per_token: 0.000001, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-v3.2-exp', + input_cost_per_token: 2.7e-7, + output_cost_per_token: 4.1e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'deepseek/deepseek-v3.2-exp-thinking', + input_cost_per_token: 2.8e-7, + output_cost_per_token: 4.2e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.0-flash', + input_cost_per_token: 1e-7, + output_cost_per_token: 4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.0-flash-lite', + input_cost_per_token: 7.5e-8, + output_cost_per_token: 3e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-flash', + input_cost_per_token: 3e-7, + output_cost_per_token: 0.0000025, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-flash-image', + input_cost_per_token: 3e-7, + output_cost_per_token: 0.0000025, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-flash-image-preview', + input_cost_per_token: 3e-7, + output_cost_per_token: 0.0000025, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-flash-lite', + input_cost_per_token: 1e-7, + output_cost_per_token: 4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-flash-lite-preview-09-2025', + input_cost_per_token: 1e-7, + output_cost_per_token: 4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-flash-preview-09-2025', + input_cost_per_token: 3e-7, + output_cost_per_token: 0.0000025, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemini-2.5-pro', + input_cost_per_token: 0.0000025, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'google/gemma-2-9b', + input_cost_per_token: 2e-7, + output_cost_per_token: 2e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'inception/mercury-coder-small', + input_cost_per_token: 2.5e-7, + output_cost_per_token: 0.000001, + provider: 'VercelAIGateway', + }, + { + model_id: 'meituan/longcat-flash-chat', + input_cost_per_token: 0, + output_cost_per_token: 0, + provider: 'VercelAIGateway', + }, + { + model_id: 'meituan/longcat-flash-thinking', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 0.0000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3-70b', + input_cost_per_token: 5.9e-7, + output_cost_per_token: 7.9e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3-8b', + input_cost_per_token: 5e-8, + output_cost_per_token: 8e-8, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.1-70b', + input_cost_per_token: 7.2e-7, + output_cost_per_token: 7.2e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.1-8b', + input_cost_per_token: 5e-8, + output_cost_per_token: 8e-8, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.2-11b', + input_cost_per_token: 1.6e-7, + output_cost_per_token: 1.6e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.2-1b', + input_cost_per_token: 1e-7, + output_cost_per_token: 1e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.2-3b', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 1.5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.2-90b', + input_cost_per_token: 7.2e-7, + output_cost_per_token: 7.2e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-3.3-70b', + input_cost_per_token: 7.2e-7, + output_cost_per_token: 7.2e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-4-maverick', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 6e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'meta/llama-4-scout', + input_cost_per_token: 8e-8, + output_cost_per_token: 3e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'minimax/minimax-m2', + input_cost_per_token: 0, + output_cost_per_token: 0, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/codestral', + input_cost_per_token: 3e-7, + output_cost_per_token: 9e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/devstral-small', + input_cost_per_token: 1e-7, + output_cost_per_token: 3e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/magistral-medium', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000005, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/magistral-medium-2506', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000005, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/magistral-small', + input_cost_per_token: 5e-7, + output_cost_per_token: 0.0000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/magistral-small-2506', + input_cost_per_token: 5e-7, + output_cost_per_token: 0.0000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/ministral-3b', + input_cost_per_token: 4e-8, + output_cost_per_token: 4e-8, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/ministral-8b', + input_cost_per_token: 1e-7, + output_cost_per_token: 1e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/mistral-large', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000006, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/mistral-medium', + input_cost_per_token: 4e-7, + output_cost_per_token: 0.000002, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/mistral-small', + input_cost_per_token: 1e-7, + output_cost_per_token: 3e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/mixtral-8x22b-instruct', + input_cost_per_token: 0.0000012, + output_cost_per_token: 0.0000012, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/pixtral-12b', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 1.5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'mistral/pixtral-large', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000006, + provider: 'VercelAIGateway', + }, + { + model_id: 'moonshotai/kimi-k2', + input_cost_per_token: 5e-7, + output_cost_per_token: 0.000002, + provider: 'VercelAIGateway', + }, + { + model_id: 'moonshotai/kimi-k2-0905', + input_cost_per_token: 6e-7, + output_cost_per_token: 0.0000025, + provider: 'VercelAIGateway', + }, + { + model_id: 'moonshotai/kimi-k2-turbo', + input_cost_per_token: 0.0000024, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'morph/morph-v3-fast', + input_cost_per_token: 8e-7, + output_cost_per_token: 0.0000012, + provider: 'VercelAIGateway', + }, + { + model_id: 'morph/morph-v3-large', + input_cost_per_token: 9e-7, + output_cost_per_token: 0.0000019, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-3.5-turbo', + input_cost_per_token: 5e-7, + output_cost_per_token: 0.0000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-3.5-turbo-instruct', + input_cost_per_token: 0.0000015, + output_cost_per_token: 0.000002, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-4-turbo', + input_cost_per_token: 0.00001, + output_cost_per_token: 0.00003, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-4.1', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000008, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-4.1-mini', + input_cost_per_token: 4e-7, + output_cost_per_token: 0.0000016, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-4.1-nano', + input_cost_per_token: 1e-7, + output_cost_per_token: 4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-4o', + input_cost_per_token: 0.0000025, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-4o-mini', + input_cost_per_token: 1.5e-7, + output_cost_per_token: 6e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-5', + input_cost_per_token: 0.00000125, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-5-codex', + input_cost_per_token: 0.00000125, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-5-mini', + input_cost_per_token: 2.5e-7, + output_cost_per_token: 0.000002, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-5-nano', + input_cost_per_token: 5e-8, + output_cost_per_token: 4e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-5-pro', + input_cost_per_token: 0.000015, + output_cost_per_token: 0.00012, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-oss-120b', + input_cost_per_token: 1e-7, + output_cost_per_token: 5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/gpt-oss-20b', + input_cost_per_token: 7e-8, + output_cost_per_token: 3e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/o1', + input_cost_per_token: 0.000015, + output_cost_per_token: 0.00006, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/o3', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000008, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/o3-mini', + input_cost_per_token: 0.0000011, + output_cost_per_token: 0.0000044, + provider: 'VercelAIGateway', + }, + { + model_id: 'openai/o4-mini', + input_cost_per_token: 0.0000011, + output_cost_per_token: 0.0000044, + provider: 'VercelAIGateway', + }, + { + model_id: 'perplexity/sonar', + input_cost_per_token: 0.000001, + output_cost_per_token: 0.000001, + provider: 'VercelAIGateway', + }, + { + model_id: 'perplexity/sonar-pro', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'perplexity/sonar-reasoning', + input_cost_per_token: 0.000001, + output_cost_per_token: 0.000005, + provider: 'VercelAIGateway', + }, + { + model_id: 'perplexity/sonar-reasoning-pro', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.000008, + provider: 'VercelAIGateway', + }, + { + model_id: 'stealth/sonoma-dusk-alpha', + input_cost_per_token: 2e-7, + output_cost_per_token: 5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'stealth/sonoma-sky-alpha', + input_cost_per_token: 2e-7, + output_cost_per_token: 5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'vercel/v0-1.0-md', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'vercel/v0-1.5-md', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-2', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-2-vision', + input_cost_per_token: 0.000002, + output_cost_per_token: 0.00001, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-3', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-3-fast', + input_cost_per_token: 0.000005, + output_cost_per_token: 0.000025, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-3-mini', + input_cost_per_token: 3e-7, + output_cost_per_token: 5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-3-mini-fast', + input_cost_per_token: 6e-7, + output_cost_per_token: 0.000004, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-4', + input_cost_per_token: 0.000003, + output_cost_per_token: 0.000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-4-fast-non-reasoning', + input_cost_per_token: 2e-7, + output_cost_per_token: 5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-4-fast-reasoning', + input_cost_per_token: 2e-7, + output_cost_per_token: 5e-7, + provider: 'VercelAIGateway', + }, + { + model_id: 'xai/grok-code-fast-1', + input_cost_per_token: 2e-7, + output_cost_per_token: 0.0000015, + provider: 'VercelAIGateway', + }, + { + model_id: 'zai/glm-4.5', + input_cost_per_token: 6e-7, + output_cost_per_token: 0.0000022, + provider: 'VercelAIGateway', + }, + { + model_id: 'zai/glm-4.5-air', + input_cost_per_token: 2e-7, + output_cost_per_token: 0.0000011, + provider: 'VercelAIGateway', + }, + { + model_id: 'zai/glm-4.5v', + input_cost_per_token: 6e-7, + output_cost_per_token: 0.0000018, + provider: 'VercelAIGateway', + }, + { + model_id: 'zai/glm-4.6', + input_cost_per_token: 4.5e-7, + output_cost_per_token: 0.0000018, + provider: 'VercelAIGateway', + }, +]; diff --git a/packages/sdk/ts/src/supported-models/index.ts b/packages/sdk/ts/src/supported-models/index.ts index 3f641501d..0dacc62fa 100644 --- a/packages/sdk/ts/src/supported-models/index.ts +++ b/packages/sdk/ts/src/supported-models/index.ts @@ -4,7 +4,10 @@ export * from './chat/anthropic'; export * from './chat/gemini'; export * from './chat/openai'; export * from './chat/openrouter'; +export * from './chat/vercel-ai-gateway'; export * from './image/openai'; export * from './responses/openai'; export * from './video/gemini'; export * from './video/vertex-ai'; +export * from './speech/openai'; +export * from './transcription/openai'; diff --git a/packages/sdk/ts/src/supported-models/speech/openai.ts b/packages/sdk/ts/src/supported-models/speech/openai.ts new file mode 100644 index 000000000..215cf0556 --- /dev/null +++ b/packages/sdk/ts/src/supported-models/speech/openai.ts @@ -0,0 +1,16 @@ +import { SupportedSpeechModel } from '../types'; + +export type OpenAISpeechModel = 'tts-1' | 'tts-1-hd'; + +export const OpenAISpeechModels: SupportedSpeechModel[] = [ + { + model_id: 'tts-1', + cost_per_character: 0.000015, // $15 per 1M characters + provider: 'OpenAI', + }, + { + model_id: 'tts-1-hd', + cost_per_character: 0.00003, // $30 per 1M characters + provider: 'OpenAI', + }, +]; diff --git a/packages/sdk/ts/src/supported-models/transcription/openai.ts b/packages/sdk/ts/src/supported-models/transcription/openai.ts new file mode 100644 index 000000000..7420079bc --- /dev/null +++ b/packages/sdk/ts/src/supported-models/transcription/openai.ts @@ -0,0 +1,12 @@ +import { SupportedTranscriptionModel } from '../types'; + + +export type OpenAITranscriptionModel = 'whisper-1'; + +export const OpenAITranscriptionModels: SupportedTranscriptionModel[] = [ + { + model_id: 'whisper-1', + cost_per_second: 0.0001, // $0.006 per minute = $0.0001 per second + provider: 'OpenAI', + }, +]; diff --git a/packages/sdk/ts/src/supported-models/types.ts b/packages/sdk/ts/src/supported-models/types.ts index 715f37664..2ea361d7c 100644 --- a/packages/sdk/ts/src/supported-models/types.ts +++ b/packages/sdk/ts/src/supported-models/types.ts @@ -84,3 +84,15 @@ export interface SupportedVideoModel { cost_per_second_without_audio: number; provider: string; } + +export interface SupportedSpeechModel { + model_id: string; + cost_per_character: number; + provider: string; +} + +export interface SupportedTranscriptionModel { + model_id: string; + cost_per_second: number; + provider: string; +} diff --git a/packages/tests/provider-smoke/package.json b/packages/tests/provider-smoke/package.json index 6519f0ad7..93abb63c6 100644 --- a/packages/tests/provider-smoke/package.json +++ b/packages/tests/provider-smoke/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@ai-sdk/anthropic": "^2.0.6", + "@ai-sdk/gateway": "^1.0.12", "@ai-sdk/google": "^2.0.7", "@ai-sdk/openai": "^2.0.16", "@merit-systems/echo-typescript-sdk": "workspace:*", diff --git a/packages/tests/provider-smoke/vercel-ai-gateway-generate-text.test.ts b/packages/tests/provider-smoke/vercel-ai-gateway-generate-text.test.ts new file mode 100644 index 000000000..b2055ae81 --- /dev/null +++ b/packages/tests/provider-smoke/vercel-ai-gateway-generate-text.test.ts @@ -0,0 +1,58 @@ +import { + VercelAIGatewayModels, + createEchoVercelAIGateway, +} from '@merit-systems/echo-typescript-sdk'; +import { generateText } from 'ai'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { + ECHO_APP_ID, + assertEnv, + baseRouterUrl, + getApiErrorDetails, + getToken, + shouldSkipModelInTests, +} from './test-helpers'; + +beforeAll(assertEnv); + +// Vercel AI Gateway models that may have issues or are too slow +export const BLACKLISTED_GATEWAY_MODELS = new Set([ + // Add any problematic Gateway models here +]); + +describe.concurrent('Vercel AI Gateway generateText per model', () => { + const vercelGateway = createEchoVercelAIGateway( + { appId: ECHO_APP_ID!, baseRouterUrl }, + getToken + ); + + for (const { model_id } of VercelAIGatewayModels) { + if ( + BLACKLISTED_GATEWAY_MODELS.has(model_id) || + shouldSkipModelInTests(model_id) + ) { + console.log( + 'Skipping generateText for blacklisted/slow model', + model_id + ); + continue; + } + + it(`Vercel AI Gateway ${model_id}`, async () => { + try { + const { text } = await generateText({ + model: vercelGateway(model_id), + prompt: 'Say hello in one word.', + }); + expect(text).toBeDefined(); + expect(text).not.toBe(''); + } catch (err) { + const details = getApiErrorDetails(err); + throw new Error( + `[generateText] Vercel AI Gateway ${model_id} failed: ${details}` + ); + } + }); + } +}); + diff --git a/packages/tests/provider-smoke/vercel-ai-gateway-stream-text.test.ts b/packages/tests/provider-smoke/vercel-ai-gateway-stream-text.test.ts new file mode 100644 index 000000000..1df6ed907 --- /dev/null +++ b/packages/tests/provider-smoke/vercel-ai-gateway-stream-text.test.ts @@ -0,0 +1,57 @@ +import { + VercelAIGatewayModels, + createEchoVercelAIGateway, +} from '@merit-systems/echo-typescript-sdk'; +import { streamText } from 'ai'; +import { BLACKLISTED_GATEWAY_MODELS } from './vercel-ai-gateway-generate-text.test'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { + ECHO_APP_ID, + assertEnv, + baseRouterUrl, + getApiErrorDetails, + getToken, + shouldSkipModelInTests, +} from './test-helpers'; + +beforeAll(assertEnv); + +describe.concurrent('Vercel AI Gateway streamText per model', () => { + const vercelGateway = createEchoVercelAIGateway( + { appId: ECHO_APP_ID!, baseRouterUrl }, + getToken + ); + + for (const { model_id } of VercelAIGatewayModels) { + if ( + BLACKLISTED_GATEWAY_MODELS.has(model_id) || + shouldSkipModelInTests(model_id) + ) { + console.log('Skipping streamText for blacklisted/slow model', model_id); + continue; + } + + it(`Vercel AI Gateway stream ${model_id}`, async () => { + try { + const { textStream } = streamText({ + model: vercelGateway(model_id), + prompt: 'Say hello in one word.', + }); + + let streamed = ''; + for await (const delta of textStream) { + streamed += delta; + } + + expect(streamed).toBeDefined(); + expect(streamed).not.toBe(''); + } catch (err) { + const details = getApiErrorDetails(err); + throw new Error( + `[streamText] Vercel AI Gateway ${model_id} failed: ${details}` + ); + } + }); + } +}); +