diff --git a/apps/studio/hooks/misc/useOrgOptedIntoAi.ts b/apps/studio/hooks/misc/useOrgOptedIntoAi.ts index 4071bb7151b..18572d3045e 100644 --- a/apps/studio/hooks/misc/useOrgOptedIntoAi.ts +++ b/apps/studio/hooks/misc/useOrgOptedIntoAi.ts @@ -2,8 +2,16 @@ import { useDisallowHipaa } from 'hooks/misc/useDisallowHipaa' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { OPT_IN_TAGS } from 'lib/constants' import { IS_PLATFORM } from 'lib/constants' +import { z } from 'zod' -export type AiOptInLevel = 'disabled' | 'schema' | 'schema_and_log' | 'schema_and_log_and_data' +export const aiOptInLevelSchema = z.enum([ + 'disabled', + 'schema', + 'schema_and_log', + 'schema_and_log_and_data', +]) + +export type AiOptInLevel = z.infer // Exported helper function export const getAiOptInLevel = (tags: string[] | undefined): AiOptInLevel => { @@ -11,19 +19,15 @@ export const getAiOptInLevel = (tags: string[] | undefined): AiOptInLevel => { const hasData = tags?.includes(OPT_IN_TAGS.AI_DATA) const hasLog = tags?.includes(OPT_IN_TAGS.AI_LOG) - let level: AiOptInLevel - if (hasData) { - level = 'schema_and_log_and_data' + return 'schema_and_log_and_data' } else if (hasLog) { - level = 'schema_and_log' + return 'schema_and_log' } else if (hasSql) { - level = 'schema' + return 'schema' } else { - level = 'disabled' + return 'disabled' } - - return level } /** diff --git a/apps/studio/lib/ai/bedrock.ts b/apps/studio/lib/ai/bedrock.ts new file mode 100644 index 00000000000..fd0555276c1 --- /dev/null +++ b/apps/studio/lib/ai/bedrock.ts @@ -0,0 +1,17 @@ +import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' +import { fromNodeProviderChain } from '@aws-sdk/credential-providers' + +const credentialProvider = fromNodeProviderChain() + +export const bedrock = createAmazonBedrock({ + credentialProvider, +}) + +export async function checkAwsCredentials() { + try { + const credentials = await credentialProvider() + return !!credentials + } catch (error) { + return false + } +} diff --git a/apps/studio/lib/ai/model.test.ts b/apps/studio/lib/ai/model.test.ts new file mode 100644 index 00000000000..06453b78479 --- /dev/null +++ b/apps/studio/lib/ai/model.test.ts @@ -0,0 +1,72 @@ +import { openai } from '@ai-sdk/openai' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import * as bedrockModule from './bedrock' +import { bedrock } from './bedrock' +import { getModel, modelsByProvider } from './model' + +// Mock dependencies +vi.mock('@ai-sdk/openai', () => ({ + openai: vi.fn(() => 'openai-model'), +})) + +vi.mock('./bedrock', () => ({ + bedrock: vi.fn(() => 'bedrock-model'), + checkAwsCredentials: vi.fn(), +})) + +describe('getModel', () => { + const originalEnv = { ...process.env } + + beforeEach(() => { + vi.resetAllMocks() + }) + + afterEach(() => { + process.env = { ...originalEnv } + }) + + it('should return bedrock model when AWS credentials are available and AWS_REGION is set', async () => { + vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(true) + process.env.AWS_REGION = 'us-east-1' + + const { model, error } = await getModel() + + console.log('Model:', model, 'Error:', error) + + expect(model).toEqual('bedrock-model') + expect(bedrock).toHaveBeenCalledWith(modelsByProvider.bedrock) + expect(error).toBeUndefined() + }) + + it('should return error when AWS credentials are available but AWS_REGION is not set', async () => { + vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(true) + delete process.env.AWS_REGION + + const { error } = await getModel() + + expect(error).toEqual(new Error('AWS_REGION is not set')) + }) + + it('should return OpenAI model when AWS credentials are not available but OPENAI_API_KEY is set', async () => { + vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(false) + process.env.OPENAI_API_KEY = 'test-key' + + const { model } = await getModel() + + expect(model).toEqual('openai-model') + expect(openai).toHaveBeenCalledWith(modelsByProvider.openai) + }) + + it('should return error when neither AWS credentials nor OPENAI_API_KEY is available', async () => { + vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(false) + delete process.env.OPENAI_API_KEY + + const { error } = await getModel() + + expect(error).toEqual( + new Error( + 'No valid AI model available. Please set up a local AWS profile (for Bedrock) or pass an OPENAI_API_KEY.' + ) + ) + }) +}) diff --git a/apps/studio/lib/ai/model.ts b/apps/studio/lib/ai/model.ts new file mode 100644 index 00000000000..07a0b4675fd --- /dev/null +++ b/apps/studio/lib/ai/model.ts @@ -0,0 +1,52 @@ +import { openai } from '@ai-sdk/openai' +import { LanguageModel } from 'ai' +import { bedrock, checkAwsCredentials } from './bedrock' + +export const modelsByProvider = { + bedrock: 'us.anthropic.claude-sonnet-4-20250514-v1:0', + openai: 'gpt-4.1-2025-04-14', +} + +export type ModelSuccess = { + model: LanguageModel + error?: never +} + +export type ModelError = { + model?: never + error: Error +} + +export type ModelResponse = ModelSuccess | ModelError + +/** + * Retrieves the appropriate AI model based on available credentials. + */ +export async function getModel(): Promise { + const hasAwsCredentials = await checkAwsCredentials() + const hasOpenAIKey = !!process.env.OPENAI_API_KEY + + if (hasAwsCredentials) { + if (!process.env.AWS_REGION) { + return { + error: new Error('AWS_REGION is not set'), + } + } + + return { + model: bedrock(modelsByProvider.bedrock), + } + } + + if (hasOpenAIKey) { + return { + model: openai(modelsByProvider.openai), + } + } + + return { + error: new Error( + 'No valid AI model available. Please set up a local AWS profile (for Bedrock) or pass an OPENAI_API_KEY.' + ), + } +} diff --git a/apps/studio/package.json b/apps/studio/package.json index e0f74ac3785..86e3c86f0a0 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -23,7 +23,8 @@ "build:deno-types": "tsx scripts/deno-types.ts" }, "dependencies": { - "@ai-sdk/amazon-bedrock": "^2.2.8", + "@ai-sdk/amazon-bedrock": "^2.2.9", + "@ai-sdk/openai": "^1.3.22", "@ai-sdk/react": "^1.2.12", "@aws-sdk/credential-providers": "^3.804.0", "@dagrejs/dagre": "^1.0.4", @@ -60,7 +61,7 @@ "@vercel/flags": "^2.6.0", "@vitejs/plugin-react": "^4.3.4", "@zip.js/zip.js": "^2.7.29", - "ai": "^4.3.9", + "ai": "^4.3.16", "ai-commands": "workspace:*", "awesome-debounce-promise": "^2.1.0", "common": "workspace:*", diff --git a/apps/studio/pages/api/ai/edge-function/complete.ts b/apps/studio/pages/api/ai/edge-function/complete.ts index cd1b65382a3..f6f6c797f19 100644 --- a/apps/studio/pages/api/ai/edge-function/complete.ts +++ b/apps/studio/pages/api/ai/edge-function/complete.ts @@ -1,54 +1,19 @@ -import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' -import { fromNodeProviderChain } from '@aws-sdk/credential-providers' - import pgMeta from '@supabase/pg-meta' import { streamText } from 'ai' import { IS_PLATFORM } from 'common' +import { source } from 'common-tags' import { executeSql } from 'data/sql/execute-sql-query' +import { getModel } from 'lib/ai/model' import apiWrapper from 'lib/api/apiWrapper' import { queryPgMetaSelfHosted } from 'lib/self-hosted' import { NextApiRequest, NextApiResponse } from 'next' import { getTools } from '../sql/tools' export const maxDuration = 30 -const credentialProvider = fromNodeProviderChain() -const bedrock = createAmazonBedrock({ - credentialProvider, -}) - -async function hasAwsCredentials() { - try { - const credentials = await credentialProvider() - return !!credentials - } catch (error) { - return false - } -} - -const hasCredentials = await hasAwsCredentials() const pgMetaSchemasList = pgMeta.schemas.list() async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!process.env.AWS_REGION) { - return new Response( - JSON.stringify({ - error: 'AWS_REGION is not set', - }), - { status: 500, headers: { 'Content-Type': 'application/json' } } - ) - } - - if (!hasCredentials) { - return new Response( - JSON.stringify({ - error: - 'AWS credentials are not configured. Set up a local profile or add environment variables.', - }), - { status: 500, headers: { 'Content-Type': 'application/json' } } - ) - } - const { method } = req switch (method) { @@ -72,6 +37,12 @@ export default wrapper async function handlePost(req: NextApiRequest, res: NextApiResponse) { try { + const { model, error: modelError } = await getModel() + + if (modelError) { + return res.status(500).json({ error: modelError.message }) + } + const { completionMetadata, projectRef, connectionString, includeSchemaMetadata } = req.body const { textBeforeCursor, textAfterCursor, language, prompt, selection } = completionMetadata @@ -100,225 +71,226 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { : { result: [] } const result = await streamText({ - model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'), + model, maxSteps: 5, tools: getTools({ projectRef, connectionString, authorization, includeSchemaMetadata }), - system: ` - VERY IMPORTANT RULES: - 1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED TYPESCRIPT/JAVASCRIPT TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS. - 2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool. + system: source` + VERY IMPORTANT RULES: + 1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED TYPESCRIPT/JAVASCRIPT TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS. + 2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool. - You are a Supabase Edge Functions expert helping a user edit their TypeScript/JavaScript code based on a selection and a prompt. - Your goal is to modify the selected code according to the user's prompt, using the available tools to understand the database schema if necessary. - You MUST respond ONLY with the modified code that should replace the user's selection. Do not explain the changes or the tool results in the final output. + You are a Supabase Edge Functions expert helping a user edit their TypeScript/JavaScript code based on a selection and a prompt. + Your goal is to modify the selected code according to the user's prompt, using the available tools to understand the database schema if necessary. + You MUST respond ONLY with the modified code that should replace the user's selection. Do not explain the changes or the tool results in the final output. - # Core Task: Modify Selected Code - - Focus solely on altering the provided TypeScript/JavaScript selection based on the user's instructions for a Supabase Edge Function. - - Use the \`getSchema\` tool if the function interacts with the database and you need to understand table structures or relationships. + # Core Task: Modify Selected Code + - Focus solely on altering the provided TypeScript/JavaScript selection based on the user's instructions for a Supabase Edge Function. + - Use the \`getSchema\` tool if the function interacts with the database and you need to understand table structures or relationships. - # Edge Function Guidelines: - You're an expert in writing TypeScript and Deno JavaScript runtime. Generate **high-quality Supabase Edge Functions** that adhere to the following best practices: - 1. Try to use Web APIs and Deno's core APIs instead of external dependencies (eg: use fetch instead of Axios, use WebSockets API instead of node-ws) - 2. Do NOT use bare specifiers when importing dependencies. If you need to use an external dependency, make sure it's prefixed with either \`npm:\` or \`jsr:\`. For example, \`@supabase/supabase-js\` should be written as \`npm:@supabase/supabase-js\`. - 3. For external imports, always define a version. For example, \`npm:@express\` should be written as \`npm:express@4.18.2\`. - 4. For external dependencies, importing via \`npm:\` and \`jsr:\` is preferred. Minimize the use of imports from \`@deno.land/x\` , \`esm.sh\` and \`@unpkg.com\` . If you have a package from one of those CDNs, you can replace the CDN hostname with \`npm:\` specifier. - 5. You can also use Node built-in APIs. You will need to import them using \`node:\` specifier. For example, to import Node process: \`import process from "node:process"\`. Use Node APIs when you find gaps in Deno APIs. - 6. Do NOT use \`import { serve } from "https://deno.land/std@0.168.0/http/server.ts"\`. Instead use the built-in \`Deno.serve\`. - 7. Following environment variables (ie. secrets) are pre-populated in both local and hosted Supabase environments. Users don't need to manually set them: - * SUPABASE_URL - * SUPABASE_ANON_KEY - * SUPABASE_SERVICE_ROLE_KEY - * SUPABASE_DB_URL - 8. To set other environment variables the user can go to project settings then edge functions to set them - 9. A single Edge Function can handle multiple routes. It is recommended to use a library like Express or Hono to handle the routes as it's easier for developer to understand and maintain. Each route must be prefixed with \`/function-name\` so they are routed correctly. - 10. File write operations are ONLY permitted on \`/tmp\` directory. You can use either Deno or Node File APIs. - 11. Use \`EdgeRuntime.waitUntil(promise)\` static method to run long-running tasks in the background without blocking response to a request. Do NOT assume it is available in the request / execution context. + # Edge Function Guidelines: + You're an expert in writing TypeScript and Deno JavaScript runtime. Generate **high-quality Supabase Edge Functions** that adhere to the following best practices: + 1. Try to use Web APIs and Deno's core APIs instead of external dependencies (eg: use fetch instead of Axios, use WebSockets API instead of node-ws) + 2. Do NOT use bare specifiers when importing dependencies. If you need to use an external dependency, make sure it's prefixed with either \`npm:\` or \`jsr:\`. For example, \`@supabase/supabase-js\` should be written as \`npm:@supabase/supabase-js\`. + 3. For external imports, always define a version. For example, \`npm:@express\` should be written as \`npm:express@4.18.2\`. + 4. For external dependencies, importing via \`npm:\` and \`jsr:\` is preferred. Minimize the use of imports from \`@deno.land/x\` , \`esm.sh\` and \`@unpkg.com\` . If you have a package from one of those CDNs, you can replace the CDN hostname with \`npm:\` specifier. + 5. You can also use Node built-in APIs. You will need to import them using \`node:\` specifier. For example, to import Node process: \`import process from "node:process"\`. Use Node APIs when you find gaps in Deno APIs. + 6. Do NOT use \`import { serve } from "https://deno.land/std@0.168.0/http/server.ts"\`. Instead use the built-in \`Deno.serve\`. + 7. Following environment variables (ie. secrets) are pre-populated in both local and hosted Supabase environments. Users don't need to manually set them: + * SUPABASE_URL + * SUPABASE_ANON_KEY + * SUPABASE_SERVICE_ROLE_KEY + * SUPABASE_DB_URL + 8. To set other environment variables the user can go to project settings then edge functions to set them + 9. A single Edge Function can handle multiple routes. It is recommended to use a library like Express or Hono to handle the routes as it's easier for developer to understand and maintain. Each route must be prefixed with \`/function-name\` so they are routed correctly. + 10. File write operations are ONLY permitted on \`/tmp\` directory. You can use either Deno or Node File APIs. + 11. Use \`EdgeRuntime.waitUntil(promise)\` static method to run long-running tasks in the background without blocking response to a request. Do NOT assume it is available in the request / execution context. - # Database Integration: - - Use the getSchema tool to understand the database structure when needed - - Reference existing tables and schemas to ensure edge functions work with the user's data model - - Use proper types that match the database schema - - When accessing the database: - - Use RLS policies appropriately for security - - Handle database errors gracefully - - Use efficient queries and proper indexing - - Consider rate limiting for resource-intensive operations - - Use connection pooling when appropriate - - Implement proper error handling for database operations + # Database Integration: + - Use the getSchema tool to understand the database structure when needed + - Reference existing tables and schemas to ensure edge functions work with the user's data model + - Use proper types that match the database schema + - When accessing the database: + - Use RLS policies appropriately for security + - Handle database errors gracefully + - Use efficient queries and proper indexing + - Consider rate limiting for resource-intensive operations + - Use connection pooling when appropriate + - Implement proper error handling for database operations - # Example Templates: - ### Simple Hello World Function - \`\`\`typescript - // Setup type definitions for built-in Supabase Runtime APIs - import "jsr:@supabase/functions-js/edge-runtime.d.ts"; - interface reqPayload { - name: string; - } - - console.info('server started'); - - Deno.serve(async (req: Request) => { - const { name }: reqPayload = await req.json(); - const data = { - message: \`Hello \${name} from foo!\`, - }; - - return new Response( - JSON.stringify(data), - { headers: { 'Content-Type': 'application/json', 'Connection': 'keep-alive' }} - ); - }); - \`\`\` - - ### Example Function using Node built-in API - \`\`\`typescript - // Setup type definitions for built-in Supabase Runtime APIs - import "jsr:@supabase/functions-js/edge-runtime.d.ts"; - import { randomBytes } from "node:crypto"; - import { createServer } from "node:http"; - import process from "node:process"; - - const generateRandomString = (length: number) => { - const buffer = randomBytes(length); - return buffer.toString('hex'); - }; - - const randomString = generateRandomString(10); - console.log(randomString); - - const server = createServer((req, res) => { - const message = \`Hello\`; - res.end(message); - }); - - server.listen(9999); - \`\`\` - - ### Using npm packages in Functions - \`\`\`typescript - // Setup type definitions for built-in Supabase Runtime APIs - import "jsr:@supabase/functions-js/edge-runtime.d.ts"; - import express from "npm:express@4.18.2"; - - const app = express(); - - app.get(/(.*)/, (req, res) => { - res.send("Welcome to Supabase"); - }); - - app.listen(8000); - \`\`\` - - ### Generate embeddings using built-in @Supabase.ai API - \`\`\`typescript - // Setup type definitions for built-in Supabase Runtime APIs - import "jsr:@supabase/functions-js/edge-runtime.d.ts"; - const model = new Supabase.ai.Session('gte-small'); - - Deno.serve(async (req: Request) => { - const params = new URL(req.url).searchParams; - const input = params.get('text'); - const output = await model.run(input, { mean_pool: true, normalize: true }); - return new Response( - JSON.stringify(output), - { - headers: { - 'Content-Type': 'application/json', - 'Connection': 'keep-alive', - }, - }, - ); - }); - \`\`\` - - ### Integrating with Supabase Auth - \`\`\`typescript + # Example Templates: + ### Simple Hello World Function + \`\`\`typescript // Setup type definitions for built-in Supabase Runtime APIs import "jsr:@supabase/functions-js/edge-runtime.d.ts"; - import { createClient } from 'jsr:@supabase/supabase-js@2' - import { corsHeaders } from '../_shared/cors.ts' // Assuming cors.ts is in a shared folder + interface reqPayload { + name: string; + } - console.log(\`Function "select-from-table-with-auth-rls" up and running!\`) + console.info('server started'); Deno.serve(async (req: Request) => { - // This is needed if you're planning to invoke your function from a browser. - if (req.method === 'OPTIONS') { - return new Response('ok', { headers: corsHeaders }) - } + const { name }: reqPayload = await req.json(); + const data = { + message: \`Hello \${name} from foo!\`, + }; - try { - // Create a Supabase client with the Auth context of the logged in user. - const supabaseClient = createClient( - // Supabase API URL - env var exported by default. - Deno.env.get('SUPABASE_URL')!, - // Supabase API ANON KEY - env var exported by default. - Deno.env.get('SUPABASE_ANON_KEY')!, - // Create client with Auth context of the user that called the function. - // This way your row-level-security (RLS) policies are applied. - { - global: { - headers: { Authorization: req.headers.get('Authorization')! }, - }, - } - ) + return new Response( + JSON.stringify(data), + { headers: { 'Content-Type': 'application/json', 'Connection': 'keep-alive' }} + ); + }); + \`\`\` - // First get the token from the Authorization header - const authHeader = req.headers.get('Authorization') - if (!authHeader) { - throw new Error('Missing Authorization header') + ### Example Function using Node built-in API + \`\`\`typescript + // Setup type definitions for built-in Supabase Runtime APIs + import "jsr:@supabase/functions-js/edge-runtime.d.ts"; + import { randomBytes } from "node:crypto"; + import { createServer } from "node:http"; + import process from "node:process"; + + const generateRandomString = (length: number) => { + const buffer = randomBytes(length); + return buffer.toString('hex'); + }; + + const randomString = generateRandomString(10); + console.log(randomString); + + const server = createServer((req, res) => { + const message = \`Hello\`; + res.end(message); + }); + + server.listen(9999); + \`\`\` + + ### Using npm packages in Functions + \`\`\`typescript + // Setup type definitions for built-in Supabase Runtime APIs + import "jsr:@supabase/functions-js/edge-runtime.d.ts"; + import express from "npm:express@4.18.2"; + + const app = express(); + + app.get(/(.*)/, (req, res) => { + res.send("Welcome to Supabase"); + }); + + app.listen(8000); + \`\`\` + + ### Generate embeddings using built-in @Supabase.ai API + \`\`\`typescript + // Setup type definitions for built-in Supabase Runtime APIs + import "jsr:@supabase/functions-js/edge-runtime.d.ts"; + const model = new Supabase.ai.Session('gte-small'); + + Deno.serve(async (req: Request) => { + const params = new URL(req.url).searchParams; + const input = params.get('text'); + const output = await model.run(input, { mean_pool: true, normalize: true }); + return new Response( + JSON.stringify(output), + { + headers: { + 'Content-Type': 'application/json', + 'Connection': 'keep-alive', + }, + }, + ); + }); + \`\`\` + + ### Integrating with Supabase Auth + \`\`\`typescript + // Setup type definitions for built-in Supabase Runtime APIs + import "jsr:@supabase/functions-js/edge-runtime.d.ts"; + import { createClient } from 'jsr:@supabase/supabase-js@2' + import { corsHeaders } from '../_shared/cors.ts' // Assuming cors.ts is in a shared folder + + console.log(\`Function "select-from-table-with-auth-rls" up and running!\`) + + Deno.serve(async (req: Request) => { + // This is needed if you're planning to invoke your function from a browser. + if (req.method === 'OPTIONS') { + return new Response('ok', { headers: corsHeaders }) } - const token = authHeader.replace('Bearer ', '') - // Now we can get the session or user object - const { - data: { user }, error: userError - } = await supabaseClient.auth.getUser(token) - if (userError) throw userError + try { + // Create a Supabase client with the Auth context of the logged in user. + const supabaseClient = createClient( + // Supabase API URL - env var exported by default. + Deno.env.get('SUPABASE_URL')!, + // Supabase API ANON KEY - env var exported by default. + Deno.env.get('SUPABASE_ANON_KEY')!, + // Create client with Auth context of the user that called the function. + // This way your row-level-security (RLS) policies are applied. + { + global: { + headers: { Authorization: req.headers.get('Authorization')! }, + }, + } + ) - // Example: Select data associated with the authenticated user - // Replace 'your_table' and 'user_id' with your actual table and column names - // const { data, error } = await supabaseClient.from('your_table').select('*').eq('user_id', user.id) - // if (error) throw error + // First get the token from the Authorization header + const authHeader = req.headers.get('Authorization') + if (!authHeader) { + throw new Error('Missing Authorization header') + } + const token = authHeader.replace('Bearer ', '') - // Return some data (replace with your actual logic) - return new Response(JSON.stringify({ user/*, data*/ }), { // Uncomment data if you query - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 200, - }) - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { ...corsHeaders, 'Content-Type': 'application/json' }, - status: 400, - }) - } - }) + // Now we can get the session or user object + const { + data: { user }, error: userError + } = await supabaseClient.auth.getUser(token) + if (userError) throw userError - // To invoke: - // curl -i --location --request POST 'http://localhost:54321/functions/v1/your-function-name' \\ - // --header 'Authorization: Bearer ' \\ - // --header 'Content-Type: application/json' \\ - // --data '{"some":"payload"}' // Optional payload - \`\`\` + // Example: Select data associated with the authenticated user + // Replace 'your_table' and 'user_id' with your actual table and column names + // const { data, error } = await supabaseClient.from('your_table').select('*').eq('user_id', user.id) + // if (error) throw error - # Tool Usage: - - First look at the list of provided schemas if database interaction is needed. - - Use \`getSchema\` to understand the data model you're working with if the edge function needs to interact with user data. - - Check both the public and auth schemas to understand the authentication setup if relevant. - - The available database schema names are: ${schemas} + // Return some data (replace with your actual logic) + return new Response(JSON.stringify({ user/*, data*/ }), { // Uncomment data if you query + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 200, + }) + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 400, + }) + } + }) - # Response Format: - - Your response MUST be ONLY the modified TypeScript/JavaScript text intended to replace the user's selection. - - Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT. - - Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`). - - Avoid duplicating variable declarations, imports, or function definitions already present in the surrounding context. - - If there is no surrounding context (before or after), ensure your response is a complete, valid Deno Edge Function including necessary imports and setup. + // To invoke: + // curl -i --location --request POST 'http://localhost:54321/functions/v1/your-function-name' \\ + // --header 'Authorization: Bearer ' \\ + // --header 'Content-Type: application/json' \\ + // --data '{"some":"payload"}' // Optional payload + \`\`\` - REMEMBER: ONLY OUTPUT THE CODE MODIFICATION. + # Tool Usage: + - First look at the list of provided schemas if database interaction is needed. + - Use \`getSchema\` to understand the data model you're working with if the edge function needs to interact with user data. + - Check both the public and auth schemas to understand the authentication setup if relevant. + - The available database schema names are: ${schemas} + + # Response Format: + - Your response MUST be ONLY the modified TypeScript/JavaScript text intended to replace the user's selection. + - Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT. + - Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`). + - Avoid duplicating variable declarations, imports, or function definitions already present in the surrounding context. + - If there is no surrounding context (before or after), ensure your response is a complete, valid Deno Edge Function including necessary imports and setup. + + REMEMBER: ONLY OUTPUT THE CODE MODIFICATION. `, messages: [ { role: 'user', - content: `You are helping me write TypeScript/JavaScript code for an edge function. + content: source` + You are helping me write TypeScript/JavaScript code for an edge function. Here is the context: ${textBeforeCursor}${selection}${textAfterCursor} @@ -331,7 +303,8 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { 6. Avoid duplicating variable declarations, imports, or function definitions when considering the full code 7. If there is no surrounding context (before or after), make sure your response is a complete valid Deno Edge Function including imports. - Modify the selected text now:`, + Modify the selected text now: + `, }, ], }) diff --git a/apps/studio/pages/api/ai/onboarding/design.ts b/apps/studio/pages/api/ai/onboarding/design.ts index 813b0193ffd..33f713af7a0 100644 --- a/apps/studio/pages/api/ai/onboarding/design.ts +++ b/apps/studio/pages/api/ai/onboarding/design.ts @@ -1,11 +1,10 @@ -import { bedrock } from '@ai-sdk/amazon-bedrock' import { streamText, tool } from 'ai' +import { source } from 'common-tags' +import { getModel } from 'lib/ai/model' import apiWrapper from 'lib/api/apiWrapper' import { NextApiRequest, NextApiResponse } from 'next' import { z } from 'zod' -const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID - export const maxDuration = 30 const ServiceSchema = z.object({ @@ -47,12 +46,6 @@ const getTools = () => { } async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!awsAccessKeyId) { - return res.status(400).json({ - error: 'No AWS_ACCESS_KEY_ID set. Create this environment variable to use AI features.', - }) - } - const { method } = req switch (method) { @@ -70,12 +63,18 @@ const wrapper = (req: NextApiRequest, res: NextApiResponse) => export default wrapper async function handlePost(req: NextApiRequest, res: NextApiResponse) { + const { model, error: modelError } = await getModel() + + if (modelError) { + return res.status(500).json({ error: modelError.message }) + } + const { messages } = req.body const result = await streamText({ - model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'), + model, maxSteps: 7, - system: ` + system: source` You are a Supabase expert who helps people set up their Supabase project. You specializes in database schema design. You are to help the user design a database schema for their application but also suggest Supabase services they should use. When designing database schemas, follow these rules: @@ -95,7 +94,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { 4. Always respond with a short single paragraph of less than 80 words of what you changed and the current state of the schema. If user requests to reset the database, call the reset tool. - `, + `, messages, tools: getTools(), }) diff --git a/apps/studio/pages/api/ai/sql/check-api-key.ts b/apps/studio/pages/api/ai/sql/check-api-key.ts index a4bead534b5..b13df3e6dc2 100644 --- a/apps/studio/pages/api/ai/sql/check-api-key.ts +++ b/apps/studio/pages/api/ai/sql/check-api-key.ts @@ -13,7 +13,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { case 'GET': return handleGet(req, res) default: - res.setHeader('Allow', ['POST']) + res.setHeader('Allow', ['GET']) res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } }) } } diff --git a/apps/studio/pages/api/ai/sql/complete.ts b/apps/studio/pages/api/ai/sql/complete.ts index 4b98e6e4744..fd3c70af311 100644 --- a/apps/studio/pages/api/ai/sql/complete.ts +++ b/apps/studio/pages/api/ai/sql/complete.ts @@ -1,59 +1,19 @@ -import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' -import { fromNodeProviderChain } from '@aws-sdk/credential-providers' import pgMeta from '@supabase/pg-meta' import { streamText } from 'ai' import { IS_PLATFORM } from 'common' +import { source } from 'common-tags' import { executeSql } from 'data/sql/execute-sql-query' +import { getModel } from 'lib/ai/model' import apiWrapper from 'lib/api/apiWrapper' import { queryPgMetaSelfHosted } from 'lib/self-hosted' import { NextApiRequest, NextApiResponse } from 'next' import { getTools } from '../sql/tools' export const maxDuration = 30 -const credentialProvider = fromNodeProviderChain() -const bedrock = createAmazonBedrock({ - credentialProvider, -}) - -async function hasAwsCredentials() { - try { - const credentials = await credentialProvider() - return !!credentials - } catch (error) { - return false - } -} - -const hasCredentials = await hasAwsCredentials() const pgMetaSchemasList = pgMeta.schemas.list() async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!process.env.AWS_REGION) { - return new Response( - JSON.stringify({ - error: 'AWS_REGION is not set', - }), - { - status: 500, - headers: { 'Content-Type': 'application/json' }, - } - ) - } - - if (!hasCredentials) { - return new Response( - JSON.stringify({ - error: - 'AWS credentials are not configured. Set up a local profile or add environment variables.', - }), - { - status: 500, - headers: { 'Content-Type': 'application/json' }, - } - ) - } - if (req.method !== 'POST') { return new Response( JSON.stringify({ data: null, error: { message: `Method ${req.method} Not Allowed` } }), @@ -65,6 +25,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } try { + const { model, error: modelError } = await getModel() + + if (modelError) { + return res.status(500).json({ error: modelError.message }) + } + const { completionMetadata, projectRef, connectionString, includeSchemaMetadata } = req.body const { textBeforeCursor, textAfterCursor, language, prompt, selection } = completionMetadata @@ -92,85 +58,86 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ) : { result: [] } - const result = await streamText({ - model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'), + const result = streamText({ + model, maxSteps: 5, tools: getTools({ projectRef, connectionString, authorization, includeSchemaMetadata }), - system: ` - VERY IMPORTANT RULES: - 1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED SQL TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS. - 2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool. + system: source` + VERY IMPORTANT RULES: + 1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED SQL TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS. + 2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool. - You are a Supabase Postgres expert helping a user edit their SQL code based on a selection and a prompt. - Your goal is to modify the selected SQL according to the user's prompt, using the available tools to understand the schema and RLS policies if necessary. - You MUST respond ONLY with the modified SQL that should replace the user's selection. Do not explain the changes or the tool results in the final output. + You are a Supabase Postgres expert helping a user edit their SQL code based on a selection and a prompt. + Your goal is to modify the selected SQL according to the user's prompt, using the available tools to understand the schema and RLS policies if necessary. + You MUST respond ONLY with the modified SQL that should replace the user's selection. Do not explain the changes or the tool results in the final output. - # Core Task: Modify Selected SQL - - Focus solely on altering the provided SQL selection based on the user's instructions. - - Use the \`getSchemaTables\` tool to understand table structures relevant to the edit. - - Use the \`getRlsKnowledge\` tool to understand existing RLS policies if the edit involves them. - - Adhere strictly to the SQL generation guidelines below when modifying or creating SQL. + # Core Task: Modify Selected SQL + - Focus solely on altering the provided SQL selection based on the user's instructions. + - Use the \`getSchemaTables\` tool to understand table structures relevant to the edit. + - Use the \`getRlsKnowledge\` tool to understand existing RLS policies if the edit involves them. + - Adhere strictly to the SQL generation guidelines below when modifying or creating SQL. - # SQL Style: - - Generated/modified SQL must be valid Postgres SQL. - - Always use double apostrophes for escaped single quotes (e.g., 'Night''s watch'). - - Always use semicolons at the end of SQL statements (unless modifying a fragment where it wouldn't fit). - - Use \`vector(384)\` for embedding/vector related queries. - - Prefer \`text\` over \`varchar\`. - - Prefer \`timestamp with time zone\` over \`date\`. - - Feel free to suggest corrections for suspected typos in the user's selection or prompt. + # SQL Style: + - Generated/modified SQL must be valid Postgres SQL. + - Always use double apostrophes for escaped single quotes (e.g., 'Night''s watch'). + - Always use semicolons at the end of SQL statements (unless modifying a fragment where it wouldn't fit). + - Use \`vector(384)\` for embedding/vector related queries. + - Prefer \`text\` over \`varchar\`. + - Prefer \`timestamp with time zone\` over \`date\`. + - Feel free to suggest corrections for suspected typos in the user's selection or prompt. - # Best Practices & Object Generation (Apply when relevant to the edit): - - **Auth Schema**: The \`auth.users\` table stores user authentication data. If editing involves user data, consider if a \`public.profiles\` table linked to \`auth.users\` (via user_id referencing auth.users.id) is more appropriate for user-specific public data. Do not directly modify/query \`auth.users\` structure unless explicitly asked. Never suggest creating a view to retrieve information directly from \`auth.users\`. - - **Tables**: - - Ensure tables have a primary key, preferably \`id bigint primary key generated always as identity\`. - - Ensure Row Level Security (RLS) is enabled on tables (\`enable row level security\`). If creating a table snippet, mention the need for policies. - - Prefer defining foreign key references within the \`CREATE TABLE\` statement if adding one. - - If adding a foreign key, consider suggesting a separate \`CREATE INDEX\` statement for the foreign key column(s) to optimize joins. - - **Foreign Tables**: If the edit involves foreign tables, they should ideally be in a schema named \`private\`. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api. - - **Views**: - - Include \`with (security_invoker=on)\` immediately after \`CREATE VIEW view_name\` if creating/modifying a view definition. - - **Materialized Views**: If the edit involves materialized views, they should ideally be in the \`private\` schema. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api. - - **Extensions**: - - Extensions should be installed in the \`extensions\` schema or a dedicated schema, **never** in \`public\`. - - **RLS Policies**: - - When modifying policies using functions from the \`auth\` schema (like \`auth.uid()\`): - - Wrap the function call in parentheses: \`(select auth.uid())\`. - - Use \`CREATE POLICY\` or \`ALTER POLICY\`. Policy names should be descriptive text in double quotes. - - Specify roles using \`TO authenticated\` or \`TO anon\`. - - Use separate policies for SELECT, INSERT, UPDATE, DELETE actions. Do not use \`FOR ALL\`. - - Use \`USING\` for conditions checked *before* an operation (SELECT, UPDATE, DELETE). Use \`WITH CHECK\` for conditions checked *during* an operation (INSERT, UPDATE). - - SELECT: \`USING (condition)\` - - INSERT: \`WITH CHECK (condition)\` - - UPDATE: \`USING (condition) WITH CHECK (condition)\` - - DELETE: \`USING (condition)\` - - Prefer \`PERMISSIVE\` policies unless \`RESTRICTIVE\` is explicitly needed. - - Leverage Supabase helper functions: \`auth.uid()\`, \`auth.jwt()\` (\`app_metadata\` for authz, \`user_metadata\` is user-updatable). - - **Performance**: Indexes on columns used in RLS policies are crucial. Minimize joins within policy definitions. - - **Functions**: - - Use \`security definer\` for functions returning type \`trigger\`; otherwise, default to \`security invoker\`. - - Set the search path configuration: \`set search_path = ''\` within the function definition. - - Use \`create or replace function\` when possible if modifying a function signature. + # Best Practices & Object Generation (Apply when relevant to the edit): + - **Auth Schema**: The \`auth.users\` table stores user authentication data. If editing involves user data, consider if a \`public.profiles\` table linked to \`auth.users\` (via user_id referencing auth.users.id) is more appropriate for user-specific public data. Do not directly modify/query \`auth.users\` structure unless explicitly asked. Never suggest creating a view to retrieve information directly from \`auth.users\`. + - **Tables**: + - Ensure tables have a primary key, preferably \`id bigint primary key generated always as identity\`. + - Ensure Row Level Security (RLS) is enabled on tables (\`enable row level security\`). If creating a table snippet, mention the need for policies. + - Prefer defining foreign key references within the \`CREATE TABLE\` statement if adding one. + - If adding a foreign key, consider suggesting a separate \`CREATE INDEX\` statement for the foreign key column(s) to optimize joins. + - **Foreign Tables**: If the edit involves foreign tables, they should ideally be in a schema named \`private\`. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api. + - **Views**: + - Include \`with (security_invoker=on)\` immediately after \`CREATE VIEW view_name\` if creating/modifying a view definition. + - **Materialized Views**: If the edit involves materialized views, they should ideally be in the \`private\` schema. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api. + - **Extensions**: + - Extensions should be installed in the \`extensions\` schema or a dedicated schema, **never** in \`public\`. + - **RLS Policies**: + - When modifying policies using functions from the \`auth\` schema (like \`auth.uid()\`): + - Wrap the function call in parentheses: \`(select auth.uid())\`. + - Use \`CREATE POLICY\` or \`ALTER POLICY\`. Policy names should be descriptive text in double quotes. + - Specify roles using \`TO authenticated\` or \`TO anon\`. + - Use separate policies for SELECT, INSERT, UPDATE, DELETE actions. Do not use \`FOR ALL\`. + - Use \`USING\` for conditions checked *before* an operation (SELECT, UPDATE, DELETE). Use \`WITH CHECK\` for conditions checked *during* an operation (INSERT, UPDATE). + - SELECT: \`USING (condition)\` + - INSERT: \`WITH CHECK (condition)\` + - UPDATE: \`USING (condition) WITH CHECK (condition)\` + - DELETE: \`USING (condition)\` + - Prefer \`PERMISSIVE\` policies unless \`RESTRICTIVE\` is explicitly needed. + - Leverage Supabase helper functions: \`auth.uid()\`, \`auth.jwt()\` (\`app_metadata\` for authz, \`user_metadata\` is user-updatable). + - **Performance**: Indexes on columns used in RLS policies are crucial. Minimize joins within policy definitions. + - **Functions**: + - Use \`security definer\` for functions returning type \`trigger\`; otherwise, default to \`security invoker\`. + - Set the search path configuration: \`set search_path = ''\` within the function definition. + - Use \`create or replace function\` when possible if modifying a function signature. - # Tool Usage: - - Before generating the final SQL modification: - - Use \`getSchemaTables\` if you need to retrieve information about tables in relevant schemas (usually \`public\`, potentially \`auth\` if user-related). - - Use \`getRlsKnowledge\` if you need to retrieve existing RLS policies and guidelines if the edit concerns policies. - - The available database schema names are: ${schemas} + # Tool Usage: + - Before generating the final SQL modification: + - Use \`getSchemaTables\` if you need to retrieve information about tables in relevant schemas (usually \`public\`, potentially \`auth\` if user-related). + - Use \`getRlsKnowledge\` if you need to retrieve existing RLS policies and guidelines if the edit concerns policies. + - The available database schema names are: ${schemas} - # Response Format: - - Your response MUST be ONLY the modified SQL text intended to replace the user's selection. - - Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT. - - Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`). - - Avoid duplicating SQL keywords already present in the surrounding context. - - If there is no surrounding context, ensure your response is a complete, valid SQL statement. + # Response Format: + - Your response MUST be ONLY the modified SQL text intended to replace the user's selection. + - Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT. + - Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`). + - Avoid duplicating SQL keywords already present in the surrounding context. + - If there is no surrounding context, ensure your response is a complete, valid SQL statement. - REMEMBER: ONLY OUTPUT THE SQL MODIFICATION. + REMEMBER: ONLY OUTPUT THE SQL MODIFICATION. `, messages: [ { role: 'user', - content: `You are helping me edit some pgsql code. + content: source` + You are helping me edit some pgsql code. Here is the context: ${textBeforeCursor}${selection}${textAfterCursor} @@ -186,7 +153,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { 6. Avoid duplicating SQL keywords (SELECT, FROM, WHERE, etc) when considering the full statement 7. If there is no surrounding context (before or after), make sure your response is a complete valid SQL statement that can be run and resolves the prompt. - Modify the selected text now:`, + Modify the selected text now: + `, }, ], }) diff --git a/apps/studio/pages/api/ai/sql/cron.ts b/apps/studio/pages/api/ai/sql/cron.ts index 743ed719ceb..0f550a3206f 100644 --- a/apps/studio/pages/api/ai/sql/cron.ts +++ b/apps/studio/pages/api/ai/sql/cron.ts @@ -1,45 +1,15 @@ -import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' -import { fromNodeProviderChain } from '@aws-sdk/credential-providers' import { generateObject } from 'ai' +import { source } from 'common-tags' +import { getModel } from 'lib/ai/model' +import apiWrapper from 'lib/api/apiWrapper' import { NextApiRequest, NextApiResponse } from 'next' import { z } from 'zod' -import apiWrapper from 'lib/api/apiWrapper' - -const credentialProvider = fromNodeProviderChain() - -const bedrock = createAmazonBedrock({ - credentialProvider, -}) - -async function hasAwsCredentials() { - try { - const credentials = await credentialProvider() - return !!credentials - } catch (error) { - return false - } -} - -const hasCredentials = await hasAwsCredentials() const cronSchema = z.object({ cron_expression: z.string().describe('The generated cron expression.'), }) async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!process.env.AWS_REGION) { - return res.status(500).json({ - error: 'AWS_REGION is not set', - }) - } - - if (!hasCredentials) { - return res.status(500).json({ - error: - 'AWS credentials are not configured. Set up a local profile or add environment variables.', - }) - } - const { method } = req switch (method) { @@ -63,43 +33,51 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) { } try { + const { model, error: modelError } = await getModel() + + if (modelError) { + return res.status(500).json({ error: modelError.message }) + } + const result = await generateObject({ - model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'), + model, schema: cronSchema, - prompt: `You are a cron syntax expert. Your purpose is to convert natural language time descriptions into valid cron expressions for pg_cron. + prompt: source` + You are a cron syntax expert. Your purpose is to convert natural language time descriptions into valid cron expressions for pg_cron. -Rules for responses: -- For standard intervals (minutes and above), output cron expressions in the 5-field format supported by pg_cron -- For second-based intervals, use the special pg_cron "x seconds" syntax -- Do not provide any explanation of what the cron expression does -- Do not ask for clarification if you need it. Just output the cron expression. + Rules for responses: + - For standard intervals (minutes and above), output cron expressions in the 5-field format supported by pg_cron + - For second-based intervals, use the special pg_cron "x seconds" syntax + - Do not provide any explanation of what the cron expression does + - Do not ask for clarification if you need it. Just output the cron expression. -Example input: "Every Monday at 3am" -Example output: 0 3 * * 1 + Example input: "Every Monday at 3am" + Example output: 0 3 * * 1 -Example input: "Every 30 seconds" -Example output: 30 seconds + Example input: "Every 30 seconds" + Example output: 30 seconds -Additional examples: -- Every minute: * * * * * -- Every 5 minutes: */5 * * * * -- Every first of the month, at 00:00: 0 0 1 * * -- Every night at midnight: 0 0 * * * -- Every Monday at 2am: 0 2 * * 1 -- Every 15 seconds: 15 seconds -- Every 45 seconds: 45 seconds + Additional examples: + - Every minute: * * * * * + - Every 5 minutes: */5 * * * * + - Every first of the month, at 00:00: 0 0 1 * * + - Every night at midnight: 0 0 * * * + - Every Monday at 2am: 0 2 * * 1 + - Every 15 seconds: 15 seconds + - Every 45 seconds: 45 seconds -Field order for standard cron: -- minute (0-59) -- hour (0-23) -- day (1-31) -- month (1-12) -- weekday (0-6, Sunday=0) + Field order for standard cron: + - minute (0-59) + - hour (0-23) + - day (1-31) + - month (1-12) + - weekday (0-6, Sunday=0) -Important: pg_cron uses "x seconds" for second-based intervals, not "x * * * *". -If the user asks for seconds, do not use the 5-field format, instead use "x seconds". + Important: pg_cron uses "x seconds" for second-based intervals, not "x * * * *". + If the user asks for seconds, do not use the 5-field format, instead use "x seconds". -Here is the user's prompt: ${prompt}`, + Here is the user's prompt: ${prompt} + `, temperature: 0, }) diff --git a/apps/studio/pages/api/ai/sql/generate-v3.ts b/apps/studio/pages/api/ai/sql/generate-v3.ts index 9c71c7a7b3e..4adeb7c70f5 100644 --- a/apps/studio/pages/api/ai/sql/generate-v3.ts +++ b/apps/studio/pages/api/ai/sql/generate-v3.ts @@ -1,49 +1,24 @@ -import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' -import { fromNodeProviderChain } from '@aws-sdk/credential-providers' import crypto from 'crypto' import { NextApiRequest, NextApiResponse } from 'next' import { z } from 'zod' -import { streamText, tool, type Tool } from 'ai' -import { AiOptInLevel } from 'hooks/misc/useOrgOptedIntoAi' +import { streamText, tool, ToolSet } from 'ai' +import { source } from 'common-tags' +import { aiOptInLevelSchema } from 'hooks/misc/useOrgOptedIntoAi' +import { getModel } from 'lib/ai/model' import apiWrapper from 'lib/api/apiWrapper' -import { createSupabaseMCPClient } from './supabase-mcp' +import { + createSupabaseMCPClient, + expectedToolsSchema, + filterToolsByOptInLevel, + transformToolResult, +} from './supabase-mcp' export const maxDuration = 30 -const credentialProvider = fromNodeProviderChain() - -const bedrock = createAmazonBedrock({ - credentialProvider, -}) - -async function hasAwsCredentials() { - try { - const credentials = await credentialProvider() - return !!credentials - } catch (error) { - return false - } -} - -const hasCredentials = await hasAwsCredentials() - async function handler(req: NextApiRequest, res: NextApiResponse) { const { method } = req - if (!process.env.AWS_REGION) { - return res.status(500).json({ - error: 'AWS_REGION is not set', - }) - } - - if (!hasCredentials) { - return res.status(500).json({ - error: - 'AWS credentials are not configured. Set up a local profile or add environment variables.', - }) - } - switch (method) { case 'POST': return handlePost(req, res) @@ -58,21 +33,15 @@ const wrapper = (req: NextApiRequest, res: NextApiResponse) => export default wrapper +const requestBodySchema = z.object({ + messages: z.array(z.any()), + projectRef: z.string(), + aiOptInLevel: aiOptInLevelSchema, + schema: z.string().optional(), + table: z.string().optional(), +}) + async function handlePost(req: NextApiRequest, res: NextApiResponse) { - const { messages, projectRef, aiOptInLevel, schema, table } = req.body as { - messages: any[] // Use stronger types if available - projectRef: string - aiOptInLevel: AiOptInLevel - schema?: string - table?: string - } - - if (!projectRef) { - return res.status(400).json({ - error: 'Missing project_ref in query parameters', - }) - } - const authorization = req.headers.authorization const accessToken = authorization?.replace('Bearer ', '') @@ -80,175 +49,122 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { return res.status(401).json({ error: 'Authorization token is required' }) } - let mcpTools: Record> = {} - let wrappedExecuteSqlTool: Tool | undefined = undefined + const { model, error: modelError } = await getModel() + + if (modelError) { + return res.status(500).json({ error: modelError.message }) + } + + const { data, error: parseError } = requestBodySchema.safeParse(req.body) + + if (parseError) { + return res.status(400).json({ + error: 'Invalid request body', + issues: parseError.issues, + }) + } + + const { messages, projectRef, aiOptInLevel, schema, table } = data try { - const privacyMessage = - "You don't have permission to use this tool. This is an organization-wide setting requiring you to opt-in. Please choose your preferred data sharing level in your organization's settings. Supabase Assistant uses Amazon Bedrock, which does not store or log your prompts and completions, use them to train AWS models, or distribute them to third parties. By default, no data is shared. Granting permission allows Supabase to send information (like schema, logs, or data, depending on your chosen level) to Bedrock solely to generate responses." - const condensedPrivacyMessage = - 'Requires opting in to sending data to Bedrock which does not store, train on, or distribute it. You can opt in via organization settings.' - - const createPrivacyMessageTool = (toolInstance: Tool) => { - return { - ...toolInstance, - description: `${toolInstance.description} (Note: ${condensedPrivacyMessage})`, - execute: async (_args: any, _context: any) => ({ status: privacyMessage }), - } - } - // Fetch MCP client and tools const mcpClient = await createSupabaseMCPClient({ accessToken, projectId: projectRef, }) - let availableMcpTools = await mcpClient.tools() + const availableMcpTools = await mcpClient.tools() - let allAllowedTools = [ - 'list_tables', - 'list_extensions', - 'list_edge_functions', - 'list_branches', - 'get_project', - 'execute_sql', - 'get_logs', - ] + // Validate that the expected tools are available + const { data: validatedTools, error: validationError } = + expectedToolsSchema.safeParse(availableMcpTools) - // Filter out tools that should never be available across any permission level - availableMcpTools = Object.fromEntries( - Object.entries(availableMcpTools).filter(([key]) => allAllowedTools.includes(key)) - ) - - // Build allowed tools based on permission level - let allowedTools: string[] = [] - - // For schema and above permission levels - if ( - aiOptInLevel === 'schema' || - aiOptInLevel === 'schema_and_log' || - aiOptInLevel === 'schema_and_log_and_data' - ) { - allowedTools = [ - 'list_tables', - 'list_extensions', - 'list_edge_functions', - 'list_branches', - 'get_project', - ] + if (validationError) { + console.error('MCP tools validation error:', validationError) + return res.status(500).json({ + error: 'Internal error: MCP tools validation failed', + issues: validationError.issues, + }) } - // For schema_and_log permission level, add log access tools - if (aiOptInLevel === 'schema_and_log' || aiOptInLevel === 'schema_and_log_and_data') { - allowedTools = [...allowedTools, 'get_logs'] - } + // Modify the execute_sql tool to add manualToolCallId + const modifiedMcpTools = { + ...availableMcpTools, + execute_sql: transformToolResult(validatedTools.execute_sql, (result) => { + const manualToolCallId = `manual_${crypto.randomUUID()}` - // For schema_and_log_and_data permission level, add data access tools - if (aiOptInLevel === 'schema_and_log_and_data') { - allowedTools = [...allowedTools, 'execute_sql'] - } - - // Process tools based on permission level - mcpTools = Object.fromEntries( - Object.entries(availableMcpTools).map(([key, toolInstance]) => { - if (allowedTools.includes(key)) { - return [key, toolInstance] + if (typeof result === 'object') { + return { ...result, manualToolCallId } + } else { + console.warn('execute_sql result is not an object, cannot add manualToolCallId') + return { + error: 'Internal error: Unexpected tool result format', + manualToolCallId, + } } - return [key, createPrivacyMessageTool(toolInstance)] - }) - ) - - // Wrap execute_sql only if it's allowed and exists - const originalExecuteSqlTool = mcpTools.execute_sql as Tool | undefined - if (originalExecuteSqlTool && allowedTools.includes('execute_sql')) { - wrappedExecuteSqlTool = tool({ - ...originalExecuteSqlTool, - execute: async (args, context) => { - if (!originalExecuteSqlTool.execute) { - throw new Error('Original execute_sql tool has no execute function.') - } - const originalResult = await originalExecuteSqlTool.execute(args, context) - const manualToolCallId = `manual_${crypto.randomUUID()}` - - if (originalResult && typeof originalResult === 'object') { - ;(originalResult as any).manualToolCallId = manualToolCallId - } else { - console.warn('execute_sql result is not an object, cannot add manualToolCallId') - return { - error: 'Internal error: Unexpected tool result format', - manualToolCallId: manualToolCallId, - } - } - - return originalResult - }, - }) - } else { - console.warn( - 'execute_sql tool not found in mcpTools or not allowed at this permission level.' - ) + }), } - const display_query = tool({ - description: - 'Displays SQL query results (table or chart) or renders SQL for write/DDL operations. Use this for all query display needs. Optionally references a previous execute_sql call via manualToolCallId for displaying SELECT results.', - parameters: z.object({ - manualToolCallId: z - .string() - .optional() - .describe( - 'The manual ID from the corresponding execute_sql result (for SELECT queries).' - ), - sql: z.string().describe('The SQL query.'), - label: z - .string() - .describe( - 'The title or label for this query block (e.g., "Users Over Time", "Create Users Table").' - ), - view: z - .enum(['table', 'chart']) - .optional() - .describe( - 'Display mode for SELECT results: table or chart. Required if manualToolCallId is provided.' - ), - xAxis: z.string().optional().describe('Key for the x-axis (required if view is chart).'), - yAxis: z.string().optional().describe('Key for the y-axis (required if view is chart).'), - runQuery: z - .boolean() - .optional() - .describe( - 'Whether to automatically run the query. Set to true for read-only queries when manualToolCallId does not exist due to permissions. Should be false for write/DDL operations.' - ), - }), - execute: async (args) => { - const statusMessage = args.manualToolCallId - ? 'Tool call sent to client for rendering SELECT results.' - : 'Tool call sent to client for rendering write/DDL query.' - return { status: statusMessage } - }, - }) + // Filter tools based on the AI opt-in level + const mcpTools = filterToolsByOptInLevel(modifiedMcpTools, aiOptInLevel) - const display_edge_function = tool({ - description: 'Renders the code for a Supabase Edge Function for the user to deploy manually.', - parameters: z.object({ - name: z - .string() - .describe('The URL-friendly name of the Edge Function (e.g., "my-function").'), - code: z.string().describe('The TypeScript code for the Edge Function.'), - }), - execute: async (args) => { - return { status: 'Tool call sent to client for rendering.' } - }, - }) - - const allTools = { + // Combine MCP tools with custom tools + const tools: ToolSet = { ...mcpTools, - ...(wrappedExecuteSqlTool && { execute_sql: wrappedExecuteSqlTool }), - display_query, - display_edge_function, + display_query: tool({ + description: + 'Displays SQL query results (table or chart) or renders SQL for write/DDL operations. Use this for all query display needs. Optionally references a previous execute_sql call via manualToolCallId for displaying SELECT results.', + parameters: z.object({ + manualToolCallId: z + .string() + .optional() + .describe( + 'The manual ID from the corresponding execute_sql result (for SELECT queries).' + ), + sql: z.string().describe('The SQL query.'), + label: z + .string() + .describe( + 'The title or label for this query block (e.g., "Users Over Time", "Create Users Table").' + ), + view: z + .enum(['table', 'chart']) + .optional() + .describe( + 'Display mode for SELECT results: table or chart. Required if manualToolCallId is provided.' + ), + xAxis: z.string().optional().describe('Key for the x-axis (required if view is chart).'), + yAxis: z.string().optional().describe('Key for the y-axis (required if view is chart).'), + runQuery: z + .boolean() + .optional() + .describe( + 'Whether to automatically run the query. Set to true for read-only queries when manualToolCallId does not exist due to permissions. Should be false for write/DDL operations.' + ), + }), + execute: async (args) => { + const statusMessage = args.manualToolCallId + ? 'Tool call sent to client for rendering SELECT results.' + : 'Tool call sent to client for rendering write/DDL query.' + return { status: statusMessage } + }, + }), + display_edge_function: tool({ + description: + 'Renders the code for a Supabase Edge Function for the user to deploy manually.', + parameters: z.object({ + name: z + .string() + .describe('The URL-friendly name of the Edge Function (e.g., "my-function").'), + code: z.string().describe('The TypeScript code for the Edge Function.'), + }), + execute: async () => { + return { status: 'Tool call sent to client for rendering.' } + }, + }), } - let systemPrompt = ` + const system = source` The current project is ${projectRef}. You are a Supabase Postgres expert. Your goal is to generate SQL or Edge Function code based on user requests, using specific tools for rendering. @@ -392,16 +308,19 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { ` const result = streamText({ - model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'), + model, maxSteps: 10, - system: systemPrompt.trim(), + system, messages, - tools: allTools, + tools, }) result.pipeDataStreamToResponse(res) - } catch (error: any) { + } catch (error) { console.error('Error in handlePost:', error) - return res.status(500).json({ message: error.message }) + if (error instanceof Error) { + return res.status(500).json({ message: error.message }) + } + return res.status(500).json({ message: 'An unexpected error occurred.' }) } } diff --git a/apps/studio/pages/api/ai/sql/supabase-mcp.test.ts b/apps/studio/pages/api/ai/sql/supabase-mcp.test.ts new file mode 100644 index 00000000000..f8c64aa76a6 --- /dev/null +++ b/apps/studio/pages/api/ai/sql/supabase-mcp.test.ts @@ -0,0 +1,198 @@ +import { Tool, ToolExecutionOptions, ToolSet } from 'ai' +import { describe, expect, it, vitest } from 'vitest' +import { z } from 'zod' +import { + expectedToolsSchema, + filterToolsByOptInLevel, + getAllowedTools, + transformToolResult, +} from './supabase-mcp' + +describe('getAllowedTools', () => { + it('should return empty array for disabled opt-in level', () => { + const tools = getAllowedTools('disabled') + expect(tools).toEqual([]) + }) + + it('should return schema tools for schema opt-in level', () => { + const tools = getAllowedTools('schema') + expect(tools).toContain('list_tables') + expect(tools).toContain('list_extensions') + expect(tools).toContain('list_edge_functions') + expect(tools).toContain('list_branches') + expect(tools).not.toContain('get_logs') + expect(tools).not.toContain('execute_sql') + }) + + it('should return schema and log tools for schema_and_log opt-in level', () => { + const tools = getAllowedTools('schema_and_log') + expect(tools).toContain('list_tables') + expect(tools).toContain('list_extensions') + expect(tools).toContain('list_edge_functions') + expect(tools).toContain('list_branches') + expect(tools).toContain('get_logs') + expect(tools).not.toContain('execute_sql') + }) + + it('should return all tools for schema_and_log_and_data opt-in level', () => { + const tools = getAllowedTools('schema_and_log_and_data') + expect(tools).toContain('list_tables') + expect(tools).toContain('list_extensions') + expect(tools).toContain('list_edge_functions') + expect(tools).toContain('list_branches') + expect(tools).toContain('get_logs') + expect(tools).toContain('execute_sql') + }) +}) + +describe('filterToolsByOptInLevel', () => { + const mockTools: ToolSet = { + list_tables: { execute: vitest.fn() }, + list_extensions: { execute: vitest.fn() }, + list_edge_functions: { execute: vitest.fn() }, + list_branches: { execute: vitest.fn() }, + get_logs: { execute: vitest.fn() }, + execute_sql: { execute: vitest.fn() }, + other: { execute: vitest.fn() }, // This tool should be filtered out + } as unknown as ToolSet + + const stubResultSchema = z.object({ + status: z.string(), + }) + + async function checkStub(name: string, tool: Tool) { + if (!tool.execute) { + throw new Error(`Tool ${name} does not have an execute function`) + } + + const result: { status: string } = await (tool.execute as any)() + const parsedResult = stubResultSchema.safeParse(result) + + return ( + parsedResult.success && + parsedResult.data.status.includes("You don't have permission to use this tool") + ) + } + + async function expectStubsFor(tools: ToolSet, expectedStubTools: string[]) { + for (const toolName in tools) { + const tool = tools[toolName] + + const shouldBeStub = expectedStubTools.includes(toolName) + const hasStub = await checkStub(toolName, tool) + + expect(hasStub).toBe(shouldBeStub) + } + } + + it('should filter out tools not in tool whitelist', async () => { + const tools = filterToolsByOptInLevel(mockTools, 'disabled') + + expect(tools).not.toHaveProperty('other') + }) + + it('should stub all functions for disabled opt-in level', async () => { + const tools = filterToolsByOptInLevel(mockTools, 'disabled') + + await expectStubsFor(tools, [ + 'list_tables', + 'list_extensions', + 'list_edge_functions', + 'list_branches', + 'get_logs', + 'execute_sql', + ]) + }) + + it('should stub log and execute tools for schema opt-in level', async () => { + const tools = filterToolsByOptInLevel(mockTools, 'schema') + + await expectStubsFor(tools, ['get_logs', 'execute_sql']) + }) + + it('should stub execute tool for schema_and_log opt-in level', async () => { + const tools = filterToolsByOptInLevel(mockTools, 'schema_and_log') + + await expectStubsFor(tools, ['execute_sql']) + }) + + it('should not stub any tools for schema_and_log_and_data opt-in level', async () => { + const tools = filterToolsByOptInLevel(mockTools, 'schema_and_log_and_data') + + await expectStubsFor(tools, []) + }) +}) + +describe('transformToolResult', () => { + it('should wrap a tool with a result transformation function', async () => { + const originalResult = { data: 'original' } + + const mockTool = { + description: 'Test tool', + execute: vitest.fn().mockResolvedValue(originalResult), + } as unknown as Tool + + const transformFn = vitest.fn((result: typeof originalResult) => ({ + data: `${result.data} - transformed`, + })) + + const transformedTool = transformToolResult(mockTool, transformFn) + + // Tool properties should be preserved + expect(transformedTool.description).toBe(mockTool.description) + + // Execute the transformed tool + const args = { key: 'value' } + const options = {} as ToolExecutionOptions + + if (!transformedTool.execute) { + throw new Error('Transformed tool does not have an execute function') + } + + const result = await transformedTool.execute(args, options) + + // Original tool should have been called with the same arguments + expect(mockTool.execute).toHaveBeenCalledWith(args, options) + + // Transform function should have been called with the original result + expect(transformFn).toHaveBeenCalledWith(originalResult) + + // Final result should be the transformed value + expect(result).toEqual({ data: 'original - transformed' }) + }) + + it('should throw an error if tool is null', () => { + expect(() => transformToolResult(null as any, () => ({}))).toThrow('Tool is required') + }) + + it('should throw an error if tool does not have an execute function', () => { + const invalidTool = { name: 'invalid' } as any + expect(() => transformToolResult(invalidTool, () => ({}))).toThrow( + 'Tool does not have an execute function' + ) + }) +}) + +describe('expectedToolsSchema', () => { + it('should validate the expected tools schema', () => { + const validTools = { + list_tables: {}, + list_extensions: {}, + list_edge_functions: {}, + list_branches: {}, + get_logs: {}, + execute_sql: {}, + other: {}, + } + + const validationResult = expectedToolsSchema.safeParse(validTools) + expect(validationResult.success).toBe(true) + + // Test with missing tool + const invalidTools = { ...validTools } + delete (invalidTools as any).execute_sql + + const invalidValidationResult = expectedToolsSchema.safeParse(invalidTools) + expect(invalidValidationResult.success).toBe(false) + }) +}) diff --git a/apps/studio/pages/api/ai/sql/supabase-mcp.ts b/apps/studio/pages/api/ai/sql/supabase-mcp.ts index 74cd23a3297..0bbaccda652 100644 --- a/apps/studio/pages/api/ai/sql/supabase-mcp.ts +++ b/apps/studio/pages/api/ai/sql/supabase-mcp.ts @@ -1,7 +1,14 @@ -import { StreamTransport } from '@supabase/mcp-utils' -import { experimental_createMCPClient as createMCPClient } from 'ai' import { createSupabaseMcpServer } from '@supabase/mcp-server-supabase' +import { StreamTransport } from '@supabase/mcp-utils' +import { + experimental_createMCPClient as createMCPClient, + Tool, + ToolExecutionOptions, + ToolSet, +} from 'ai' +import { AiOptInLevel } from 'hooks/misc/useOrgOptedIntoAi' import { API_URL } from 'lib/constants' +import { z } from 'zod' export async function createSupabaseMCPClient({ accessToken, @@ -36,3 +43,104 @@ export async function createSupabaseMCPClient({ return client } + +const basicToolSchema = z.custom((value) => typeof value === 'object') + +/** + * Schema to validate that the expected tools are available from the Supabase MCP. + * + * Note that tool structure itself is not validated, only that the tools exist. + */ +export const expectedToolsSchema = z.object({ + list_tables: basicToolSchema, + list_extensions: basicToolSchema, + list_edge_functions: basicToolSchema, + list_branches: basicToolSchema, + get_logs: basicToolSchema, + execute_sql: basicToolSchema, +}) + +export const toolWhitelist = Object.keys(expectedToolsSchema.shape) + +export function createPrivacyMessageTool(toolInstance: Tool) { + const privacyMessage = + "You don't have permission to use this tool. This is an organization-wide setting requiring you to opt-in. Please choose your preferred data sharing level in your organization's settings. Supabase Assistant uses Amazon Bedrock, which does not store or log your prompts and completions, use them to train AWS models, or distribute them to third parties. By default, no data is shared. Granting permission allows Supabase to send information (like schema, logs, or data, depending on your chosen level) to Bedrock solely to generate responses." + const condensedPrivacyMessage = + 'Requires opting in to sending data to Bedrock which does not store, train on, or distribute it. You can opt in via organization settings.' + + return { + ...toolInstance, + description: `${toolInstance.description} (Note: ${condensedPrivacyMessage})`, + execute: async (_args: any, _context: any) => ({ status: privacyMessage }), + } +} + +export function filterToolsByOptInLevel(tools: ToolSet, aiOptInLevel: AiOptInLevel) { + // Get allowed tools based on the AI opt-in level + const allowedTools = getAllowedTools(aiOptInLevel) + + // Filter the tools to only include those that are allowed + return Object.fromEntries( + Object.entries(tools) + .filter(([key]) => toolWhitelist.includes(key)) + .map(([key, toolInstance]) => { + if (allowedTools.includes(key)) { + return [key, toolInstance] + } + + // If the tool is not allowed, provide a stub that returns a privacy message + return [key, createPrivacyMessageTool(toolInstance)] + }) + ) +} + +/** + * Transforms the result of a tool execution to a new output. + */ +export function transformToolResult( + tool: Tool, + execute: (result: OriginalResult) => NewResult +): Tool { + if (!tool) { + throw new Error('Tool is required') + } + + if (!tool.execute) { + throw new Error('Tool does not have an execute function') + } + + // Intercept the tool to add a custom execute function + return { + ...tool, + execute: async (args: any, options: ToolExecutionOptions) => { + const result = await tool.execute!(args, options) + return execute(result) + }, + } as Tool +} + +export function getAllowedTools(aiOptInLevel: AiOptInLevel) { + // Build allowed tools based on permission level + const allowedTools: string[] = [] + + // For schema and above permission levels + if ( + aiOptInLevel === 'schema' || + aiOptInLevel === 'schema_and_log' || + aiOptInLevel === 'schema_and_log_and_data' + ) { + allowedTools.push('list_tables', 'list_extensions', 'list_edge_functions', 'list_branches') + } + + // For schema_and_log permission level, add log access tools + if (aiOptInLevel === 'schema_and_log' || aiOptInLevel === 'schema_and_log_and_data') { + allowedTools.push('get_logs') + } + + // For schema_and_log_and_data permission level, add data access tools + if (aiOptInLevel === 'schema_and_log_and_data') { + allowedTools.push('execute_sql') + } + + return allowedTools +} diff --git a/apps/studio/pages/api/ai/sql/title.ts b/apps/studio/pages/api/ai/sql/title.ts index 638d7071d7b..129793a9bd7 100644 --- a/apps/studio/pages/api/ai/sql/title.ts +++ b/apps/studio/pages/api/ai/sql/title.ts @@ -1,26 +1,9 @@ -import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock' -import { fromNodeProviderChain } from '@aws-sdk/credential-providers' import { generateObject } from 'ai' +import { source } from 'common-tags' +import { getModel } from 'lib/ai/model' +import apiWrapper from 'lib/api/apiWrapper' import { NextApiRequest, NextApiResponse } from 'next' import { z } from 'zod' -import apiWrapper from 'lib/api/apiWrapper' - -const credentialProvider = fromNodeProviderChain() - -const bedrock = createAmazonBedrock({ - credentialProvider, -}) - -async function hasAwsCredentials() { - try { - const credentials = await credentialProvider() - return !!credentials - } catch (error) { - return false - } -} - -const hasCredentials = await hasAwsCredentials() const titleSchema = z.object({ title: z @@ -32,19 +15,6 @@ const titleSchema = z.object({ }) async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!process.env.AWS_REGION) { - return res.status(500).json({ - error: 'AWS_REGION is not set', - }) - } - - if (!hasCredentials) { - return res.status(500).json({ - error: - 'AWS credentials are not configured. Set up a local profile or add environment variables.', - }) - } - const { method } = req switch (method) { @@ -68,14 +38,22 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) { } try { + const { model, error: modelError } = await getModel() + + if (modelError) { + return res.status(500).json({ error: modelError.message }) + } + const result = await generateObject({ - model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'), + model, schema: titleSchema, - prompt: `Generate a short title and summarized description for this Postgres SQL snippet: + prompt: source` + Generate a short title and summarized description for this Postgres SQL snippet: -${sql} + ${sql} -The description should describe why this table was created (eg. "Table to track todos") or what the query does.`, + The description should describe why this table was created (eg. "Table to track todos") or what the query does. + `, temperature: 0, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f0e6581d3c..868ba1a0362 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,7 +200,7 @@ importers: version: 1.6.0 tailwindcss: specifier: ^3.3.0 - version: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) + version: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) tsconfig: specifier: workspace:* version: link:../../packages/tsconfig @@ -572,8 +572,11 @@ importers: apps/studio: dependencies: '@ai-sdk/amazon-bedrock': - specifier: ^2.2.8 + specifier: ^2.2.9 version: 2.2.9(zod@3.23.8) + '@ai-sdk/openai': + specifier: ^1.3.22 + version: 1.3.22(zod@3.23.8) '@ai-sdk/react': specifier: ^1.2.12 version: 1.2.12(react@18.3.1)(zod@3.23.8) @@ -683,8 +686,8 @@ importers: specifier: ^2.7.29 version: 2.7.30 ai: - specifier: ^4.3.9 - version: 4.3.15(react@18.3.1)(zod@3.23.8) + specifier: ^4.3.16 + version: 4.3.16(react@18.3.1)(zod@3.23.8) ai-commands: specifier: workspace:* version: link:../../packages/ai-commands @@ -994,7 +997,7 @@ importers: version: 4.4.2 '@vitest/coverage-v8': specifier: ^3.0.9 - version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)) + version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9) '@vitest/ui': specifier: ^3.0.0 version: 3.0.4(vitest@3.0.9) @@ -1259,7 +1262,7 @@ importers: version: 14.2.3 '@next/mdx': specifier: ^14.2.3 - version: 14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1))(@mdx-js/react@2.3.0(react@18.3.1)) + version: 14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)))(@mdx-js/react@2.3.0(react@18.3.1)) '@octokit/auth-app': specifier: ^7.0.0 version: 7.1.5 @@ -1497,7 +1500,7 @@ importers: version: 2.49.3 ai: specifier: ^3.4.33 - version: 3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.13(typescript@5.5.2))(zod@3.23.8) + version: 3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.2.0(svelte@5.33.10))(svelte@5.33.10)(vue@3.5.16(typescript@5.5.2))(zod@3.23.8) common-tags: specifier: ^1.8.2 version: 1.8.2 @@ -1655,10 +1658,10 @@ importers: version: 0.1.9 '@tailwindcss/forms': specifier: ^0.5.0 - version: 0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) + version: 0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))) '@tailwindcss/typography': specifier: ^0.5.9 - version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) + version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))) deepmerge: specifier: ^4.2.2 version: 4.3.1 @@ -1671,10 +1674,10 @@ importers: devDependencies: tailwindcss: specifier: ^3.4.1 - version: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) + version: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) tailwindcss-animate: specifier: ^1.0.6 - version: 1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))) + version: 1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))) packages/eslint-config-supabase: dependencies: @@ -2221,6 +2224,12 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/openai@1.3.22': + resolution: {integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@ai-sdk/provider-utils@1.0.22': resolution: {integrity: sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==} engines: {node: '>=18'} @@ -2646,6 +2655,10 @@ packages: resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} @@ -2654,6 +2667,10 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.25.9': resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} @@ -2681,6 +2698,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.27.3': + resolution: {integrity: sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-syntax-decorators@7.25.9': resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==} engines: {node: '>=6.9.0'} @@ -2759,6 +2781,10 @@ packages: resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} + '@babel/types@7.27.3': + resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -7507,6 +7533,11 @@ packages: '@supabase/supabase-js@2.49.3': resolution: {integrity: sha512-42imTuAm9VEQGlXT0O6zrSwNnsIblU1eieqrAWj8HSmFaYkxepk/IuUVw1M5hKelk0ZYlqDKNwRErI1rF1EL4w==} + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -8333,34 +8364,34 @@ packages: '@vitest/utils@3.0.9': resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} - '@vue/compiler-core@3.5.13': - resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + '@vue/compiler-core@3.5.16': + resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} - '@vue/compiler-dom@3.5.13': - resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + '@vue/compiler-dom@3.5.16': + resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} - '@vue/compiler-sfc@3.5.13': - resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + '@vue/compiler-sfc@3.5.16': + resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} - '@vue/compiler-ssr@3.5.13': - resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + '@vue/compiler-ssr@3.5.16': + resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} - '@vue/reactivity@3.5.13': - resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + '@vue/reactivity@3.5.16': + resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} - '@vue/runtime-core@3.5.13': - resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + '@vue/runtime-core@3.5.16': + resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} - '@vue/runtime-dom@3.5.13': - resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + '@vue/runtime-dom@3.5.16': + resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} - '@vue/server-renderer@3.5.13': - resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + '@vue/server-renderer@3.5.16': + resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} peerDependencies: - vue: 3.5.13 + vue: 3.5.16 - '@vue/shared@3.5.13': - resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@vue/shared@3.5.16': + resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -8538,8 +8569,8 @@ packages: zod: optional: true - ai@4.3.15: - resolution: {integrity: sha512-TYKRzbWg6mx/pmTadlAEIhuQtzfHUV0BbLY72+zkovXwq/9xhcH24IlQmkyBpElK6/4ArS0dHdOOtR1jOPVwtg==} + ai@4.3.16: + resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -9206,9 +9237,6 @@ packages: code-block-writer@13.0.1: resolution: {integrity: sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==} - code-red@1.0.4: - resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} - codemirror-graphql@2.0.10: resolution: {integrity: sha512-rC9NxibCsSzWtCQjHLfwKCkyYdGv2BT/BCgyDoKPrc/e7aGiyLyeT0fB60d+0imwlvhX3lIHncl6JMz2YxQ/jg==} peerDependencies: @@ -9510,10 +9538,6 @@ packages: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} - css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -10327,6 +10351,9 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + esniff@2.0.1: resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} engines: {node: '>=0.10'} @@ -10344,6 +10371,9 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} + esrap@1.4.6: + resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -12445,9 +12475,6 @@ packages: mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - mdurl@1.0.1: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} @@ -15213,10 +15240,10 @@ packages: resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} engines: {node: '>= 8'} - sswr@2.1.0: - resolution: {integrity: sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==} + sswr@2.2.0: + resolution: {integrity: sha512-clTszLPZkmycALTHD1mXGU+mOtA/MIoLgS1KGTTzFNVm9rytQVykgRaP+z1zl572cz0bTqj4rFVoC2N+IGK4Sg==} peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0 stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} @@ -15464,9 +15491,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte@4.2.19: - resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==} - engines: {node: '>=16'} + svelte@5.33.10: + resolution: {integrity: sha512-/yArPQIBoQS2p86LKnvJywOXkVHeEXnFgrDPSxkEfIAEkykopYuy2bF6UUqHG4IbZlJD6OurLxJT8Kn7kTk9WA==} + engines: {node: '>=18'} svgson@5.3.1: resolution: {integrity: sha512-qdPgvUNWb40gWktBJnbJRelWcPzkLed/ShhnRsjbayXz8OtdPOzbil9jtiZdrYvSDumAz/VNQr6JaNfPx/gvPA==} @@ -15490,8 +15517,8 @@ packages: swrev@4.0.0: resolution: {integrity: sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==} - swrv@1.0.4: - resolution: {integrity: sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g==} + swrv@1.1.0: + resolution: {integrity: sha512-pjllRDr2s0iTwiE5Isvip51dZGR7GjLH1gCSVyE8bQnbAx6xackXsFdojau+1O5u98yHF5V73HQGOFxKUXO9gQ==} peerDependencies: vue: '>=3.2.26 < 4' @@ -16427,8 +16454,8 @@ packages: vscode-languageserver-types@3.17.5: resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - vue@3.5.13: - resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + vue@3.5.16: + resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -16693,15 +16720,13 @@ packages: yup@1.4.0: resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} - zod-to-json-schema@3.23.5: - resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} - peerDependencies: - zod: ^3.23.3 - zod-to-json-schema@3.24.5: resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} peerDependencies: @@ -16755,6 +16780,12 @@ snapshots: '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) zod: 3.23.8 + '@ai-sdk/openai@1.3.22(zod@3.23.8)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.23.8) + zod: 3.23.8 + '@ai-sdk/provider-utils@1.0.22(zod@3.23.8)': dependencies: '@ai-sdk/provider': 0.0.26 @@ -16806,13 +16837,13 @@ snapshots: transitivePeerDependencies: - zod - '@ai-sdk/svelte@0.0.57(svelte@4.2.19)(zod@3.23.8)': + '@ai-sdk/svelte@0.0.57(svelte@5.33.10)(zod@3.23.8)': dependencies: '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - sswr: 2.1.0(svelte@4.2.19) + sswr: 2.2.0(svelte@5.33.10) optionalDependencies: - svelte: 4.2.19 + svelte: 5.33.10 transitivePeerDependencies: - zod @@ -16822,7 +16853,7 @@ snapshots: '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) json-schema: 0.4.0 secure-json-parse: 2.7.0 - zod-to-json-schema: 3.23.5(zod@3.23.8) + zod-to-json-schema: 3.24.5(zod@3.23.8) optionalDependencies: zod: 3.23.8 @@ -16833,13 +16864,13 @@ snapshots: zod: 3.23.8 zod-to-json-schema: 3.24.5(zod@3.23.8) - '@ai-sdk/vue@0.0.59(vue@3.5.13(typescript@5.5.2))(zod@3.23.8)': + '@ai-sdk/vue@0.0.59(vue@3.5.16(typescript@5.5.2))(zod@3.23.8)': dependencies: '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - swrv: 1.0.4(vue@3.5.13(typescript@5.5.2)) + swrv: 1.1.0(vue@3.5.16(typescript@5.5.2)) optionalDependencies: - vue: 3.5.13(typescript@5.5.2) + vue: 3.5.16(typescript@5.5.2) transitivePeerDependencies: - zod @@ -16871,13 +16902,13 @@ snapshots: '@aws-crypto/crc32@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.468.0 + '@aws-sdk/types': 3.804.0 tslib: 1.14.1 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.468.0 + '@aws-sdk/types': 3.804.0 tslib: 2.8.1 '@aws-crypto/ie11-detection@3.0.0': @@ -17742,10 +17773,14 @@ snapshots: '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-validator-identifier@7.24.7': {} '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-option@7.25.9': {} '@babel/helpers@7.26.10': @@ -17772,6 +17807,10 @@ snapshots: dependencies: '@babel/types': 7.27.0 + '@babel/parser@7.27.3': + dependencies: + '@babel/types': 7.27.3 + '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.10(supports-color@8.1.1))': dependencies: '@babel/core': 7.26.10(supports-color@8.1.1) @@ -17880,6 +17919,11 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@babel/types@7.27.3': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@bcoe/v8-coverage@1.0.2': {} '@bundled-es-modules/cookie@2.0.1': @@ -19590,6 +19634,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2))': + dependencies: + '@mdx-js/mdx': 2.3.0(supports-color@8.1.1) + source-map: 0.7.4 + webpack: 5.94.0(esbuild@0.25.2) + transitivePeerDependencies: + - supports-color + optional: true + '@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0)': dependencies: '@mdx-js/mdx': 2.3.0(supports-color@8.1.1) @@ -19772,11 +19825,11 @@ snapshots: dependencies: glob: 10.3.10 - '@next/mdx@14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1))(@mdx-js/react@2.3.0(react@18.3.1))': + '@next/mdx@14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)))(@mdx-js/react@2.3.0(react@18.3.1))': dependencies: source-map: 0.7.4 optionalDependencies: - '@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0) + '@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)) '@mdx-js/react': 2.3.0(react@18.3.1) '@next/mdx@15.3.1(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0))(@mdx-js/react@2.3.0(react@18.3.1))': @@ -23755,6 +23808,10 @@ snapshots: - bufferutil - utf-8-validate + '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.1)': + dependencies: + acorn: 8.14.1 + '@swc/counter@0.1.3': {} '@swc/helpers@0.5.15': @@ -23775,6 +23832,11 @@ snapshots: mini-svg-data-uri: 1.4.4 tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) + '@tailwindcss/forms@0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))': + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) + '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)))': dependencies: lodash.castarray: 4.4.0 @@ -23783,6 +23845,14 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) + '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) + '@tanstack/directive-functions-plugin@1.114.12(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)': dependencies: '@babel/code-frame': 7.26.2 @@ -25068,24 +25138,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1) - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.8.1 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5) - transitivePeerDependencies: - - supports-color - '@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.7.3(@types/node@20.12.11)(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))': dependencies: '@ampproject/remapping': 2.3.0 @@ -25122,6 +25174,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + debug: 4.4.0(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1) + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.8.1 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5) + transitivePeerDependencies: + - supports-color + '@vitest/expect@3.0.9': dependencies: '@vitest/spy': 3.0.9 @@ -25202,59 +25272,59 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 - '@vue/compiler-core@3.5.13': + '@vue/compiler-core@3.5.16': dependencies: - '@babel/parser': 7.27.0 - '@vue/shared': 3.5.13 + '@babel/parser': 7.27.3 + '@vue/shared': 3.5.16 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.13': + '@vue/compiler-dom@3.5.16': dependencies: - '@vue/compiler-core': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/compiler-core': 3.5.16 + '@vue/shared': 3.5.16 - '@vue/compiler-sfc@3.5.13': + '@vue/compiler-sfc@3.5.16': dependencies: - '@babel/parser': 7.27.0 - '@vue/compiler-core': 3.5.13 - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-ssr': 3.5.13 - '@vue/shared': 3.5.13 + '@babel/parser': 7.27.3 + '@vue/compiler-core': 3.5.16 + '@vue/compiler-dom': 3.5.16 + '@vue/compiler-ssr': 3.5.16 + '@vue/shared': 3.5.16 estree-walker: 2.0.2 magic-string: 0.30.17 postcss: 8.5.3 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.13': + '@vue/compiler-ssr@3.5.16': dependencies: - '@vue/compiler-dom': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/compiler-dom': 3.5.16 + '@vue/shared': 3.5.16 - '@vue/reactivity@3.5.13': + '@vue/reactivity@3.5.16': dependencies: - '@vue/shared': 3.5.13 + '@vue/shared': 3.5.16 - '@vue/runtime-core@3.5.13': + '@vue/runtime-core@3.5.16': dependencies: - '@vue/reactivity': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/reactivity': 3.5.16 + '@vue/shared': 3.5.16 - '@vue/runtime-dom@3.5.13': + '@vue/runtime-dom@3.5.16': dependencies: - '@vue/reactivity': 3.5.13 - '@vue/runtime-core': 3.5.13 - '@vue/shared': 3.5.13 + '@vue/reactivity': 3.5.16 + '@vue/runtime-core': 3.5.16 + '@vue/shared': 3.5.16 csstype: 3.1.3 - '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.5.2))': + '@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.5.2))': dependencies: - '@vue/compiler-ssr': 3.5.13 - '@vue/shared': 3.5.13 - vue: 3.5.13(typescript@5.5.2) + '@vue/compiler-ssr': 3.5.16 + '@vue/shared': 3.5.16 + vue: 3.5.16(typescript@5.5.2) - '@vue/shared@3.5.13': {} + '@vue/shared@3.5.16': {} '@webassemblyjs/ast@1.14.1': dependencies: @@ -25442,32 +25512,32 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.13(typescript@5.5.2))(zod@3.23.8): + ai@3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.2.0(svelte@5.33.10))(svelte@5.33.10)(vue@3.5.16(typescript@5.5.2))(zod@3.23.8): dependencies: '@ai-sdk/provider': 0.0.26 '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) '@ai-sdk/react': 0.0.70(react@18.3.1)(zod@3.23.8) '@ai-sdk/solid': 0.0.54(zod@3.23.8) - '@ai-sdk/svelte': 0.0.57(svelte@4.2.19)(zod@3.23.8) + '@ai-sdk/svelte': 0.0.57(svelte@5.33.10)(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - '@ai-sdk/vue': 0.0.59(vue@3.5.13(typescript@5.5.2))(zod@3.23.8) + '@ai-sdk/vue': 0.0.59(vue@3.5.16(typescript@5.5.2))(zod@3.23.8) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0 jsondiffpatch: 0.6.0 secure-json-parse: 2.7.0 - zod-to-json-schema: 3.23.5(zod@3.23.8) + zod-to-json-schema: 3.24.5(zod@3.23.8) optionalDependencies: openai: 4.71.1(encoding@0.1.13)(zod@3.23.8) react: 18.3.1 - sswr: 2.1.0(svelte@4.2.19) - svelte: 4.2.19 + sswr: 2.2.0(svelte@5.33.10) + svelte: 5.33.10 zod: 3.23.8 transitivePeerDependencies: - solid-js - vue - ai@4.3.15(react@18.3.1)(zod@3.23.8): + ai@4.3.16(react@18.3.1)(zod@3.23.8): dependencies: '@ai-sdk/provider': 1.1.3 '@ai-sdk/provider-utils': 2.2.8(zod@3.23.8) @@ -26242,14 +26312,6 @@ snapshots: code-block-writer@13.0.1: {} - code-red@1.0.4: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.5 - acorn: 8.14.1 - estree-walker: 3.0.3 - periscopic: 3.1.0 - codemirror-graphql@2.0.10(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0): dependencies: '@codemirror/language': 6.11.0 @@ -26566,11 +26628,6 @@ snapshots: mdn-data: 2.0.14 source-map: 0.6.1 - css-tree@2.3.1: - dependencies: - mdn-data: 2.0.30 - source-map-js: 1.2.1 - css-what@6.1.0: {} css.escape@1.5.1: {} @@ -27300,7 +27357,7 @@ snapshots: debug: 4.4.0(supports-color@8.1.1) enhanced-resolve: 5.17.1 eslint: 8.57.0(supports-color@8.1.1) - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) fast-glob: 3.3.2 get-tsconfig: 4.7.2 @@ -27312,7 +27369,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: @@ -27339,7 +27396,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0(supports-color@8.1.1) eslint-import-resolver-node: 0.3.9(supports-color@8.1.1) - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -27462,6 +27519,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + esniff@2.0.1: dependencies: d: 1.0.2 @@ -27481,6 +27540,10 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@1.4.6: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -30179,8 +30242,6 @@ snapshots: mdn-data@2.0.14: {} - mdn-data@2.0.30: {} - mdurl@1.0.1: {} mdurl@2.0.0: {} @@ -33876,9 +33937,9 @@ snapshots: dependencies: minipass: 3.3.6 - sswr@2.1.0(svelte@4.2.19): + sswr@2.2.0(svelte@5.33.10): dependencies: - svelte: 4.2.19 + svelte: 5.33.10 swrev: 4.0.0 stack-generator@2.0.10: @@ -34155,22 +34216,22 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte@4.2.19: + svelte@5.33.10: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1) '@types/estree': 1.0.5 acorn: 8.14.1 aria-query: 5.3.2 axobject-query: 4.1.0 - code-red: 1.0.4 - css-tree: 2.3.1 - estree-walker: 3.0.3 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 1.4.6 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.17 - periscopic: 3.1.0 + zimmerframe: 1.1.2 svgson@5.3.1: dependencies: @@ -34207,9 +34268,9 @@ snapshots: swrev@4.0.0: {} - swrv@1.0.4(vue@3.5.13(typescript@5.5.2)): + swrv@1.1.0(vue@3.5.16(typescript@5.5.2)): dependencies: - vue: 3.5.13(typescript@5.5.2) + vue: 3.5.16(typescript@5.5.2) symbol-tree@3.2.4: optional: true @@ -34226,9 +34287,9 @@ snapshots: tailwind-merge@1.14.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))): dependencies: - tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)) + tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)) tailwindcss-radix@2.8.0: {} @@ -35453,13 +35514,13 @@ snapshots: vscode-languageserver-types@3.17.5: {} - vue@3.5.13(typescript@5.5.2): + vue@3.5.16(typescript@5.5.2): dependencies: - '@vue/compiler-dom': 3.5.13 - '@vue/compiler-sfc': 3.5.13 - '@vue/runtime-dom': 3.5.13 - '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.5.2)) - '@vue/shared': 3.5.13 + '@vue/compiler-dom': 3.5.16 + '@vue/compiler-sfc': 3.5.16 + '@vue/runtime-dom': 3.5.16 + '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.5.2)) + '@vue/shared': 3.5.16 optionalDependencies: typescript: 5.5.2 @@ -35784,16 +35845,14 @@ snapshots: toposort: 2.0.2 type-fest: 2.19.0 + zimmerframe@1.1.2: {} + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 compress-commons: 6.0.2 readable-stream: 4.6.0 - zod-to-json-schema@3.23.5(zod@3.23.8): - dependencies: - zod: 3.23.8 - zod-to-json-schema@3.24.5(zod@3.23.8): dependencies: zod: 3.23.8