diff --git a/packages/types/src/providers/vercel-ai-gateway.ts b/packages/types/src/providers/vercel-ai-gateway.ts index 38e959649e..a231dd51d3 100644 --- a/packages/types/src/providers/vercel-ai-gateway.ts +++ b/packages/types/src/providers/vercel-ai-gateway.ts @@ -53,12 +53,14 @@ export const VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS = new Set([ "anthropic/claude-3-sonnet", "anthropic/claude-3.5-sonnet", "anthropic/claude-3.7-sonnet", + "anthropic/claude-haiku-4.5", "anthropic/claude-opus-4", "anthropic/claude-opus-4.1", "anthropic/claude-opus-4.5", "anthropic/claude-opus-4.6", "anthropic/claude-fable-5", "anthropic/claude-sonnet-4", + "anthropic/claude-sonnet-4.5", "anthropic/claude-sonnet-4.6", "anthropic/claude-sonnet-5", "google/gemini-1.5-flash", @@ -85,6 +87,9 @@ export const VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS = new Set([ "openai/gpt-4.5-preview", "openai/gpt-4o", "openai/gpt-4o-mini", + "openai/gpt-5", + "openai/gpt-5-mini", + "openai/gpt-5-nano", "openai/gpt-oss-120b", "openai/gpt-oss-20b", "openai/o3", diff --git a/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts b/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts index dfb8ad4b3b..28e08d776b 100644 --- a/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts +++ b/src/api/providers/fetchers/__tests__/vercel-ai-gateway.spec.ts @@ -269,6 +269,39 @@ describe("Vercel AI Gateway Fetchers", () => { ) }) + it("prefers live vision tags over hardcoded allowlists", () => { + const taggedVision = parseVercelAiGatewayModel({ + id: "anthropic/claude-sonnet-4.5", + model: { + ...baseModel, + id: "anthropic/claude-sonnet-4.5", + tags: ["tool-use", "vision"], + }, + }) + expect(taggedVision.supportsImages).toBe(true) + + const taggedTextOnly = parseVercelAiGatewayModel({ + id: "anthropic/claude-sonnet-4", + model: { + ...baseModel, + id: "anthropic/claude-sonnet-4", + tags: ["tool-use"], + }, + }) + expect(taggedTextOnly.supportsImages).toBe(false) + }) + + it("falls back to allowlists when tags are absent", () => { + const result = parseVercelAiGatewayModel({ + id: "anthropic/claude-sonnet-4.5", + model: { + ...baseModel, + id: "anthropic/claude-sonnet-4.5", + }, + }) + expect(result.supportsImages).toBe(true) + }) + it("handles missing cache pricing", () => { const modelNoCachePricing = { ...baseModel, diff --git a/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts b/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts index 0eb4a753dc..ae9bdcc4b1 100644 --- a/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts +++ b/src/api/providers/fetchers/__tests__/zoo-gateway.spec.ts @@ -159,6 +159,28 @@ describe("Zoo Gateway Fetchers", () => { }) describe("parseZooGatewayModel", () => { + it("enables image attachment from Zoo Gateway vision tags", () => { + const result = parseZooGatewayModel({ + id: "anthropic/claude-sonnet-4.5", + model: { + id: "anthropic/claude-sonnet-4.5", + object: "model", + owned_by: "anthropic", + name: "Claude Sonnet 4.5", + context_window: 200000, + max_tokens: 64000, + type: "language", + tags: ["tool-use", "vision"], + pricing: { + input: "3.00", + output: "15.00", + }, + }, + }) + + expect(result.supportsImages).toBe(true) + }) + it("delegates to the vercel-ai-gateway parser", () => { const result = parseZooGatewayModel({ id: "anthropic/claude-sonnet-4", diff --git a/src/api/providers/fetchers/vercel-ai-gateway.ts b/src/api/providers/fetchers/vercel-ai-gateway.ts index ba748ddd51..1c4a4279e1 100644 --- a/src/api/providers/fetchers/vercel-ai-gateway.ts +++ b/src/api/providers/fetchers/vercel-ai-gateway.ts @@ -34,6 +34,7 @@ const vercelAiGatewayModelSchema = z.object({ context_window: z.number(), max_tokens: z.number(), type: z.string(), + tags: z.array(z.string()).optional(), pricing: vercelAiGatewayPricingSchema, }) @@ -99,8 +100,9 @@ export const parseVercelAiGatewayModel = ({ id, model }: { id: string; model: Ve const cacheReadsPrice = model.pricing?.input_cache_read ? parseApiPrice(model.pricing?.input_cache_read) : undefined const supportsPromptCache = typeof cacheWritesPrice !== "undefined" && typeof cacheReadsPrice !== "undefined" - const supportsImages = - VERCEL_AI_GATEWAY_VISION_ONLY_MODELS.has(id) || VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS.has(id) + const supportsImages = Array.isArray(model.tags) + ? model.tags.includes("vision") + : VERCEL_AI_GATEWAY_VISION_ONLY_MODELS.has(id) || VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS.has(id) const modelInfo: ModelInfo = { maxTokens: model.max_tokens,