diff --git a/components/SettingsModal.tsx b/components/SettingsModal.tsx index 083bc55..9f555e5 100644 --- a/components/SettingsModal.tsx +++ b/components/SettingsModal.tsx @@ -54,8 +54,6 @@ const PROVIDER_OPTIONS: { value: ProviderProtocol | ""; label: string }[] = [ { value: "", label: "自动推断(推荐)" }, { value: "openai_compatible", label: "OpenAI Compatible" }, { value: "openai", label: "OpenAI (Native)" }, - { value: "anthropic", label: "Anthropic" }, - { value: "google", label: "Google Gemini" }, { value: "runware", label: "Runware" }, ]; diff --git a/lib/ai-client/chat.ts b/lib/ai-client/chat.ts index f869c8f..6707c40 100644 --- a/lib/ai-client/chat.ts +++ b/lib/ai-client/chat.ts @@ -1,29 +1,24 @@ -import { generateText } from "ai"; -import type { LanguageModelUsage, ModelMessage } from "ai"; +import OpenAI from "openai"; import type { ProviderConfig } from "@infiplot/types"; -import { createLanguageModel, resolveProtocol } from "./model"; +import { normalizeBaseUrl } from "./normalizeUrl"; export type ChatMessage = { role: "system" | "user" | "assistant"; content: string; }; -// AI SDK 6 unifies cache stats across providers into usage.inputTokenDetails, -// so a single shape covers Anthropic, Gemini, and OpenAI-compatible providers. function summarizeSdkUsage( tag: string, - usage: LanguageModelUsage | undefined, + usage: OpenAI.Completions.CompletionUsage | undefined, ): string { if (!usage) return `[cache] ${tag} no-usage`; - const input = usage.inputTokens ?? 0; - const output = usage.outputTokens ?? 0; - const read = usage.inputTokenDetails?.cacheReadTokens; - const write = usage.inputTokenDetails?.cacheWriteTokens; - if (typeof read === "number" || typeof write === "number") { - const hit = read ?? 0; - const create = write ?? 0; - const rate = input > 0 ? ((hit / input) * 100).toFixed(1) : "n/a"; - return `[cache] ${tag} hit=${hit} create=${create} input=${input} rate=${rate}% completion=${output}`; + const input = usage.prompt_tokens ?? 0; + const output = usage.completion_tokens ?? 0; + const details = (usage as { prompt_tokens_details?: { cached_tokens?: number } }).prompt_tokens_details; + const cached = details?.cached_tokens; + if (typeof cached === "number") { + const rate = input > 0 ? ((cached / input) * 100).toFixed(1) : "n/a"; + return `[cache] ${tag} hit=${cached} input=${input} rate=${rate}% completion=${output}`; } return `[cache] ${tag} input=${input} completion=${output} (provider didn't report cache stats)`; } @@ -36,28 +31,28 @@ export async function chat( tag?: string; }, ): Promise { - const protocol = resolveProtocol(config); - const model = createLanguageModel(config, protocol); - - const system = messages.find((m) => m.role === "system")?.content; - const convo: ModelMessage[] = messages - .filter((m) => m.role !== "system") - .map((m) => ({ - role: m.role as "user" | "assistant", - content: m.content, - })); - - const { text, usage } = await generateText({ - model, - system, - messages: convo, - temperature: opts?.temperature ?? 0.9, + const client = new OpenAI({ + apiKey: config.apiKey, + baseURL: normalizeBaseUrl(config.baseUrl, "openai_compatible"), + maxRetries: 0, + dangerouslyAllowBrowser: true, }); - console.log(summarizeSdkUsage(opts?.tag ?? "chat", usage)); + const completion = await client.chat.completions.create({ + model: config.model, + messages: messages.map((m) => ({ + role: m.role as "system" | "user" | "assistant", + content: m.content, + })), + temperature: opts?.temperature ?? 0.9, + stream: false, + }); - if (typeof text !== "string" || text.length === 0) { - throw new Error(`Chat API (AI SDK ${protocol}) returned no content.`); + const text = completion.choices[0]?.message?.content ?? ""; + console.log(summarizeSdkUsage(opts?.tag ?? "chat", completion.usage ?? undefined)); + + if (text.length === 0) { + throw new Error(`Chat API returned no content.`); } return text; } diff --git a/lib/ai-client/image.ts b/lib/ai-client/image.ts index e7c5a48..5cae0f8 100644 --- a/lib/ai-client/image.ts +++ b/lib/ai-client/image.ts @@ -1,6 +1,4 @@ -import { generateImage as generateImageSdk } from "ai"; -import { createOpenAI } from "@ai-sdk/openai"; -import { createGoogleGenerativeAI } from "@ai-sdk/google"; +import OpenAI, { toFile, type Uploadable } from "openai"; import type { Orientation, ProviderConfig, ProviderProtocol } from "@infiplot/types"; import { fetchWithRetry } from "./fetchWithRetry"; import { normalizeBaseUrl } from "./normalizeUrl"; @@ -48,8 +46,8 @@ export type GenerateImageOptions = { /** * Reference images (UUIDs, URLs, or base64) to condition generation on — * typically character portraits + the prior scene image. Runware caps at 4; - * we silently truncate beyond that. On the OpenAI/Gemini AI SDK paths these - * map to `prompt.images` (the SDK accepts public URLs or data URLs). + * we silently truncate beyond that. On the native OpenAI path these are + * fetched/decoded and sent to `images.edit`. */ referenceImages?: string[]; /** 0–1, FLUX needs ≥ 0.8 to actually have an effect. Runware-only. */ @@ -58,7 +56,7 @@ export type GenerateImageOptions = { * Output aspect, locked per session. "portrait" → 9:16 vertical for mobile; * default/"landscape" → 16:9 widescreen. Mapped to each provider's nearest * supported size: Runware 1024×1792, OpenAI-compatible REST 1024x1792, - * native gpt-image 1024x1536, Gemini aspectRatio 9:16. + * native gpt-image 1024x1536. */ orientation?: Orientation; }; @@ -66,8 +64,8 @@ export type GenerateImageOptions = { export type GenerateImageResult = { /** * Image the client can render directly. A Runware CDN URL on the Runware - * path; a `data:;base64,...` URI on the AI SDK paths (OpenAI/Gemini - * return raw bytes, not a hosted URL). + * path; a `data:;base64,...` URI on the native OpenAI path when GPT + * image models return raw bytes instead of a hosted URL. */ imageUrl: string; /** @@ -117,63 +115,124 @@ export async function generateImage( const protocol = resolveImageProtocol(config); switch (protocol) { case "openai": - case "google": - return generateImageViaAiSdk(config, prompt, options, protocol); + return generateImageOpenAi(config, prompt, options); case "runware": return generateImageRunware(config, prompt, options); - case "anthropic": - throw new Error( - 'IMAGE_PROVIDER "anthropic" does not generate images. Use "openai", "google", "runware", or "openai_compatible".', - ); case "openai_compatible": default: return generateImageOpenAiCompatible(config, prompt, options); } } -// Native OpenAI (gpt-image) / Gemini (Nano Banana) via the Vercel AI SDK. -// Unlike the fetch path, this supports reference-image editing via -// `prompt.images`. The SDK returns raw bytes (no hosted URL), so we hand the -// client a data URI and synthesize a UUID; continuity references reuse the -// data URI rather than a provider UUID. -async function generateImageViaAiSdk( +// Native OpenAI (gpt-image) via the official OpenAI SDK. Unlike the compatible +// fetch path, this supports reference-image editing through `images.edit`. +// GPT image models return raw bytes, so we hand the client a data URI and +// synthesize a UUID; continuity references reuse the data URI rather than a +// provider UUID. +async function generateImageOpenAi( config: ProviderConfig, prompt: string, - options: GenerateImageOptions | undefined, - protocol: "openai" | "google", + options?: GenerateImageOptions, ): Promise { - const baseURL = normalizeBaseUrl(config.baseUrl, protocol); - const imageModel = - protocol === "openai" - ? createOpenAI({ apiKey: config.apiKey, baseURL }).image(config.model) - : createGoogleGenerativeAI({ apiKey: config.apiKey, baseURL }).image( - config.model, - ); - - const refs = (options?.referenceImages ?? []).slice(0, MAX_REFERENCE_IMAGES); - const promptArg = - refs.length > 0 ? { text: prompt, images: refs } : prompt; - - // Session-locked aspect. gpt-image takes an explicit `size` (portrait / - // landscape options are 1024x1536 / 1536x1024); Gemini takes an `aspectRatio`. - const portrait = options?.orientation === "portrait"; - const { image } = await generateImageSdk({ - model: imageModel, - prompt: promptArg, - ...(protocol === "openai" - ? { size: (portrait ? "1024x1536" : "1536x1024") as `${number}x${number}` } - : { aspectRatio: (portrait ? "9:16" : "16:9") as `${number}:${number}` }), + const client = new OpenAI({ + apiKey: config.apiKey, + baseURL: normalizeBaseUrl(config.baseUrl, "openai"), + maxRetries: 2, + dangerouslyAllowBrowser: true, }); + const refs = (options?.referenceImages ?? []).slice(0, MAX_REFERENCE_IMAGES); + const portrait = options?.orientation === "portrait"; + const size = portrait ? "1024x1536" : "1536x1024"; - return { - imageUrl: `data:${image.mediaType};base64,${image.base64}`, - imageUuid: crypto.randomUUID(), - }; + const response = + refs.length > 0 + ? await client.images.edit({ + model: config.model, + prompt, + image: await Promise.all(refs.map(referenceImageToUploadable)), + n: 1, + size, + }) + : await client.images.generate({ + model: config.model, + prompt, + n: 1, + size, + }); + + return imageResponseToResult(response); +} + +async function referenceImageToUploadable(ref: string): Promise { + if (ref.startsWith("data:")) { + const response = await fetch(ref); + if (!response.ok) { + throw new Error(`Failed to read data URL reference image.`); + } + const mediaType = response.headers.get("content-type") ?? "image/png"; + return toFile(response, `reference.${extensionFromMediaType(mediaType)}`, { + type: mediaType, + }); + } + + if (/^https?:\/\//i.test(ref)) { + const response = await fetch(ref); + if (!response.ok) { + throw new Error( + `Failed to fetch reference image ${ref}: HTTP ${response.status}`, + ); + } + const mediaType = response.headers.get("content-type") ?? "image/png"; + return toFile(response, filenameFromUrl(ref, mediaType), { + type: mediaType, + }); + } + + throw new Error( + `Native OpenAI image editing requires reference image URLs or data URLs; got "${ref.slice(0, 32)}...".`, + ); +} + +function imageResponseToResult( + response: OpenAI.Images.ImagesResponse, +): GenerateImageResult { + const data = response.data?.[0]; + const b64 = data?.b64_json; + if (b64) { + const format = response.output_format ?? "png"; + return { + imageUrl: `data:image/${format};base64,${b64}`, + imageUuid: crypto.randomUUID(), + }; + } + + const imageUrl = data?.url; + if (imageUrl) { + return { imageUrl, imageUuid: crypto.randomUUID() }; + } + + throw new Error(`No image data in OpenAI response.`); +} + +function filenameFromUrl(url: string, mediaType: string): string { + try { + const name = new URL(url).pathname.split("/").filter(Boolean).at(-1); + if (name && /\.[a-z0-9]+$/i.test(name)) return name; + } catch { + // Fall back to the media type below. + } + return `reference.${extensionFromMediaType(mediaType)}`; +} + +function extensionFromMediaType(mediaType: string): string { + if (mediaType.includes("jpeg") || mediaType.includes("jpg")) return "jpg"; + if (mediaType.includes("webp")) return "webp"; + return "png"; } // OpenAI-compatible REST route (GPTGod, DALL-E proxies, etc.). Basic // text-to-image only — no reference images on this path; for editing/anchoring -// set IMAGE_PROVIDER=openai (or google) to take the AI SDK path above. +// set IMAGE_PROVIDER=openai to take the native OpenAI path above. async function generateImageOpenAiCompatible( config: ProviderConfig, prompt: string, diff --git a/lib/ai-client/model.ts b/lib/ai-client/model.ts deleted file mode 100644 index 155e424..0000000 --- a/lib/ai-client/model.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { createAnthropic } from "@ai-sdk/anthropic"; -import { createGoogleGenerativeAI } from "@ai-sdk/google"; -import { createOpenAI } from "@ai-sdk/openai"; -import type { ProviderConfig, ProviderProtocol } from "@infiplot/types"; -import { normalizeBaseUrl } from "./normalizeUrl"; - -export function resolveProtocol(config: ProviderConfig): ProviderProtocol { - return config.provider ?? "openai_compatible"; -} - -export function createLanguageModel(config: ProviderConfig, protocol: ProviderProtocol) { - const baseURL = normalizeBaseUrl(config.baseUrl, protocol); - switch (protocol) { - case "anthropic": - return createAnthropic({ apiKey: config.apiKey, baseURL })(config.model); - case "google": - return createGoogleGenerativeAI({ apiKey: config.apiKey, baseURL })(config.model); - case "openai_compatible": - case "openai": - default: - return createOpenAI({ apiKey: config.apiKey, baseURL }).chat(config.model); - } -} diff --git a/lib/ai-client/normalizeUrl.ts b/lib/ai-client/normalizeUrl.ts index 10de5f3..d4a7101 100644 --- a/lib/ai-client/normalizeUrl.ts +++ b/lib/ai-client/normalizeUrl.ts @@ -31,8 +31,6 @@ const ENDPOINT_SUFFIX = const DEFAULT_VERSION_SEGMENT: Record = { openai_compatible: "v1", openai: "v1", - anthropic: "v1", - google: "v1beta", // Runware posts to the bare base URL with no version-pathed sub-resource, // so never inject a segment for it. runware: null, diff --git a/lib/ai-client/vision.ts b/lib/ai-client/vision.ts index 12df0fa..ec15b51 100644 --- a/lib/ai-client/vision.ts +++ b/lib/ai-client/vision.ts @@ -1,7 +1,6 @@ -import { generateText } from "ai"; -import type { ModelMessage } from "ai"; +import OpenAI from "openai"; import type { ProviderConfig } from "@infiplot/types"; -import { createLanguageModel, resolveProtocol } from "./model"; +import { normalizeBaseUrl } from "./normalizeUrl"; const VISION_TIMEOUT_MS = 60_000; @@ -22,34 +21,32 @@ export async function analyzeImageDataUrl( imageDataUrl: string, prompt: string, ): Promise { - const protocol = resolveProtocol(config); - const model = createLanguageModel(config, protocol); + const client = new OpenAI({ + apiKey: config.apiKey, + baseURL: normalizeBaseUrl(config.baseUrl, "openai_compatible"), + maxRetries: 0, + timeout: VISION_TIMEOUT_MS, + dangerouslyAllowBrowser: true, + }); - const messages: ModelMessage[] = [ - { - role: "user", - content: [ - { type: "text", text: prompt }, - { type: "image", image: imageDataUrl }, - ], - }, - ]; + const completion = await client.chat.completions.create({ + model: config.model, + messages: [ + { + role: "user", + content: [ + { type: "text", text: prompt }, + { type: "image_url", image_url: { url: imageDataUrl } }, + ], + }, + ], + temperature: 0.2, + stream: false, + }); - const timeoutCtrl = new AbortController(); - const timeoutId = setTimeout(() => timeoutCtrl.abort(), VISION_TIMEOUT_MS); - try { - const { text } = await generateText({ - model, - messages, - temperature: 0.2, - maxRetries: 0, - abortSignal: timeoutCtrl.signal, - }); - if (typeof text !== "string" || text.length === 0) { - throw new Error(`Vision API (AI SDK ${protocol}) returned no content.`); - } - return text; - } finally { - clearTimeout(timeoutId); + const text = completion.choices[0]?.message?.content ?? ""; + if (text.length === 0) { + throw new Error(`Vision API returned no content.`); } + return text; } diff --git a/lib/clientModelConfig.ts b/lib/clientModelConfig.ts index febce55..d075a22 100644 --- a/lib/clientModelConfig.ts +++ b/lib/clientModelConfig.ts @@ -10,8 +10,6 @@ const STORAGE_KEY = "infiplot:model"; const VALID_PROTOCOLS: ProviderProtocol[] = [ "openai_compatible", - "anthropic", - "google", "openai", "runware", ]; diff --git a/lib/config.ts b/lib/config.ts index e30ea70..c733df9 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -6,8 +6,6 @@ import type { const VALID_PROTOCOLS = [ "openai_compatible", - "anthropic", - "google", "openai", "runware", ] as const; diff --git a/lib/types/index.ts b/lib/types/index.ts index c0a1989..ba0a7fa 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -327,19 +327,15 @@ export type VisionClassify = "insert-beat" | "change-scene"; * openai_compatible text / vision / image — OpenAI Chat Completions + * `/images/generations` (self-implemented fetch; the * default for text/vision when unset) - * anthropic text / vision — native Anthropic Messages (AI SDK) - * google text / vision / image — native Gemini (AI SDK); image - * uses the Nano Banana family - * openai image only — OpenAI gpt-image via AI SDK, - * unlocks reference-image editing (for text/vision use - * openai_compatible, which already speaks OpenAI's format) + * openai image only — OpenAI gpt-image via the + * official OpenAI SDK, unlocks reference-image editing + * (for text/vision use openai_compatible, which already + * speaks OpenAI's format) * runware image only — Runware task-array protocol * (self-implemented; the default for runware.ai URLs) */ export type ProviderProtocol = | "openai_compatible" - | "anthropic" - | "google" | "openai" | "runware"; diff --git a/package.json b/package.json index 751430a..94d7212 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,10 @@ "deploy:cf": "opennextjs-cloudflare deploy" }, "dependencies": { - "@ai-sdk/anthropic": "^3.0.81", - "@ai-sdk/google": "^3.0.80", - "@ai-sdk/openai": "^3.0.67", - "ai": "^6.0.196", "jsonrepair": "^3.14.0", "jszip": "^3.10.1", "next": "^16.0.0", + "openai": "^6.42.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 094b998..cb56ee5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,18 +8,6 @@ importers: .: dependencies: - '@ai-sdk/anthropic': - specifier: ^3.0.81 - version: 3.0.81(zod@4.4.3) - '@ai-sdk/google': - specifier: ^3.0.80 - version: 3.0.80(zod@4.4.3) - '@ai-sdk/openai': - specifier: ^3.0.67 - version: 3.0.67(zod@4.4.3) - ai: - specifier: ^6.0.196 - version: 6.0.196(zod@4.4.3) jsonrepair: specifier: ^3.14.0 version: 3.14.0 @@ -29,6 +17,9 @@ importers: next: specifier: ^16.0.0 version: 16.2.7(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + openai: + specifier: ^6.42.0 + version: 6.42.0(ws@8.20.1)(zod@4.4.3) react: specifier: ^19.0.0 version: 19.2.7 @@ -69,40 +60,6 @@ importers: packages: - '@ai-sdk/anthropic@3.0.81': - resolution: {integrity: sha512-B1JDd9Ugq9R5AgIaW3674lhGCMMYJcPUxnrZh8fzbGojgg4QvHFRv6eZahGQAUsmGHbcf74G9bdSBDLWQGY2GA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/gateway@3.0.124': - resolution: {integrity: sha512-h8CrmbSG+8X0C+M/E1M4oiDHYevqwbzAPN+uLRHS0eJaatF2MZ+juNtOHXNOjk7Bsk9mD2RjYMjJO9dFkb9I7Q==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/google@3.0.80': - resolution: {integrity: sha512-5ORbm/yFUPO0MEvZsxBMN0cdKw2+lwU/wVn5KN3KF8Dmk1LughuDuUohMh/7iU/XFTiyB0OvmTW/tdV/J7O9zg==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/openai@3.0.67': - resolution: {integrity: sha512-oAiGC9eWG7IgtdsdS74bOCnAAHarAfTJhWN9x5INwnWPekL802AvF+0I5DvLzIF1MIRmNw4N8mPSL/GUVbX9Mw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider-utils@4.0.27': - resolution: {integrity: sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - - '@ai-sdk/provider@3.0.10': - resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==} - engines: {node: '>=18'} - '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -1257,9 +1214,6 @@ packages: '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -1283,10 +1237,6 @@ packages: '@types/react@19.2.16': resolution: {integrity: sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==} - '@vercel/oidc@3.2.0': - resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} - engines: {node: '>= 20'} - abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -1304,12 +1254,6 @@ packages: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} - ai@6.0.196: - resolution: {integrity: sha512-2T45UeqKL4a11KQ14I5i1YYHOvCFrMF478E1k6PVjlQSGUvXSv4xrxIaQbUL4qgv91DADSbddwv3oR49pPAK3g==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -1618,10 +1562,6 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventsource-parser@3.1.0: - resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} - engines: {node: '>=18.0.0'} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1833,9 +1773,6 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - jsonrepair@3.14.0: resolution: {integrity: sha512-tWPGKMZf/8UPim+fcW2EfcQ/d/7aKUrP6IECz9G3Tu6Q5dX0orSleqJ9z6sSw7qrQkjF8/Edo4DvsWBZ8H+HNg==} hasBin: true @@ -2028,6 +1965,17 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + openai@6.42.0: + resolution: {integrity: sha512-1WFEt/uXMXOLhYRNkgJWo08Y2YNvNwpVU72K7ibrWgWpNOXd4VojXLbe6SQ4bLiUQ3Y8jz4IiyVkylJCL1DtZg==} + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -2495,42 +2443,6 @@ packages: snapshots: - '@ai-sdk/anthropic@3.0.81(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3) - zod: 4.4.3 - - '@ai-sdk/gateway@3.0.124(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3) - '@vercel/oidc': 3.2.0 - zod: 4.4.3 - - '@ai-sdk/google@3.0.80(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3) - zod: 4.4.3 - - '@ai-sdk/openai@3.0.67(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3) - zod: 4.4.3 - - '@ai-sdk/provider-utils@4.0.27(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 3.0.10 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.1.0 - zod: 4.4.3 - - '@ai-sdk/provider@3.0.10': - dependencies: - json-schema: 0.4.0 - '@alloc/quick-lru@5.2.0': {} '@ast-grep/napi-darwin-arm64@0.40.5': @@ -3632,7 +3544,8 @@ snapshots: - encoding - supports-color - '@opentelemetry/api@1.9.1': {} + '@opentelemetry/api@1.9.1': + optional: true '@poppinss/colors@4.1.6': dependencies: @@ -3844,8 +3757,6 @@ snapshots: '@speed-highlight/core@1.2.15': {} - '@standard-schema/spec@1.1.0': {} - '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -3873,8 +3784,6 @@ snapshots: dependencies: csstype: 3.2.3 - '@vercel/oidc@3.2.0': {} - abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -3890,14 +3799,6 @@ snapshots: dependencies: humanize-ms: 1.2.1 - ai@6.0.196(zod@4.4.3): - dependencies: - '@ai-sdk/gateway': 3.0.124(zod@4.4.3) - '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3) - '@opentelemetry/api': 1.9.1 - zod: 4.4.3 - ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} @@ -4213,8 +4114,6 @@ snapshots: event-target-shim@5.0.1: {} - eventsource-parser@3.1.0: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -4460,8 +4359,6 @@ snapshots: jiti@1.21.7: {} - json-schema@0.4.0: {} - jsonrepair@3.14.0: {} jszip@3.10.1: @@ -4617,6 +4514,11 @@ snapshots: dependencies: mimic-fn: 2.1.0 + openai@6.42.0(ws@8.20.1)(zod@4.4.3): + optionalDependencies: + ws: 8.20.1 + zod: 4.4.3 + package-json-from-dist@1.0.1: {} pako@1.0.11: {} @@ -5132,4 +5034,5 @@ snapshots: cookie: 1.1.1 youch-core: 0.3.3 - zod@4.4.3: {} + zod@4.4.3: + optional: true