diff --git a/apps/web/__tests__/unit/generate-ai-title.test.ts b/apps/web/__tests__/unit/generate-ai-title.test.ts index a21560b65e2..ace77a351e1 100644 --- a/apps/web/__tests__/unit/generate-ai-title.test.ts +++ b/apps/web/__tests__/unit/generate-ai-title.test.ts @@ -12,9 +12,9 @@ vi.mock("@cap/web-backend", () => ({ Storage: {}, })); -vi.mock("@/lib/groq-client", () => ({ - GROQ_MODEL: "test-model", - getGroqClient: vi.fn(() => null), +vi.mock("@/lib/ai-provider", () => ({ + getAiClient: vi.fn(() => null), + getAiModel: vi.fn(() => "test-model"), })); vi.mock("@/lib/server", () => ({ diff --git a/apps/web/actions/videos/get-status.ts b/apps/web/actions/videos/get-status.ts index 4d3bc5c4fa5..e535471ca34 100644 --- a/apps/web/actions/videos/get-status.ts +++ b/apps/web/actions/videos/get-status.ts @@ -3,11 +3,11 @@ import { db } from "@cap/database"; import { users, videos, videoUploads } from "@cap/database/schema"; import type { VideoMetadata } from "@cap/database/types"; -import { serverEnv } from "@cap/env"; import { provideOptionalAuth, VideosPolicy } from "@cap/web-backend"; import { Policy, type Video } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { Effect, Exit } from "effect"; +import { isAiConfigured, isSttConfigured } from "@/lib/ai-provider"; import { isRetryableDesktopSegmentsFinalizationError, queueDesktopSegmentsFinalization, @@ -60,7 +60,7 @@ export async function getVideoStatus( const metadata: VideoMetadata = (video.metadata as VideoMetadata) || {}; - if (!video.transcriptionStatus && serverEnv().DEEPGRAM_API_KEY) { + if (!video.transcriptionStatus && isSttConfigured()) { const activeUpload = await db() .select({ videoId: videoUploads.videoId, @@ -151,7 +151,7 @@ export async function getVideoStatus( video.transcriptionStatus === "COMPLETE" && !metadata.aiGenerationStatus && !metadata.summary && - (serverEnv().GROQ_API_KEY || serverEnv().OPENAI_API_KEY); + isAiConfigured(); if (shouldTriggerAiGeneration) { try { diff --git a/apps/web/actions/videos/translate-transcript.ts b/apps/web/actions/videos/translate-transcript.ts index 7ca24d4a10f..b4687187076 100644 --- a/apps/web/actions/videos/translate-transcript.ts +++ b/apps/web/actions/videos/translate-transcript.ts @@ -6,7 +6,7 @@ import { Storage } from "@cap/web-backend"; import type { Video } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { Effect, Option } from "effect"; -import { GROQ_MODEL, getGroqClient } from "@/lib/groq-client"; +import { getAiClient, getAiModel } from "@/lib/ai-provider"; import { runPromise } from "@/lib/server"; import { decodeStorageVideo } from "@/lib/video-storage"; import { @@ -38,8 +38,8 @@ export async function translateTranscript( }; } - const groq = getGroqClient(); - if (!groq) { + const ai = getAiClient(); + if (!ai) { return { success: false, message: "Translation service not configured", @@ -94,7 +94,7 @@ export async function translateTranscript( const translatedVtt = await translateVttContent( originalVtt.value, targetLanguage, - groq, + ai, ); if (!translatedVtt) { @@ -124,7 +124,7 @@ export async function translateTranscript( async function translateVttContent( vttContent: string, targetLanguage: LanguageCode, - groq: NonNullable>, + ai: NonNullable>, ): Promise { const targetLanguageName = SUPPORTED_LANGUAGES[targetLanguage]; @@ -144,8 +144,8 @@ VTT content to translate: ${vttContent}`; try { - const response = await groq.chat.completions.create({ - model: GROQ_MODEL, + const response = await ai.chat.completions.create({ + model: getAiModel(), messages: [{ role: "user", content: prompt }], temperature: 0.3, max_tokens: 8000, diff --git a/apps/web/lib/ai-provider.ts b/apps/web/lib/ai-provider.ts new file mode 100644 index 00000000000..9679f980c57 --- /dev/null +++ b/apps/web/lib/ai-provider.ts @@ -0,0 +1,124 @@ +import "server-only"; + +import { serverEnv } from "@cap/env"; +import OpenAI from "openai"; + +const GROQ_DEFAULT_BASE_URL = "https://api.groq.com/openai/v1"; +const GROQ_DEFAULT_MODEL = "openai/gpt-oss-120b"; +const OPENAI_DEFAULT_MODEL = "gpt-4o-mini"; +const OPENAI_WHISPER_DEFAULT_MODEL = "whisper-1"; + +const AI_TIMEOUT_MS = 120_000; +const STT_TIMEOUT_MS = 300_000; +const MAX_RETRIES = 2; + +export function isAiConfigured(): boolean { + const env = serverEnv(); + return Boolean(env.AI_BASE_URL || env.GROQ_API_KEY || env.OPENAI_API_KEY); +} + +export function isSttConfigured(): boolean { + const env = serverEnv(); + return Boolean(env.STT_BASE_URL || env.DEEPGRAM_API_KEY); +} + +export function getAiClient(): OpenAI | null { + const env = serverEnv(); + + if (env.AI_BASE_URL) { + if (!env.AI_API_KEY) { + throw new Error( + "AI_API_KEY is required when AI_BASE_URL is set. Use any non-empty string for local providers that ignore auth.", + ); + } + if (!env.AI_MODEL) { + throw new Error("AI_MODEL is required when AI_BASE_URL is set."); + } + return new OpenAI({ + baseURL: env.AI_BASE_URL, + apiKey: env.AI_API_KEY, + timeout: AI_TIMEOUT_MS, + maxRetries: MAX_RETRIES, + }); + } + + if (env.GROQ_API_KEY) { + return new OpenAI({ + baseURL: GROQ_DEFAULT_BASE_URL, + apiKey: env.GROQ_API_KEY, + timeout: AI_TIMEOUT_MS, + maxRetries: MAX_RETRIES, + }); + } + + if (env.OPENAI_API_KEY) { + return new OpenAI({ + apiKey: env.OPENAI_API_KEY, + timeout: AI_TIMEOUT_MS, + maxRetries: MAX_RETRIES, + }); + } + + return null; +} + +export function getAiFallbackClient(): { + client: OpenAI; + model: string; +} | null { + const env = serverEnv(); + if (env.AI_BASE_URL) return null; + if (!env.GROQ_API_KEY || !env.OPENAI_API_KEY) return null; + return { + client: new OpenAI({ + apiKey: env.OPENAI_API_KEY, + timeout: AI_TIMEOUT_MS, + maxRetries: MAX_RETRIES, + }), + model: OPENAI_DEFAULT_MODEL, + }; +} + +export function getAiModel(): string { + const env = serverEnv(); + if (env.AI_BASE_URL) { + if (!env.AI_MODEL) { + throw new Error("AI_MODEL is required when AI_BASE_URL is set."); + } + return env.AI_MODEL; + } + if (env.GROQ_API_KEY) return GROQ_DEFAULT_MODEL; + return OPENAI_DEFAULT_MODEL; +} + +export function getSttClient(): OpenAI | null { + const env = serverEnv(); + if (!env.STT_BASE_URL) return null; + + if (!env.STT_API_KEY) { + throw new Error( + "STT_API_KEY is required when STT_BASE_URL is set. Use any non-empty string for local providers that ignore auth.", + ); + } + if (!env.STT_MODEL) { + throw new Error("STT_MODEL is required when STT_BASE_URL is set."); + } + + return new OpenAI({ + baseURL: env.STT_BASE_URL, + apiKey: env.STT_API_KEY, + timeout: STT_TIMEOUT_MS, + maxRetries: MAX_RETRIES, + }); +} + +export function getSttModel(): string { + const env = serverEnv(); + if (env.STT_BASE_URL) { + if (!env.STT_MODEL) { + throw new Error("STT_MODEL is required when STT_BASE_URL is set."); + } + return env.STT_MODEL; + } + return OPENAI_WHISPER_DEFAULT_MODEL; +} diff --git a/apps/web/lib/generate-ai.ts b/apps/web/lib/generate-ai.ts index 641a6e1545d..e22db92c725 100644 --- a/apps/web/lib/generate-ai.ts +++ b/apps/web/lib/generate-ai.ts @@ -1,10 +1,10 @@ import { db } from "@cap/database"; import { videos } from "@cap/database/schema"; import type { VideoMetadata } from "@cap/database/types"; -import { serverEnv } from "@cap/env"; import type { Video } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { start } from "workflow/api"; +import { isAiConfigured } from "@/lib/ai-provider"; import { generateAiWorkflow } from "@/workflows/generate-ai"; type GenerateAiResult = { @@ -16,10 +16,11 @@ export async function startAiGeneration( videoId: Video.VideoId, userId: string, ): Promise { - if (!serverEnv().GROQ_API_KEY && !serverEnv().OPENAI_API_KEY) { + if (!isAiConfigured()) { return { success: false, - message: "Missing AI API keys (Groq or OpenAI)", + message: + "No AI provider configured (set AI_BASE_URL, GROQ_API_KEY, or OPENAI_API_KEY)", }; } diff --git a/apps/web/lib/groq-client.ts b/apps/web/lib/groq-client.ts deleted file mode 100644 index f22ee9c4525..00000000000 --- a/apps/web/lib/groq-client.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { serverEnv } from "@cap/env"; -import Groq from "groq-sdk"; - -let groqClient: Groq | null = null; - -export function getGroqClient(): Groq | null { - if (!serverEnv().GROQ_API_KEY) { - return null; - } - - if (!groqClient) { - groqClient = new Groq({ - apiKey: serverEnv().GROQ_API_KEY, - }); - } - - return groqClient; -} - -export const GROQ_MODEL = "openai/gpt-oss-120b"; diff --git a/apps/web/lib/messenger/agent.ts b/apps/web/lib/messenger/agent.ts index 6ca70b36bf0..e544b11e34d 100644 --- a/apps/web/lib/messenger/agent.ts +++ b/apps/web/lib/messenger/agent.ts @@ -2,7 +2,7 @@ import "server-only"; import type { MessengerMessageRole } from "@cap/database/schema"; import { serverEnv } from "@cap/env"; -import { GROQ_MODEL, getGroqClient } from "@/lib/groq-client"; +import { getAiClient, getAiModel } from "@/lib/ai-provider"; import { CAP_REFERENCE_GUIDE, MESSENGER_AGENT_PROMPT } from "./constants"; import { getKnowledgeTag, searchSupermemory } from "./supermemory"; @@ -165,18 +165,18 @@ const callOpenAi = async ({ return parseOpenAiContent(payload); }; -const callGroq = async ({ +const callAiProvider = async ({ systemPrompt, history, }: { systemPrompt: string; history: ConversationMessage[]; }) => { - const client = getGroqClient(); + const client = getAiClient(); if (!client) return null; const completion = await client.chat.completions.create({ - model: GROQ_MODEL, + model: getAiModel(), temperature: 0.65, max_tokens: 500, messages: [ @@ -227,8 +227,10 @@ export const generateMessengerAgentReply = async ({ ); if (fromOpenAi) return fromOpenAi; - const fromGroq = await callGroq({ systemPrompt, history }).catch(() => null); - if (fromGroq) return fromGroq; + const fromAi = await callAiProvider({ systemPrompt, history }).catch( + () => null, + ); + if (fromAi) return fromAi; return "Oh no, I'm so sorry about this! I'm having a little technical hiccup on my end. Someone from the team will jump in here shortly to help you out though!"; }; diff --git a/apps/web/lib/transcribe.ts b/apps/web/lib/transcribe.ts index 606ef72f741..5869479b8aa 100644 --- a/apps/web/lib/transcribe.ts +++ b/apps/web/lib/transcribe.ts @@ -1,9 +1,9 @@ import { db } from "@cap/database"; import { organizations, videos, videoUploads } from "@cap/database/schema"; -import { serverEnv } from "@cap/env"; import type { Video } from "@cap/web-domain"; import { eq } from "drizzle-orm"; import { start } from "workflow/api"; +import { isSttConfigured } from "@/lib/ai-provider"; import { transcribeVideoWorkflow } from "@/workflows/transcribe"; type TranscribeResult = { @@ -17,10 +17,11 @@ export async function transcribeVideo( aiGenerationEnabled = false, _isRetry = false, ): Promise { - if (!serverEnv().DEEPGRAM_API_KEY) { + if (!isSttConfigured()) { return { success: false, - message: "Missing necessary environment variables", + message: + "No transcription provider configured (set DEEPGRAM_API_KEY or STT_BASE_URL)", }; } diff --git a/apps/web/package.json b/apps/web/package.json index 0d21caf3bae..69ffbc4dd4a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -100,7 +100,6 @@ "framer-motion": "^11.13.1", "geist": "^1.3.1", "gif.js": "0.2.0", - "groq-sdk": "^0.29.0", "hls.js": "^1.5.3", "hono": "^4.7.1", "js-cookie": "^3.0.5", @@ -114,6 +113,7 @@ "next": "16.2.1", "next-auth": "^4.24.5", "next-mdx-remote": "^6.0.0", + "openai": "^4.86.0", "posthog-js": "^1.215.3", "posthog-node": "^4.11.2", "prettier": "^3.7.4", diff --git a/apps/web/workflows/generate-ai.ts b/apps/web/workflows/generate-ai.ts index 9a6d8b11096..0289e240147 100644 --- a/apps/web/workflows/generate-ai.ts +++ b/apps/web/workflows/generate-ai.ts @@ -1,7 +1,6 @@ import { db } from "@cap/database"; import { organizations, videos } from "@cap/database/schema"; import type { VideoMetadata } from "@cap/database/types"; -import { serverEnv } from "@cap/env"; import { Storage } from "@cap/web-backend"; import { AI_GENERATION_LANGUAGE_AUTO, @@ -13,7 +12,12 @@ import { import { eq } from "drizzle-orm"; import { Effect, Option } from "effect"; import { FatalError } from "workflow"; -import { GROQ_MODEL, getGroqClient } from "@/lib/groq-client"; +import { + getAiClient, + getAiFallbackClient, + getAiModel, + isAiConfigured, +} from "@/lib/ai-provider"; import { runPromise } from "@/lib/server"; import { decodeStorageVideo } from "@/lib/video-storage"; @@ -96,9 +100,10 @@ export async function generateAiWorkflow(payload: GenerateAiWorkflowPayload) { async function validateAndSetProcessing(videoId: string): Promise { "use step"; - const groqClient = getGroqClient(); - if (!groqClient && !serverEnv().OPENAI_API_KEY) { - throw new FatalError("Missing Groq or OpenAI API key"); + if (!isAiConfigured()) { + throw new FatalError( + "No AI provider configured (set AI_BASE_URL, GROQ_API_KEY, or OPENAI_API_KEY)", + ); } const query = await db() @@ -195,7 +200,13 @@ async function generateWithAi( ): Promise { "use step"; - const groqClient = getGroqClient(); + const aiClient = getAiClient(); + if (!aiClient) { + throw new FatalError( + "No AI provider configured (set AI_BASE_URL, GROQ_API_KEY, or OPENAI_API_KEY)", + ); + } + const model = getAiModel(); const chunks = chunkTranscriptWithTimestamps(transcript.segments); const videoDuration = getVideoDuration(transcript.segments); @@ -206,14 +217,16 @@ async function generateWithAi( result = await generateSingleChunk( transcript.segments, videoDuration, - groqClient, + aiClient, + model, languageInstruction, ); } else { result = await generateMultipleChunks( chunks, videoDuration, - groqClient, + aiClient, + model, languageInstruction, ); } @@ -367,45 +380,56 @@ function chunkTranscriptWithTimestamps( async function callAiApi( prompt: string, - groqClient: ReturnType, + client: NonNullable>, + model: string, ): Promise { - if (groqClient) { - try { - const completion = await groqClient.chat.completions.create({ - messages: [{ role: "user", content: prompt }], - model: GROQ_MODEL, - }); - return completion.choices?.[0]?.message?.content || "{}"; - } catch (groqError) { - if (serverEnv().OPENAI_API_KEY) { - return callOpenAi(prompt); - } - throw groqError; - } - } else if (serverEnv().OPENAI_API_KEY) { - return callOpenAi(prompt); + try { + return await invokeChat(client, model, prompt); + } catch (primaryError) { + const fallback = getAiFallbackClient(); + if (!fallback) throw primaryError; + const message = String( + (primaryError as { message?: unknown })?.message ?? primaryError, + ).slice(0, 200); + console.warn( + `[generate-ai] primary AI provider failed, falling back to OpenAI: ${message}`, + ); + return await invokeChat(fallback.client, fallback.model, prompt); } - return "{}"; } -async function callOpenAi(prompt: string): Promise { - const aiRes = await fetch("https://api.openai.com/v1/chat/completions", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${serverEnv().OPENAI_API_KEY}`, - }, - body: JSON.stringify({ - model: "gpt-4o-mini", +async function invokeChat( + client: NonNullable>, + model: string, + prompt: string, +): Promise { + try { + const completion = await client.chat.completions.create({ + messages: [{ role: "user", content: prompt }], + model, + response_format: { type: "json_object" }, + }); + return completion.choices?.[0]?.message?.content || "{}"; + } catch (error) { + if (!isUnsupportedJsonFormatError(error)) throw error; + const completion = await client.chat.completions.create({ messages: [{ role: "user", content: prompt }], - }), - }); - if (!aiRes.ok) { - const errorText = await aiRes.text(); - throw new Error(`OpenAI API error: ${aiRes.status} ${errorText}`); + model, + }); + return completion.choices?.[0]?.message?.content || "{}"; } - const aiJson = await aiRes.json(); - return aiJson.choices?.[0]?.message?.content || "{}"; +} + +function isUnsupportedJsonFormatError(error: unknown): boolean { + if (!error || typeof error !== "object") return false; + const message = String( + (error as { message?: unknown }).message ?? "", + ).toLowerCase(); + return ( + message.includes("response_format") || + message.includes("json_object") || + message.includes("json mode") + ); } function cleanJsonResponse(content: string): string { @@ -421,7 +445,8 @@ function cleanJsonResponse(content: string): string { async function generateSingleChunk( segments: VttSegment[], videoDuration: number, - groqClient: ReturnType, + client: NonNullable>, + model: string, languageInstruction: string, ): Promise { const transcriptWithTimestamps = segments @@ -454,14 +479,15 @@ Return ONLY valid JSON without any markdown formatting or code blocks. Transcript: ${transcriptWithTimestamps}`; - const content = await callAiApi(prompt, groqClient); + const content = await callAiApi(prompt, client, model); return parseAiResponse(content); } async function generateMultipleChunks( chunks: { text: string; startTime: number; endTime: number }[], videoDuration: number, - groqClient: ReturnType, + client: NonNullable>, + model: string, languageInstruction: string, ): Promise { const chunkSummaries: { @@ -493,7 +519,7 @@ Return ONLY valid JSON without any markdown formatting or code blocks. Transcript section: ${chunk.text}`; - const chunkContent = await callAiApi(chunkPrompt, groqClient); + const chunkContent = await callAiApi(chunkPrompt, client, model); try { const parsed = JSON.parse(cleanJsonResponse(chunkContent).trim()); chunkSummaries.push({ @@ -549,7 +575,7 @@ ${languageInstruction} Keep JSON property names exactly as shown. Return ONLY valid JSON without any markdown formatting or code blocks.`; - const finalContent = await callAiApi(finalPrompt, groqClient); + const finalContent = await callAiApi(finalPrompt, client, model); try { const parsed = JSON.parse(cleanJsonResponse(finalContent).trim()); return { diff --git a/apps/web/workflows/transcribe.ts b/apps/web/workflows/transcribe.ts index 1f985ce47a2..db77d6c469a 100644 --- a/apps/web/workflows/transcribe.ts +++ b/apps/web/workflows/transcribe.ts @@ -19,7 +19,9 @@ import { } from "@cap/web-domain"; import { createClient } from "@deepgram/sdk"; import { eq } from "drizzle-orm"; +import { toFile } from "openai"; import { FatalError } from "workflow"; +import { getSttClient, getSttModel, isSttConfigured } from "@/lib/ai-provider"; import { ENHANCED_AUDIO_CONTENT_TYPE, ENHANCED_AUDIO_EXTENSION, @@ -76,7 +78,7 @@ export async function transcribeVideoWorkflow( } const [transcription] = await Promise.all([ - transcribeWithDeepgram(audioUrl, videoData.aiGenerationLanguage), + transcribeAudio(audioUrl, videoData.aiGenerationLanguage), ]); await saveTranscription(videoId, userId, videoData.video, transcription); @@ -98,8 +100,10 @@ export async function transcribeVideoWorkflow( async function validateVideo(videoId: string): Promise { "use step"; - if (!serverEnv().DEEPGRAM_API_KEY) { - throw new FatalError("Missing DEEPGRAM_API_KEY"); + if (!isSttConfigured()) { + throw new FatalError( + "No transcription provider configured (set DEEPGRAM_API_KEY or STT_BASE_URL)", + ); } const query = await db() @@ -324,7 +328,7 @@ export function getDeepgramTranscriptionOptions( }; } -async function transcribeWithDeepgram( +async function transcribeAudio( audioUrl: string, language: AiGenerationLanguage, ): Promise { @@ -339,6 +343,16 @@ async function transcribeWithDeepgram( const audioBuffer = Buffer.from(await audioResponse.arrayBuffer()); + if (serverEnv().STT_BASE_URL) { + return transcribeViaSttProvider(audioBuffer, language); + } + return transcribeViaDeepgram(audioBuffer, language); +} + +async function transcribeViaDeepgram( + audioBuffer: Buffer, + language: AiGenerationLanguage, +): Promise { const deepgram = createClient(serverEnv().DEEPGRAM_API_KEY as string); const { result, error } = await deepgram.listen.prerecorded.transcribeFile( @@ -355,6 +369,39 @@ async function transcribeWithDeepgram( return formatToWebVTT(result as unknown as DeepgramResult); } +async function transcribeViaSttProvider( + audioBuffer: Buffer, + language: AiGenerationLanguage, +): Promise { + const client = getSttClient(); + if (!client) { + throw new Error("STT client not configured"); + } + + const file = await toFile(audioBuffer, "audio.mp3", { + type: "audio/mpeg", + }); + + const response = await client.audio.transcriptions.create({ + file, + model: getSttModel(), + response_format: "vtt" as const, + language: language !== AI_GENERATION_LANGUAGE_AUTO ? language : undefined, + }); + + if ( + typeof response !== "string" || + !/^WEBVTT/m.test(response) || + !response.includes("-->") + ) { + throw new Error( + "STT provider did not return valid WebVTT (verify STT_MODEL supports response_format=vtt)", + ); + } + + return response; +} + const DEEPGRAM_DETECTABLE_LANGUAGES = [ "en", "es", diff --git a/docker-compose.coolify.env.example b/docker-compose.coolify.env.example index 144db76dc0e..43e3bbba652 100644 --- a/docker-compose.coolify.env.example +++ b/docker-compose.coolify.env.example @@ -60,13 +60,25 @@ RESEND_FROM_DOMAIN= # AI FEATURES (OPTIONAL) # =================== -# For video transcription +# For video transcription via Deepgram DEEPGRAM_API_KEY= # For AI summaries (Groq is preferred, OpenAI as fallback) GROQ_API_KEY= OPENAI_API_KEY= +# OpenAI-compatible chat provider (Ollama, vLLM, OpenRouter, LiteLLM, etc.). +# When AI_BASE_URL is set, it overrides GROQ/OPENAI for summary/chapter generation. +AI_BASE_URL= +AI_API_KEY= +AI_MODEL= + +# OpenAI-compatible audio transcription (faster-whisper-server, whisper.cpp HTTP, Groq, etc.). +# When STT_BASE_URL is set, it overrides Deepgram for transcription. +STT_BASE_URL= +STT_API_KEY= +STT_MODEL= + # =================== # OAUTH (OPTIONAL) # =================== diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 7471a6feb77..b1895be2832 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -28,6 +28,15 @@ services: MEDIA_SERVER_URL: http://media-server:3456 MEDIA_SERVER_WEBHOOK_SECRET: ${MEDIA_SERVER_WEBHOOK_SECRET} MEDIA_SERVER_WEBHOOK_URL: http://cap-web:3000 + DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY:-} + GROQ_API_KEY: ${GROQ_API_KEY:-} + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + AI_BASE_URL: ${AI_BASE_URL:-} + AI_API_KEY: ${AI_API_KEY:-} + AI_MODEL: ${AI_MODEL:-} + STT_BASE_URL: ${STT_BASE_URL:-} + STT_API_KEY: ${STT_API_KEY:-} + STT_MODEL: ${STT_MODEL:-} ports: - "3000:3000" healthcheck: diff --git a/docker-compose.template.yml b/docker-compose.template.yml index e21f40d2802..315df6e6ee5 100644 --- a/docker-compose.template.yml +++ b/docker-compose.template.yml @@ -26,6 +26,17 @@ services: MEDIA_SERVER_URL: http://cap-media-server:3456 # CHANGE THESE TO YOUR OWN VALUES MEDIA_SERVER_WEBHOOK_SECRET: fe337b52749070bb7b5d2c78cff9948439ea73cbc1869ba39d350e6c24db53b1 + # AI providers (all optional). Set one of GROQ/OPENAI/AI_BASE_URL for chat summaries; + # set DEEPGRAM_API_KEY or STT_BASE_URL for transcription. + DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY:-} + GROQ_API_KEY: ${GROQ_API_KEY:-} + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + AI_BASE_URL: ${AI_BASE_URL:-} + AI_API_KEY: ${AI_API_KEY:-} + AI_MODEL: ${AI_MODEL:-} + STT_BASE_URL: ${STT_BASE_URL:-} + STT_API_KEY: ${STT_API_KEY:-} + STT_MODEL: ${STT_MODEL:-} ports: - 3000:3000 diff --git a/docker-compose.yml b/docker-compose.yml index 6b9152d06ca..5f3ed5ce52e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,15 @@ services: RESEND_FROM_DOMAIN: ${RESEND_FROM_DOMAIN:-} MEDIA_SERVER_URL: http://media-server:3456 MEDIA_SERVER_WEBHOOK_URL: http://cap-web:3000 + DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY:-} + GROQ_API_KEY: ${GROQ_API_KEY:-} + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + AI_BASE_URL: ${AI_BASE_URL:-} + AI_API_KEY: ${AI_API_KEY:-} + AI_MODEL: ${AI_MODEL:-} + STT_BASE_URL: ${STT_BASE_URL:-} + STT_API_KEY: ${STT_API_KEY:-} + STT_MODEL: ${STT_MODEL:-} ports: - "${CAP_PORT:-3000}:3000" healthcheck: diff --git a/packages/env/server.ts b/packages/env/server.ts index febc9e3e873..881d55d2609 100644 --- a/packages/env/server.ts +++ b/packages/env/server.ts @@ -86,6 +86,50 @@ function createServerEnv() { ANTHROPIC_API_KEY: z.string().optional().describe("AI chat"), OPENAI_API_KEY: z.string().optional().describe("AI summaries"), GROQ_API_KEY: z.string().optional().describe("AI summaries"), + AI_BASE_URL: z + .string() + .optional() + .refine( + (v) => !v || /^https?:\/\//.test(v), + "AI_BASE_URL must start with http:// or https:// when set", + ) + .describe( + "OpenAI-compatible chat completions base URL (e.g. http://host.docker.internal:11434/v1 for Ollama). Overrides GROQ/OPENAI defaults when set. Requires AI_API_KEY and AI_MODEL.", + ), + AI_API_KEY: z + .string() + .optional() + .describe( + "API key for AI_BASE_URL. Required when AI_BASE_URL is set (use any non-empty string for local providers that ignore auth).", + ), + AI_MODEL: z + .string() + .optional() + .describe( + "Chat model for AI_BASE_URL (e.g. gemma3:12b). Required when AI_BASE_URL is set.", + ), + STT_BASE_URL: z + .string() + .optional() + .refine( + (v) => !v || /^https?:\/\//.test(v), + "STT_BASE_URL must start with http:// or https:// when set", + ) + .describe( + "OpenAI-compatible audio transcription base URL (e.g. faster-whisper-server). When set, replaces the Deepgram path. Requires STT_API_KEY and STT_MODEL. The provider must support response_format=vtt.", + ), + STT_API_KEY: z + .string() + .optional() + .describe( + "API key for STT_BASE_URL. Required when STT_BASE_URL is set (use any non-empty string for local providers that ignore auth).", + ), + STT_MODEL: z + .string() + .optional() + .describe( + "Transcription model for STT_BASE_URL (e.g. Systran/faster-whisper-large-v3-turbo). Required when STT_BASE_URL is set.", + ), REPLICATE_API_TOKEN: z .string() .optional() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2810356dc57..8ff5a44b28b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,7 +115,7 @@ importers: version: 0.14.10(solid-js@1.9.6) '@solidjs/start': specifier: ^1.1.3 - version: 1.1.3(@testing-library/jest-dom@6.5.0)(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(solid-js@1.9.6)(terser@5.44.0)(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1) + version: 1.1.3(@testing-library/jest-dom@6.5.0)(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(solid-js@1.9.6)(terser@5.44.0)(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1) '@tanstack/solid-query': specifier: ^5.51.21 version: 5.75.4(solid-js@1.9.6) @@ -205,7 +205,7 @@ importers: version: 9.0.1 vinxi: specifier: ^0.5.6 - version: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) + version: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) webcodecs: specifier: ^0.1.0 version: 0.1.0 @@ -642,9 +642,6 @@ importers: gif.js: specifier: 0.2.0 version: 0.2.0 - groq-sdk: - specifier: ^0.29.0 - version: 0.29.0(encoding@0.1.13) hls.js: specifier: ^1.5.3 version: 1.6.2 @@ -684,6 +681,9 @@ importers: next-mdx-remote: specifier: ^6.0.0 version: 6.0.0(@types/react@19.2.14)(acorn@8.16.0)(react@19.2.4) + openai: + specifier: ^4.86.0 + version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76) posthog-js: specifier: ^1.215.3 version: 1.240.0 @@ -4907,8 +4907,8 @@ packages: resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==} engines: {node: '>=14'} - '@oxc-project/types@0.130.0': - resolution: {integrity: sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==} + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} '@oxc-project/types@0.94.0': resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==} @@ -5895,8 +5895,8 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-android-arm64@1.0.1': - resolution: {integrity: sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==} + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -5907,8 +5907,8 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.0.1': - resolution: {integrity: sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==} + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -5919,8 +5919,8 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.1': - resolution: {integrity: sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==} + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -5931,8 +5931,8 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.0.1': - resolution: {integrity: sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==} + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -5943,8 +5943,8 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': - resolution: {integrity: sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -5955,8 +5955,8 @@ packages: cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.1': - resolution: {integrity: sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==} + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -5967,20 +5967,20 @@ packages: cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.1': - resolution: {integrity: sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==} + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-ppc64-gnu@1.0.1': - resolution: {integrity: sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==} + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@rolldown/binding-linux-s390x-gnu@1.0.1': - resolution: {integrity: sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -5991,8 +5991,8 @@ packages: cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.1': - resolution: {integrity: sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==} + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -6003,8 +6003,8 @@ packages: cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.1': - resolution: {integrity: sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==} + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -6015,8 +6015,8 @@ packages: cpu: [arm64] os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.0.1': - resolution: {integrity: sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==} + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -6026,8 +6026,8 @@ packages: engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.0.1': - resolution: {integrity: sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==} + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] @@ -6037,8 +6037,8 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.0.1': - resolution: {integrity: sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==} + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -6055,8 +6055,8 @@ packages: cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.1': - resolution: {integrity: sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==} + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -7191,10 +7191,10 @@ packages: react-dom: optional: true - '@storybook/builder-vite@10.5.0-alpha.0': - resolution: {integrity: sha512-4iCteRr03HHLbeD3osey14D8ZQS8hu5OkamlmARR4JLJUF/o6hjxckZ9xD+Ci7jjeGrlGLom71Y+TneN1iLx4g==} + '@storybook/builder-vite@10.5.0-alpha.3': + resolution: {integrity: sha512-ke/DOtGeLiV6cpNHrM+9gEEsWfplH8gsTQt35prQqaBerPkd2AXsUylpOBUbZdUxtdTdbYWJvYvZpqAeaGiq0A==} peerDependencies: - storybook: ^10.5.0-alpha.0 + storybook: ^10.5.0-alpha.3 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 '@storybook/core@8.6.12': @@ -7205,12 +7205,12 @@ packages: prettier: optional: true - '@storybook/csf-plugin@10.5.0-alpha.0': - resolution: {integrity: sha512-XO6PgW7aldty1BsvwL7HFljELSJK4noZsGqgt+nHXvq46mXyl2OXJUpO+z06DoN8KabQGup//y49aYh4uBm/uw==} + '@storybook/csf-plugin@10.5.0-alpha.3': + resolution: {integrity: sha512-X5XbPt6Z8zp+xeoCJw91GVnhYE/+y+RbeKIl+DvK6fr9rV6jFSFiXyAivOiOmmxEuD017k8BKWwSmZ2KyI2hmg==} peerDependencies: esbuild: '*' rollup: '*' - storybook: ^10.5.0-alpha.0 + storybook: ^10.5.0-alpha.3 vite: '*' webpack: '*' peerDependenciesMeta: @@ -11469,9 +11469,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - groq-sdk@0.29.0: - resolution: {integrity: sha512-oDbTqE5yg7mEzSRG0WnvezA/loaM6rY+hHl1Es8MbeOs/zgZFw+DZu3zTXJ0ik4fdNQ3/N6MluOjmytUMkZHtQ==} - gzip-size@7.0.0: resolution: {integrity: sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -13408,6 +13405,18 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + openai@4.104.0: + resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + opencontrol@0.0.6: resolution: {integrity: sha512-QeCrpOK5D15QV8kjnGVeD/BHFLwcVr+sn4T6KKmP0WAMs2pww56e4h+eOGHb5iPOufUQXbdbBKi6WV2kk7tefQ==} hasBin: true @@ -14382,8 +14391,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.0.1: - resolution: {integrity: sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==} + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -20496,7 +20505,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.37.0': {} - '@oxc-project/types@0.130.0': {} + '@oxc-project/types@0.133.0': {} '@oxc-project/types@0.94.0': {} @@ -21681,67 +21690,67 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-android-arm64@1.0.1': + '@rolldown/binding-android-arm64@1.0.3': optional: true '@rolldown/binding-darwin-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-darwin-arm64@1.0.1': + '@rolldown/binding-darwin-arm64@1.0.3': optional: true '@rolldown/binding-darwin-x64@1.0.0-beta.42': optional: true - '@rolldown/binding-darwin-x64@1.0.1': + '@rolldown/binding-darwin-x64@1.0.3': optional: true '@rolldown/binding-freebsd-x64@1.0.0-beta.42': optional: true - '@rolldown/binding-freebsd-x64@1.0.1': + '@rolldown/binding-freebsd-x64@1.0.3': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.1': + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.1': + '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true '@rolldown/binding-linux-arm64-musl@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.1': + '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.1': + '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.1': + '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.1': + '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true '@rolldown/binding-linux-x64-musl@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-x64-musl@1.0.1': + '@rolldown/binding-linux-x64-musl@1.0.3': optional: true '@rolldown/binding-openharmony-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-openharmony-arm64@1.0.1': + '@rolldown/binding-openharmony-arm64@1.0.3': optional: true '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': @@ -21749,7 +21758,7 @@ snapshots: '@napi-rs/wasm-runtime': 1.0.6 optional: true - '@rolldown/binding-wasm32-wasi@1.0.1': + '@rolldown/binding-wasm32-wasi@1.0.3': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 @@ -21759,7 +21768,7 @@ snapshots: '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.1': + '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.42': @@ -21768,7 +21777,7 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.1': + '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true '@rolldown/pluginutils@1.0.0': {} @@ -23081,11 +23090,11 @@ snapshots: dependencies: solid-js: 1.9.6 - '@solidjs/start@1.1.3(@testing-library/jest-dom@6.5.0)(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(solid-js@1.9.6)(terser@5.44.0)(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1)': + '@solidjs/start@1.1.3(@testing-library/jest-dom@6.5.0)(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(solid-js@1.9.6)(terser@5.44.0)(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1)': dependencies: '@tanstack/server-functions-plugin': 1.119.2(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1) - '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1)) - '@vinxi/server-components': 0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1)) + '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1)) + '@vinxi/server-components': 0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1)) defu: 6.1.4 error-stack-parser: 2.1.4 html-to-image: 1.11.13 @@ -23096,7 +23105,7 @@ snapshots: source-map-js: 1.2.1 terracotta: 1.0.6(solid-js@1.9.6) tinyglobby: 0.2.13 - vinxi: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) + vinxi: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) vite-plugin-solid: 2.11.6(@testing-library/jest-dom@6.5.0)(solid-js@1.9.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1)) transitivePeerDependencies: - '@testing-library/jest-dom' @@ -23226,9 +23235,9 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@storybook/builder-vite@10.5.0-alpha.0(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': + '@storybook/builder-vite@10.5.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': dependencies: - '@storybook/csf-plugin': 10.5.0-alpha.0(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) + '@storybook/csf-plugin': 10.5.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) storybook: 8.6.12(prettier@3.7.4) ts-dedent: 2.2.0 vite: 6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1) @@ -23258,7 +23267,7 @@ snapshots: - supports-color - utf-8-validate - '@storybook/csf-plugin@10.5.0-alpha.0(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': + '@storybook/csf-plugin@10.5.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5))': dependencies: storybook: 8.6.12(prettier@3.7.4) unplugin: 2.3.11 @@ -24627,7 +24636,7 @@ snapshots: untun: 0.1.3 uqr: 0.1.2 - '@vinxi/plugin-directives@0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))': + '@vinxi/plugin-directives@0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))': dependencies: '@babel/parser': 7.27.2 acorn: 8.14.1 @@ -24638,18 +24647,18 @@ snapshots: magicast: 0.2.11 recast: 0.23.11 tslib: 2.8.1 - vinxi: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) + vinxi: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) - '@vinxi/server-components@0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))': + '@vinxi/server-components@0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1))': dependencies: - '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1)) + '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1)) acorn: 8.14.1 acorn-loose: 8.5.0 acorn-typescript: 1.4.13(acorn@8.14.1) astring: 1.9.0 magicast: 0.2.11 recast: 0.23.11 - vinxi: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) + vinxi: 0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1) '@virtual-grid/core@2.0.1': {} @@ -28544,18 +28553,6 @@ snapshots: graphemer@1.4.0: {} - groq-sdk@0.29.0(encoding@0.1.13): - dependencies: - '@types/node': 18.19.97 - '@types/node-fetch': 2.6.12 - 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(encoding@0.1.13) - transitivePeerDependencies: - - encoding - gzip-size@7.0.0: dependencies: duplexer: 0.1.2 @@ -30540,7 +30537,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - nitropack@2.11.11(@planetscale/database@1.19.0)(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(mysql2@3.15.2)(rolldown@1.0.1)(xml2js@0.6.2): + nitropack@2.11.11(@planetscale/database@1.19.0)(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(mysql2@3.15.2)(rolldown@1.0.3)(xml2js@0.6.2): dependencies: '@cloudflare/kv-asset-handler': 0.4.0 '@netlify/functions': 3.1.5(encoding@0.1.13)(rollup@4.40.2) @@ -30594,7 +30591,7 @@ snapshots: pretty-bytes: 6.1.1 radix3: 1.1.2 rollup: 4.40.2 - rollup-plugin-visualizer: 5.14.0(rolldown@1.0.1)(rollup@4.40.2) + rollup-plugin-visualizer: 5.14.0(rolldown@1.0.3)(rollup@4.40.2) scule: 1.3.0 semver: 7.7.2 serve-placeholder: 2.0.2 @@ -30956,6 +30953,21 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76): + dependencies: + '@types/node': 18.19.97 + '@types/node-fetch': 2.6.12 + 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(encoding@0.1.13) + optionalDependencies: + ws: 8.18.3 + zod: 3.25.76 + transitivePeerDependencies: + - encoding + opencontrol@0.0.6: dependencies: '@modelcontextprotocol/sdk': 1.6.1 @@ -32054,7 +32066,7 @@ snapshots: dependencies: glob: 7.2.3 - rolldown-plugin-dts@0.16.11(rolldown@1.0.1)(typescript@5.8.3): + rolldown-plugin-dts@0.16.11(rolldown@1.0.3)(typescript@5.8.3): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -32065,7 +32077,7 @@ snapshots: dts-resolver: 2.1.2 get-tsconfig: 4.11.0 magic-string: 0.30.19 - rolldown: 1.0.1 + rolldown: 1.0.3 optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -32093,26 +32105,26 @@ snapshots: '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.42 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.42 - rolldown@1.0.1: + rolldown@1.0.3: dependencies: - '@oxc-project/types': 0.130.0 + '@oxc-project/types': 0.133.0 '@rolldown/pluginutils': 1.0.0 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.1 - '@rolldown/binding-darwin-arm64': 1.0.1 - '@rolldown/binding-darwin-x64': 1.0.1 - '@rolldown/binding-freebsd-x64': 1.0.1 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.1 - '@rolldown/binding-linux-arm64-gnu': 1.0.1 - '@rolldown/binding-linux-arm64-musl': 1.0.1 - '@rolldown/binding-linux-ppc64-gnu': 1.0.1 - '@rolldown/binding-linux-s390x-gnu': 1.0.1 - '@rolldown/binding-linux-x64-gnu': 1.0.1 - '@rolldown/binding-linux-x64-musl': 1.0.1 - '@rolldown/binding-openharmony-arm64': 1.0.1 - '@rolldown/binding-wasm32-wasi': 1.0.1 - '@rolldown/binding-win32-arm64-msvc': 1.0.1 - '@rolldown/binding-win32-x64-msvc': 1.0.1 + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 rollup-plugin-inject@3.0.2: dependencies: @@ -32124,14 +32136,14 @@ snapshots: dependencies: rollup-plugin-inject: 3.0.2 - rollup-plugin-visualizer@5.14.0(rolldown@1.0.1)(rollup@4.40.2): + rollup-plugin-visualizer@5.14.0(rolldown@1.0.3)(rollup@4.40.2): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rolldown: 1.0.1 + rolldown: 1.0.3 rollup: 4.40.2 rollup-pluginutils@2.8.2: @@ -32759,7 +32771,7 @@ snapshots: storybook-solidjs-vite@1.0.0-beta.7(@storybook/test@8.6.12(storybook@8.6.12(prettier@3.7.4)))(esbuild@0.25.5)(rollup@4.40.2)(solid-js@1.9.6)(storybook@8.6.12(prettier@3.7.4))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.5.0)(solid-js@1.9.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1)))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)): dependencies: - '@storybook/builder-vite': 10.5.0-alpha.0(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) + '@storybook/builder-vite': 10.5.0-alpha.3(esbuild@0.25.5)(rollup@4.40.2)(storybook@8.6.12(prettier@3.7.4))(vite@6.3.5(@types/node@22.15.17)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.44.0)(yaml@2.8.1))(webpack@5.101.3(esbuild@0.25.5)) '@storybook/types': 9.0.0-alpha.1(storybook@8.6.12(prettier@3.7.4)) magic-string: 0.30.17 solid-js: 1.9.6 @@ -33378,8 +33390,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.1 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.1)(typescript@5.8.3) + rolldown: 1.0.3 + rolldown-plugin-dts: 0.16.11(rolldown@1.0.3)(typescript@5.8.3) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.15 @@ -34051,7 +34063,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.1)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1): + vinxi@0.5.6(@planetscale/database@1.19.0)(@types/node@22.15.17)(db0@0.3.2(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(mysql2@3.15.2))(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(ioredis@5.6.1)(jiti@2.6.1)(lightningcss@1.32.0)(mysql2@3.15.2)(rolldown@1.0.3)(terser@5.44.0)(xml2js@0.6.2)(yaml@2.8.1): dependencies: '@babel/core': 7.27.1 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.1) @@ -34073,7 +34085,7 @@ snapshots: hookable: 5.5.3 http-proxy: 1.18.1 micromatch: 4.0.8 - nitropack: 2.11.11(@planetscale/database@1.19.0)(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(mysql2@3.15.2)(rolldown@1.0.1)(xml2js@0.6.2) + nitropack: 2.11.11(@planetscale/database@1.19.0)(drizzle-orm@0.44.6(@cloudflare/workers-types@4.20250507.0)(@opentelemetry/api@1.9.0)(@planetscale/database@1.19.0)(bun-types@1.3.14)(mysql2@3.15.2))(encoding@0.1.13)(mysql2@3.15.2)(rolldown@1.0.3)(xml2js@0.6.2) node-fetch-native: 1.6.6 path-to-regexp: 6.3.0 pathe: 1.1.2