From 0408e9de66e3ae52ea4889c36e95348889f1ac61 Mon Sep 17 00:00:00 2001 From: bettercallzaal Date: Tue, 14 Apr 2026 20:55:23 -0400 Subject: [PATCH 1/3] feat: add OpenRouter and direct OpenAI as alternative LLM providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `createModel()` helper that resolves model strings through whichever provider is configured, with priority: 1. Vercel AI Gateway (existing production behavior) 2. OpenRouter (one key, many models) 3. Direct OpenAI (strip provider prefix) This lets contributors run the app locally without needing a Vercel AI Gateway API key — just set OPENROUTER_API_KEY or OPENAI_API_KEY instead. All AI SDK call sites updated to use createModel(). No changes to direct OpenAI API calls (video/transcription/image generation) which remain provider-specific. Also fixes bare model string "gemini-2.5-pro" to use the correct "google/gemini-2.5-pro" provider-prefixed format. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/agents/CompactAgent/createCompactAgent.ts | 3 +- .../EmailReplyAgent/createEmailReplyAgent.ts | 3 +- .../createImageGenerationAgent.ts | 3 +- .../content/createContentPromptAgent.ts | 3 +- lib/agents/generalAgent/getGeneralAgent.ts | 3 +- lib/ai/createModel.ts | 33 +++++++++++++++ lib/ai/generateArray.ts | 3 +- lib/ai/generateText.ts | 3 +- lib/ai/getAvailableModels.ts | 42 +++++++++++++++---- lib/catalog/analyzeCatalogBatch.ts | 3 +- lib/chat/toolChains/toolChains.ts | 33 ++++++++------- lib/evals/scorers/CatalogAvailability.ts | 3 +- lib/evals/scorers/QuestionAnswered.ts | 3 +- package.json | 1 + pnpm-lock.yaml | 15 +++++++ 15 files changed, 120 insertions(+), 34 deletions(-) create mode 100644 lib/ai/createModel.ts diff --git a/lib/agents/CompactAgent/createCompactAgent.ts b/lib/agents/CompactAgent/createCompactAgent.ts index 144a60284..bd5b309ff 100644 --- a/lib/agents/CompactAgent/createCompactAgent.ts +++ b/lib/agents/CompactAgent/createCompactAgent.ts @@ -1,5 +1,6 @@ import { ToolLoopAgent, stepCountIs } from "ai"; import { LIGHTWEIGHT_MODEL } from "@/lib/const"; +import { createModel } from "@/lib/ai/createModel"; const DEFAULT_INSTRUCTIONS = `You are a conversation summarizer. Create a concise summary of the conversation that: - Preserves key information, decisions, and action items @@ -17,7 +18,7 @@ Respond with only the summary text, no additional commentary.`; */ export function createCompactAgent(customInstructions?: string) { return new ToolLoopAgent({ - model: LIGHTWEIGHT_MODEL, + model: createModel(LIGHTWEIGHT_MODEL), instructions: customInstructions || DEFAULT_INSTRUCTIONS, stopWhen: stepCountIs(1), }); diff --git a/lib/agents/EmailReplyAgent/createEmailReplyAgent.ts b/lib/agents/EmailReplyAgent/createEmailReplyAgent.ts index 29452b7d3..91991d277 100644 --- a/lib/agents/EmailReplyAgent/createEmailReplyAgent.ts +++ b/lib/agents/EmailReplyAgent/createEmailReplyAgent.ts @@ -1,6 +1,7 @@ import { Output, ToolLoopAgent, stepCountIs } from "ai"; import { z } from "zod"; import { LIGHTWEIGHT_MODEL, INBOUND_EMAIL_DOMAIN } from "@/lib/const"; +import { createModel } from "@/lib/ai/createModel"; const replyDecisionSchema = z.object({ shouldReply: z.boolean().describe("Whether the Recoup AI assistant should reply to this email"), @@ -22,7 +23,7 @@ Rules (check in this order): */ export function createEmailReplyAgent() { return new ToolLoopAgent({ - model: LIGHTWEIGHT_MODEL, + model: createModel(LIGHTWEIGHT_MODEL), instructions, output: Output.object({ schema: replyDecisionSchema }), stopWhen: stepCountIs(1), diff --git a/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts b/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts index fe8801516..dd34930fa 100644 --- a/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts +++ b/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts @@ -1,4 +1,5 @@ import { ToolLoopAgent } from "ai"; +import { createModel } from "@/lib/ai/createModel"; /** * Creates a ToolLoopAgent configured for image generation. @@ -7,7 +8,7 @@ import { ToolLoopAgent } from "ai"; */ export function createImageGenerationAgent() { return new ToolLoopAgent({ - model: "google/gemini-3-pro-image", + model: createModel("google/gemini-3-pro-image"), instructions: "You are an image generation assistant. Generate / Edit images based on prompts.", }); } diff --git a/lib/agents/content/createContentPromptAgent.ts b/lib/agents/content/createContentPromptAgent.ts index 93dc5d10e..3ae1e394f 100644 --- a/lib/agents/content/createContentPromptAgent.ts +++ b/lib/agents/content/createContentPromptAgent.ts @@ -1,6 +1,7 @@ import { Output, ToolLoopAgent, stepCountIs } from "ai"; import { z } from "zod"; import { LIGHTWEIGHT_MODEL } from "@/lib/const"; +import { createModel } from "@/lib/ai/createModel"; import { CONTENT_TEMPLATES, DEFAULT_CONTENT_TEMPLATE } from "@/lib/content/contentTemplates"; import { CAPTION_LENGTHS } from "@/lib/content/captionLengths"; import { songsSchema } from "@/lib/content/songsSchema"; @@ -68,7 +69,7 @@ Defaults: lipsync=${DEFAULT_CONTENT_PROMPT_FLAGS.lipsync}, batch=${DEFAULT_CONTE */ export function createContentPromptAgent() { return new ToolLoopAgent({ - model: LIGHTWEIGHT_MODEL, + model: createModel(LIGHTWEIGHT_MODEL), instructions, output: Output.object({ schema: contentPromptFlagsSchema }), stopWhen: stepCountIs(1), diff --git a/lib/agents/generalAgent/getGeneralAgent.ts b/lib/agents/generalAgent/getGeneralAgent.ts index 7c2c9407b..3e271da40 100644 --- a/lib/agents/generalAgent/getGeneralAgent.ts +++ b/lib/agents/generalAgent/getGeneralAgent.ts @@ -3,6 +3,7 @@ import { AnthropicProviderOptions } from "@ai-sdk/anthropic"; import { GoogleGenerativeAIProviderOptions } from "@ai-sdk/google"; import { OpenAIResponsesProviderOptions } from "@ai-sdk/openai"; import { DEFAULT_MODEL } from "@/lib/const"; +import { createModel } from "@/lib/ai/createModel"; import { RoutingDecision } from "@/lib/chat/types"; import { extractImageUrlsFromMessages } from "@/lib/messages/extractImageUrlsFromMessages"; import { buildSystemPromptWithImages } from "@/lib/chat/buildSystemPromptWithImages"; @@ -51,7 +52,7 @@ export default async function getGeneralAgent(body: ChatRequestBody): Promise OpenRouter > direct OpenAI. + */ +export function createModel(modelId: string): LanguageModel { + // Vercel AI Gateway — existing production behavior + if (process.env.VERCEL_AI_GATEWAY_API_KEY) { + return gateway(modelId); + } + + // OpenRouter — supports "provider/model" format natively + if (process.env.OPENROUTER_API_KEY) { + const openrouter = createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY }); + return openrouter(modelId); + } + + // Direct OpenAI — strip provider prefix + if (process.env.OPENAI_API_KEY) { + const bareModel = modelId.startsWith("openai/") ? modelId.slice(7) : modelId; + return openai(bareModel); + } + + throw new Error( + "No LLM provider configured. Set VERCEL_AI_GATEWAY_API_KEY, OPENROUTER_API_KEY, or OPENAI_API_KEY.", + ); +} diff --git a/lib/ai/generateArray.ts b/lib/ai/generateArray.ts index c33c459fe..79006132b 100644 --- a/lib/ai/generateArray.ts +++ b/lib/ai/generateArray.ts @@ -1,5 +1,6 @@ import { generateObject } from "ai"; import { DEFAULT_MODEL } from "@/lib/const"; +import { createModel } from "./createModel"; import { z } from "zod"; export interface GenerateArrayResult { @@ -15,7 +16,7 @@ const generateArray = async ({ prompt: string; }): Promise => { const result = await generateObject({ - model: DEFAULT_MODEL, + model: createModel(DEFAULT_MODEL), system, prompt, output: "array", diff --git a/lib/ai/generateText.ts b/lib/ai/generateText.ts index 0d509f899..ac9bce921 100644 --- a/lib/ai/generateText.ts +++ b/lib/ai/generateText.ts @@ -1,5 +1,6 @@ import { generateText as generate } from "ai"; import { DEFAULT_MODEL } from "@/lib/const"; +import { createModel } from "./createModel"; const generateText = async ({ system, @@ -12,7 +13,7 @@ const generateText = async ({ }) => { const result = await generate({ system, - model: model || DEFAULT_MODEL, + model: createModel(model || DEFAULT_MODEL), prompt, }); diff --git a/lib/ai/getAvailableModels.ts b/lib/ai/getAvailableModels.ts index a46fd79ee..7d103d937 100644 --- a/lib/ai/getAvailableModels.ts +++ b/lib/ai/getAvailableModels.ts @@ -1,16 +1,42 @@ import { gateway, GatewayLanguageModelEntry } from "@ai-sdk/gateway"; import isEmbedModel from "./isEmbedModel"; +import { DEFAULT_MODEL, LIGHTWEIGHT_MODEL } from "@/lib/const"; /** - * Returns the list of available LLMs from the Vercel AI Gateway. - * Filters out embed models that are not suitable for chat. + * Default model list for non-gateway providers (OpenRouter, direct OpenAI). + * Returned when the Vercel AI Gateway is not configured. + */ +const DEFAULT_MODELS: GatewayLanguageModelEntry[] = [ + { + id: DEFAULT_MODEL, + name: "GPT-5 Mini", + description: "Default model for chat and generation", + specification: { specificationVersion: "v2", provider: "openai", modelId: DEFAULT_MODEL }, + }, + { + id: LIGHTWEIGHT_MODEL, + name: "GPT-4o Mini", + description: "Lightweight model for simple tasks", + specification: { specificationVersion: "v2", provider: "openai", modelId: LIGHTWEIGHT_MODEL }, + }, +]; + +/** + * Returns the list of available LLMs. + * Uses Vercel AI Gateway when configured, otherwise returns a default list. */ export const getAvailableModels = async (): Promise => { - try { - const apiResponse = await gateway.getAvailableModels(); - const gatewayModels = apiResponse.models.filter(m => !isEmbedModel(m)); - return gatewayModels; - } catch { - return []; + // Use Vercel AI Gateway when configured + if (process.env.VERCEL_AI_GATEWAY_API_KEY) { + try { + const apiResponse = await gateway.getAvailableModels(); + const gatewayModels = apiResponse.models.filter(m => !isEmbedModel(m)); + return gatewayModels; + } catch { + return DEFAULT_MODELS; + } } + + // Fallback for OpenRouter or direct OpenAI + return DEFAULT_MODELS; }; diff --git a/lib/catalog/analyzeCatalogBatch.ts b/lib/catalog/analyzeCatalogBatch.ts index 15a4a7aac..4cc716544 100644 --- a/lib/catalog/analyzeCatalogBatch.ts +++ b/lib/catalog/analyzeCatalogBatch.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { generateObject } from "ai"; import type { CatalogSongWithArtists } from "@/lib/supabase/catalog_songs/selectCatalogSongsWithArtists"; import { DEFAULT_MODEL } from "@/lib/const"; +import { createModel } from "@/lib/ai/createModel"; /** * Analyzes a single batch of catalog songs using AI to filter by criteria @@ -17,7 +18,7 @@ export async function analyzeCatalogBatch( ): Promise { // Use AI to select relevant songs from this batch const { object } = await generateObject({ - model: DEFAULT_MODEL, + model: createModel(DEFAULT_MODEL), schema: z.object({ selected_song_isrcs: z .array(z.string()) diff --git a/lib/chat/toolChains/toolChains.ts b/lib/chat/toolChains/toolChains.ts index 98f2c6f78..28419ee4d 100644 --- a/lib/chat/toolChains/toolChains.ts +++ b/lib/chat/toolChains/toolChains.ts @@ -1,6 +1,7 @@ import { LanguageModel, ModelMessage } from "ai"; import { createReleaseReportToolChain } from "./create_release_report/createReleaseReportToolChain"; import { createNewArtistToolChain } from "./createNewArtistToolChain"; +import { createModel } from "@/lib/ai/createModel"; export type ToolChainItem = { toolName: string; @@ -18,22 +19,22 @@ export type PrepareStepResult = { // Forced toolChoice is incompatible with Anthropic extended thinking. // Every tool used in a chain must have a model here to avoid the conflict. export const TOOL_MODEL_MAP: Record = { - update_account_info: "gemini-2.5-pro", - get_spotify_search: "openai/gpt-5.4-mini", - update_artist_socials: "openai/gpt-5.4-mini", - artist_deep_research: "openai/gpt-5.4-mini", - spotify_deep_research: "openai/gpt-5.4-mini", - get_artist_socials: "openai/gpt-5.4-mini", - get_spotify_artist_top_tracks: "openai/gpt-5.4-mini", - get_spotify_artist_albums: "openai/gpt-5.4-mini", - get_spotify_album: "openai/gpt-5.4-mini", - search_web: "openai/gpt-5.4-mini", - generate_txt_file: "openai/gpt-5.4-mini", - create_segments: "openai/gpt-5.4-mini", - youtube_login: "openai/gpt-5.4-mini", - web_deep_research: "openai/gpt-5.4-mini", - create_knowledge_base: "openai/gpt-5.4-mini", - send_email: "openai/gpt-5.4-mini", + update_account_info: createModel("google/gemini-2.5-pro"), + get_spotify_search: createModel("openai/gpt-5.4-mini"), + update_artist_socials: createModel("openai/gpt-5.4-mini"), + artist_deep_research: createModel("openai/gpt-5.4-mini"), + spotify_deep_research: createModel("openai/gpt-5.4-mini"), + get_artist_socials: createModel("openai/gpt-5.4-mini"), + get_spotify_artist_top_tracks: createModel("openai/gpt-5.4-mini"), + get_spotify_artist_albums: createModel("openai/gpt-5.4-mini"), + get_spotify_album: createModel("openai/gpt-5.4-mini"), + search_web: createModel("openai/gpt-5.4-mini"), + generate_txt_file: createModel("openai/gpt-5.4-mini"), + create_segments: createModel("openai/gpt-5.4-mini"), + youtube_login: createModel("openai/gpt-5.4-mini"), + web_deep_research: createModel("openai/gpt-5.4-mini"), + create_knowledge_base: createModel("openai/gpt-5.4-mini"), + send_email: createModel("openai/gpt-5.4-mini"), }; // Map trigger tool -> sequence AFTER trigger diff --git a/lib/evals/scorers/CatalogAvailability.ts b/lib/evals/scorers/CatalogAvailability.ts index f4829ea41..47f68779a 100644 --- a/lib/evals/scorers/CatalogAvailability.ts +++ b/lib/evals/scorers/CatalogAvailability.ts @@ -1,5 +1,6 @@ import { DEFAULT_MODEL } from "@/lib/consts"; import { generateObject } from "ai"; +import { createModel } from "@/lib/ai/createModel"; import { getCatalogDataAsCSV } from "@/lib/catalog/getCatalogDataAsCSV"; import { z } from "zod"; @@ -18,7 +19,7 @@ export const CatalogAvailability = async ({ const catalog = await getCatalogDataAsCSV(catalogId); const result = await generateObject({ - model: DEFAULT_MODEL, + model: createModel(DEFAULT_MODEL), system: `You are a music catalog analyst. Your job is to analyze song recommendations and determine which ones are available in the provided music catalog. Instructions: diff --git a/lib/evals/scorers/QuestionAnswered.ts b/lib/evals/scorers/QuestionAnswered.ts index abe0222cb..71bb1a360 100644 --- a/lib/evals/scorers/QuestionAnswered.ts +++ b/lib/evals/scorers/QuestionAnswered.ts @@ -1,5 +1,6 @@ import { DEFAULT_MODEL } from "@/lib/consts"; import { generateObject } from "ai"; +import { createModel } from "@/lib/ai/createModel"; import { z } from "zod"; /** @@ -17,7 +18,7 @@ export const QuestionAnswered = async ({ }) => { try { const result = await generateObject({ - model: DEFAULT_MODEL, + model: createModel(DEFAULT_MODEL), system: `You are an AI evaluation expert. Your job is to determine if an AI assistant actually answered the customer's question with a specific answer, or if it deflected, explained why it couldn't answer, or gave generic suggestions without providing the requested information. Instructions: diff --git a/package.json b/package.json index 5d12b8b07..f2ecfb716 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@composio/vercel": "^0.3.4", "@fal-ai/client": "^1.9.5", "@modelcontextprotocol/sdk": "^1.24.3", + "@openrouter/ai-sdk-provider": "^2.6.0", "@privy-io/node": "^0.6.2", "@supabase/supabase-js": "^2.86.0", "@trigger.dev/sdk": "^4.4.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72b683bc4..8958a87ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: '@modelcontextprotocol/sdk': specifier: ^1.24.3 version: 1.24.3(zod@4.1.13) + '@openrouter/ai-sdk-provider': + specifier: ^2.6.0 + version: 2.6.0(ai@6.0.0-beta.122(zod@4.1.13))(zod@4.1.13) '@privy-io/node': specifier: ^0.6.2 version: 0.6.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)) @@ -1298,6 +1301,13 @@ packages: '@octokit/types@14.1.0': resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} + '@openrouter/ai-sdk-provider@2.6.0': + resolution: {integrity: sha512-6rQw/ORDjV9Q+S+uxJwpDyZtWANUr7cDDxtuS4cQ/8UhS/hNNjKcTJVfx56hwypvd0DlRM+KgWHwxFYb90km3w==} + engines: {node: '>=18'} + peerDependencies: + ai: ^6.0.0 + zod: ^3.25.0 || ^4.0.0 + '@opentelemetry/api-logs@0.203.0': resolution: {integrity: sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==} engines: {node: '>=8.0.0'} @@ -7800,6 +7810,11 @@ snapshots: dependencies: '@octokit/openapi-types': 25.1.0 + '@openrouter/ai-sdk-provider@2.6.0(ai@6.0.0-beta.122(zod@4.1.13))(zod@4.1.13)': + dependencies: + ai: 6.0.0-beta.122(zod@4.1.13) + zod: 4.1.13 + '@opentelemetry/api-logs@0.203.0': dependencies: '@opentelemetry/api': 1.9.0 From 5a3cac6a92e0d0f28c7c9c4795be913afb41c5fe Mon Sep 17 00:00:00 2001 From: bettercallzaal Date: Tue, 14 Apr 2026 21:09:15 -0400 Subject: [PATCH 2/3] fix: revert provider-specific image agent, add OpenAI model validation - Revert createImageGenerationAgent to use bare model string since google/gemini-3-pro-image is provider-specific and can't route through OpenRouter or direct OpenAI - Add explicit error when direct OpenAI fallback receives a non-OpenAI model (e.g. "google/..." or "anthropic/...") instead of silently passing it to the OpenAI API Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ImageGenerationAgent/createImageGenerationAgent.ts | 3 +-- lib/ai/createModel.ts | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts b/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts index dd34930fa..fe8801516 100644 --- a/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts +++ b/lib/agents/ImageGenerationAgent/createImageGenerationAgent.ts @@ -1,5 +1,4 @@ import { ToolLoopAgent } from "ai"; -import { createModel } from "@/lib/ai/createModel"; /** * Creates a ToolLoopAgent configured for image generation. @@ -8,7 +7,7 @@ import { createModel } from "@/lib/ai/createModel"; */ export function createImageGenerationAgent() { return new ToolLoopAgent({ - model: createModel("google/gemini-3-pro-image"), + model: "google/gemini-3-pro-image", instructions: "You are an image generation assistant. Generate / Edit images based on prompts.", }); } diff --git a/lib/ai/createModel.ts b/lib/ai/createModel.ts index bb571673c..24652a976 100644 --- a/lib/ai/createModel.ts +++ b/lib/ai/createModel.ts @@ -21,8 +21,14 @@ export function createModel(modelId: string): LanguageModel { return openrouter(modelId); } - // Direct OpenAI — strip provider prefix + // Direct OpenAI — only supports openai/* models if (process.env.OPENAI_API_KEY) { + if (!modelId.startsWith("openai/") && modelId.includes("/")) { + throw new Error( + `Model "${modelId}" is not an OpenAI model. Direct OpenAI mode only supports openai/* models. ` + + `Use OPENROUTER_API_KEY or VERCEL_AI_GATEWAY_API_KEY for multi-provider support.`, + ); + } const bareModel = modelId.startsWith("openai/") ? modelId.slice(7) : modelId; return openai(bareModel); } From 8f6b5b16b7c969a0aac08885797ab12ac739d767 Mon Sep 17 00:00:00 2001 From: bettercallzaal Date: Tue, 14 Apr 2026 21:15:10 -0400 Subject: [PATCH 3/3] fix: address review feedback on provider detection and model list - Add VERCEL_OIDC_TOKEN check to gateway detection in createModel - Return [] on gateway failure instead of DEFAULT_MODELS to avoid masking gateway errors - Add pricing metadata to fallback model entries Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/ai/createModel.ts | 2 +- lib/ai/getAvailableModels.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/ai/createModel.ts b/lib/ai/createModel.ts index 24652a976..9d6b0f066 100644 --- a/lib/ai/createModel.ts +++ b/lib/ai/createModel.ts @@ -11,7 +11,7 @@ import { openai } from "@ai-sdk/openai"; */ export function createModel(modelId: string): LanguageModel { // Vercel AI Gateway — existing production behavior - if (process.env.VERCEL_AI_GATEWAY_API_KEY) { + if (process.env.VERCEL_AI_GATEWAY_API_KEY || process.env.VERCEL_OIDC_TOKEN) { return gateway(modelId); } diff --git a/lib/ai/getAvailableModels.ts b/lib/ai/getAvailableModels.ts index 7d103d937..4e4941094 100644 --- a/lib/ai/getAvailableModels.ts +++ b/lib/ai/getAvailableModels.ts @@ -4,19 +4,21 @@ import { DEFAULT_MODEL, LIGHTWEIGHT_MODEL } from "@/lib/const"; /** * Default model list for non-gateway providers (OpenRouter, direct OpenAI). - * Returned when the Vercel AI Gateway is not configured. + * Returned only when the Vercel AI Gateway is not configured. */ const DEFAULT_MODELS: GatewayLanguageModelEntry[] = [ { id: DEFAULT_MODEL, name: "GPT-5 Mini", description: "Default model for chat and generation", + pricing: { input: "0.0001", output: "0.0004" }, specification: { specificationVersion: "v2", provider: "openai", modelId: DEFAULT_MODEL }, }, { id: LIGHTWEIGHT_MODEL, name: "GPT-4o Mini", description: "Lightweight model for simple tasks", + pricing: { input: "0.00015", output: "0.0006" }, specification: { specificationVersion: "v2", provider: "openai", modelId: LIGHTWEIGHT_MODEL }, }, ]; @@ -32,8 +34,9 @@ export const getAvailableModels = async (): Promise const apiResponse = await gateway.getAvailableModels(); const gatewayModels = apiResponse.models.filter(m => !isEmbedModel(m)); return gatewayModels; - } catch { - return DEFAULT_MODELS; + } catch (error) { + console.error("[getAvailableModels] Gateway fetch failed:", error); + return []; } }