diff --git a/app/api/claude/route.ts b/app/api/claude/route.ts index 93c1fba..638dc20 100644 --- a/app/api/claude/route.ts +++ b/app/api/claude/route.ts @@ -1,5 +1,7 @@ -import Anthropic from "@anthropic-ai/sdk"; -import OpenAI from "openai"; +// ABOUTME: API route for LLM-powered music generation +// ABOUTME: Uses Vercel AI SDK to abstract over Anthropic and OpenAI providers + +import { streamText } from "ai"; import { NextRequest } from "next/server"; import { buildSystemPrompt } from "@/lib/systemPrompt"; import { @@ -8,6 +10,12 @@ import { buildRetryPrompt, } from "@/lib/creativeDirectives"; import { validateStrudelCode } from "@/lib/validateOutput"; +import { + getProviderFromModel, + validateApiKeyForProvider, + createModel, + type Provider, +} from "@/lib/ai/providers"; import type { GenerationMode, ChatMessage } from "@/lib/sessionStore"; // extracts code from response for validation @@ -80,98 +88,40 @@ function formatChatHistory(chat: ChatMessage[]): string { return `\nprevious conversation:\n${formatted}\n`; } -// streams claude response -async function streamClaude( - client: Anthropic, +// stream text and return accumulated result, outputting in the expected SSE format +async function streamToClient( + provider: Provider, + modelId: string, + apiKey: string, systemPrompt: string, userPrompt: string, maxTokens: number, controller: ReadableStreamDefaultController, - encoder: TextEncoder, - model: string + encoder: TextEncoder ): Promise { - const stream = await client.messages.stream({ - model: model, - max_tokens: maxTokens, - system: systemPrompt, - messages: [{ role: "user", content: userPrompt }], - }); - - let fullText = ""; + const model = createModel(provider, modelId, apiKey); - for await (const event of stream) { - if (event.type === "content_block_delta") { - const delta = event.delta as { type: string; text?: string }; - if (delta.type === "text_delta" && delta.text) { - fullText += delta.text; - const data = JSON.stringify({ - type: "content_block_delta", - delta: { text: delta.text }, - }); - controller.enqueue(encoder.encode(`data: ${data}\n\n`)); - } - } - } - - return fullText; -} - -// streams OpenAI response -async function streamOpenAI( - client: OpenAI, - systemPrompt: string, - userPrompt: string, - maxTokens: number, - controller: ReadableStreamDefaultController, - encoder: TextEncoder, - model: string -): Promise { - const stream = await client.chat.completions.create({ - model: model, - max_completion_tokens: maxTokens, - messages: [ - { role: "system", content: systemPrompt }, - { role: "user", content: userPrompt }, - ], - stream: true, + const result = streamText({ + model, + system: systemPrompt, + prompt: userPrompt, + maxOutputTokens: maxTokens, }); let fullText = ""; - for await (const chunk of stream) { - const delta = chunk.choices[0]?.delta?.content; - if (delta) { - fullText += delta; - const data = JSON.stringify({ - type: "content_block_delta", - delta: { text: delta }, - }); - controller.enqueue(encoder.encode(`data: ${data}\n\n`)); - } + for await (const chunk of result.textStream) { + fullText += chunk; + const data = JSON.stringify({ + type: "content_block_delta", + delta: { text: chunk }, + }); + controller.enqueue(encoder.encode(`data: ${data}\n\n`)); } return fullText; } -// Helper to determine provider from model ID -function getProviderFromModel(model: string): "anthropic" | "openai" { - if (model.startsWith("gpt-") || model.startsWith("o1") || model.startsWith("o3")) { - return "openai"; - } - return "anthropic"; -} - -// Helper to detect API key type -function detectApiKeyProvider(apiKey: string): "anthropic" | "openai" | "unknown" { - if (apiKey.startsWith("sk-ant-")) { - return "anthropic"; - } - if (apiKey.startsWith("sk-") || apiKey.startsWith("sk-proj-")) { - return "openai"; - } - return "unknown"; -} - export async function POST(req: NextRequest) { try { const body = await req.json(); @@ -202,18 +152,14 @@ export async function POST(req: NextRequest) { const apiKey = requestApiKey; // Use client-provided model if available, otherwise default - const model = requestModel || "claude-sonnet-4-20250514"; - const provider = getProviderFromModel(model); - - // Detect API key type and validate it matches the selected provider - const keyProvider = detectApiKeyProvider(apiKey); - if (keyProvider !== "unknown" && keyProvider !== provider) { - const providerName = provider === "openai" ? "OpenAI" : "Anthropic"; - const keyProviderName = keyProvider === "openai" ? "OpenAI" : "Anthropic"; + const modelId = requestModel || "claude-sonnet-4-20250514"; + const provider = getProviderFromModel(modelId); + + // Validate API key matches the selected provider + const keyError = validateApiKeyForProvider(apiKey, provider); + if (keyError) { return new Response( - JSON.stringify({ - error: `You selected an ${providerName} model but provided an ${keyProviderName} API key. Please update your API key in Settings to match the selected model.`, - }), + JSON.stringify({ error: keyError }), { status: 400, headers: { "Content-Type": "application/json" }, @@ -228,10 +174,6 @@ export async function POST(req: NextRequest) { }); } - // Create appropriate client based on provider - const anthropicClient = provider === "anthropic" ? new Anthropic({ apiKey }) : null; - const openaiClient = provider === "openai" ? new OpenAI({ apiKey }) : null; - // get balanced config values const config = getConfigValues(); @@ -242,7 +184,7 @@ export async function POST(req: NextRequest) { let userPrompt: string; if (mode === "edit" && currentCode && currentCode.trim()) { - // edit mode: include current code context + // edit mode: include current code context and chat history const chatContext = formatChatHistory(truncateChatHistory(chatHistory, 6)); userPrompt = buildEditContext(currentCode, prompt) + chatContext; } else { @@ -259,30 +201,16 @@ export async function POST(req: NextRequest) { async start(controller) { try { // first attempt - streaming - let fullText: string; - if (provider === "openai" && openaiClient) { - fullText = await streamOpenAI( - openaiClient, - systemPrompt, - userPrompt, - maxTokens, - controller, - encoder, - model - ); - } else if (anthropicClient) { - fullText = await streamClaude( - anthropicClient, - systemPrompt, - userPrompt, - maxTokens, - controller, - encoder, - model - ); - } else { - throw new Error("No valid API client configured"); - } + const fullText = await streamToClient( + provider, + modelId, + apiKey, + systemPrompt, + userPrompt, + maxTokens, + controller, + encoder + ); // validate the output const extractedCode = extractCodeForValidation(fullText); @@ -305,7 +233,7 @@ export async function POST(req: NextRequest) { // build retry prompt with issues const retryPrompt = buildRetryPrompt(prompt, validation.issues); - + // for edit mode, include the current code in retry let retryUserPrompt: string; if (mode === "edit" && currentCode && currentCode.trim()) { @@ -319,27 +247,16 @@ export async function POST(req: NextRequest) { controller.enqueue(encoder.encode(`data: ${clearMsg}\n\n`)); // second attempt - also streaming - if (provider === "openai" && openaiClient) { - await streamOpenAI( - openaiClient, - systemPrompt, - retryUserPrompt, - maxTokens, - controller, - encoder, - model - ); - } else if (anthropicClient) { - await streamClaude( - anthropicClient, - systemPrompt, - retryUserPrompt, - maxTokens, - controller, - encoder, - model - ); - } + await streamToClient( + provider, + modelId, + apiKey, + systemPrompt, + retryUserPrompt, + maxTokens, + controller, + encoder + ); } } @@ -347,7 +264,7 @@ export async function POST(req: NextRequest) { controller.close(); } catch (error) { let errorMessage = "stream error"; - + if (error instanceof Error) { console.error("API Error:", error.message, error); // Check for authentication/invalid API key errors @@ -368,7 +285,7 @@ export async function POST(req: NextRequest) { } else { console.error("Unknown API Error:", error); } - + const data = JSON.stringify({ type: "error", error: { message: errorMessage }, diff --git a/lib/__tests__/providers.test.ts b/lib/__tests__/providers.test.ts new file mode 100644 index 0000000..0a83440 --- /dev/null +++ b/lib/__tests__/providers.test.ts @@ -0,0 +1,124 @@ +// ABOUTME: Tests for AI provider utilities +// ABOUTME: Validates provider detection, API key validation, and model creation + +import { describe, it, expect } from "vitest"; +import { + getProviderFromModel, + detectApiKeyProvider, + validateApiKeyForProvider, +} from "../ai/providers"; + +describe("getProviderFromModel", () => { + describe("OpenAI models", () => { + it("detects gpt-4 as OpenAI", () => { + expect(getProviderFromModel("gpt-4")).toBe("openai"); + }); + + it("detects gpt-4o as OpenAI", () => { + expect(getProviderFromModel("gpt-4o")).toBe("openai"); + }); + + it("detects gpt-3.5-turbo as OpenAI", () => { + expect(getProviderFromModel("gpt-3.5-turbo")).toBe("openai"); + }); + + it("detects o1 models as OpenAI", () => { + expect(getProviderFromModel("o1-preview")).toBe("openai"); + expect(getProviderFromModel("o1-mini")).toBe("openai"); + }); + + it("detects o3 models as OpenAI", () => { + expect(getProviderFromModel("o3-mini")).toBe("openai"); + }); + }); + + describe("Anthropic models", () => { + it("detects claude-sonnet as Anthropic", () => { + expect(getProviderFromModel("claude-sonnet-4-20250514")).toBe("anthropic"); + }); + + it("detects claude-3-opus as Anthropic", () => { + expect(getProviderFromModel("claude-3-opus-20240229")).toBe("anthropic"); + }); + + it("detects claude-3-haiku as Anthropic", () => { + expect(getProviderFromModel("claude-3-haiku-20240307")).toBe("anthropic"); + }); + + it("defaults unknown models to Anthropic", () => { + expect(getProviderFromModel("some-unknown-model")).toBe("anthropic"); + }); + }); +}); + +describe("detectApiKeyProvider", () => { + describe("Anthropic keys", () => { + it("detects sk-ant- prefix as Anthropic", () => { + expect(detectApiKeyProvider("sk-ant-api03-xxxx")).toBe("anthropic"); + }); + }); + + describe("OpenAI keys", () => { + it("detects sk- prefix as OpenAI", () => { + expect(detectApiKeyProvider("sk-xxxx")).toBe("openai"); + }); + + it("detects sk-proj- prefix as OpenAI", () => { + expect(detectApiKeyProvider("sk-proj-xxxx")).toBe("openai"); + }); + }); + + describe("Unknown keys", () => { + it("returns unknown for unrecognized prefixes", () => { + expect(detectApiKeyProvider("abc-123")).toBe("unknown"); + }); + + it("returns unknown for empty string", () => { + expect(detectApiKeyProvider("")).toBe("unknown"); + }); + }); +}); + +describe("validateApiKeyForProvider", () => { + describe("valid combinations", () => { + it("accepts Anthropic key for Anthropic provider", () => { + expect(validateApiKeyForProvider("sk-ant-api03-xxxx", "anthropic")).toBeNull(); + }); + + it("accepts OpenAI key for OpenAI provider", () => { + expect(validateApiKeyForProvider("sk-xxxx", "openai")).toBeNull(); + }); + + it("accepts OpenAI project key for OpenAI provider", () => { + expect(validateApiKeyForProvider("sk-proj-xxxx", "openai")).toBeNull(); + }); + + it("accepts unknown key format for any provider", () => { + expect(validateApiKeyForProvider("abc-123", "anthropic")).toBeNull(); + expect(validateApiKeyForProvider("abc-123", "openai")).toBeNull(); + }); + }); + + describe("invalid combinations", () => { + it("rejects Anthropic key for OpenAI provider", () => { + const error = validateApiKeyForProvider("sk-ant-api03-xxxx", "openai"); + expect(error).not.toBeNull(); + expect(error).toContain("OpenAI model"); + expect(error).toContain("Anthropic API key"); + }); + + it("rejects OpenAI key for Anthropic provider", () => { + const error = validateApiKeyForProvider("sk-xxxx", "anthropic"); + expect(error).not.toBeNull(); + expect(error).toContain("Anthropic model"); + expect(error).toContain("OpenAI API key"); + }); + + it("rejects OpenAI project key for Anthropic provider", () => { + const error = validateApiKeyForProvider("sk-proj-xxxx", "anthropic"); + expect(error).not.toBeNull(); + expect(error).toContain("Anthropic model"); + expect(error).toContain("OpenAI API key"); + }); + }); +}); diff --git a/lib/ai/providers.ts b/lib/ai/providers.ts new file mode 100644 index 0000000..021468d --- /dev/null +++ b/lib/ai/providers.ts @@ -0,0 +1,65 @@ +// ABOUTME: AI provider utilities for model detection and creation +// ABOUTME: Abstracts provider-specific logic for Anthropic and OpenAI + +import { createAnthropic } from "@ai-sdk/anthropic"; +import { createOpenAI } from "@ai-sdk/openai"; + +export type Provider = "anthropic" | "openai"; +export type ApiKeyProvider = Provider | "unknown"; + +/** + * Determine provider from model ID based on naming conventions + */ +export function getProviderFromModel(model: string): Provider { + if (model.startsWith("gpt-") || model.startsWith("o1") || model.startsWith("o3")) { + return "openai"; + } + return "anthropic"; +} + +/** + * Detect API key type from its prefix + */ +export function detectApiKeyProvider(apiKey: string): ApiKeyProvider { + if (apiKey.startsWith("sk-ant-")) { + return "anthropic"; + } + if (apiKey.startsWith("sk-") || apiKey.startsWith("sk-proj-")) { + return "openai"; + } + return "unknown"; +} + +/** + * Validate that the API key matches the selected provider + * Returns an error message if mismatched, null if valid + */ +export function validateApiKeyForProvider( + apiKey: string, + provider: Provider +): string | null { + const keyProvider = detectApiKeyProvider(apiKey); + if (keyProvider !== "unknown" && keyProvider !== provider) { + const providerName = provider === "openai" ? "OpenAI" : "Anthropic"; + const keyProviderName = keyProvider === "openai" ? "OpenAI" : "Anthropic"; + return `You selected an ${providerName} model but provided an ${keyProviderName} API key. Please update your API key in Settings to match the selected model.`; + } + return null; +} + +/** + * Create the appropriate model instance based on provider + */ +export function createModel( + provider: Provider, + modelId: string, + apiKey: string +) { + if (provider === "openai") { + const openai = createOpenAI({ apiKey }); + return openai(modelId); + } else { + const anthropic = createAnthropic({ apiKey }); + return anthropic(modelId); + } +} diff --git a/package.json b/package.json index 6635872..9adc30a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "build-dataset": "tsx public/assets/dataset/scripts/build_index.ts && tsx public/assets/dataset/scripts/build_style_priors.ts" }, "dependencies": { - "@anthropic-ai/sdk": "^0.32.1", + "@ai-sdk/anthropic": "^3.0.2", + "@ai-sdk/openai": "^3.0.2", "@strudel/codemirror": "^1.2.5", "@strudel/core": "^1.2.5", "@strudel/draw": "^1.2.5", @@ -23,8 +24,8 @@ "@strudel/tonal": "^1.2.5", "@strudel/transpiler": "^1.2.5", "@strudel/webaudio": "^1.2.5", + "ai": "^6.0.5", "next": "14.2.21", - "openai": "^6.15.0", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb07c03..4faf7fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,12 @@ importers: .: dependencies: - '@anthropic-ai/sdk': - specifier: ^0.32.1 - version: 0.32.1 + '@ai-sdk/anthropic': + specifier: ^3.0.2 + version: 3.0.2(zod@4.3.4) + '@ai-sdk/openai': + specifier: ^3.0.2 + version: 3.0.2(zod@4.3.4) '@strudel/codemirror': specifier: ^1.2.5 version: 1.2.6(@codemirror/lint@6.9.2) @@ -38,12 +41,12 @@ importers: '@strudel/webaudio': specifier: ^1.2.5 version: 1.2.6 + ai: + specifier: ^6.0.5 + version: 6.0.5(zod@4.3.4) next: specifier: 14.2.21 - version: 14.2.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - openai: - specifier: ^6.15.0 - version: 6.15.0 + version: 14.2.21(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -77,17 +80,42 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.16 - version: 4.0.16(@types/node@20.19.27)(jiti@1.21.7)(tsx@4.21.0) + version: 4.0.16(@opentelemetry/api@1.9.0)(@types/node@20.19.27)(jiti@1.21.7)(tsx@4.21.0) packages: + '@ai-sdk/anthropic@3.0.2': + resolution: {integrity: sha512-D6iSsrOYryBSPsFtOiEDv54jnjVCU/flIuXdjuRY7LdikB0KGjpazN8Dt4ONXzL+ux69ds2nzFNKke/w/fgLAA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/gateway@3.0.4': + resolution: {integrity: sha512-OlccjNYZ5+4FaNyvs0kb3N5H6U/QCKlKPTGsgUo8IZkqfMQu8ALI1XD6l/BCuTKto+OO9xUPObT/W7JhbqJ5nA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@3.0.2': + resolution: {integrity: sha512-GONwavgSWtcWO+t9+GpGK8l7nIYh+zNtCL/NYDSeHxHiw6ksQS9XMRWrZyE5NpJ0EXNxSAWCHIDmb1WvTqhq9Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@4.0.2': + resolution: {integrity: sha512-KaykkuRBdF/ffpI5bwpL4aSCmO/99p8/ci+VeHwJO8tmvXtiVAb99QeyvvvXmL61e9Zrvv4GBGoajW19xdjkVQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@3.0.1': + resolution: {integrity: sha512-2lR4w7mr9XrydzxBSjir4N6YMGdXD+Np1Sh0RXABh7tWdNFFwIeRI1Q+SaYZMbfL8Pg8RRLcrxQm51yxTLhokg==} + engines: {node: '>=18'} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@anthropic-ai/sdk@0.32.1': - resolution: {integrity: sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==} - '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} @@ -375,6 +403,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + '@replit/codemirror-emacs@6.1.0': resolution: {integrity: sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==} peerDependencies: @@ -656,12 +688,6 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/node-fetch@2.6.13': - resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - - '@types/node@18.19.130': - resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@20.19.27': resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} @@ -676,6 +702,10 @@ packages: '@types/react@18.3.27': resolution: {integrity: sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==} + '@vercel/oidc@3.0.5': + resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} + engines: {node: '>= 20'} + '@vitest/expect@4.0.16': resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==} @@ -705,18 +735,16 @@ packages: '@vitest/utils@4.0.16': resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true - agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} + ai@6.0.5: + resolution: {integrity: sha512-CKL3dDHedWskC6EY67LrULonZBU9vL+Bwa+xQEcprBhJfxpogntG3utjiAkYuy5ZQatyWk+SmWG8HLvcnhvbRg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 ansi-regex@3.0.1: resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} @@ -744,9 +772,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - autoprefixer@10.4.23: resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} @@ -775,10 +800,6 @@ packages: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -814,10 +835,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -842,10 +859,6 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -855,35 +868,15 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -914,9 +907,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} @@ -946,17 +939,6 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - form-data-encoder@1.7.2: - resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - formdata-node@4.4.1: - resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} - engines: {node: '>= 12.20'} - fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} @@ -972,14 +954,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} @@ -995,28 +969,13 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1056,6 +1015,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + jzz@1.9.6: resolution: {integrity: sha512-J7ENLhXwfm2BNDKRUrL8eKtPhUS/CtMBpiafxQHDBcOWSocLhearDKEdh+ylnZFcr5OXWTed0gj6l/txeQA9vg==} @@ -1077,10 +1039,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1089,17 +1047,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -1131,20 +1078,6 @@ packages: sass: optional: true - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -1163,18 +1096,6 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - openai@6.15.0: - resolution: {integrity: sha512-F1Lvs5BoVvmZtzkUEVyh8mDQPPFolq4F+xdsx/DO8Hee8YF3IGAlZqUIsF+DVGhqf4aU0a3bTghsxB6OIsRy1g==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -1431,9 +1352,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -1450,9 +1368,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -1542,20 +1457,10 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - web-streams-polyfill@4.0.0-beta.3: - resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} - engines: {node: '>= 14'} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webmidi@3.1.14: resolution: {integrity: sha512-K9GzNm0J3R/61NJWAW7ipAJGWU5D/8bEjOir3PymFjLDpbQJ+ygjvm5jx/WQ8atQ1hu23St3lvnc5g1NKbOsrw==} engines: {node: '>=8.5'} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} @@ -1579,21 +1484,42 @@ packages: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} + zod@4.3.4: + resolution: {integrity: sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==} + snapshots: - '@alloc/quick-lru@5.2.0': {} + '@ai-sdk/anthropic@3.0.2(zod@4.3.4)': + dependencies: + '@ai-sdk/provider': 3.0.1 + '@ai-sdk/provider-utils': 4.0.2(zod@4.3.4) + zod: 4.3.4 - '@anthropic-ai/sdk@0.32.1': + '@ai-sdk/gateway@3.0.4(zod@4.3.4)': dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.13 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding + '@ai-sdk/provider': 3.0.1 + '@ai-sdk/provider-utils': 4.0.2(zod@4.3.4) + '@vercel/oidc': 3.0.5 + zod: 4.3.4 + + '@ai-sdk/openai@3.0.2(zod@4.3.4)': + dependencies: + '@ai-sdk/provider': 3.0.1 + '@ai-sdk/provider-utils': 4.0.2(zod@4.3.4) + zod: 4.3.4 + + '@ai-sdk/provider-utils@4.0.2(zod@4.3.4)': + dependencies: + '@ai-sdk/provider': 3.0.1 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.0.6 + zod: 4.3.4 + + '@ai-sdk/provider@3.0.1': + dependencies: + json-schema: 0.4.0 + + '@alloc/quick-lru@5.2.0': {} '@babel/runtime@7.28.4': {} @@ -1808,6 +1734,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@opentelemetry/api@1.9.0': {} + '@replit/codemirror-emacs@6.1.0(@codemirror/autocomplete@6.20.0)(@codemirror/commands@6.10.1)(@codemirror/search@6.5.11)(@codemirror/state@6.5.3)(@codemirror/view@6.39.8)': dependencies: '@codemirror/autocomplete': 6.20.0 @@ -2178,15 +2106,6 @@ snapshots: '@types/estree@1.0.8': {} - '@types/node-fetch@2.6.13': - dependencies: - '@types/node': 20.19.27 - form-data: 4.0.5 - - '@types/node@18.19.130': - dependencies: - undici-types: 5.26.5 - '@types/node@20.19.27': dependencies: undici-types: 6.21.0 @@ -2202,6 +2121,8 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.2.3 + '@vercel/oidc@3.0.5': {} + '@vitest/expect@4.0.16': dependencies: '@standard-schema/spec': 1.1.0 @@ -2241,15 +2162,15 @@ snapshots: '@vitest/pretty-format': 4.0.16 tinyrainbow: 3.0.3 - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - acorn@8.15.0: {} - agentkeepalive@4.6.0: + ai@6.0.5(zod@4.3.4): dependencies: - humanize-ms: 1.2.1 + '@ai-sdk/gateway': 3.0.4(zod@4.3.4) + '@ai-sdk/provider': 3.0.1 + '@ai-sdk/provider-utils': 4.0.2(zod@4.3.4) + '@opentelemetry/api': 1.9.0 + zod: 4.3.4 ansi-regex@3.0.1: {} @@ -2270,8 +2191,6 @@ snapshots: assertion-error@2.0.1: {} - asynckit@0.4.0: {} - autoprefixer@10.4.23(postcss@8.5.6): dependencies: browserslist: 4.28.1 @@ -2301,11 +2220,6 @@ snapshots: dependencies: streamsearch: 1.1.0 - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - camelcase-css@2.0.1: {} camelcase@5.3.1: {} @@ -2344,10 +2258,6 @@ snapshots: color-name@1.1.4: {} - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - commander@4.1.1: {} cowsay@1.6.0: @@ -2365,8 +2275,6 @@ snapshots: decamelize@1.2.0: {} - delayed-stream@1.0.0: {} - didyoumean@1.2.2: {} djipevents@2.0.7: @@ -2375,33 +2283,12 @@ snapshots: dlv@1.1.3: {} - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - electron-to-chromium@1.5.267: {} emoji-regex@8.0.0: {} - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -2451,7 +2338,7 @@ snapshots: esutils@2.0.3: {} - event-target-shim@5.0.1: {} + eventsource-parser@3.0.6: {} expect-type@1.3.0: {} @@ -2480,21 +2367,6 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 - form-data-encoder@1.7.2: {} - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - formdata-node@4.4.1: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 4.0.0-beta.3 - fraction.js@5.3.4: {} fsevents@2.3.3: @@ -2504,24 +2376,6 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - get-stdin@8.0.0: {} get-tsconfig@4.13.0: @@ -2536,24 +2390,12 @@ snapshots: dependencies: is-glob: 4.0.3 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - hasown@2.0.2: dependencies: function-bind: 1.1.2 - humanize-ms@1.2.1: - dependencies: - ms: 2.1.3 - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -2581,6 +2423,8 @@ snapshots: js-tokens@4.0.0: {} + json-schema@0.4.0: {} + jzz@1.9.6: dependencies: jazz-midi: 1.7.9 @@ -2602,8 +2446,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - math-intrinsics@1.1.0: {} - merge2@1.4.1: {} micromatch@4.0.8: @@ -2611,14 +2453,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - ms@2.1.3: {} - mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -2629,7 +2463,7 @@ snapshots: nanostores@0.11.4: {} - next@14.2.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.21(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.21 '@swc/helpers': 0.5.5 @@ -2650,16 +2484,11 @@ snapshots: '@next/swc-win32-arm64-msvc': 14.2.21 '@next/swc-win32-ia32-msvc': 14.2.21 '@next/swc-win32-x64-msvc': 14.2.21 + '@opentelemetry/api': 1.9.0 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - node-domexception@1.0.0: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - node-releases@2.0.27: {} normalize-path@3.0.0: {} @@ -2670,8 +2499,6 @@ snapshots: obug@2.1.1: {} - openai@6.15.0: {} - p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -2932,8 +2759,6 @@ snapshots: dependencies: is-number: 7.0.0 - tr46@0.0.3: {} - ts-interface-checker@0.1.13: {} tslib@2.8.1: {} @@ -2947,8 +2772,6 @@ snapshots: typescript@5.9.3: {} - undici-types@5.26.5: {} - undici-types@6.21.0: {} update-browserslist-db@1.2.3(browserslist@4.28.1): @@ -2973,7 +2796,7 @@ snapshots: jiti: 1.21.7 tsx: 4.21.0 - vitest@4.0.16(@types/node@20.19.27)(jiti@1.21.7)(tsx@4.21.0): + vitest@4.0.16(@opentelemetry/api@1.9.0)(@types/node@20.19.27)(jiti@1.21.7)(tsx@4.21.0): dependencies: '@vitest/expect': 4.0.16 '@vitest/mocker': 4.0.16(vite@7.3.0(@types/node@20.19.27)(jiti@1.21.7)(tsx@4.21.0)) @@ -2996,6 +2819,7 @@ snapshots: vite: 7.3.0(@types/node@20.19.27)(jiti@1.21.7)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: + '@opentelemetry/api': 1.9.0 '@types/node': 20.19.27 transitivePeerDependencies: - jiti @@ -3012,21 +2836,12 @@ snapshots: w3c-keyname@2.2.8: {} - web-streams-polyfill@4.0.0-beta.3: {} - - webidl-conversions@3.0.1: {} - webmidi@3.1.14: dependencies: djipevents: 2.0.7 optionalDependencies: jzz: 1.9.6 - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-module@2.0.1: {} why-is-node-running@2.3.0: @@ -3060,3 +2875,5 @@ snapshots: which-module: 2.0.1 y18n: 4.0.3 yargs-parser: 18.1.3 + + zod@4.3.4: {}