From e54e8fa367f9ba6ab51691f5f99ac2f34044afce Mon Sep 17 00:00:00 2001 From: urso Date: Tue, 6 May 2025 17:49:32 +0200 Subject: [PATCH 1/5] Experiment: model with dependency injection --- .../projects/[project]/chats/actions.ts | 27 +- apps/dbagent/src/app/api/chat/route.ts | 128 ++++-- .../components/chat/artifacts/text/server.ts | 52 +-- .../src/components/monitoring/actions.ts | 24 +- apps/dbagent/src/lib/ai/agent.ts | 21 +- apps/dbagent/src/lib/ai/model.ts | 368 ++++++++++++++++++ apps/dbagent/src/lib/ai/prompts.ts | 14 + apps/dbagent/src/lib/ai/tools/cluster.ts | 1 + 8 files changed, 546 insertions(+), 89 deletions(-) create mode 100644 apps/dbagent/src/lib/ai/model.ts diff --git a/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts b/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts index 26157aec..c57cc4a5 100644 --- a/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts +++ b/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts @@ -1,27 +1,28 @@ 'use server'; -import { generateText, Message } from 'ai'; -import { getModelInstance } from '~/lib/ai/agent'; +import { Message } from 'ai'; +import { AugmentedLanguageModel } from '~/lib/ai/model'; +import { getProviderRegistry } from '~/lib/ai/providers'; import { deleteMessagesByChatIdAfterTimestamp, getMessageById } from '~/lib/db/chats'; import { getUserSessionDBAccess } from '~/lib/db/db'; -export async function generateTitleFromUserMessage({ message }: { message: Message }) { - const { text: title } = await generateText({ - model: await getModelInstance('title'), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['internal', 'chat', 'title'] - } - }, - system: `\n +const titleModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'title', + systemPrompt: `\n - you will generate a short title based on the first message a user begins a conversation with - ensure it is not more than 80 characters long - the title should be a summary of the user's message - do not use quotes or colons`, + metadata: { + tags: ['internal', 'chat', 'title'] + } +}); + +export async function generateTitleFromUserMessage({ message }: { message: Message }) { + const { text: title } = await titleModel.generateText({ prompt: JSON.stringify(message) }); - return title; } diff --git a/apps/dbagent/src/app/api/chat/route.ts b/apps/dbagent/src/app/api/chat/route.ts index ba83e6e0..0c9bf1dc 100644 --- a/apps/dbagent/src/app/api/chat/route.ts +++ b/apps/dbagent/src/app/api/chat/route.ts @@ -1,16 +1,32 @@ -import { UIMessage, appendResponseMessages, createDataStreamResponse, smoothStream, streamText } from 'ai'; +import { appendResponseMessages, createDataStreamResponse, DataStreamWriter, UIMessage } from 'ai'; import { notFound } from 'next/navigation'; import { NextRequest } from 'next/server'; import { generateTitleFromUserMessage } from '~/app/(main)/projects/[project]/chats/actions'; -import { generateUUID } from '~/components/chat/utils'; -import { getChatSystemPrompt } from '~/lib/ai/agent'; -import { getLanguageModel } from '~/lib/ai/providers'; -import { getTools } from '~/lib/ai/tools'; +import { AugmentedLanguageModel } from '~/lib/ai/model'; +import { + artifactsPrompt, + awsCloudProviderPrompt, + chatSystemPrompt, + commonSystemPrompt, + gcpCloudProviderPrompt +} from '~/lib/ai/prompts'; +import { getLanguageModel, getProviderRegistry } from '~/lib/ai/providers'; +import { + AWSDBClusterTools, + CommonDBClusterTools, + commonToolset, + GCPDBClusterTools, + getDBSQLTools, + getPlaybookToolset +} from '~/lib/ai/tools'; +import { getArtifactTools } from '~/lib/ai/tools/artifacts'; +import { mcpToolset } from '~/lib/ai/tools/user-mcp'; import { deleteChatById, getChatById, getChatsByProject, saveChat } from '~/lib/db/chats'; import { getConnection } from '~/lib/db/connections'; -import { getUserSessionDBAccess } from '~/lib/db/db'; +import { DBAccess, getUserSessionDBAccess } from '~/lib/db/db'; import { getProjectById } from '~/lib/db/projects'; -import { getTargetDbPool } from '~/lib/targetdb/db'; +import { Connection, Project } from '~/lib/db/schema'; +import { getTargetDbPool, Pool } from '~/lib/targetdb/db'; import { requireUserSession } from '~/utils/route'; export const maxDuration = 60; @@ -33,6 +49,66 @@ export async function GET(request: NextRequest) { return Response.json({ chats }); } +type ChatModelDeps = { + dbAccess: DBAccess; + project: Project; + connection: Connection; + targetDb: Pool; + useArtifacts: boolean; + userId: string; + dataStream?: DataStreamWriter; +}; + +const chatModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'chat', + metadata: { + tags: ['chat'] + }, + systemPrompt: [commonSystemPrompt, chatSystemPrompt], + toolsSets: [ + { tools: mcpToolset.listMCPTools }, + { tools: commonToolset }, + { tools: async ({ targetDb }) => getDBSQLTools(targetDb).toolset() }, + + // Playbook support + { tools: async ({ dbAccess, project }) => getPlaybookToolset(dbAccess, project.id) }, + + // Common cloud provider DB support + { + tools: async ({ dbAccess, connection }) => + new CommonDBClusterTools(dbAccess, () => Promise.resolve(connection)).toolset() + } + ] +}); + +// AWS cloud provider support +chatModel.addSystemPrompts(({ project }) => (project.cloudProvider === 'aws' ? awsCloudProviderPrompt : '')); +chatModel.addToolSet({ + active: (deps?: ChatModelDeps) => deps?.project.cloudProvider === 'aws', + tools: async ({ dbAccess, connection }) => { + return new AWSDBClusterTools(dbAccess, () => Promise.resolve(connection)).toolset(); + } +}); + +// GCP cloud provider support +chatModel.addSystemPrompts(({ project }) => (project.cloudProvider === 'gcp' ? gcpCloudProviderPrompt : '')); +chatModel.addToolSet({ + active: (deps?: ChatModelDeps) => deps?.project.cloudProvider === 'gcp', + tools: async ({ dbAccess, connection }) => { + return new GCPDBClusterTools(dbAccess, () => Promise.resolve(connection)).toolset(); + } +}); + +// Artifacts support +chatModel.addSystemPrompts(({ useArtifacts }) => (useArtifacts ? artifactsPrompt : '')); +chatModel.addToolSet({ + active: (deps?: ChatModelDeps) => !!deps?.useArtifacts && !!deps?.dataStream, + tools: async ({ dbAccess, userId, project, dataStream }) => { + return getArtifactTools({ dbAccess, userId, projectId: project.id, dataStream: dataStream! }); + } +}); + export async function POST(request: Request) { try { const { id, messages, connectionId, model: modelId, useArtifacts } = await request.json(); @@ -59,34 +135,32 @@ export async function POST(request: Request) { if (!chat) notFound(); const targetDb = getTargetDbPool(connection.connectionString); - const context = getChatSystemPrompt({ cloudProvider: project.cloudProvider, useArtifacts }); + // const context = getChatSystemPrompt({ cloudProvider: project.cloudProvider, useArtifacts }); const model = await getLanguageModel(modelId); return createDataStreamResponse({ execute: async (dataStream) => { - const tools = await getTools({ project, connection, targetDb, useArtifacts, userId, dataStream }); - - const result = streamText({ + const result = await chatModel.streamText({ model: model.instance(), - system: context, + deps: { + dbAccess: await getUserSessionDBAccess(), + project, + connection, + targetDb, + useArtifacts, + userId, + dataStream + }, messages, maxSteps: 20, - toolCallStreaming: true, - experimental_transform: smoothStream({ chunking: 'word' }), - experimental_generateMessageId: generateUUID, - experimental_telemetry: { - isEnabled: true, - metadata: { - projectId: connection.projectId, - connectionId: connectionId, - sessionId: id, - model: model.info().id, - userId, - cloudProvider: project.cloudProvider, - tags: ['chat'] - } + metadata: { + projectId: connection.projectId, + connectionId: connectionId, + sessionId: id, + model: model.info().id, + userId, + cloudProvider: project.cloudProvider }, - tools, onFinish: async ({ response }) => { try { const assistantId = getTrailingMessageId({ diff --git a/apps/dbagent/src/components/chat/artifacts/text/server.ts b/apps/dbagent/src/components/chat/artifacts/text/server.ts index c59c52aa..1be19db9 100644 --- a/apps/dbagent/src/components/chat/artifacts/text/server.ts +++ b/apps/dbagent/src/components/chat/artifacts/text/server.ts @@ -1,26 +1,36 @@ -import { smoothStream, streamText } from 'ai'; -import { getModelInstance } from '~/lib/ai/agent'; +import { AugmentedLanguageModel } from '~/lib/ai/model'; import { updateDocumentPrompt } from '~/lib/ai/prompts'; +import { getProviderRegistry } from '~/lib/ai/providers'; import { createDocumentHandler } from '../server'; +const titleModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'title', + systemPrompt: 'Write about the given topic. Markdown is supported. Use headings wherever appropriate.', + metadata: { + tags: ['artifact', 'text', 'create'] + } +}); + +type DocumentUpdateDeps = { + content: string | null; +}; + +const documentUpdateModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'chat', + systemPrompt: ({ content }) => updateDocumentPrompt(content, 'text'), + metadata: { + tags: ['artifact', 'text', 'update'] + } +}); + export const textDocumentHandler = createDocumentHandler<'text'>({ kind: 'text', onCreateDocument: async ({ title, dataStream }) => { let draftContent = ''; - const { fullStream } = streamText({ - model: await getModelInstance('chat'), - system: 'Write about the given topic. Markdown is supported. Use headings wherever appropriate.', - experimental_transform: smoothStream({ chunking: 'word' }), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['artifact', 'text', 'create'] - } - }, - prompt: title - }); - + const { fullStream } = await titleModel.streamText({ prompt: title }); for await (const delta of fullStream) { const { type } = delta; @@ -41,16 +51,8 @@ export const textDocumentHandler = createDocumentHandler<'text'>({ onUpdateDocument: async ({ document, description, dataStream }) => { let draftContent = ''; - const { fullStream } = streamText({ - model: await getModelInstance('chat'), - system: updateDocumentPrompt(document.content, 'text'), - experimental_transform: smoothStream({ chunking: 'word' }), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['artifact', 'text', 'update'] - } - }, + const { fullStream } = await documentUpdateModel.streamText({ + deps: { content: document.content }, prompt: description, experimental_providerMetadata: { openai: { diff --git a/apps/dbagent/src/components/monitoring/actions.ts b/apps/dbagent/src/components/monitoring/actions.ts index 237ed1ec..dd4b298c 100644 --- a/apps/dbagent/src/components/monitoring/actions.ts +++ b/apps/dbagent/src/components/monitoring/actions.ts @@ -1,8 +1,8 @@ 'use server'; -import { openai } from '@ai-sdk/openai'; -import { generateText } from 'ai'; import { auth } from '~/auth'; +import { AugmentedLanguageModel } from '~/lib/ai/model'; +import { getProviderRegistry } from '~/lib/ai/providers'; import { getUserDBAccess, getUserSessionDBAccess } from '~/lib/db/db'; import { getScheduleRuns } from '~/lib/db/schedule-runs'; import { @@ -17,21 +17,19 @@ import { Schedule, ScheduleInsert, ScheduleRun } from '~/lib/db/schema'; import { scheduleGetNextRun, utcToLocalDate } from '~/lib/monitoring/scheduler'; import { listPlaybooks } from '~/lib/tools/playbooks'; +const utilModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'openai:gpt-4o', + metadata: { + tags: ['internal', 'monitoring', 'cron'] + } +}); + export async function generateCronExpression(description: string): Promise { const prompt = `Generate a cron expression for the following schedule description: "${description}". Return strictly the cron expression, no quotes or anything else.`; - const { text } = await generateText({ - model: openai('gpt-4o'), - prompt: prompt, - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['internal', 'monitoring', 'cron'] - } - } - }); - + const { text } = await utilModel.generateText({ prompt }); return text.trim(); } diff --git a/apps/dbagent/src/lib/ai/agent.ts b/apps/dbagent/src/lib/ai/agent.ts index 387e5821..926704d0 100644 --- a/apps/dbagent/src/lib/ai/agent.ts +++ b/apps/dbagent/src/lib/ai/agent.ts @@ -1,22 +1,21 @@ import { LanguageModel } from 'ai'; import { CloudProvider } from '../db/schema'; -import { artifactsPrompt, chatSystemPrompt, commonSystemPrompt, monitoringSystemPrompt } from './prompts'; +import { + artifactsPrompt, + awsCloudProviderPrompt, + chatSystemPrompt, + commonSystemPrompt, + gcpCloudProviderPrompt, + monitoringSystemPrompt +} from './prompts'; import { getLanguageModel, getLanguageModelWithFallback, ModelWithFallback } from './providers'; function getCloudProviderPrompt(cloudProvider: string): string { switch (cloudProvider) { case 'aws': - return `All Postgres instances in this project are hosted on AWS, either RDS or Aurora. - When recommending actions, only recommend actions that can be performed on RDS or Aurora. - If you need to know more about the instance, you can use the getClusterInfo tool. - If you want to recommend changes to the instance, provide instructions specific to RDS or Aurora. - `; + return awsCloudProviderPrompt; case 'gcp': - return `All Postgres instances in this project are GCP Cloud SQL instances. - When recommending actions, only recommend actions that can be performed on GCP Cloud SQL. - If you need to know more about the instance, you can use the getInstanceInfo tool. - If you want to recommend changes to the instance, provide instructions specific to GCP Cloud SQL. - `; + return gcpCloudProviderPrompt; default: return ''; } diff --git a/apps/dbagent/src/lib/ai/model.ts b/apps/dbagent/src/lib/ai/model.ts new file mode 100644 index 00000000..f2e2f413 --- /dev/null +++ b/apps/dbagent/src/lib/ai/model.ts @@ -0,0 +1,368 @@ +import { AttributeValue } from '@opentelemetry/api'; +import { + CoreMessage, + DataStreamOptions, + DataStreamWriter, + generateText, + GenerateTextResult, + LanguageModel, + Message, + ProviderMetadata, + Tool as SDKTool, + tool as sdkTool, + ToolSet as SDKToolSet, + smoothStream, + streamText, + StreamTextOnChunkCallback, + StreamTextOnErrorCallback, + StreamTextOnFinishCallback, + StreamTextOnStepFinishCallback, + TextStreamPart +} from 'ai'; + +function generateUUID(): string { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} + +type PromptOptions = { + system?: string; + prompt?: string; + messages?: Array | Array>; +}; + +interface Telemetry { + metadata?: Record; +} + +type StreamTextOptions = PromptOptions & + Telemetry & { + model?: LanguageModel | string; + deps?: DEPS; + maxSteps?: number; + onChunk?: StreamTextOnChunkCallback; + onError?: StreamTextOnErrorCallback; + onFinish?: StreamTextOnFinishCallback; + onStepFinish?: StreamTextOnStepFinishCallback; + experimental_providerMetadata?: ProviderMetadata; + }; + +type AsyncIterableStream = AsyncIterable & ReadableStream; + +interface StreamResult { + readonly fullStream: AsyncIterableStream>; + consumeStream(options?: { onError?: (error: unknown) => void }): Promise; + mergeIntoDataStream(dataStream: DataStreamWriter, options?: DataStreamOptions): void; +} + +type GenerateTextOptions = PromptOptions & + Telemetry & { + model?: LanguageModel | string; + deps?: DEPS; + maxSteps?: number; + }; + +interface Model { + streamText(options: StreamTextOptions): Promise; + generateText(options: GenerateTextOptions): Promise>; +} + +export class AugmentedLanguageModel implements Model { + #providerRegistry?: ProviderRegistry | (() => Promise); + #baseModel?: LanguageModel | (() => Promise) | string; + #systemPrompt: Prompt[] = []; + #tools: Record> = {}; + #toolSets: ToolSet[] = []; + #metadata?: Record; + + constructor({ + baseModel, + providerRegistry, + metadata, + systemPrompt, + toolsSets + }: { + baseModel?: LanguageModel | string; + providerRegistry?: ProviderRegistry | (() => Promise); + metadata?: Record; + systemPrompt?: Prompt | Prompt[]; + toolsSets?: ToolSet | ToolSet[]; + }) { + if (!baseModel && !providerRegistry) { + throw new Error('baseModel and providerRegistry are required.'); + } + this.#baseModel = baseModel; + this.#providerRegistry = providerRegistry; + this.#metadata = metadata; + if (systemPrompt) { + if (Array.isArray(systemPrompt)) { + this.#systemPrompt.push(...systemPrompt); + } else { + this.#systemPrompt.push(systemPrompt); + } + } + if (toolsSets) { + if (Array.isArray(toolsSets)) { + this.#toolSets.push(...toolsSets); + } else { + this.#toolSets.push(toolsSets); + } + } + } + + addSystemPrompt(prompt: Prompt) { + this.#systemPrompt.push(prompt); + } + + addSystemPrompts(...prompts: Prompt[]) { + this.#systemPrompt.push(...prompts); + } + + addTool(name: string, tool: Tool) { + this.#tools[name] = tool; + } + + addTools(tools: Record>) { + Object.entries(tools).forEach(([name, tool]) => { + this.addTool(name, tool); + }); + } + + addToolSet(toolSet: ToolSet) { + this.#toolSets.push(toolSet); + } + + addToolSets(...toolSets: ToolSet[]) { + this.#toolSets.push(...toolSets); + } + + private async getModel(optModel?: LanguageModel | string): Promise { + const model = optModel || this.#baseModel; + if (typeof model === 'string') { + const providerRegistry = + typeof this.#providerRegistry === 'function' ? await this.#providerRegistry() : this.#providerRegistry; + const m = providerRegistry!.languageModel(model, true); + return m.instance(); + } + if (typeof model === 'function') { + const m = await model(); + return m; + } + return model as LanguageModel; + } + + private async getTools(deps?: DEPS): Promise { + const toolsetTools: Record> = ( + await Promise.all( + this.#toolSets.map(async (toolSet) => { + if (toolSet.active && !toolSet.active(deps)) { + return {}; + } + if (typeof toolSet.tools === 'function') { + return deps ? await toolSet.tools(deps) : {}; + } + return toolSet.tools; + }) + ) + ).reduce((acc: Record>, tools: Record>) => { + return { ...acc, ...tools }; + }, {}); + + const allTools = { ...toolsetTools, ...this.#tools }; + if (!deps) { + return Object.fromEntries( + Object.entries(allTools) + .filter(([_, t]) => t.type !== 'function-deps') + .map(([key, t]) => [key, t as SDKTool]) + ); + } + + return Object.fromEntries( + Object.entries(allTools) + .map(([key, t]) => [key, toSDKTool(t, deps)] as [string, SDKTool | null]) + .filter(([, tool]) => tool !== null) + ) as SDKToolSet; + } + + private getSystemPrompt(deps?: DEPS, system?: string) { + let systemPrompt = generatePrompt(this.#systemPrompt, deps); + if (system) { + systemPrompt = joinPrompts([systemPrompt, system]); + } + return systemPrompt; + } + + private getTelemetryConfig(metadata?: Record) { + return { + isEnabled: true, + metadata: { + ...this.#metadata, + ...metadata + } + }; + } + async streamText({ + model, + deps, + system, + prompt, + messages, + maxSteps, + onChunk, + onError, + onFinish, + onStepFinish, + metadata + }: StreamTextOptions): Promise { + const systemPrompt = this.getSystemPrompt(deps, system); + const tools: SDKToolSet = await this.getTools(deps); + const llm = await this.getModel(model); + const telemetry = this.getTelemetryConfig(metadata); + + console.log('systemPrompt', systemPrompt); + console.log('prompt', prompt); + console.log('messages', messages); + console.log('tools', tools); + + return streamText({ + model: llm, + system: systemPrompt, + prompt, + messages, + maxSteps, + onChunk, + onError, + onFinish, + onStepFinish, + tools, + toolCallStreaming: true, + experimental_telemetry: telemetry, + experimental_transform: smoothStream({ chunking: 'word' }), + experimental_generateMessageId: generateUUID + }); + } + + async generateText({ + model, + deps, + system, + prompt, + messages, + maxSteps, + metadata + }: GenerateTextOptions): Promise> { + const systemPrompt = this.getSystemPrompt(deps, system); + const tools: SDKToolSet = await this.getTools(deps); + const llm = await this.getModel(model); + const telemetry = this.getTelemetryConfig(metadata); + + return generateText({ + model: llm, + system: systemPrompt, + prompt, + messages, + maxSteps, + tools, + experimental_telemetry: telemetry + }); + } +} + +type Prompt = StaticPrompt | PromptFunction; + +export function generatePrompt(prompts: Prompt[], deps?: DEPS): string { + return joinPrompts(prompts.map((p) => callPrompt(p, deps)).filter((p) => p !== '')); +} + +function callPrompt(prompt: Prompt, deps?: DEPS): string { + if (typeof prompt === 'string') { + return prompt; + } + if (!deps) { + return ''; + } + return prompt(deps); +} + +function joinPrompts(prompts: string[]): string { + return prompts.join('\n\n'); +} + +type StaticPrompt = string; + +type PromptFunction = (deps: DepsT) => string; + +import { ToolExecutionOptions } from 'ai'; +import { z } from 'zod'; +import { ProviderRegistry } from './providers'; + +type ToolParameters = z.ZodTypeAny; + +type Tool = + | SDKTool + | { + type: 'function-deps'; + description?: string; + parameters: PARAMETERS; + active?: (deps?: DEPS) => boolean; + execute: (deps: DEPS, args: z.infer, options: ToolExecutionOptions) => PromiseLike; + }; + +type ToolSet = { + active?: (deps?: DEPS) => boolean; + tools: Record> | ((deps: DEPS) => Promise>>); +}; + +function toSDKTool( + tool: Tool, + deps: DEPS +): SDKTool | null { + if (tool.type === 'function-deps') { + if (tool.active && !tool.active(deps)) { + return null; + } + + return sdkTool({ + description: tool.description, + parameters: tool.parameters, + execute: (args, options) => tool.execute(deps, args, options) + }); + } + return tool; +} + +export function tool({ + description, + parameters, + execute +}: { + description?: string; + parameters: PARAMETERS; + active?: (deps: DEPS) => boolean; + execute: (deps: DEPS, args: z.infer) => PromiseLike; +}): Tool; +export function tool(tool: { + description?: string; + parameters: PARAMETERS; + execute: (args: z.infer) => PromiseLike; +}): Tool; +export function tool(tool: { + description?: string; + parameters: PARAMETERS; + active?: (deps?: DEPS) => boolean; + execute: any; +}): Tool { + if (typeof tool.execute === 'function' && tool.execute.length === 2) { + return { + type: 'function-deps', + description: tool.description, + parameters: tool.parameters, + active: tool.active, + execute: tool.execute + }; + } + return sdkTool(tool); +} diff --git a/apps/dbagent/src/lib/ai/prompts.ts b/apps/dbagent/src/lib/ai/prompts.ts index fdddc1a7..8037f56b 100644 --- a/apps/dbagent/src/lib/ai/prompts.ts +++ b/apps/dbagent/src/lib/ai/prompts.ts @@ -26,6 +26,20 @@ Then use the contents of the playbook as an action plan. Execute the plan step b At the end of your execution, print a summary of the results. `; +export const awsCloudProviderPrompt = ` +All Postgres instances in this project are hosted on AWS, either RDS or Aurora. +When recommending actions, only recommend actions that can be performed on RDS or Aurora. +If you need to know more about the instance, you can use the getClusterInfo tool. +If you want to recommend changes to the instance, provide instructions specific to RDS or Aurora. +`; + +export const gcpCloudProviderPrompt = ` +All Postgres instances in this project are GCP Cloud SQL instances. +When recommending actions, only recommend actions that can be performed on GCP Cloud SQL. +If you need to know more about the instance, you can use the getInstanceInfo tool. +If you want to recommend changes to the instance, provide instructions specific to GCP Cloud SQL. +`; + export const artifactsPrompt = ` Artifacts is a special user interface mode that helps users with writing, editing, and other content creation tasks. When artifact is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the artifacts and visible to the user. diff --git a/apps/dbagent/src/lib/ai/tools/cluster.ts b/apps/dbagent/src/lib/ai/tools/cluster.ts index abe52652..8e43cc9b 100644 --- a/apps/dbagent/src/lib/ai/tools/cluster.ts +++ b/apps/dbagent/src/lib/ai/tools/cluster.ts @@ -8,6 +8,7 @@ import { getPostgresExtensions, getTablesInfo } from '~/lib/tools/dbinfo'; import { getInstanceLogsGCP, getInstanceLogsRDS } from '~/lib/tools/logs'; import { getClusterMetricGCP, getClusterMetricRDS } from '~/lib/tools/metrics'; import { mergeToolsets, Toolset, ToolsetGroup } from './types'; + export function getDBClusterTools( dbAccess: DBAccess, connection: Connection | (() => Promise), From 25142bfc332e3fe3254a48e1cdbc5f549bb2c416 Mon Sep 17 00:00:00 2001 From: urso Date: Mon, 12 May 2025 18:17:51 +0200 Subject: [PATCH 2/5] cont --- apps/dbagent/playground/default/index.ts | 86 +- apps/dbagent/playground/index.ts | 77 +- apps/dbagent/playground/tools/index.ts | 98 +- .../projects/[project]/chats/actions.ts | 21 +- apps/dbagent/src/app/api/chat/route.ts | 101 +- .../components/chat/artifacts/sheet/server.ts | 29 +- .../src/components/monitoring/actions.ts | 18 +- .../src/components/playbooks/action.ts | 35 +- apps/dbagent/src/components/tools/action.ts | 26 +- apps/dbagent/src/evals/lib/chat-runner.ts | 23 +- apps/dbagent/src/lib/ai/agent.ts | 131 +- apps/dbagent/src/lib/ai/model.ts | 269 ++- apps/dbagent/src/lib/ai/tools/artifacts.ts | 357 ++-- apps/dbagent/src/lib/ai/tools/cluster.ts | 323 ++-- apps/dbagent/src/lib/ai/tools/db.ts | 359 ++-- apps/dbagent/src/lib/ai/tools/index.ts | 41 - apps/dbagent/src/lib/ai/tools/playbook.ts | 78 +- apps/dbagent/src/lib/ai/tools/types.ts | 21 - apps/dbagent/src/lib/monitoring/runner.ts | 24 +- pnpm-lock.yaml | 1438 ++++++++++------- 20 files changed, 2085 insertions(+), 1470 deletions(-) delete mode 100644 apps/dbagent/src/lib/ai/tools/types.ts diff --git a/apps/dbagent/playground/default/index.ts b/apps/dbagent/playground/default/index.ts index f2375f06..ea126a46 100644 --- a/apps/dbagent/playground/default/index.ts +++ b/apps/dbagent/playground/default/index.ts @@ -1,29 +1,38 @@ import { Agent } from '@mastra/core/agent'; -import { AnswerRelevancyMetric, PromptAlignmentMetric } from '@mastra/evals/llm'; +import { AnswerRelevancyMetric } from '@mastra/evals/llm'; import { CompletenessMetric } from '@mastra/evals/nlp'; import { CloudProvider } from '~/lib/db/schema'; import { openai } from '@ai-sdk/openai'; +import { RuntimeContext } from '@mastra/core/runtime-context'; +import { z } from 'zod'; import { getChatSystemPrompt, getMonitoringSystemPrompt } from '~/lib/ai/agent'; -import { getBuiltinProviderRegistry } from '~/lib/ai/providers'; +import { getProviderRegistry } from '~/lib/ai/providers'; import { buildPlaygroundTools } from '../tools'; -/* eslint-disable no-process-env */ -const defaultModel = getBuiltinProviderRegistry() - .languageModel(process.env.MASTRA_MODEL ?? 'chat') - .instance(); -const cloudProvider = (process.env.MASTRA_CLOUD_PROVIDER ?? 'aws') as CloudProvider; -const defaultTools = buildPlaygroundTools({ - projectConnection: process.env.MASTRA_PROJECT_CONNECTION ?? undefined, - dbUrl: process.env.MASTRA_DB_URL ?? undefined, - userId: process.env.MASTRA_USER_ID ?? undefined +export const runtimeContextSchema = z.object({ + cloudProvider: z.enum(['other', 'aws', 'gcp']).default('other'), + model_id: z.string().default('chat'), + projectConnection: z.string().optional(), + dbUrl: z.string().optional(), + userId: z.string().optional() }); -/* eslint-enable no-process-env */ - -const chatPrompt = getChatSystemPrompt({ cloudProvider }); -const monitoringPrompt = getMonitoringSystemPrompt({ cloudProvider }); export function createAgents() { + /* eslint-disable no-process-env */ + const runtimeContext = new RuntimeContext( + Object.entries({ + cloudProvider: (process.env.MASTRA_CLOUD_PROVIDER ?? 'aws') as CloudProvider, + model_id: process.env.MASTRA_MODEL ?? 'chat', + projectConnection: process.env.MASTRA_PROJECT_CONNECTION ?? undefined, + dbUrl: process.env.MASTRA_DB_URL ?? undefined, + userId: process.env.MASTRA_USER_ID ?? undefined + }) + ); + // verify runtimeContextSchema + console.log(runtimeContextSchema.parse(runtimeContext)); + /* eslint-enable no-process-env */ + const evals = { completeness: new CompletenessMetric(), relevancy: new AnswerRelevancyMetric(openai('gpt-4o-mini'), { @@ -32,33 +41,44 @@ export function createAgents() { }) }; + const defaultOptions = { + defaultGenerateOptions: { runtimeContext }, + defaultStreamOptions: { runtimeContext }, + model: getModel, + tools: getTools, + evals: { + ...evals + } + }; + // TODO: create agent per supported model (assuming we have the keys)? return { monitoringAgent: new Agent({ name: 'monitoring-agent', - instructions: monitoringPrompt, - model: defaultModel, - tools: defaultTools, - evals: { - ...evals, - 'prompt-alignment': new PromptAlignmentMetric(openai('gpt-4o-mini'), { - instructions: [monitoringPrompt], - scale: 1 // Scale for the final score - }) + ...defaultOptions, + instructions: ({ runtimeContext }) => { + return getMonitoringSystemPrompt({ cloudProvider: runtimeContext.get('cloudProvider') }); } }), chatAgent: new Agent({ name: 'chat-agent', - instructions: chatPrompt, - model: defaultModel, - tools: defaultTools, - evals: { - ...evals, - 'prompt-alignment': new PromptAlignmentMetric(openai('gpt-4o-mini'), { - instructions: [chatPrompt], - scale: 1 // Scale for the final score - }) + ...defaultOptions, + instructions: ({ runtimeContext }) => { + return getChatSystemPrompt({ cloudProvider: runtimeContext.get('cloudProvider') }); } }) }; } + +const getModel = async ({ runtimeContext }: { runtimeContext: RuntimeContext }) => { + const { model_id } = runtimeContextSchema.parse(runtimeContext); + console.log('model_id', model_id); + const registry = await getProviderRegistry(); + return registry.languageModel(model_id || 'chat', true).instance(); +}; + +const getTools = async ({ runtimeContext }: { runtimeContext: RuntimeContext }) => { + const entries = runtimeContextSchema.parse(runtimeContext); + console.log('entries', entries); + return await buildPlaygroundTools(entries); +}; diff --git a/apps/dbagent/playground/index.ts b/apps/dbagent/playground/index.ts index 01f5059b..bd9ae795 100644 --- a/apps/dbagent/playground/index.ts +++ b/apps/dbagent/playground/index.ts @@ -1,5 +1,80 @@ +import { Agent } from '@mastra/core/agent'; +import { AnswerRelevancyMetric } from '@mastra/evals/llm'; +import { CompletenessMetric } from '@mastra/evals/nlp'; +import { CloudProvider } from '~/lib/db/schema'; + +import { openai } from '@ai-sdk/openai'; import { Mastra } from '@mastra/core'; -import { createAgents } from './default'; +import { RuntimeContext } from '@mastra/core/runtime-context'; +import { z } from 'zod'; +import { getChatSystemPrompt, getMonitoringSystemPrompt } from '~/lib/ai/agent'; +import { getProviderRegistry } from '~/lib/ai/providers'; +import { buildPlaygroundTools } from './tools'; + +/* eslint-disable no-process-env */ +const cloudProvider = process.env.MASTRA_CLOUD_PROVIDER ?? ('aws' as CloudProvider); + +export const runtimeContextSchema = z.object({ + cloudProvider: z.enum(['other', 'aws', 'gcp']).default(cloudProvider as any as CloudProvider), + model_id: z.string().default(process.env.MASTRA_MODEL ?? 'chat'), + projectConnection: process.env.MASTRA_PROJECT_CONNECTION + ? z.string().default(process.env.MASTRA_PROJECT_CONNECTION) + : z.string().optional(), + dbUrl: process.env.MASTRA_DB_URL ? z.string().default(process.env.MASTRA_DB_URL) : z.string().optional(), + userId: process.env.MASTRA_USER_ID ? z.string().default(process.env.MASTRA_USER_ID) : z.string().optional() +}); +/* eslint-enable no-process-env */ + +// verify runtimeContextSchema + +export function createAgents() { + const evals = { + completeness: new CompletenessMetric(), + relevancy: new AnswerRelevancyMetric(openai('gpt-4o-mini'), { + uncertaintyWeight: 0.3, // Weight for 'unsure' verdicts + scale: 1 // Scale for the final score + }) + }; + + const defaultOptions = { + model: getModel, + tools: getTools, + evals: { + ...evals + } + }; + + // TODO: create agent per supported model (assuming we have the keys)? + return { + monitoringAgent: new Agent({ + name: 'monitoring-agent', + ...defaultOptions, + instructions: ({ runtimeContext }) => { + return getMonitoringSystemPrompt({ cloudProvider: runtimeContext.get('cloudProvider') }); + } + }), + chatAgent: new Agent({ + name: 'chat-agent', + ...defaultOptions, + instructions: ({ runtimeContext }) => { + return getChatSystemPrompt({ cloudProvider: runtimeContext.get('cloudProvider') }); + } + }) + }; +} + +const getModel = async ({ runtimeContext }: { runtimeContext: RuntimeContext }) => { + const { model_id } = runtimeContextSchema.parse(runtimeContext); + console.log('model_id', model_id); + const registry = await getProviderRegistry(); + return registry.languageModel(model_id || 'chat', true).instance(); +}; + +const getTools = async ({ runtimeContext }: { runtimeContext: RuntimeContext }) => { + const entries = runtimeContextSchema.parse(runtimeContext); + console.log('entries', entries); + return await buildPlaygroundTools(entries); +}; export const mastra = new Mastra({ agents: createAgents() diff --git a/apps/dbagent/playground/tools/index.ts b/apps/dbagent/playground/tools/index.ts index 843d9e42..cef7382a 100644 --- a/apps/dbagent/playground/tools/index.ts +++ b/apps/dbagent/playground/tools/index.ts @@ -2,17 +2,18 @@ import { Tool } from 'ai'; import { builtinPlaybookToolset, commonToolset, - DBSQLTools, getDBClusterTools, - mergeToolsets, - playbookTools, - Toolset + getDBSQLTools, + getPlaybookToolset, + getProjectClusterService, + projectPlaybookService, + targetDBService } from '~/lib/ai/tools'; import { getConnectionByName, getDefaultConnection } from '~/lib/db/connections'; import { DBUserAccess } from '~/lib/db/db'; import { getProjectByName } from '~/lib/db/projects'; import { CloudProvider, Connection } from '~/lib/db/schema'; -import { getTargetDbPool, Pool } from '~/lib/targetdb/db'; +import { getTargetDbPool } from '~/lib/targetdb/db'; type PlaygroundToolsConfig = { projectConnection?: string; @@ -21,8 +22,8 @@ type PlaygroundToolsConfig = { cloudProvider?: CloudProvider; }; -export function buildPlaygroundTools(config: PlaygroundToolsConfig): Record { - const toolsets: Toolset[] = [commonToolset]; +export async function buildPlaygroundTools(config: PlaygroundToolsConfig): Promise> { + const toolsets: Record[] = [commonToolset]; const connString: string | null = config.dbUrl ?? null; // Set up user ID provider if configured @@ -37,79 +38,56 @@ export function buildPlaygroundTools(config: PlaygroundToolsConfig): Record { - const conn = await connGetter(); - return { projectId: conn.projectId }; - }); + const playbookToolset = getPlaybookToolset(projectPlaybookService(db, conn.projectId)); toolsets.push(playbookToolset); hasPlaybookToolset = true; if (!connString) { - const targetDBPoolGetter = createTargetDBPoolGetter(connGetter); - toolsets.push(new DBSQLTools(targetDBPoolGetter)); + const targetDBPool = getTargetDbPool(conn.connectionString); + toolsets.push(getDBSQLTools({ db: targetDBService(targetDBPool) })); hasDBTools = true; } } } if (connString && !hasDBTools) { - toolsets.push(new DBSQLTools(getTargetDbPool(connString))); + toolsets.push(getDBSQLTools({ db: targetDBService(getTargetDbPool(connString)) })); } if (!hasPlaybookToolset) { toolsets.push(builtinPlaybookToolset); } - return mergeToolsets(...toolsets); + return toolsets.reduce((acc, toolset) => { + return { + ...acc, + ...toolset + }; + }, {}); } -function createConnectionGetter( - userId: string, - projectName: string, - connectionName?: string -): () => Promise { - let cachedConnection: Connection | null = null; - return async () => { - if (cachedConnection) { - return cachedConnection; - } - - const db = new DBUserAccess(userId); - - // First find the project by name where user has access - const project = await getProjectByName(db, projectName); - if (!project) { - throw new Error(`Project "${projectName}" not found or access denied`); - } - - const connection = connectionName - ? await getConnectionByName(db, project.id, connectionName) - : await getDefaultConnection(db, project.id); - if (!connection) { - throw new Error( - connectionName - ? `Connection "${connectionName}" not found in project "${projectName}"` - : `No default connection found in project "${projectName}"` - ); - } +export async function getConnection(userId: string, projectName: string, connectionName?: string): Promise { + const db = new DBUserAccess(userId); + const project = await getProjectByName(db, projectName); + if (!project) { + throw new Error(`Project "${projectName}" not found or access denied`); + } - cachedConnection = connection; - return connection; - }; -} + const connection = connectionName + ? await getConnectionByName(db, project.id, connectionName) + : await getDefaultConnection(db, project.id); + if (!connection) { + throw new Error( + connectionName + ? `Connection "${connectionName}" not found in project "${projectName}"` + : `No default connection found in project "${projectName}"` + ); + } -function createTargetDBPoolGetter(connGetter: () => Promise) { - let cachedPool: Pool | null = null; - return async () => { - if (cachedPool) { - return cachedPool; - } - const connection = await connGetter(); - cachedPool = getTargetDbPool(connection.connectionString); - return cachedPool; - }; + return connection; } diff --git a/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts b/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts index c57cc4a5..f6cb0452 100644 --- a/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts +++ b/apps/dbagent/src/app/(main)/projects/[project]/chats/actions.ts @@ -1,34 +1,17 @@ 'use server'; import { Message } from 'ai'; -import { AugmentedLanguageModel } from '~/lib/ai/model'; -import { getProviderRegistry } from '~/lib/ai/providers'; +import { titleModel } from '~/lib/ai/agent'; import { deleteMessagesByChatIdAfterTimestamp, getMessageById } from '~/lib/db/chats'; import { getUserSessionDBAccess } from '~/lib/db/db'; -const titleModel = new AugmentedLanguageModel({ - providerRegistry: getProviderRegistry, - baseModel: 'title', - systemPrompt: `\n - - you will generate a short title based on the first message a user begins a conversation with - - ensure it is not more than 80 characters long - - the title should be a summary of the user's message - - do not use quotes or colons`, - metadata: { - tags: ['internal', 'chat', 'title'] - } -}); - export async function generateTitleFromUserMessage({ message }: { message: Message }) { - const { text: title } = await titleModel.generateText({ - prompt: JSON.stringify(message) - }); + const { text: title } = await titleModel.generateText({ prompt: JSON.stringify(message) }); return title; } export async function deleteTrailingMessages({ id }: { id: string }) { const dbAccess = await getUserSessionDBAccess(); - const [message] = await getMessageById(dbAccess, { id }); if (!message) { throw new Error('Message not found'); diff --git a/apps/dbagent/src/app/api/chat/route.ts b/apps/dbagent/src/app/api/chat/route.ts index 0c9bf1dc..73e344f4 100644 --- a/apps/dbagent/src/app/api/chat/route.ts +++ b/apps/dbagent/src/app/api/chat/route.ts @@ -1,32 +1,14 @@ -import { appendResponseMessages, createDataStreamResponse, DataStreamWriter, UIMessage } from 'ai'; +import { appendResponseMessages, createDataStreamResponse, UIMessage } from 'ai'; import { notFound } from 'next/navigation'; import { NextRequest } from 'next/server'; import { generateTitleFromUserMessage } from '~/app/(main)/projects/[project]/chats/actions'; -import { AugmentedLanguageModel } from '~/lib/ai/model'; -import { - artifactsPrompt, - awsCloudProviderPrompt, - chatSystemPrompt, - commonSystemPrompt, - gcpCloudProviderPrompt -} from '~/lib/ai/prompts'; -import { getLanguageModel, getProviderRegistry } from '~/lib/ai/providers'; -import { - AWSDBClusterTools, - CommonDBClusterTools, - commonToolset, - GCPDBClusterTools, - getDBSQLTools, - getPlaybookToolset -} from '~/lib/ai/tools'; -import { getArtifactTools } from '~/lib/ai/tools/artifacts'; -import { mcpToolset } from '~/lib/ai/tools/user-mcp'; +import { agentModelDeps, chatModel } from '~/lib/ai/agent'; +import { getLanguageModel } from '~/lib/ai/providers'; import { deleteChatById, getChatById, getChatsByProject, saveChat } from '~/lib/db/chats'; import { getConnection } from '~/lib/db/connections'; -import { DBAccess, getUserSessionDBAccess } from '~/lib/db/db'; +import { getUserSessionDBAccess } from '~/lib/db/db'; import { getProjectById } from '~/lib/db/projects'; -import { Connection, Project } from '~/lib/db/schema'; -import { getTargetDbPool, Pool } from '~/lib/targetdb/db'; +import { getTargetDbPool } from '~/lib/targetdb/db'; import { requireUserSession } from '~/utils/route'; export const maxDuration = 60; @@ -49,66 +31,6 @@ export async function GET(request: NextRequest) { return Response.json({ chats }); } -type ChatModelDeps = { - dbAccess: DBAccess; - project: Project; - connection: Connection; - targetDb: Pool; - useArtifacts: boolean; - userId: string; - dataStream?: DataStreamWriter; -}; - -const chatModel = new AugmentedLanguageModel({ - providerRegistry: getProviderRegistry, - baseModel: 'chat', - metadata: { - tags: ['chat'] - }, - systemPrompt: [commonSystemPrompt, chatSystemPrompt], - toolsSets: [ - { tools: mcpToolset.listMCPTools }, - { tools: commonToolset }, - { tools: async ({ targetDb }) => getDBSQLTools(targetDb).toolset() }, - - // Playbook support - { tools: async ({ dbAccess, project }) => getPlaybookToolset(dbAccess, project.id) }, - - // Common cloud provider DB support - { - tools: async ({ dbAccess, connection }) => - new CommonDBClusterTools(dbAccess, () => Promise.resolve(connection)).toolset() - } - ] -}); - -// AWS cloud provider support -chatModel.addSystemPrompts(({ project }) => (project.cloudProvider === 'aws' ? awsCloudProviderPrompt : '')); -chatModel.addToolSet({ - active: (deps?: ChatModelDeps) => deps?.project.cloudProvider === 'aws', - tools: async ({ dbAccess, connection }) => { - return new AWSDBClusterTools(dbAccess, () => Promise.resolve(connection)).toolset(); - } -}); - -// GCP cloud provider support -chatModel.addSystemPrompts(({ project }) => (project.cloudProvider === 'gcp' ? gcpCloudProviderPrompt : '')); -chatModel.addToolSet({ - active: (deps?: ChatModelDeps) => deps?.project.cloudProvider === 'gcp', - tools: async ({ dbAccess, connection }) => { - return new GCPDBClusterTools(dbAccess, () => Promise.resolve(connection)).toolset(); - } -}); - -// Artifacts support -chatModel.addSystemPrompts(({ useArtifacts }) => (useArtifacts ? artifactsPrompt : '')); -chatModel.addToolSet({ - active: (deps?: ChatModelDeps) => !!deps?.useArtifacts && !!deps?.dataStream, - tools: async ({ dbAccess, userId, project, dataStream }) => { - return getArtifactTools({ dbAccess, userId, projectId: project.id, dataStream: dataStream! }); - } -}); - export async function POST(request: Request) { try { const { id, messages, connectionId, model: modelId, useArtifacts } = await request.json(); @@ -142,15 +64,16 @@ export async function POST(request: Request) { execute: async (dataStream) => { const result = await chatModel.streamText({ model: model.instance(), - deps: { - dbAccess: await getUserSessionDBAccess(), - project, - connection, + deps: agentModelDeps({ targetDb, - useArtifacts, + dbAccess, + connection, + cloudProvider: project.cloudProvider, userId, + withMCP: true, + withArtifacts: useArtifacts, dataStream - }, + }), messages, maxSteps: 20, metadata: { diff --git a/apps/dbagent/src/components/chat/artifacts/sheet/server.ts b/apps/dbagent/src/components/chat/artifacts/sheet/server.ts index 7ddee6e4..ce60c777 100644 --- a/apps/dbagent/src/components/chat/artifacts/sheet/server.ts +++ b/apps/dbagent/src/components/chat/artifacts/sheet/server.ts @@ -1,6 +1,5 @@ -import { streamObject } from 'ai'; import { z } from 'zod'; -import { getModelInstance } from '~/lib/ai/agent'; +import { commonModel } from '~/lib/ai/agent'; import { sheetPrompt, updateDocumentPrompt } from '~/lib/ai/prompts'; import { createDocumentHandler } from '../server'; @@ -9,15 +8,11 @@ export const sheetDocumentHandler = createDocumentHandler<'sheet'>({ onCreateDocument: async ({ title, dataStream }) => { let draftContent = ''; - const { fullStream } = streamObject({ - model: await getModelInstance('chat'), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['artifact', 'sheet', 'create'] - } - }, + const { fullStream } = await commonModel.streamObject({ system: sheetPrompt, + metadata: { + tags: ['artifact', 'sheet', 'create'] + }, prompt: title, schema: z.object({ csv: z.string().describe('CSV data') @@ -52,19 +47,15 @@ export const sheetDocumentHandler = createDocumentHandler<'sheet'>({ onUpdateDocument: async ({ document, description, dataStream }) => { let draftContent = ''; - const { fullStream } = streamObject({ - model: await getModelInstance('chat'), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['artifact', 'sheet', 'update'] - } - }, + const { fullStream } = await commonModel.streamObject({ system: updateDocumentPrompt(document.content, 'sheet'), prompt: description, schema: z.object({ csv: z.string() - }) + }), + metadata: { + tags: ['artifact', 'sheet', 'update'] + } }); for await (const delta of fullStream) { diff --git a/apps/dbagent/src/components/monitoring/actions.ts b/apps/dbagent/src/components/monitoring/actions.ts index dd4b298c..34e1fd08 100644 --- a/apps/dbagent/src/components/monitoring/actions.ts +++ b/apps/dbagent/src/components/monitoring/actions.ts @@ -1,8 +1,7 @@ 'use server'; import { auth } from '~/auth'; -import { AugmentedLanguageModel } from '~/lib/ai/model'; -import { getProviderRegistry } from '~/lib/ai/providers'; +import { commonModel } from '~/lib/ai/agent'; import { getUserDBAccess, getUserSessionDBAccess } from '~/lib/db/db'; import { getScheduleRuns } from '~/lib/db/schedule-runs'; import { @@ -17,19 +16,16 @@ import { Schedule, ScheduleInsert, ScheduleRun } from '~/lib/db/schema'; import { scheduleGetNextRun, utcToLocalDate } from '~/lib/monitoring/scheduler'; import { listPlaybooks } from '~/lib/tools/playbooks'; -const utilModel = new AugmentedLanguageModel({ - providerRegistry: getProviderRegistry, - baseModel: 'openai:gpt-4o', - metadata: { - tags: ['internal', 'monitoring', 'cron'] - } -}); - export async function generateCronExpression(description: string): Promise { const prompt = `Generate a cron expression for the following schedule description: "${description}". Return strictly the cron expression, no quotes or anything else.`; - const { text } = await utilModel.generateText({ prompt }); + const { text } = await commonModel.generateText({ + prompt, + metadata: { + tags: ['internal', 'monitoring', 'cron'] + } + }); return text.trim(); } diff --git a/apps/dbagent/src/components/playbooks/action.ts b/apps/dbagent/src/components/playbooks/action.ts index bfc43371..e1210d72 100644 --- a/apps/dbagent/src/components/playbooks/action.ts +++ b/apps/dbagent/src/components/playbooks/action.ts @@ -1,8 +1,7 @@ 'use server'; -import { openai } from '@ai-sdk/openai'; -import { generateText } from 'ai'; import { auth } from '~/auth'; +import { commonModel } from '~/lib/ai/agent'; import { dbCreatePlaybook, dbDeletePlaybook, dbUpdatePlaybook } from '~/lib/db/custom-playbooks'; import { getUserDBAccess, getUserSessionDBAccess } from '~/lib/db/db'; @@ -31,29 +30,17 @@ export async function actionGeneratePlaybookContent(name: string, description: s Please generate the playbook content:`; - const { text } = await generateText({ - model: openai('gpt-4o'), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['playbook', 'generate'] - } + const { text } = await commonModel.generateText({ + modelSettings: { + //lower values for temperature and topP are more deterministic higher are more creative(max 2.0) + //0.1 is deterministic, 0.7 is creative + temperature: 0.2, + topP: 0.1, + maxTokens: 1000 }, - messages: [ - { - role: 'system', - content: 'You are a database expert who creates detailed, step-by-step playbooks for database operations.' - }, - { - role: 'user', - content: prompt - } - ], - //lower values for temperature and topP are more deterministic higher are more creative(max 2.0) - //0.1 is deterministic, 0.7 is creative - temperature: 0.2, - topP: 0.1, - maxTokens: 1000 + metadata: { tags: ['playbook', 'generate'] }, + system: 'You are a database expert who creates detailed, step-by-step playbooks for database operations.', + prompt }); try { diff --git a/apps/dbagent/src/components/tools/action.ts b/apps/dbagent/src/components/tools/action.ts index 627697ea..c256c6e6 100644 --- a/apps/dbagent/src/components/tools/action.ts +++ b/apps/dbagent/src/components/tools/action.ts @@ -1,10 +1,6 @@ 'use server'; -import { getDBClusterTools } from '~/lib/ai/tools/cluster'; -import { commonToolset } from '~/lib/ai/tools/common'; -import { getDBSQLTools } from '~/lib/ai/tools/db'; -import { getPlaybookToolset } from '~/lib/ai/tools/playbook'; -import { mergeToolsets } from '~/lib/ai/tools/types'; +import { agentModelDeps, chatModel } from '~/lib/ai/agent'; import { mcpToolset } from '~/lib/ai/tools/user-mcp'; import { getConnection, listConnections } from '~/lib/db/connections'; import { getUserSessionDBAccess } from '~/lib/db/db'; @@ -43,20 +39,26 @@ export async function actionGetBuiltInAndCustomTools(connectionId: string): Prom export async function actionGetBuiltInTools(connectionId: string): Promise { try { await requireUserSession(); + const userId = await requireUserSession(); const dbAccess = await getUserSessionDBAccess(); const connection = await getConnection(dbAccess, connectionId); if (!connection) { throw new Error('Connection not found'); } - const targetDb = getTargetDbPool(connection.connectionString); - const dbTools = getDBSQLTools(targetDb); + const chatTools = await chatModel.getTools( + agentModelDeps({ + targetDb: getTargetDbPool(connection.connectionString), + dbAccess, + connection, + userId, + cloudProvider: 'aws', + withArtifacts: false, + withMCP: false + }) + ); - const clusterTools = getDBClusterTools(dbAccess, connection, 'aws'); // Default to AWS for now - const playbookToolset = getPlaybookToolset(dbAccess, connection.projectId); - const mergedTools = mergeToolsets(commonToolset, playbookToolset, dbTools, clusterTools); - - return Object.entries(mergedTools).map(([name, tool]) => ({ + return Object.entries(chatTools).map(([name, tool]) => ({ name, description: tool.description || 'No description available', isBuiltIn: true diff --git a/apps/dbagent/src/evals/lib/chat-runner.ts b/apps/dbagent/src/evals/lib/chat-runner.ts index 3fab4297..bc6ffb95 100644 --- a/apps/dbagent/src/evals/lib/chat-runner.ts +++ b/apps/dbagent/src/evals/lib/chat-runner.ts @@ -1,8 +1,8 @@ -import { CoreMessage, generateText, Message as SDKMessage } from 'ai'; +import { CoreMessage, Message as SDKMessage } from 'ai'; import { ExpectStatic } from 'vitest'; import { generateUUID } from '~/components/chat/utils'; -import { getChatSystemPrompt, getModelInstance } from '~/lib/ai/agent'; -import { getTools } from '~/lib/ai/tools'; +import { agentModelDeps, chatModel, getChatSystemPrompt } from '~/lib/ai/agent'; +import { getUserDBAccess } from '~/lib/db/db'; import { Connection, Project } from '~/lib/db/schema'; import { env } from '~/lib/env/eval'; import { getTargetDbPool } from '~/lib/targetdb/db'; @@ -31,14 +31,23 @@ export const evalChat = async ({ isDefault: true }; + const userId = 'evalUser'; const targetDb = getTargetDbPool(connection.connectionString); + const dbAccess = await getUserDBAccess(userId); try { - const tools = await getTools({ project, connection, targetDb, userId: 'evalUser' }); - const response = await generateText({ - model: await getModelInstance(env.CHAT_MODEL), + const response = await chatModel.generateText({ + model: env.CHAT_MODEL, system: getChatSystemPrompt({ cloudProvider: project.cloudProvider }), maxSteps: 20, - tools, + deps: agentModelDeps({ + withMCP: true, + targetDb, + dbAccess, + connection, + cloudProvider: project.cloudProvider, + userId, + withArtifacts: false + }), messages }); traceVercelAiResponse(response, expect); diff --git a/apps/dbagent/src/lib/ai/agent.ts b/apps/dbagent/src/lib/ai/agent.ts index 926704d0..247c68a8 100644 --- a/apps/dbagent/src/lib/ai/agent.ts +++ b/apps/dbagent/src/lib/ai/agent.ts @@ -1,5 +1,8 @@ -import { LanguageModel } from 'ai'; -import { CloudProvider } from '../db/schema'; +import { DataStreamWriter, LanguageModel } from 'ai'; +import { Pool } from 'pg'; +import { DBAccess } from '../db/db'; +import { CloudProvider, Connection } from '../db/schema'; +import { AugmentedLanguageModel, ToolSet } from './model'; import { artifactsPrompt, awsCloudProviderPrompt, @@ -8,7 +11,129 @@ import { gcpCloudProviderPrompt, monitoringSystemPrompt } from './prompts'; -import { getLanguageModel, getLanguageModelWithFallback, ModelWithFallback } from './providers'; +import { getLanguageModel, getLanguageModelWithFallback, getProviderRegistry, ModelWithFallback } from './providers'; +import { commonToolset } from './tools'; +import { ArtifactsService, getArtifactTools, projectArtifactService } from './tools/artifacts'; +import { ClusterService, getDBClusterTools, getProjectClusterService } from './tools/cluster'; +import { DBService, getDBSQLTools, targetDBService } from './tools/db'; +import { getPlaybookToolset, PlaybookService, projectPlaybookService } from './tools/playbook'; +import { mcpToolset } from './tools/user-mcp'; + +export type AgentModelDeps = { + withMCP?: boolean; + targetDb: DBService; + playbookService: PlaybookService; + clusterService: ClusterService; + artifactsService?: ArtifactsService; +}; + +export function agentModelDeps({ + withMCP, + targetDb, + dbAccess, + connection, + userId, + cloudProvider, + withArtifacts, + dataStream +}: { + withMCP: boolean; + targetDb: Pool; + dbAccess: DBAccess; + connection: Connection; + cloudProvider: CloudProvider; + userId: string; + withArtifacts?: boolean; + dataStream?: DataStreamWriter; +}): AgentModelDeps { + return { + withMCP, + targetDb: targetDBService(targetDb), + playbookService: projectPlaybookService(dbAccess, connection.projectId), + clusterService: getProjectClusterService(dbAccess, connection, cloudProvider), + artifactsService: + withArtifacts && dataStream + ? projectArtifactService({ + userId: userId, + projectId: connection.projectId, + dataStream: dataStream, + dbAccess: dbAccess + }) + : undefined + }; +} + +export const toolsets = [ + { + active: (deps?: { withMCP: boolean }) => deps?.withMCP || true, + tools: mcpToolset.listMCPTools + }, + { tools: commonToolset }, + + // Target DB support + { tools: async ({ targetDb }: { targetDb: DBService }) => getDBSQLTools({ db: targetDb }) }, + + // Playbook support + { tools: async ({ playbookService }: { playbookService: PlaybookService }) => getPlaybookToolset(playbookService) }, + + // Cloud provider support + { + systemPrompt: ({ clusterService }: { clusterService: ClusterService }) => + clusterService.type === 'aws' + ? awsCloudProviderPrompt + : clusterService.type === 'gcp' + ? gcpCloudProviderPrompt + : '', + tools: async ({ clusterService }: { clusterService: ClusterService }) => getDBClusterTools(clusterService) + }, + + // Artifacts support + { + active: (deps?: { artifactsService: ArtifactsService }) => !!deps?.artifactsService, + systemPrompt: artifactsPrompt, + tools: async ({ artifactsService }: { artifactsService: ArtifactsService }) => { + return getArtifactTools({ tools: artifactsService }); + } + } +]; + +export const commonModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'chat' +}); + +export const chatModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'chat', + metadata: { + tags: ['chat'] + }, + systemPrompt: [commonSystemPrompt, chatSystemPrompt], + toolsets: toolsets as ToolSet[] +}); + +export const monitoringModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'chat', + metadata: { + tags: ['monitoring'] + }, + systemPrompt: [commonSystemPrompt, monitoringSystemPrompt], + toolsets: toolsets as ToolSet[] +}); + +export const titleModel = new AugmentedLanguageModel({ + providerRegistry: getProviderRegistry, + baseModel: 'title', + systemPrompt: `\n + - you will generate a short title based on the first message a user begins a conversation with + - ensure it is not more than 80 characters long + - the title should be a summary of the user's message + - do not use quotes or colons`, + metadata: { + tags: ['internal', 'chat', 'title'] + } +}); function getCloudProviderPrompt(cloudProvider: string): string { switch (cloudProvider) { diff --git a/apps/dbagent/src/lib/ai/model.ts b/apps/dbagent/src/lib/ai/model.ts index f2e2f413..dafc4d44 100644 --- a/apps/dbagent/src/lib/ai/model.ts +++ b/apps/dbagent/src/lib/ai/model.ts @@ -1,23 +1,26 @@ import { AttributeValue } from '@opentelemetry/api'; import { CoreMessage, - DataStreamOptions, - DataStreamWriter, + DeepPartial, + generateObject, + GenerateObjectResult, generateText, GenerateTextResult, LanguageModel, Message, ProviderMetadata, + Schema, Tool as SDKTool, tool as sdkTool, ToolSet as SDKToolSet, smoothStream, + streamObject, + StreamObjectResult, streamText, StreamTextOnChunkCallback, StreamTextOnErrorCallback, StreamTextOnFinishCallback, - StreamTextOnStepFinishCallback, - TextStreamPart + StreamTextOnStepFinishCallback } from 'ai'; function generateUUID(): string { @@ -38,9 +41,23 @@ interface Telemetry { metadata?: Record; } +type ModelSettings = { + maxTokens?: number; + temperature?: number; + topP?: number; + topK?: number; + presencePenalty?: number; + frequencyPenalty?: number; + stopSequences?: string[]; + seed?: number; + maxRetries?: number; + abortSignal?: AbortSignal; +}; + type StreamTextOptions = PromptOptions & Telemetry & { model?: LanguageModel | string; + modelSettings?: ModelSettings; deps?: DEPS; maxSteps?: number; onChunk?: StreamTextOnChunkCallback; @@ -50,19 +67,38 @@ type StreamTextOptions = PromptOptions & experimental_providerMetadata?: ProviderMetadata; }; -type AsyncIterableStream = AsyncIterable & ReadableStream; - -interface StreamResult { - readonly fullStream: AsyncIterableStream>; - consumeStream(options?: { onError?: (error: unknown) => void }): Promise; - mergeIntoDataStream(dataStream: DataStreamWriter, options?: DataStreamOptions): void; -} +type StreamResult = Awaited>; type GenerateTextOptions = PromptOptions & Telemetry & { model?: LanguageModel | string; + modelSettings?: ModelSettings; + deps?: DEPS; + maxSteps?: number; + }; + +type GenerateObjectOptions = PromptOptions & + Telemetry & { + model?: LanguageModel | string; + modelSettings?: ModelSettings; + deps?: DEPS; + maxSteps?: number; + schema: z.Schema; + schemaName?: string; + schemaDescription?: string; + mode?: 'auto' | 'json' | 'tool'; + }; + +type StreamObjectOptions = PromptOptions & + Telemetry & { + model?: LanguageModel | string; + modelSettings?: ModelSettings; deps?: DEPS; maxSteps?: number; + schema: z.Schema | Schema; + schemaName?: string; + schemaDescription?: string; + mode?: 'auto' | 'json' | 'tool'; }; interface Model { @@ -83,13 +119,13 @@ export class AugmentedLanguageModel implements Model { providerRegistry, metadata, systemPrompt, - toolsSets + toolsets }: { baseModel?: LanguageModel | string; providerRegistry?: ProviderRegistry | (() => Promise); metadata?: Record; systemPrompt?: Prompt | Prompt[]; - toolsSets?: ToolSet | ToolSet[]; + toolsets?: ToolSet | ToolSet[]; }) { if (!baseModel && !providerRegistry) { throw new Error('baseModel and providerRegistry are required.'); @@ -104,11 +140,11 @@ export class AugmentedLanguageModel implements Model { this.#systemPrompt.push(systemPrompt); } } - if (toolsSets) { - if (Array.isArray(toolsSets)) { - this.#toolSets.push(...toolsSets); + if (toolsets) { + if (Array.isArray(toolsets)) { + this.#toolSets.push(...toolsets); } else { - this.#toolSets.push(toolsSets); + this.#toolSets.push(toolsets); } } } @@ -154,17 +190,25 @@ export class AugmentedLanguageModel implements Model { return model as LanguageModel; } - private async getTools(deps?: DEPS): Promise { + async getTools(deps?: DEPS): Promise { + const { tools } = await this.getToolsWithPrompt(deps); + return tools; + } + + private async getToolsWithPrompt(deps?: DEPS): Promise<{ tools: SDKToolSet; systemPrompts: Prompt[] }> { + const systemPrompts: Prompt[] = []; const toolsetTools: Record> = ( await Promise.all( this.#toolSets.map(async (toolSet) => { if (toolSet.active && !toolSet.active(deps)) { return {}; } - if (typeof toolSet.tools === 'function') { - return deps ? await toolSet.tools(deps) : {}; + + const tools = typeof toolSet.tools !== 'function' ? toolSet.tools : deps ? await toolSet.tools(deps) : {}; + if (toolSet.systemPrompt && Object.keys(tools).length > 0) { + systemPrompts.push(toolSet.systemPrompt); } - return toolSet.tools; + return tools; }) ) ).reduce((acc: Record>, tools: Record>) => { @@ -173,26 +217,36 @@ export class AugmentedLanguageModel implements Model { const allTools = { ...toolsetTools, ...this.#tools }; if (!deps) { - return Object.fromEntries( - Object.entries(allTools) - .filter(([_, t]) => t.type !== 'function-deps') - .map(([key, t]) => [key, t as SDKTool]) - ); + return { + systemPrompts, + tools: Object.fromEntries( + Object.entries(allTools) + .filter(([_, t]) => t.type !== 'function-deps') + .map(([key, t]) => [key, t as SDKTool]) + ) + }; } - return Object.fromEntries( - Object.entries(allTools) - .map(([key, t]) => [key, toSDKTool(t, deps)] as [string, SDKTool | null]) - .filter(([, tool]) => tool !== null) - ) as SDKToolSet; + return { + systemPrompts, + tools: Object.fromEntries( + Object.entries(allTools) + .map(([key, t]) => [key, toSDKTool(t, deps)] as [string, SDKTool | null]) + .filter(([, tool]) => tool !== null) + ) as SDKToolSet + }; } - private getSystemPrompt(deps?: DEPS, system?: string) { - let systemPrompt = generatePrompt(this.#systemPrompt, deps); + private getSystemPrompt(deps?: DEPS, system?: string, toolSystemPrompts?: Prompt[]) { + const systemPrompts = [generatePrompt(this.#systemPrompt, deps)]; if (system) { - systemPrompt = joinPrompts([systemPrompt, system]); + systemPrompts.push(system); } - return systemPrompt; + if (toolSystemPrompts) { + const toolPrompts = generatePrompt(toolSystemPrompts, deps); + systemPrompts.push(toolPrompts); + } + return joinPrompts(systemPrompts); } private getTelemetryConfig(metadata?: Record) { @@ -204,31 +258,30 @@ export class AugmentedLanguageModel implements Model { } }; } - async streamText({ - model, - deps, - system, - prompt, - messages, - maxSteps, - onChunk, - onError, - onFinish, - onStepFinish, - metadata - }: StreamTextOptions): Promise { - const systemPrompt = this.getSystemPrompt(deps, system); - const tools: SDKToolSet = await this.getTools(deps); + async streamText(options: StreamTextOptions): Promise { + const { + model, + modelSettings, + deps, + system, + prompt, + messages, + metadata, + maxSteps, + onChunk, + onError, + onFinish, + onStepFinish + } = options; + + const { tools, systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); + const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); const llm = await this.getModel(model); const telemetry = this.getTelemetryConfig(metadata); - console.log('systemPrompt', systemPrompt); - console.log('prompt', prompt); - console.log('messages', messages); - console.log('tools', tools); - return streamText({ model: llm, + ...modelSettings, system: systemPrompt, prompt, messages, @@ -245,22 +298,16 @@ export class AugmentedLanguageModel implements Model { }); } - async generateText({ - model, - deps, - system, - prompt, - messages, - maxSteps, - metadata - }: GenerateTextOptions): Promise> { - const systemPrompt = this.getSystemPrompt(deps, system); - const tools: SDKToolSet = await this.getTools(deps); + async generateText(options: GenerateTextOptions): Promise> { + const { model, modelSettings, deps, system, prompt, messages, metadata, maxSteps } = options; + const { tools, systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); + const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); const llm = await this.getModel(model); const telemetry = this.getTelemetryConfig(metadata); return generateText({ model: llm, + ...modelSettings, system: systemPrompt, prompt, messages, @@ -269,6 +316,93 @@ export class AugmentedLanguageModel implements Model { experimental_telemetry: telemetry }); } + + async generateObject(options: GenerateObjectOptions): Promise> { + const { + model, + modelSettings, + deps, + system, + prompt, + messages, + metadata, + schema, + schemaName, + schemaDescription, + mode + } = options; + const { systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); + const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); + const llm = await this.getModel(model); + const telemetry = this.getTelemetryConfig(metadata); + + return generateObject({ + model: llm, + ...modelSettings, + system: systemPrompt, + messages, + prompt, + schema: schema, + schemaName, + schemaDescription, + mode, + experimental_telemetry: telemetry + }); + } + + async streamObject( + options: StreamObjectOptions & { + output?: 'object' | undefined; + } + ): Promise, OBJECT, never>>; + async streamObject( + options: StreamObjectOptions & { + output: 'array'; + } + ): Promise, Array, AsyncIterable & ReadableStream>>; + + async streamObject( + options: StreamObjectOptions & { output?: 'object' } + ): Promise, OBJECT, never>>; + async streamObject( + options: StreamObjectOptions> & { output: 'array' } + ): Promise, Array, AsyncIterable & ReadableStream>>; + async streamObject( + options: StreamObjectOptions & { output?: 'object' | 'array' } + ): Promise> { + const { + model, + output = 'object', + modelSettings, + deps, + system, + prompt, + messages, + metadata, + schema, + schemaName, + schemaDescription, + mode + } = options; + const { systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); + const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); + const llm = await this.getModel(model); + const telemetry = this.getTelemetryConfig(metadata); + + return streamObject({ + model: llm, + output: output as any, + ...modelSettings, + system: systemPrompt, + messages, + prompt, + schema, + schemaName, + schemaDescription, + mode, + experimental_telemetry: telemetry + }) as unknown as StreamObjectResult; + } } type Prompt = StaticPrompt | PromptFunction; @@ -311,8 +445,9 @@ type Tool, options: ToolExecutionOptions) => PromiseLike; }; -type ToolSet = { +export type ToolSet = { active?: (deps?: DEPS) => boolean; + systemPrompt?: Prompt; tools: Record> | ((deps: DEPS) => Promise>>); }; diff --git a/apps/dbagent/src/lib/ai/tools/artifacts.ts b/apps/dbagent/src/lib/ai/tools/artifacts.ts index 3c7285c0..1eb2f99d 100644 --- a/apps/dbagent/src/lib/ai/tools/artifacts.ts +++ b/apps/dbagent/src/lib/ai/tools/artifacts.ts @@ -1,33 +1,57 @@ -import { DataStreamWriter, streamObject, tool, Tool } from 'ai'; +import { DataStreamWriter, tool, Tool } from 'ai'; import { z } from 'zod'; +import { ArtifactKind } from '~/components/chat/artifacts/artifact'; import { artifactKinds, documentHandlersByArtifactKind } from '~/components/chat/artifacts/server'; import { generateUUID } from '~/components/chat/utils'; import { getDocumentById, saveSuggestions } from '~/lib/db/chats'; import { DBAccess } from '~/lib/db/db'; import { ArtifactSuggestion } from '~/lib/db/schema'; -import { getModelInstance } from '../agent'; +import { commonModel } from '../agent'; interface ArtifactToolProps { + tools: ArtifactsService; +} + +export type DocumentOpResult = + | { + id: string; + title: string; + kind: ArtifactKind; + } + | { + error: string; + }; + +export interface ArtifactsService { + createDocument({ title, kind }: { title: string; kind: (typeof artifactKinds)[number] }): Promise; + updateDocument({ id, description }: { id: string; description: string }): Promise; + requestSuggestions({ documentId }: { documentId: string }): Promise; +} + +export function getArtifactTools({ tools }: ArtifactToolProps): Record { + return { + createDocument: createDocument({ tools }), + updateDocument: updateDocument({ tools }), + requestSuggestions: requestSuggestions({ tools }) + }; +} + +type ArtifactProjectProps = { userId: string; projectId: string; dataStream: DataStreamWriter; dbAccess: DBAccess; -} +}; -export function getArtifactTools({ userId, projectId, dataStream, dbAccess }: ArtifactToolProps): Record { +export function projectArtifactService(project: ArtifactProjectProps): ArtifactsService { return { - createDocument: createDocument({ userId, projectId, dataStream, dbAccess }), - updateDocument: updateDocument({ userId, projectId, dataStream, dbAccess }), - requestSuggestions: requestSuggestions({ - userId, - projectId, - dataStream, - dbAccess - }) + createDocument: async ({ title, kind }) => await execCreateDocument(project, { title, kind }), + updateDocument: async ({ id, description }) => await execUpdateDocument(project, { id, description }), + requestSuggestions: async ({ documentId }) => await execRequestSuggestions(project, { documentId }) }; } -export const createDocument = ({ userId, projectId, dataStream, dbAccess }: ArtifactToolProps) => +export const createDocument = ({ tools }: ArtifactToolProps) => tool({ description: 'Create a document for a writing or content creation activities. This tool will call other functions that will generate the contents of the document based on the title and kind.', @@ -36,57 +60,67 @@ export const createDocument = ({ userId, projectId, dataStream, dbAccess }: Arti kind: z.enum(artifactKinds) }), execute: async ({ title, kind }) => { - const id = generateUUID(); - - dataStream.writeData({ - type: 'kind', - content: kind - }); - - dataStream.writeData({ - type: 'id', - content: id - }); - - dataStream.writeData({ - type: 'title', - content: title - }); - - dataStream.writeData({ - type: 'clear', - content: '' - }); - - const documentHandler = documentHandlersByArtifactKind.find( - (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === kind - ); - - if (!documentHandler) { - throw new Error(`No document handler found for kind: ${kind}`); - } - - await documentHandler.onCreateDocument({ - id, - title, - dataStream, - userId, - projectId, - dbAccess - }); - - dataStream.writeData({ type: 'finish', content: '' }); - + const result = await tools.createDocument({ title, kind }); return { - id, - title, - kind, + ...result, content: 'A document was created and is now visible to the user.' }; } }); -export const updateDocument = ({ userId, projectId, dataStream, dbAccess }: ArtifactToolProps) => +const execCreateDocument = async ( + { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, + { title, kind }: { title: string; kind: ArtifactKind } +): Promise => { + const id = generateUUID(); + + dataStream.writeData({ + type: 'kind', + content: kind + }); + + dataStream.writeData({ + type: 'id', + content: id + }); + + dataStream.writeData({ + type: 'title', + content: title + }); + + dataStream.writeData({ + type: 'clear', + content: '' + }); + + const documentHandler = documentHandlersByArtifactKind.find( + (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === kind + ); + + if (!documentHandler) { + throw new Error(`No document handler found for kind: ${kind}`); + } + + await documentHandler.onCreateDocument({ + id, + title, + dataStream, + userId, + projectId, + dbAccess + }); + + dataStream.writeData({ type: 'finish', content: '' }); + + return { + id, + title, + kind + }; +}; + +export const updateDocument = ({ tools }: ArtifactToolProps) => tool({ description: 'Update a document with the given description.', parameters: z.object({ @@ -94,117 +128,138 @@ export const updateDocument = ({ userId, projectId, dataStream, dbAccess }: Arti description: z.string().describe('The description of changes that need to be made') }), execute: async ({ id, description }) => { - const document = await getDocumentById(dbAccess, { id }); - - if (!document) { - return { - error: 'Document not found' - }; + const result = await tools.updateDocument({ id, description }); + if ('error' in result) { + return result; } + return { + ...result, + content: 'The document has been updated successfully.' + }; + } + }); - dataStream.writeData({ - type: 'clear', - content: document.title - }); +const execUpdateDocument = async ( + { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, + { id, description }: { id: string; description: string } +): Promise => { + const document = await getDocumentById(dbAccess, { id }); - const documentHandler = documentHandlersByArtifactKind.find( - (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === document.kind - ); + if (!document) { + return { + error: 'Document not found' + }; + } - if (!documentHandler) { - throw new Error(`No document handler found for kind: ${document.kind}`); - } + dataStream.writeData({ + type: 'clear', + content: document.title + }); - await documentHandler.onUpdateDocument({ - document, - description, - dataStream, - userId, - projectId, - dbAccess - }); + const documentHandler = documentHandlersByArtifactKind.find( + (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === document.kind + ); - dataStream.writeData({ type: 'finish', content: '' }); + if (!documentHandler) { + throw new Error(`No document handler found for kind: ${document.kind}`); + } - return { - id, - title: document.title, - kind: document.kind, - content: 'The document has been updated successfully.' - }; - } + await documentHandler.onUpdateDocument({ + document, + description, + dataStream, + userId, + projectId, + dbAccess }); -export const requestSuggestions = ({ userId, projectId, dataStream, dbAccess }: ArtifactToolProps) => + dataStream.writeData({ type: 'finish', content: '' }); + + return { + id, + title: document.title, + kind: document.kind + }; +}; + +export const requestSuggestions = ({ tools }: ArtifactToolProps) => tool({ description: 'Request suggestions for a document', parameters: z.object({ documentId: z.string().describe('The ID of the document to request edits') }), execute: async ({ documentId }) => { - const document = await getDocumentById(dbAccess, { id: documentId }); - - if (!document || !document.content) { - return { - error: 'Document not found' - }; - } - - const suggestions: Array> = []; - - const { elementStream } = streamObject({ - model: await getModelInstance('chat'), - experimental_telemetry: { - isEnabled: true, - metadata: { - tags: ['artifact', 'suggestion'] - } - }, - system: - 'You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.', - prompt: document.content, - output: 'array', - schema: z.object({ - originalSentence: z.string().describe('The original sentence'), - suggestedSentence: z.string().describe('The suggested sentence'), - description: z.string().describe('The description of the suggestion') - }) - }); - - for await (const element of elementStream) { - const suggestion = { - originalText: element.originalSentence, - suggestedText: element.suggestedSentence, - description: element.description, - id: generateUUID(), - documentId: documentId, - isResolved: false, - projectId - }; - - dataStream.writeData({ - type: 'suggestion', - content: suggestion - }); - - suggestions.push(suggestion); + const result = await tools.requestSuggestions({ documentId }); + if ('error' in result) { + return result; } - - await saveSuggestions(dbAccess, { - suggestions: suggestions.map((suggestion) => ({ - ...suggestion, - userId, - projectId, - createdAt: new Date(), - documentCreatedAt: document.createdAt - })) - }); - return { - id: documentId, - title: document.title, - kind: document.kind, + ...result, message: 'Suggestions have been added to the document' }; } }); + +export const execRequestSuggestions = async ( + { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, + { documentId }: { documentId: string } +): Promise => { + const document = await getDocumentById(dbAccess, { id: documentId }); + if (!document || !document.content) { + return { + error: 'Document not found' + }; + } + + const suggestions: Array> = []; + + const { elementStream } = await commonModel.streamObject({ + system: + 'You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.', + prompt: document.content, + output: 'array', + schema: z.object({ + originalSentence: z.string().describe('The original sentence'), + suggestedSentence: z.string().describe('The suggested sentence'), + description: z.string().describe('The description of the suggestion') + }), + metadata: { + tags: ['artifact', 'suggestion'] + } + }); + + for await (const element of elementStream) { + const suggestion = { + originalText: element.originalSentence, + suggestedText: element.suggestedSentence, + description: element.description, + id: generateUUID(), + documentId: documentId, + isResolved: false, + projectId + }; + + dataStream.writeData({ + type: 'suggestion', + content: suggestion + }); + + suggestions.push(suggestion); + } + + await saveSuggestions(dbAccess, { + suggestions: suggestions.map((suggestion) => ({ + ...suggestion, + userId, + projectId, + createdAt: new Date(), + documentCreatedAt: document.createdAt + })) + }); + + return { + id: documentId, + title: document.title, + kind: document.kind + }; +}; diff --git a/apps/dbagent/src/lib/ai/tools/cluster.ts b/apps/dbagent/src/lib/ai/tools/cluster.ts index 8e43cc9b..62d4e3fd 100644 --- a/apps/dbagent/src/lib/ai/tools/cluster.ts +++ b/apps/dbagent/src/lib/ai/tools/cluster.ts @@ -3,92 +3,87 @@ import { z } from 'zod'; import { getClusterByConnection } from '~/lib/db/aws-clusters'; import { DBAccess } from '~/lib/db/db'; import { getInstanceByConnection } from '~/lib/db/gcp-instances'; -import { CloudProvider, Connection } from '~/lib/db/schema'; +import { AWSCluster, CloudProvider, Connection, GCPInstance } from '~/lib/db/schema'; import { getPostgresExtensions, getTablesInfo } from '~/lib/tools/dbinfo'; import { getInstanceLogsGCP, getInstanceLogsRDS } from '~/lib/tools/logs'; import { getClusterMetricGCP, getClusterMetricRDS } from '~/lib/tools/metrics'; -import { mergeToolsets, Toolset, ToolsetGroup } from './types'; -export function getDBClusterTools( - dbAccess: DBAccess, - connection: Connection | (() => Promise), - cloudProvider: CloudProvider -): Record { - const connectionGetter = typeof connection === 'function' ? connection : () => Promise.resolve(connection); - const toolset: Toolset[] = [new CommonDBClusterTools(dbAccess, connectionGetter)]; +export interface ClusterService { + type: 'aws' | 'gcp' | 'other'; + getTablesInfo(): Promise; + getPostgresExtensions(): Promise; +} - if (cloudProvider === 'aws') { - toolset.push(new AWSDBClusterTools(dbAccess, connectionGetter)); - } else if (cloudProvider === 'gcp') { - toolset.push(new GCPDBClusterTools(dbAccess, connectionGetter)); - } - return mergeToolsets(...toolset); +export interface AWSProjectClusterService extends ClusterService { + type: 'aws'; + getInstanceLogs({ periodInSeconds, grep }: { periodInSeconds: number; grep?: string }): Promise; + getInstanceMetric({ + metricName, + periodInSeconds, + stat + }: { + metricName: string; + periodInSeconds: number; + stat: 'Average' | 'Maximum' | 'Minimum' | 'Sum'; + }): Promise; + getClusterInfo(): Promise; } -export class CommonDBClusterTools implements ToolsetGroup { - #dbAccess: DBAccess; - #getter: () => Promise; +export interface GCPProjectClusterService extends ClusterService { + type: 'gcp'; + getInstanceLogs({ periodInSeconds, grep }: { periodInSeconds: number; grep?: string }): Promise; + getInstanceMetric({ metricName, periodInSeconds }: { metricName: string; periodInSeconds: number }): Promise; + getInstanceInfo(): Promise; +} - constructor(dbAccess: DBAccess, getter: () => Promise) { - this.#dbAccess = dbAccess; - this.#getter = getter; +export function getDBClusterTools(tools: ClusterService): Record { + switch (tools.type) { + case 'aws': + return awsDBClusterTools(tools as AWSProjectClusterService); + case 'gcp': + return gpcDBClusterTools(tools as GCPProjectClusterService); + default: + return commonDBClusterTools(tools); } +} - toolset(): Record { +export function getProjectClusterService( + dbAccess: DBAccess, + connection: Connection | (() => Promise), + cloudProvider: CloudProvider +): ClusterService { + if (cloudProvider === 'aws') { + return awsProjectClusterTools(dbAccess, connection); + } else if (cloudProvider === 'gcp') { + return gcpProjectClusterTools(dbAccess, connection); + } else { + const getter = typeof connection === 'function' ? connection : () => Promise.resolve(connection); return { - getTablesInfo: this.getTablesInfo(), - getPostgresExtensions: this.getPostgresExtensions() + type: 'other', + getTablesInfo: async () => await getTablesInfo(dbAccess, await getter()), + getPostgresExtensions: async () => await getPostgresExtensions(dbAccess, await getter()) }; } +} - private getTablesInfo(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ +export function commonDBClusterTools(clusterTools: ClusterService): Record { + return { + getTablesInfo: tool({ description: `Get the information about tables (sizes, row counts, usage).`, parameters: z.object({}), - execute: async () => { - const connection = await getter(); - return await getTablesInfo(db, connection); - } - }); - } - - private getPostgresExtensions(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ + execute: async () => clusterTools.getTablesInfo() + }), + getPostgresExtensions: tool({ description: `Get the available and installed PostgreSQL extensions for the database.`, parameters: z.object({}), - execute: async () => { - const connection = await getter(); - return await getPostgresExtensions(db, connection); - } - }); - } + execute: async () => clusterTools.getPostgresExtensions() + }) + }; } -export class AWSDBClusterTools implements ToolsetGroup { - #dbAccess: DBAccess; - #getter: () => Promise; - - constructor(dbAccess: DBAccess, getter: () => Promise) { - this.#dbAccess = dbAccess; - this.#getter = getter; - } - - toolset(): Record { - return { - getInstanceLogs: this.getInstanceLogsRDS(), - getInstanceMetric: this.getInstanceMetricRDS(), - getClusterInfo: this.getClusterInfo() - }; - } - - private getInstanceLogsRDS(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ +export function createAWSDBClusterTools(awsClusterTools: AWSProjectClusterService): Record { + return { + getInstanceLogs: tool({ description: `Get the recent logs from the RDS instance. You can specify the period in seconds and optionally grep for a substring. If you don't want to grep for a substring, you can pass an empty string.`, parameters: z.object({ @@ -97,16 +92,10 @@ export class AWSDBClusterTools implements ToolsetGroup { }), execute: async ({ periodInSeconds, grep }) => { console.log('getInstanceLogs', periodInSeconds, grep); - const connection = await getter(); - return await getInstanceLogsRDS(db, { connection, periodInSeconds, grep }); + return await awsClusterTools.getInstanceLogs({ periodInSeconds, grep }); } - }); - } - - private getInstanceMetricRDS(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ + }), + getInstanceMetric: tool({ description: `Get the metrics for the RDS instance. If this is a cluster, the metric is read from the current writer instance. You can specify the period in seconds. The stat parameter is one of the following: Average, Maximum, Minimum, Sum.`, parameters: z.object({ @@ -116,47 +105,88 @@ export class AWSDBClusterTools implements ToolsetGroup { }), execute: async ({ metricName, periodInSeconds, stat }) => { console.log('getClusterMetric', metricName, periodInSeconds); - const connection = await getter(); - return await getClusterMetricRDS(db, { connection, metricName, periodInSeconds, stat }); + return await awsClusterTools.getInstanceMetric({ metricName, periodInSeconds, stat }); } - }); - } - - private getClusterInfo(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ + }), + getClusterInfo: tool({ description: `Get the information about the RDS cluster or instance.`, parameters: z.object({}), execute: async () => { - const connection = await getter(); - return await getClusterByConnection(db, connection.id); + return await awsClusterTools.getClusterInfo(); } - }); - } + }) + }; } -export class GCPDBClusterTools implements ToolsetGroup { - #dbAccess: DBAccess; - #getter: () => Promise; +export function awsDBClusterTools(awsClusterTools: AWSProjectClusterService): Record { + const commonTools = commonDBClusterTools(awsClusterTools); + return { + ...commonTools, + getInstanceLogs: tool({ + description: `Get the recent logs from the RDS instance. You can specify the period in seconds and optionally grep for a substring. + If you don't want to grep for a substring, you can pass an empty string.`, + parameters: z.object({ + periodInSeconds: z.number(), + grep: z.string() + }), + execute: async ({ periodInSeconds, grep }) => { + console.log('getInstanceLogs', periodInSeconds, grep); + return await awsClusterTools.getInstanceLogs({ periodInSeconds, grep }); + } + }), + getInstanceMetric: tool({ + description: `Get the metrics for the RDS instance. If this is a cluster, the metric is read from the current writer instance. + You can specify the period in seconds. The stat parameter is one of the following: Average, Maximum, Minimum, Sum.`, + parameters: z.object({ + metricName: z.string(), + periodInSeconds: z.number(), + stat: z.enum(['Average', 'Maximum', 'Minimum', 'Sum']) + }), + execute: async ({ metricName, periodInSeconds, stat }) => { + console.log('getClusterMetric', metricName, periodInSeconds); + return await awsClusterTools.getInstanceMetric({ metricName, periodInSeconds, stat }); + } + }), + getClusterInfo: tool({ + description: `Get the information about the RDS cluster or instance.`, + parameters: z.object({}), + execute: async () => { + return await awsClusterTools.getClusterInfo(); + } + }) + }; +} - constructor(dbAccess: DBAccess, getter: () => Promise) { - this.#dbAccess = dbAccess; - this.#getter = getter; - } +export function awsProjectClusterTools( + dbAccess: DBAccess, + connection: Connection | (() => Promise) +): AWSProjectClusterService { + const getter = typeof connection === 'function' ? connection : () => Promise.resolve(connection); - toolset(): Record { - return { - getInstanceLogs: this.getInstanceLogsGCP(), - getInstanceMetric: this.getInstanceMetricGCP(), - getInstanceInfo: this.getInstanceInfo() - }; - } + return { + type: 'aws', + getTablesInfo: async () => await getTablesInfo(dbAccess, await getter()), + getPostgresExtensions: async () => await getPostgresExtensions(dbAccess, await getter()), + getInstanceLogs: async ({ periodInSeconds, grep }) => { + const connection = await getter(); + return await getInstanceLogsRDS(dbAccess, { connection, periodInSeconds, grep }); + }, + getInstanceMetric: async ({ metricName, periodInSeconds, stat }) => { + const connection = await getter(); + return getClusterMetricRDS(dbAccess, { connection, metricName, periodInSeconds, stat }); + }, + getClusterInfo: async () => { + const connection = await getter(); + return await getClusterByConnection(dbAccess, connection.id); + } + }; +} - private getInstanceLogsGCP(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ +export function gpcDBClusterTools(gcpProjectClusterTools: GCPProjectClusterService): Record { + const commonTools = commonDBClusterTools(gcpProjectClusterTools); + return { + ...commonTools, + getInstanceLogs: tool({ description: `Get the recent logs from a GCP CloudSQL instance. You can specify the period in seconds and optionally grep for a substring.`, parameters: z.object({ periodInSeconds: z.number(), @@ -164,52 +194,63 @@ export class GCPDBClusterTools implements ToolsetGroup { }), execute: async ({ periodInSeconds, grep }) => { console.log('getInstanceLogs', periodInSeconds, grep); - const connection = await getter(); - return await getInstanceLogsGCP(db, { connection, periodInSeconds, grep }); + return await gcpProjectClusterTools.getInstanceLogs({ periodInSeconds, grep }); } - }); - } - - private getInstanceMetricGCP(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - - return tool({ + }), + getInstanceMetric: tool({ description: `Get the metrics for the GCP Cloud SQL instance. You can specify the period in seconds. - The metric name MUST be a valid Cloud SQL metric name. Common CloudSQL metrics are: - - cloudsql.googleapis.com/database/cpu/utilization - - cloudsql.googleapis.com/database/memory/utilization - - cloudsql.googleapis.com/database/disk/utilization - - cloudsql.googleapis.com/database/disk/write_ops_count - - cloudsql.googleapis.com/database/disk/read_ops_count - - cloudsql.googleapis.com/database/memory/total_usage - - cloudsql.googleapis.com/database/postgresql/num_backends - - cloudsql.googleapis.com/database/postgresql/new_connection_count - - cloudsql.googleapis.com/database/postgresql/deadlock_count - - cloudsql.googleapis.com/database/postgresql/write_ahead_log/written_bytes_count - `, + The metric name MUST be a valid Cloud SQL metric name. Common CloudSQL metrics are: + - cloudsql.googleapis.com/database/cpu/utilization + - cloudsql.googleapis.com/database/memory/utilization + - cloudsql.googleapis.com/database/disk/utilization + - cloudsql.googleapis.com/database/disk/write_ops_count + - cloudsql.googleapis.com/database/disk/read_ops_count + - cloudsql.googleapis.com/database/memory/total_usage + - cloudsql.googleapis.com/database/postgresql/num_backends + - cloudsql.googleapis.com/database/postgresql/new_connection_count + - cloudsql.googleapis.com/database/postgresql/deadlock_count + - cloudsql.googleapis.com/database/postgresql/write_ahead_log/written_bytes_count + `, parameters: z.object({ metricName: z.string(), periodInSeconds: z.number() }), execute: async ({ metricName, periodInSeconds }) => { console.log('getClusterMetricGCP', metricName, periodInSeconds); - const connection = await getter(); - return await getClusterMetricGCP(db, { connection, metricName, periodInSeconds }); + return await gcpProjectClusterTools.getInstanceMetric({ metricName, periodInSeconds }); } - }); - } - - private getInstanceInfo(): Tool { - const getter = this.#getter; - const db = this.#dbAccess; - return tool({ + }), + getInstanceInfo: tool({ description: `Get the information about the GCP Cloud SQL instance.`, parameters: z.object({}), execute: async () => { - const connection = await getter(); - return await getInstanceByConnection(db, connection.id); + return await gcpProjectClusterTools.getInstanceInfo(); } - }); - } + }) + }; +} + +export function gcpProjectClusterTools( + dbAccess: DBAccess, + connection: Connection | (() => Promise) +): GCPProjectClusterService { + const getter = typeof connection === 'function' ? connection : () => Promise.resolve(connection); + + return { + type: 'gcp', + getTablesInfo: async () => await getTablesInfo(dbAccess, await getter()), + getPostgresExtensions: async () => await getPostgresExtensions(dbAccess, await getter()), + getInstanceLogs: async ({ periodInSeconds, grep }) => { + const connection = await getter(); + return await getInstanceLogsGCP(dbAccess, { connection, periodInSeconds, grep }); + }, + getInstanceMetric: async ({ metricName, periodInSeconds }) => { + const connection = await getter(); + return await getClusterMetricGCP(dbAccess, { connection, metricName, periodInSeconds }); + }, + getInstanceInfo: async () => { + const connection = await getter(); + return await getInstanceByConnection(dbAccess, connection.id); + } + }; } diff --git a/apps/dbagent/src/lib/ai/tools/db.ts b/apps/dbagent/src/lib/ai/tools/db.ts index a28b7d85..3c1e1119 100644 --- a/apps/dbagent/src/lib/ai/tools/db.ts +++ b/apps/dbagent/src/lib/ai/tools/db.ts @@ -1,6 +1,6 @@ import { Tool, tool } from 'ai'; import { z } from 'zod'; -import { getPerformanceAndVacuumSettings, toolFindTableSchema } from '~/lib/tools/dbinfo'; +import { toolFindTableSchema } from '~/lib/tools/dbinfo'; import { toolDescribeTable, toolExplainQuery, toolGetSlowQueries } from '~/lib/tools/slow-queries'; import { toolCurrentActiveQueries, @@ -9,204 +9,219 @@ import { toolGetQueriesWaitingOnLocks, toolGetVacuumStats } from '~/lib/tools/stats'; -import { ToolsetGroup } from './types'; -import { Pool, withPoolConnection } from '~/lib/targetdb/db'; - -export function getDBSQLTools(targetDb: Pool): DBSQLTools { - return new DBSQLTools(targetDb); +import { getPerformanceSettings, getVacuumSettings, Pool, withPoolConnection } from '~/lib/targetdb/db'; + +type DBToolsProps = { + db: DBService; +}; + +export interface DBService { + slowQueries(thresholdMs: number): Promise; + explainQuery(schema: string, query: string): Promise; + describeTable(schema: string, table: string): Promise; + findTableSchema(table: string): Promise; + currentActiveQueries(): Promise; + queriesWaitingOnLocks(): Promise; + vacuumStats(): Promise; + connectionsStats(): Promise; + connectionsGroups(): Promise; + getPerformanceAndVacuumSettings(): Promise; } -// The DBSQLTools toolset provides tools for querying the postgres database -// directly via SQL to collect system performance information. -export class DBSQLTools implements ToolsetGroup { - #pool: Pool | (() => Promise); - - constructor(pool: Pool | (() => Promise)) { - this.#pool = pool; - } +export function getDBSQLTools(props: DBToolsProps): Record { + return { + getSlowQueries: getSlowQueries(props), + explainQuery: explainQuery(props), + describeTable: describeTable(props), + findTableSchema: findTableSchema(props), + getCurrentActiveQueries: getCurrentActiveQueries(props), + getQueriesWaitingOnLocks: getQueriesWaitingOnLocks(props), + getVacuumStats: getVacuumStats(props), + getConnectionsStats: getConnectionsStats(props), + getConnectionsGroups: getConnectionsGroups(props), + getPerformanceAndVacuumSettings: getPerformanceAndVacuumSettings(props) + }; +} - toolset(): Record { - return { - getSlowQueries: this.getSlowQueries(), - explainQuery: this.explainQuery(), - describeTable: this.describeTable(), - findTableSchema: this.findTableSchema(), - getCurrentActiveQueries: this.getCurrentActiveQueries(), - getQueriesWaitingOnLocks: this.getQueriesWaitingOnLocks(), - getVacuumStats: this.getVacuumStats(), - getConnectionsStats: this.getConnectionsStats(), - getConnectionsGroups: this.getConnectionsGroups(), - getPerformanceAndVacuumSettings: this.getPerformanceAndVacuumSettings() - }; - } +export function targetDBService(pool: Pool | (() => Promise)): DBService { + return { + slowQueries: async (thresholdMs: number) => + await withPoolConnection(pool, async (client) => await toolGetSlowQueries(client, thresholdMs)), + explainQuery: async (schema: string, query: string) => + await withPoolConnection(pool, async (client) => await toolExplainQuery(client, schema, query)), + describeTable: async (schema: string, table: string) => + await withPoolConnection(pool, async (client) => await toolDescribeTable(client, schema, table)), + findTableSchema: async (table: string) => + await withPoolConnection(pool, async (client) => await toolFindTableSchema(client, table)), + currentActiveQueries: async () => await withPoolConnection(pool, toolCurrentActiveQueries), + queriesWaitingOnLocks: async () => await withPoolConnection(pool, toolGetQueriesWaitingOnLocks), + vacuumStats: async () => await withPoolConnection(pool, toolGetVacuumStats), + connectionsStats: async () => await withPoolConnection(pool, toolGetConnectionsStats), + connectionsGroups: async () => await withPoolConnection(pool, toolGetConnectionsGroups), + getPerformanceAndVacuumSettings: async () => + await withPoolConnection(pool, async (client) => { + const performanceSettings = await getPerformanceSettings(client); + const vacuumSettings = await getVacuumSettings(client); + return ` + Performance settings: ${JSON.stringify(performanceSettings)} + Vacuum settings: ${JSON.stringify(vacuumSettings)} + `; + }) + }; +} - getSlowQueries(): Tool { - const pool = this.#pool; - return tool({ - description: `Get a list of slow queries formatted as a JSON array. Contains how many times the query was called, +function getSlowQueries({ db }: DBToolsProps): Tool { + return tool({ + description: `Get a list of slow queries formatted as a JSON array. Contains how many times the query was called, the max execution time in seconds, the mean execution time in seconds, the total execution time (all calls together) in seconds, and the query itself.`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, async (client) => await toolGetSlowQueries(client, 2000)); - } catch (error) { - return `Error getting slow queries: ${error}`; - } + parameters: z.object({}), + execute: async () => { + try { + return await db.slowQueries(2000); + } catch (error) { + return `Error getting slow queries: ${error}`; } - }); - } + } + }); +} - explainQuery(): Tool { - const pool = this.#pool; - return tool({ - description: `Run explain on a a query. Returns the explain plan as received from PostgreSQL. +function explainQuery({ db }: DBToolsProps): Tool { + return tool({ + description: `Run explain on a a query. Returns the explain plan as received from PostgreSQL. The query needs to be complete, it cannot contain $1, $2, etc. If you need to, replace the parameters with your own made up values. It's very important that $1, $2, etc. are not passed to this tool. Use the tool describeTable to get the types of the columns. If you know the schema, pass it in as well.`, - parameters: z.object({ - schema: z.string(), - query: z.string() - }), - execute: async ({ schema = 'public', query }) => { - try { - const explain = await withPoolConnection( - pool, - async (client) => await toolExplainQuery(client, schema, query) - ); - if (!explain) return 'Could not run EXPLAIN on the query'; - - return explain; - } catch (error) { - return `Error running EXPLAIN on the query: ${error}`; - } + parameters: z.object({ + schema: z.string(), + query: z.string() + }), + execute: async ({ schema = 'public', query }) => { + try { + const explain = await db.explainQuery(schema, query); + if (!explain) return 'Could not run EXPLAIN on the query'; + return explain; + } catch (error) { + return `Error running EXPLAIN on the query: ${error}`; } - }); - } + } + }); +} - describeTable(): Tool { - const pool = this.#pool; - return tool({ - description: `Describe a table. If you know the schema, pass it as a parameter. If you don't, use public.`, - parameters: z.object({ - schema: z.string(), - table: z.string() - }), - execute: async ({ schema = 'public', table }) => { - try { - return await withPoolConnection(pool, async (client) => await toolDescribeTable(client, schema, table)); - } catch (error) { - return `Error describing table: ${error}`; - } +function describeTable({ db }: DBToolsProps): Tool { + return tool({ + description: `Describe a table. If you know the schema, pass it as a parameter. If you don't, use public.`, + parameters: z.object({ + schema: z.string(), + table: z.string() + }), + execute: async ({ schema = 'public', table }) => { + try { + return await db.describeTable(schema, table); + } catch (error) { + return `Error describing table: ${error}`; } - }); - } + } + }); +} - findTableSchema(): Tool { - const pool = this.#pool; - return tool({ - description: `Find the schema of a table. Use this tool to find the schema of a table.`, - parameters: z.object({ - table: z.string() - }), - execute: async ({ table }) => { - try { - return await withPoolConnection(pool, async (client) => await toolFindTableSchema(client, table)); - } catch (error) { - return `Error finding table schema: ${error}`; - } +function findTableSchema({ db }: DBToolsProps): Tool { + return tool({ + description: `Find the schema of a table. Use this tool to find the schema of a table.`, + parameters: z.object({ + table: z.string() + }), + execute: async ({ table }) => { + try { + return await db.findTableSchema(table); + } catch (error) { + return `Error finding table schema: ${error}`; } - }); - } + } + }); +} - getCurrentActiveQueries(): Tool { - const pool = this.#pool; - return tool({ - description: `Get the currently active queries.`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, toolCurrentActiveQueries); - } catch (error) { - return `Error getting current active queries: ${error}`; - } +function getCurrentActiveQueries({ db }: DBToolsProps): Tool { + return tool({ + description: `Get the currently active queries.`, + parameters: z.object({}), + execute: async () => { + try { + return await db.currentActiveQueries(); + } catch (error) { + return `Error getting current active queries: ${error}`; } - }); - } + } + }); +} - getQueriesWaitingOnLocks(): Tool { - const pool = this.#pool; - return tool({ - description: `Get the queries that are currently blocked waiting on locks.`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, toolGetQueriesWaitingOnLocks); - } catch (error) { - return `Error getting queries waiting on locks: ${error}`; - } +function getQueriesWaitingOnLocks({ db }: DBToolsProps): Tool { + return tool({ + description: `Get the queries that are currently blocked waiting on locks.`, + parameters: z.object({}), + execute: async () => { + try { + return await db.queriesWaitingOnLocks(); + } catch (error) { + return `Error getting queries waiting on locks: ${error}`; } - }); - } + } + }); +} - getVacuumStats(): Tool { - const pool = this.#pool; - return tool({ - description: `Get the vacuum stats for the top tables in the database. They are sorted by the number of dead tuples descending.`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, toolGetVacuumStats); - } catch (error) { - return `Error getting vacuum stats: ${error}`; - } +function getVacuumStats({ db }: DBToolsProps): Tool { + return tool({ + description: `Get the vacuum stats for the top tables in the database. They are sorted by the number of dead tuples descending.`, + parameters: z.object({}), + execute: async () => { + try { + return await db.vacuumStats(); + } catch (error) { + return `Error getting vacuum stats: ${error}`; } - }); - } + } + }); +} - getConnectionsStats(): Tool { - const pool = this.#pool; - return tool({ - description: `Get the connections stats for the database.`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, toolGetConnectionsStats); - } catch (error) { - return `Error getting connections stats: ${error}`; - } +function getConnectionsStats({ db }: DBToolsProps): Tool { + return tool({ + description: `Get the connections stats for the database.`, + parameters: z.object({}), + execute: async () => { + try { + return await db.connectionsStats(); + } catch (error) { + return `Error getting connections stats: ${error}`; } - }); - } + } + }); +} - getConnectionsGroups(): Tool { - const pool = this.#pool; - return tool({ - description: `Get the connections groups for the database. This is a view in the pg_stat_activity table, grouped by (state, user, application_name, client_addr, wait_event_type, wait_event).`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, toolGetConnectionsGroups); - } catch (error) { - return `Error getting connections groups: ${error}`; - } +function getConnectionsGroups({ db }: DBToolsProps): Tool { + return tool({ + description: `Get the connections groups for the database. This is a view in the pg_stat_activity table, grouped by (state, user, application_name, client_addr, wait_event_type, wait_event).`, + parameters: z.object({}), + execute: async () => { + try { + return await db.connectionsGroups(); + } catch (error) { + return `Error getting connections groups: ${error}`; } - }); - } + } + }); +} - getPerformanceAndVacuumSettings(): Tool { - const pool = this.#pool; - return tool({ - description: `Get the performance and vacuum settings for the database.`, - parameters: z.object({}), - execute: async () => { - try { - return await withPoolConnection(pool, getPerformanceAndVacuumSettings); - } catch (error) { - return `Error getting performance and vacuum settings: ${error}`; - } +function getPerformanceAndVacuumSettings({ db }: DBToolsProps): Tool { + return tool({ + description: `Get the performance and vacuum settings for the database.`, + parameters: z.object({}), + execute: async () => { + try { + return await db.getPerformanceAndVacuumSettings(); + } catch (error) { + return `Error getting performance and vacuum settings: ${error}`; } - }); - } + } + }); } diff --git a/apps/dbagent/src/lib/ai/tools/index.ts b/apps/dbagent/src/lib/ai/tools/index.ts index 77d0cd5c..f1dbe19f 100644 --- a/apps/dbagent/src/lib/ai/tools/index.ts +++ b/apps/dbagent/src/lib/ai/tools/index.ts @@ -1,45 +1,4 @@ -import { DataStreamWriter, Tool } from 'ai'; -import { Pool } from 'pg'; -import { getUserSessionDBAccess } from '~/lib/db/db'; -import { Connection, Project } from '~/lib/db/schema'; -import { getArtifactTools } from './artifacts'; -import { getDBClusterTools } from './cluster'; -import { commonToolset } from './common'; -import { getDBSQLTools } from './db'; -import { getPlaybookToolset } from './playbook'; -import { mergeToolsets } from './types'; -import { mcpToolset } from './user-mcp'; - export * from './cluster'; export * from './common'; export * from './db'; export * from './playbook'; -export * from './types'; - -export async function getTools({ - project, - connection, - targetDb, - userId, - useArtifacts = false, - dataStream -}: { - project: Project; - connection: Connection; - targetDb: Pool; - userId: string; - useArtifacts?: boolean; - dataStream?: DataStreamWriter; -}): Promise> { - const dbAccess = await getUserSessionDBAccess(); - - const dbTools = getDBSQLTools(targetDb); - const clusterTools = getDBClusterTools(dbAccess, connection, project.cloudProvider); - const playbookToolset = getPlaybookToolset(dbAccess, project.id); - const mcpTools = await mcpToolset.listMCPTools(); - - const artifactsToolset = - useArtifacts && dataStream ? getArtifactTools({ dbAccess, userId, projectId: project.id, dataStream }) : {}; - - return mergeToolsets(mcpTools, commonToolset, playbookToolset, dbTools, clusterTools, artifactsToolset); -} diff --git a/apps/dbagent/src/lib/ai/tools/playbook.ts b/apps/dbagent/src/lib/ai/tools/playbook.ts index 4afb1dd3..3de265db 100644 --- a/apps/dbagent/src/lib/ai/tools/playbook.ts +++ b/apps/dbagent/src/lib/ai/tools/playbook.ts @@ -3,10 +3,42 @@ import { z } from 'zod'; import { DBAccess } from '~/lib/db/db'; import { getCustomPlaybookAndPlaybookTool, listCustomPlaybooksAndPlaybookTool } from '~/lib/tools/custom-playbooks'; import { getPlaybook, listPlaybooks } from '~/lib/tools/playbooks'; -import { ToolsetGroup } from './types'; -export function getPlaybookToolset(dbAccess: DBAccess, projectId: string): Record { - return new playbookTools(dbAccess, () => Promise.resolve({ projectId })).toolset(); +export interface PlaybookService { + fetch(name: string): Promise; + list(): Promise; +} + +export function getPlaybookToolset(tools?: PlaybookService): Record { + if (!tools) { + return builtinPlaybookToolset; + } + return { + fetch: playbookFetchTool(async (name: string) => tools.fetch(name)), + list: playbookListTool(async () => tools.list()) + }; +} + +export const builtinPlaybookToolset = { + getPlaybookTool: playbookFetchTool(async (name: string) => getPlaybook(name)), + listPlaybooksTool: playbookListTool(async () => listPlaybooks()) +}; + +export function projectPlaybookService( + dbAccess: DBAccess, + connProjectId: string | (() => Promise<{ projectId: string }>) +): PlaybookService { + const getter = typeof connProjectId === 'string' ? async () => ({ projectId: connProjectId }) : connProjectId; + return { + fetch: async (name: string): Promise => { + const { projectId } = await getter(); + return await getCustomPlaybookAndPlaybookTool(dbAccess, name, projectId); + }, + list: async (): Promise => { + const { projectId } = await getter(); + return await listCustomPlaybooksAndPlaybookTool(dbAccess, projectId); + } + }; } function playbookFetchTool(execute: (name: string) => Promise): Tool { @@ -26,43 +58,3 @@ function playbookListTool(execute: () => Promise): Tool { execute: async () => execute() }); } - -export const builtinPlaybookToolset = { - getPlaybookTool: playbookFetchTool(async (name: string) => getPlaybook(name)), - listPlaybooksTool: playbookListTool(async () => listPlaybooks()) -}; - -export class playbookTools implements ToolsetGroup { - #dbAccess: DBAccess; - #connProjectId: () => Promise<{ projectId: string }>; - - constructor(dbAccess: DBAccess, getter: () => Promise<{ projectId: string }>) { - this.#dbAccess = dbAccess; - this.#connProjectId = getter; - } - - toolset(): Record { - return { - getPlaybookTool: this.getPlaybookTool(), - listPlaybooksTool: this.listPlaybooksTool() - }; - } - - private getPlaybookTool(): Tool { - const db = this.#dbAccess; - const getter = this.#connProjectId; - return playbookFetchTool(async (name: string) => { - const { projectId } = await getter(); - return await getCustomPlaybookAndPlaybookTool(db, name, projectId); - }); - } - - private listPlaybooksTool(): Tool { - const db = this.#dbAccess; - const getter = this.#connProjectId; - return playbookListTool(async () => { - const { projectId } = await getter(); - return await listCustomPlaybooksAndPlaybookTool(db, projectId); - }); - } -} diff --git a/apps/dbagent/src/lib/ai/tools/types.ts b/apps/dbagent/src/lib/ai/tools/types.ts deleted file mode 100644 index 74654474..00000000 --- a/apps/dbagent/src/lib/ai/tools/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Tool } from 'ai'; - -export interface ToolsetGroup { - toolset(): Record; -} - -export type ToolsetBuilderFunction = () => Record; - -export type Toolset = ToolsetGroup | ToolsetBuilderFunction | Record; - -export function mergeToolsets(...toolsets: Toolset[]): Record { - const sets: Record[] = toolsets.map((toolset) => { - if (typeof toolset === 'function') { - return toolset(); - } else if ('toolset' in toolset) { - return (toolset as ToolsetGroup).toolset(); - } - return toolset; - }); - return sets.reduce((acc, tool) => ({ ...acc, ...tool }), {}); -} diff --git a/apps/dbagent/src/lib/monitoring/runner.ts b/apps/dbagent/src/lib/monitoring/runner.ts index 3eb6ac5e..e3c19655 100644 --- a/apps/dbagent/src/lib/monitoring/runner.ts +++ b/apps/dbagent/src/lib/monitoring/runner.ts @@ -1,10 +1,9 @@ import { Message } from '@ai-sdk/ui-utils'; import { generateId, generateObject, generateText, LanguageModelV1 } from 'ai'; import { z } from 'zod'; -import { getMonitoringModel, getMonitoringSystemPrompt } from '../ai/agent'; -import { getTools } from '../ai/tools'; +import { agentModelDeps, getMonitoringModel, getMonitoringSystemPrompt, monitoringModel } from '../ai/agent'; import { getConnectionFromSchedule } from '../db/connections'; -import { DBAccess } from '../db/db'; +import { DBAccess, getUserDBAccess } from '../db/db'; import { getProjectById } from '../db/projects'; import { insertScheduleRunLimitHistory } from '../db/schedule-runs'; import { Connection, Project, Schedule } from '../db/schema'; @@ -38,16 +37,23 @@ async function runModelPlaybook({ createdAt: new Date() }); - const monitoringSystemPrompt = getMonitoringSystemPrompt({ cloudProvider: project.cloudProvider }); const targetDb = getTargetDbPool(connection.connectionString); + const dbAccess = await getUserDBAccess(schedule.userId); + const deps = agentModelDeps({ + withMCP: true, + targetDb, + dbAccess, + connection, + cloudProvider: project.cloudProvider, + userId: schedule.userId + }); + try { - const tools = await getTools({ project, connection, targetDb, userId: schedule.userId }); - const result = await generateText({ + const result = await monitoringModel.generateText({ model: modelInstance, - system: monitoringSystemPrompt, maxSteps: 20, - tools, - messages + messages, + deps }); messages.push({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 089b40bc..56d57f71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,10 +113,10 @@ importers: version: link:../../packages/theme '@mastra/core': specifier: ^0.9.3 - version: 0.9.3(react@19.1.0)(zod@3.24.4) + version: 0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4) '@mastra/evals': specifier: ^0.1.22 - version: 0.1.22(ai@4.3.15(react@19.1.0)(zod@3.24.4))(react@19.1.0) + version: 0.1.22(ai@4.3.15(react@19.1.0)(zod@3.24.4))(openapi-types@12.1.3)(react@19.1.0) '@modelcontextprotocol/sdk': specifier: ^1.11.1 version: 1.11.1 @@ -215,7 +215,7 @@ importers: version: 12.10.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) geist: specifier: ^1.4.2 - version: 1.4.2(next@15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)) + version: 1.4.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)) import-in-the-middle: specifier: ^1.13.1 version: 1.13.1 @@ -233,10 +233,10 @@ importers: version: 0.509.0(react@19.1.0) next: specifier: ^15.3.2 - version: 15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) + version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) next-auth: specifier: 5.0.0-beta.25 - version: 5.0.0-beta.25(next@15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0) + version: 5.0.0-beta.25(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -366,7 +366,7 @@ importers: version: 6.0.0 mastra: specifier: ^0.6.2 - version: 0.6.2(@opentelemetry/api@1.9.0)(react@19.1.0)(typescript@5.8.3) + version: 0.6.2(@opentelemetry/api@1.9.0)(openapi-types@12.1.3)(react@19.1.0)(typescript@5.8.3) papaparse: specifier: ^5.5.2 version: 5.5.2 @@ -764,118 +764,106 @@ packages: resolution: {integrity: sha512-qLKslgZbU+iocKo2+OwS2typ0EieN10bpa1MmHTte87DhcNrbEFPNEGRuYwDgfQCs9gMgMkjcNlFlFL6w0yF6Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-cognito-identity@3.787.0': - resolution: {integrity: sha512-7v6nywZ5wcQxX7qdZ5M1ld15QdkzLU6fAKiEqbvJKu4dM8cFW6As+DbS990Mg46pp1xM/yvme+51xZDTfTfJZA==} + '@aws-sdk/client-cognito-identity@3.804.0': + resolution: {integrity: sha512-l4R2usRE9WHGK9MqU94+veX70m5moUj5hB+IAqI8tAZ4nAgeXpot3QKGgwdEVjKe1pKZwDP5tkHl01Umm2Ir7w==} engines: {node: '>=18.0.0'} '@aws-sdk/client-rds@3.806.0': resolution: {integrity: sha512-tHc0ca5f2iy+vTD2BqF4sYIV1J9tkyrsSBKl+k2psq9AgIvUOatSoL2rymuAKIgF7YqbA6qrpHI5vIjyEfRtaQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sagemaker@3.787.0': - resolution: {integrity: sha512-uHmF6MiMus+OjFfVnkYs46CkExjUp6kZGB/gFjwiq5IU+DGnkIlmfbOlRuIlLflqPuADPcbiTiNi4IjluHGqVA==} + '@aws-sdk/client-sagemaker@3.805.0': + resolution: {integrity: sha512-c0HWGMjKhQ1sA/m032VWt1xS0FGOjJKPMgt6YsqwEzkFLQODb0V0bvdgukuwoLe1cI47M0pQSthKL5oTWXNdXg==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.787.0': - resolution: {integrity: sha512-L8R+Mh258G0DC73ktpSVrG4TT9i2vmDLecARTDR/4q5sRivdDQSL5bUp3LKcK80Bx+FRw3UETIlX6mYMLL9PJQ==} + '@aws-sdk/client-sso@3.804.0': + resolution: {integrity: sha512-6D5iQbL0MqlJ7B5aaHdP21k9+3H/od0jHjHSXegvFd4h2KQbD+QVTdEOSLeakgBGgHYRfiQXsrdMMzUz8vcpsw==} engines: {node: '>=18.0.0'} '@aws-sdk/client-sso@3.806.0': resolution: {integrity: sha512-X0p/9/u9e6b22rlQqKucdtjdqmjSNB4c/8zDEoD5MvgYAAbMF9HNE0ST2xaA/WsJ7uE0jFfhPY2/00pslL1DqQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.775.0': - resolution: {integrity: sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==} + '@aws-sdk/core@3.804.0': + resolution: {integrity: sha512-KrYDEc6HaJE+Mx5lrwq6uhJxj1RYYfggQ+X+zQeKRyrZHl2GOxFl7PdnpdwtnaQIjX0gNkDzquhZSdyT0ar5rA==} engines: {node: '>=18.0.0'} '@aws-sdk/core@3.806.0': resolution: {integrity: sha512-HJRINPncdjPK0iL3f6cBpqCMaxVwq2oDbRCzOx04tsLZ0tNgRACBfT3d/zNVRvMt6fnOVKXoN1LAtQaw50pjEA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-cognito-identity@3.787.0': - resolution: {integrity: sha512-nF5XjgvZHFuyttOeTjMgfEsg6slZPQ6uI34yzq12Kq4icFgcD4bQsijnQClMN7A0u5qR8Ad8kume4b7+I2++Ig==} + '@aws-sdk/credential-provider-cognito-identity@3.804.0': + resolution: {integrity: sha512-bAk/1McqERHl7LbmbqD5ZGlyyO10N80+YiM/2NI2HXd8MKm/7roZBGEQH8VH2pDDZsWnI4c24rue4xFEmH+gjw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.775.0': - resolution: {integrity: sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==} + '@aws-sdk/credential-provider-env@3.804.0': + resolution: {integrity: sha512-5mjrWPa4iaBK9/HDEIVN8lGxsnjk60eBjwGaJV0I2uqxnTo1EuQmpLV3XdY/OzQeqJdpuH/DbC6XUIdy9bXNQA==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-env@3.806.0': resolution: {integrity: sha512-nbPwmZn0kt6Q1XI2FaJWP6AhF9tro4cO5HlmZQx8NU+B0H1y9WMo659Q5zLLY46BXgoQVIJEsPSZpcZk27O4aw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.775.0': - resolution: {integrity: sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==} + '@aws-sdk/credential-provider-http@3.804.0': + resolution: {integrity: sha512-TD84TXS/iDWcf+ggCq3n6yx36p1WXB2qgyHkbP/yVbdmix/vKU1twuB5qJvaY0PJWI0TOwBa9680XfsYrzaJAA==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-http@3.806.0': resolution: {integrity: sha512-e/gB2iJQQ4ZpecOVpEFhEvjGwuTqNCzhVaVsFYVc49FPfR1seuN7qBGYe1MO7mouGDQFInzJgcNup0DnYUrLiw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.787.0': - resolution: {integrity: sha512-hc2taRoDlXn2uuNuHWDJljVWYrp3r9JF1a/8XmOAZhVUNY+ImeeStylHXhXXKEA4JOjW+5PdJj0f1UDkVCHJiQ==} + '@aws-sdk/credential-provider-ini@3.804.0': + resolution: {integrity: sha512-LfReL9TnOOunJWeZbDXPePFEnvJE+jcA7iY/ItsThUALgTy+ydLUdOiwzMZFo1f0JZN/Rfrsb9FOd/xTOoZiFw==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-ini@3.806.0': resolution: {integrity: sha512-FogfbuYSEZgFxbNy0QcsBZHHe5mSv5HV3+JyB5n0kCyjOISCVCZD7gwxKdXjt8O1hXq5k5SOdQvydGULlB6rew==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.787.0': - resolution: {integrity: sha512-JioVi44B1vDMaK2CdzqimwvJD3uzvzbQhaEWXsGMBcMcNHajXAXf08EF50JG3ZhLrhhUsT1ObXpbTaPINOhh+g==} + '@aws-sdk/credential-provider-node@3.804.0': + resolution: {integrity: sha512-L2EK5fy2+7El7j7TcRcuwr2lzU5tQfXsfscg+dtFkLPjOqShknnqV/lXylb3QlWx8B3K/c/KK5rcWQl6cYUiDQ==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-node@3.806.0': resolution: {integrity: sha512-fZX8xP2Kf0k70kDTog/87fh/M+CV0E2yujSw1cUBJhDSwDX3RlUahiJk7TpB/KGw6hEFESMd6+7kq3UzYuw3rg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.775.0': - resolution: {integrity: sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==} + '@aws-sdk/credential-provider-process@3.804.0': + resolution: {integrity: sha512-s6ng/rZj7WP8GGgxBXsoPZYlSu7MZAm9O8OLgSSWcw8/vaYW7hBVSEVVNMEUkJiJeEo7Lh+Y/3d6SY27S1of/g==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-process@3.806.0': resolution: {integrity: sha512-8Y8GYEw/1e5IZRDQL02H6nsTDcRWid/afRMeWg+93oLQmbHcTtdm48tjis+7Xwqy+XazhMDmkbUht11QPTDJcQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.787.0': - resolution: {integrity: sha512-fHc08bsvwm4+dEMEQKnQ7c1irEQmmxbgS+Fq41y09pPvPh31nAhoMcjBSTWAaPHvvsRbTYvmP4Mf12ZGr8/nfg==} + '@aws-sdk/credential-provider-sso@3.804.0': + resolution: {integrity: sha512-9Tt5zmhiK2nBfJv52Is5gNtW6bhK0W20GRhckg4T+BlnxOkPy//2ui23DzYacrwETH6TE3kdoyL3xgEL++HSLg==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-sso@3.806.0': resolution: {integrity: sha512-hT9OBwCxWMPBydNhXm2gdNNzx5AJNheS9RglwDDvXWzQ9qDuRztjuMBilMSUMb0HF9K4IqQjYzGqczMuktz4qQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.787.0': - resolution: {integrity: sha512-SobmCwNbk6TfEsF283mZPQEI5vV2j6eY5tOCj8Er4Lzraxu9fBPADV+Bib2A8F6jlB1lMPJzOuDCbEasSt/RIw==} + '@aws-sdk/credential-provider-web-identity@3.804.0': + resolution: {integrity: sha512-eBICjQUnqaoiHl9/AHKVPt/YkrifDddAUNGWUj+9cb3bRml6PEBSHE0k/tbbCTMq1xz7CCP+gmnnAA92ChnseA==} engines: {node: '>=18.0.0'} '@aws-sdk/credential-provider-web-identity@3.806.0': resolution: {integrity: sha512-XxaSY9Zd3D4ClUGENYMvi52ac5FuJPPAsvRtEfyrSdEpf6QufbMpnexWBZMYRF31h/VutgqtJwosGgNytpxMEg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-providers@3.787.0': - resolution: {integrity: sha512-kR3RtI7drOc9pho13vWbUC2Bvrx9A0G4iizBDGmTs08NOdg4w3c1I4kdLG9tyPiIMeVnH+wYrsli5CM7xIfqiA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-host-header@3.775.0': - resolution: {integrity: sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==} + '@aws-sdk/credential-providers@3.804.0': + resolution: {integrity: sha512-qDPsrmp4Z35T5Rprg4+mnZF+xz1cqMJYuwB3KuBaOyJ7ja5UlMthhnCEs2mwqSZZ3oLELSLEuWLio5hgmGbjLg==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-host-header@3.804.0': resolution: {integrity: sha512-bum1hLVBrn2lJCi423Z2fMUYtsbkGI2s4N+2RI2WSjvbaVyMSv/WcejIrjkqiiMR+2Y7m5exgoKeg4/TODLDPQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.775.0': - resolution: {integrity: sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==} - engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.804.0': resolution: {integrity: sha512-w/qLwL3iq0KOPQNat0Kb7sKndl9BtceigINwBU7SpkYWX9L/Lem6f8NPEKrC9Tl4wDBht3Yztub4oRTy/horJA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.775.0': - resolution: {integrity: sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==} - engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.804.0': resolution: {integrity: sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==} engines: {node: '>=18.0.0'} @@ -884,16 +872,16 @@ packages: resolution: {integrity: sha512-aMDWvYuO3bjoUm/wFLpG7TXRuocdddguZOtHyb+jLNIwhztChXkJHKSfAjtS6hNLTKL3JoRZM9dkiyFaYA2OdQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.787.0': - resolution: {integrity: sha512-Lnfj8SmPLYtrDFthNIaNj66zZsBCam+E4XiUDr55DIHTGstH6qZ/q6vg0GfbukxwSmUcGMwSR4Qbn8rb8yd77g==} + '@aws-sdk/middleware-user-agent@3.804.0': + resolution: {integrity: sha512-HoBaun4t3vAFhMj/I7L/HNBKBrAYu7Sb5bTFINx8kFCxPbqsvF+jOrEE8WiljHNy7FbPjz0mPVRUwO7RZSYNiQ==} engines: {node: '>=18.0.0'} '@aws-sdk/middleware-user-agent@3.806.0': resolution: {integrity: sha512-XoIromVffgXnc+/mjlR2EVzQVIei3bPVtafIZNsHuEmUvIWJXiWsa2eJpt3BUqa0HF9YPknK7ommNEhqRb8ucg==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.787.0': - resolution: {integrity: sha512-xk03q1xpKNHgbuo+trEf1dFrI239kuMmjKKsqLEsHlAZbuFq4yRGMlHBrVMnKYOPBhVFDS/VineM991XI52fKg==} + '@aws-sdk/nested-clients@3.804.0': + resolution: {integrity: sha512-IOUcw6stjqYBMhLoAXlLVipYpAqLlA17jcyI0OzpS0pTD1RvBqEBckYibF4HJeReI+IiEHu/m0If0SKVR5WyXQ==} engines: {node: '>=18.0.0'} '@aws-sdk/nested-clients@3.806.0': @@ -905,8 +893,8 @@ packages: engines: {node: '>=14.0.0'} deprecated: This package has moved to @smithy/protocol-http - '@aws-sdk/region-config-resolver@3.775.0': - resolution: {integrity: sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==} + '@aws-sdk/region-config-resolver@3.804.0': + resolution: {integrity: sha512-Qlr8jVUL5U8Ej+84ElUTGeOok6hQXcJdx5IOSRoqKs6bCKVa8TtwgX1zZIajzjMhMgMlR3/V+M8oDVDKPB43Ug==} engines: {node: '>=18.0.0'} '@aws-sdk/region-config-resolver@3.806.0': @@ -918,24 +906,20 @@ packages: engines: {node: '>=14.0.0'} deprecated: This package has moved to @smithy/signature-v4 - '@aws-sdk/token-providers@3.787.0': - resolution: {integrity: sha512-d7/NIqxq308Zg0RPMNrmn0QvzniL4Hx8Qdwzr6YZWLYAbUSvZYS2ppLR3BFWSkV6SsTJUx8BuDaj3P8vttkrog==} + '@aws-sdk/token-providers@3.804.0': + resolution: {integrity: sha512-ndcLGD1nHEVJdWRl0lK8SfC0dN4j3X4gcGXEJxK16KZD23veMB2adHP69ySYXNFNo5gI6W9Ct9QXnB+tJCCS1Q==} engines: {node: '>=18.0.0'} '@aws-sdk/token-providers@3.806.0': resolution: {integrity: sha512-I6SxcsvV7yinJZmPgGullFHS0tsTKa7K3jEc5dmyCz8X+kZPfsWNffZmtmnCvWXPqMXWBvK6hVaxwomx79yeHA==} engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.775.0': - resolution: {integrity: sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==} - engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.804.0': resolution: {integrity: sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.787.0': - resolution: {integrity: sha512-fd3zkiOkwnbdbN0Xp9TsP5SWrmv0SpT70YEdbb8wAj2DWQwiCmFszaSs+YCvhoCdmlR3Wl9Spu0pGpSAGKeYvQ==} + '@aws-sdk/util-endpoints@3.804.0': + resolution: {integrity: sha512-mT2R1De1fBT3vgm00ELVFoaArblW3PqGUCVteGGSUdJA525To7h6xPThrNrw3Dn8blAcR8VYGYte/JX7vKgFxw==} engines: {node: '>=18.0.0'} '@aws-sdk/util-endpoints@3.806.0': @@ -950,14 +934,11 @@ packages: resolution: {integrity: sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.775.0': - resolution: {integrity: sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==} - '@aws-sdk/util-user-agent-browser@3.804.0': resolution: {integrity: sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==} - '@aws-sdk/util-user-agent-node@3.787.0': - resolution: {integrity: sha512-mG7Lz8ydfG4SF9e8WSXiPQ/Lsn3n8A5B5jtPROidafi06I3ckV2WxyMLdwG14m919NoS6IOfWHyRGSqWIwbVKA==} + '@aws-sdk/util-user-agent-node@3.804.0': + resolution: {integrity: sha512-TacXL50ZHOeTUvN9LbHjS3muvvJNpzZp9cAtGRKpKXzlu8zCxPHrVU7dGOF6ONuNG30GpN2xzz81/XcCtg+8/A==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -977,54 +958,54 @@ packages: '@aws-sdk/util-utf8-browser@3.259.0': resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + '@babel/compat-data@7.27.2': + resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.10': - resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + '@babel/core@7.27.1': + resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.0': - resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} + '@babel/generator@7.27.1': + resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.0': - resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + '@babel/helper-module-transforms@7.27.1': + resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@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==} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.0': - resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} + '@babel/helpers@7.27.1': + resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.0': - resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} + '@babel/parser@7.27.2': + resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} engines: {node: '>=6.0.0'} hasBin: true @@ -1032,16 +1013,16 @@ packages: resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.0': - resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.0': - resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} + '@babel/traverse@7.27.1': + resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + '@babel/types@7.27.1': + resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} engines: {node: '>=6.9.0'} '@balena/dockerignore@1.0.2': @@ -1097,150 +1078,300 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.2': resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.2': resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.2': resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.2': resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.2': resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.2': resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.2': resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.2': resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.2': resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.2': resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.2': resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.2': resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.2': resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.2': resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.2': resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.2': resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.2': resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.2': resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.2': resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.2': resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.25.2': resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.2': resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.2': resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.2': resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2626,8 +2757,8 @@ packages: '@panva/hkdf@1.2.1': resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} - '@petamoriken/float16@3.9.1': - resolution: {integrity: sha512-j+ejhYwY6PeB+v1kn7lZFACUIG97u90WxMuGosILFsl9d4Ovi0sjk0GlPfoEcx+FzvXZDAfioD+NGnnPamXgMA==} + '@petamoriken/float16@3.9.2': + resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==} '@playwright/test@1.50.1': resolution: {integrity: sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==} @@ -3433,101 +3564,201 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.39.0': resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.39.0': resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.39.0': resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.39.0': resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.39.0': resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.39.0': resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.39.0': resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.39.0': resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.39.0': resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.39.0': resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.39.0': resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.39.0': resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.39.0': resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.39.0': resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.39.0': resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.39.0': resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} + cpu: [x64] + os: [win32] + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -3571,18 +3802,10 @@ packages: resolution: {integrity: sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.1.0': - resolution: {integrity: sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==} - engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.1.2': resolution: {integrity: sha512-7r6mZGwb5LmLJ+zPtkLoznf2EtwEuSWdtid10pjGl/7HefCE4mueOkrfki8JCUm99W6UfP47/r3tbxx9CfBN5A==} engines: {node: '>=18.0.0'} - '@smithy/core@3.3.0': - resolution: {integrity: sha512-r6gvs5OfRq/w+9unPm7B3po4rmWaGh0CIL/OwHntGGux7+RhOOZLGuurbeMgWV6W55ZuyMTypJLeH0vn/ZRaWQ==} - engines: {node: '>=18.0.0'} - '@smithy/core@3.3.1': resolution: {integrity: sha512-W7AppgQD3fP1aBmo8wWo0id5zeR2/aYRy067vZsDVaa6v/mdhkg6DxXwEVuSPjZl+ZnvWAQbUMCd5ckw38+tHQ==} engines: {node: '>=18.0.0'} @@ -3630,18 +3853,10 @@ packages: resolution: {integrity: sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.1.1': - resolution: {integrity: sha512-z5RmcHxjvScL+LwEDU2mTNCOhgUs4lu5PGdF1K36IPRmUHhNFxNxgenSB7smyDiYD4vdKQ7CAZtG5cUErqib9w==} - engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.1.4': resolution: {integrity: sha512-qWyYvszzvDjT2AxRvEpNhnMTo8QX9MCAtuSA//kYbXewb+2mEGQCk1UL4dNIrKLcF5KT11dOJtxFYT0kzajq5g==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.1.2': - resolution: {integrity: sha512-qN/Mmxm8JWtFAjozJ8VSTM83KOX4cIks8UjDqqNkKIegzPrE5ZKPNCQ/DqUSIF90pue5a/NycNXnBod2NwvZZQ==} - engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.1.5': resolution: {integrity: sha512-eQguCTA2TRGyg4P7gDuhRjL2HtN5OKJXysq3Ufj0EppZe4XBmSyKIvVX9ws9KkD3lkJskw1tfE96wMFsiUShaw==} engines: {node: '>=18.0.0'} @@ -3654,10 +3869,6 @@ packages: resolution: {integrity: sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.0.2': - resolution: {integrity: sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==} - engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.1.1': resolution: {integrity: sha512-1slS5jf5icHETwl5hxEVBj+mh6B+LbVW4yRINsGtUKH+nxM5Pw2H59+qf+JqYFCHp9jssG4vX81f5WKnjMN3Vw==} engines: {node: '>=18.0.0'} @@ -3702,10 +3913,6 @@ packages: resolution: {integrity: sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.2.1': - resolution: {integrity: sha512-fbniZef60QdsBc4ZY0iyI8xbFHIiC/QRtPi66iE4ufjiE/aaz7AfUXzcWMkpO8r+QhLeNRIfmPchIG+3/QDZ6g==} - engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.2.4': resolution: {integrity: sha512-oolSEpr/ABUtVmFMdNgi6sSXsK4csV9n4XM9yXgvDJGRa32tQDUdv9s+ztFZKccay1AiTWLSGsyDj2xy1gsv7Q==} engines: {node: '>=18.0.0'} @@ -3754,22 +3961,10 @@ packages: resolution: {integrity: sha512-0vPKiC+rXWMq397tsa/RFcO/kJ1UsibgNCXScMsRwzm9WMT4QjGf43zVPWZ5hPLu3z/1XddiZFIlKcu2j/yUuQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.0.9': - resolution: {integrity: sha512-B8j0XsElvyhv6+5hlFf6vFV/uCSyLKcInpeXOGnOImX2mGXshE01RvPoGipTlRpIk53e6UfYj7WdDdgbVfXDZw==} - engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.0.12': resolution: {integrity: sha512-zCx9noceM3Pw2jvcJ3w3RbvKnPe3lCo6txH9ksZj6CeRZPkvRZPLXmKVSOvDr9QQP3VRq/WnBLd+LTZAL7+0IQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.0.9': - resolution: {integrity: sha512-wTDU8P/zdIf9DOpV5qm64HVgGRXvqjqB/fJZTEQbrz3s79JHM/E7XkMm/876Oq+ZLHJQgnXM9QHDo29dlM62eA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-endpoints@3.0.2': - resolution: {integrity: sha512-6QSutU5ZyrpNbnd51zRTL7goojlcnuOB55+F9VBD+j8JpRY50IGamsjlycrmpn8PQkmJucFW8A0LSfXj7jjtLQ==} - engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.0.4': resolution: {integrity: sha512-VfFATC1bmZLV2858B/O1NpMcL32wYo8DPPhHxYxDCodDl3f3mSZ5oJheW1IF91A0EeAADz2WsakM/hGGPGNKLg==} engines: {node: '>=18.0.0'} @@ -4428,8 +4623,8 @@ packages: resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} - axios@1.8.4: - resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==} + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -4483,6 +4678,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.24.5: + resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -4545,6 +4745,9 @@ packages: caniuse-lite@1.0.30001703: resolution: {integrity: sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==} + caniuse-lite@1.0.30001717: + resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==} + canvas-confetti@1.9.3: resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} @@ -4652,8 +4855,8 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - cohere-ai@7.16.0: - resolution: {integrity: sha512-hrG3EtVNSJLxJTaEeGRli+5rX34GiQC/UZ2WuUpaWiRwYbfzz7zKflfU/tg8SFFjkvYHDyS43UvVESepNd8C4w==} + cohere-ai@7.17.1: + resolution: {integrity: sha512-GI/uWVYYGIN3gdjJRlbjEaLJNJVXsUJyOlPqwBWgAmK18kP4CJoErxKwU0aLe3tHHOBcC2RqXe6PmGO0dz7dpQ==} color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -5109,6 +5312,9 @@ packages: resolution: {integrity: sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ==} engines: {node: '>=12.0.0'} + electron-to-chromium@1.5.150: + resolution: {integrity: sha512-rOOkP2ZUMx1yL4fCxXQKDHQ8ZXwisb2OycOQVKHgvB3ZI4CvehOd4y2tfnnLDieJ3Zs1RL1Dlp3cMkyIn7nnXA==} + electron-to-chromium@1.5.87: resolution: {integrity: sha512-mPFwmEWmRivw2F8x3w3l2m6htAUN97Gy0kwpO++2m9iT1Gt8RCFVUfv9U/sIbHJ6rY4P6/ooqFL/eL7ock+pPg==} @@ -5166,9 +5372,6 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} - es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -5204,6 +5407,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -5419,6 +5627,10 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -5793,8 +6005,8 @@ packages: highlightjs-vue@1.0.0: resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} - hono-openapi@0.4.6: - resolution: {integrity: sha512-wSDySp2cS5Zcf1OeLG7nCP3eMsCpcDomN137T9B6/Z5Qq3D0nWgMf0I3Gl41SE1rE37OBQ0Smqx3LOP9Hk//7A==} + hono-openapi@0.4.8: + resolution: {integrity: sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA==} peerDependencies: '@hono/arktype-validator': ^2.0.0 '@hono/effect-validator': ^1.2.0 @@ -5803,7 +6015,7 @@ packages: '@hono/zod-validator': ^0.4.1 '@sinclair/typebox': ^0.34.9 '@valibot/to-json-schema': ^1.0.0-beta.3 - arktype: ^2.0.0-rc.25 + arktype: ^2.0.0 effect: ^3.11.3 hono: ^4.6.13 openapi-types: ^12.1.3 @@ -5831,8 +6043,6 @@ packages: optional: true hono: optional: true - openapi-types: - optional: true valibot: optional: true zod: @@ -5840,8 +6050,8 @@ packages: zod-openapi: optional: true - hono@4.7.6: - resolution: {integrity: sha512-564rVzELU+9BRqqx5k8sT2NFwGD3I3Vifdb6P7CmM6FiarOSY+fDC+6B+k9wcCb86ReoayteZP2ki0cRLN1jbw==} + hono@4.7.8: + resolution: {integrity: sha512-PCibtFdxa7/Ldud9yddl1G81GjYaeMYYTq4ywSaNsYbB1Lug4mwtOMJf2WXykL0pntYwmpRJeOI3NmoDgD+Jxw==} engines: {node: '>=16.9.0'} html-entities@2.6.0: @@ -5982,6 +6192,10 @@ packages: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-data-view@1.0.2: resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} @@ -6259,6 +6473,7 @@ packages: libsql@0.4.7: resolution: {integrity: sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lightningcss-darwin-arm64@1.29.2: @@ -6858,6 +7073,9 @@ packages: resolution: {integrity: sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==} os: [win32, darwin, linux] + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -7501,6 +7719,11 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -7554,6 +7777,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} @@ -8157,6 +8385,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -8590,7 +8824,7 @@ snapshots: '@aws-crypto/crc32@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/types': 3.804.0 tslib: 1.14.1 '@aws-crypto/sha256-browser@5.2.0': @@ -8615,7 +8849,7 @@ snapshots: '@aws-crypto/util@3.0.0': dependencies: - '@aws-sdk/types': 3.775.0 + '@aws-sdk/types': 3.804.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -8671,43 +8905,43 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-cognito-identity@3.787.0': + '@aws-sdk/client-cognito-identity@3.804.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.775.0 - '@aws-sdk/credential-provider-node': 3.787.0 - '@aws-sdk/middleware-host-header': 3.775.0 - '@aws-sdk/middleware-logger': 3.775.0 - '@aws-sdk/middleware-recursion-detection': 3.775.0 - '@aws-sdk/middleware-user-agent': 3.787.0 - '@aws-sdk/region-config-resolver': 3.775.0 - '@aws-sdk/types': 3.775.0 - '@aws-sdk/util-endpoints': 3.787.0 - '@aws-sdk/util-user-agent-browser': 3.775.0 - '@aws-sdk/util-user-agent-node': 3.787.0 - '@smithy/config-resolver': 4.1.0 - '@smithy/core': 3.3.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/credential-provider-node': 3.804.0 + '@aws-sdk/middleware-host-header': 3.804.0 + '@aws-sdk/middleware-logger': 3.804.0 + '@aws-sdk/middleware-recursion-detection': 3.804.0 + '@aws-sdk/middleware-user-agent': 3.804.0 + '@aws-sdk/region-config-resolver': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@aws-sdk/util-endpoints': 3.804.0 + '@aws-sdk/util-user-agent-browser': 3.804.0 + '@aws-sdk/util-user-agent-node': 3.804.0 + '@smithy/config-resolver': 4.1.2 + '@smithy/core': 3.3.1 '@smithy/fetch-http-handler': 5.0.2 '@smithy/hash-node': 4.0.2 '@smithy/invalid-dependency': 4.0.2 '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.1 - '@smithy/middleware-retry': 4.1.2 + '@smithy/middleware-endpoint': 4.1.4 + '@smithy/middleware-retry': 4.1.5 '@smithy/middleware-serde': 4.0.3 '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.0.2 + '@smithy/node-config-provider': 4.1.1 '@smithy/node-http-handler': 4.0.4 '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.1 + '@smithy/smithy-client': 4.2.4 '@smithy/types': 4.2.0 '@smithy/url-parser': 4.0.2 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.9 - '@smithy/util-defaults-mode-node': 4.0.9 - '@smithy/util-endpoints': 3.0.2 + '@smithy/util-defaults-mode-browser': 4.0.12 + '@smithy/util-defaults-mode-node': 4.0.12 + '@smithy/util-endpoints': 3.0.4 '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.3 '@smithy/util-utf8': 4.0.0 @@ -8761,43 +8995,43 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sagemaker@3.787.0': + '@aws-sdk/client-sagemaker@3.805.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.775.0 - '@aws-sdk/credential-provider-node': 3.787.0 - '@aws-sdk/middleware-host-header': 3.775.0 - '@aws-sdk/middleware-logger': 3.775.0 - '@aws-sdk/middleware-recursion-detection': 3.775.0 - '@aws-sdk/middleware-user-agent': 3.787.0 - '@aws-sdk/region-config-resolver': 3.775.0 - '@aws-sdk/types': 3.775.0 - '@aws-sdk/util-endpoints': 3.787.0 - '@aws-sdk/util-user-agent-browser': 3.775.0 - '@aws-sdk/util-user-agent-node': 3.787.0 - '@smithy/config-resolver': 4.1.0 - '@smithy/core': 3.3.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/credential-provider-node': 3.804.0 + '@aws-sdk/middleware-host-header': 3.804.0 + '@aws-sdk/middleware-logger': 3.804.0 + '@aws-sdk/middleware-recursion-detection': 3.804.0 + '@aws-sdk/middleware-user-agent': 3.804.0 + '@aws-sdk/region-config-resolver': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@aws-sdk/util-endpoints': 3.804.0 + '@aws-sdk/util-user-agent-browser': 3.804.0 + '@aws-sdk/util-user-agent-node': 3.804.0 + '@smithy/config-resolver': 4.1.2 + '@smithy/core': 3.3.1 '@smithy/fetch-http-handler': 5.0.2 '@smithy/hash-node': 4.0.2 '@smithy/invalid-dependency': 4.0.2 '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.1 - '@smithy/middleware-retry': 4.1.2 + '@smithy/middleware-endpoint': 4.1.4 + '@smithy/middleware-retry': 4.1.5 '@smithy/middleware-serde': 4.0.3 '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.0.2 + '@smithy/node-config-provider': 4.1.1 '@smithy/node-http-handler': 4.0.4 '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.1 + '@smithy/smithy-client': 4.2.4 '@smithy/types': 4.2.0 '@smithy/url-parser': 4.0.2 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.9 - '@smithy/util-defaults-mode-node': 4.0.9 - '@smithy/util-endpoints': 3.0.2 + '@smithy/util-defaults-mode-browser': 4.0.12 + '@smithy/util-defaults-mode-node': 4.0.12 + '@smithy/util-endpoints': 3.0.4 '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.3 '@smithy/util-utf8': 4.0.0 @@ -8808,42 +9042,42 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.787.0': + '@aws-sdk/client-sso@3.804.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.775.0 - '@aws-sdk/middleware-host-header': 3.775.0 - '@aws-sdk/middleware-logger': 3.775.0 - '@aws-sdk/middleware-recursion-detection': 3.775.0 - '@aws-sdk/middleware-user-agent': 3.787.0 - '@aws-sdk/region-config-resolver': 3.775.0 - '@aws-sdk/types': 3.775.0 - '@aws-sdk/util-endpoints': 3.787.0 - '@aws-sdk/util-user-agent-browser': 3.775.0 - '@aws-sdk/util-user-agent-node': 3.787.0 - '@smithy/config-resolver': 4.1.0 - '@smithy/core': 3.3.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/middleware-host-header': 3.804.0 + '@aws-sdk/middleware-logger': 3.804.0 + '@aws-sdk/middleware-recursion-detection': 3.804.0 + '@aws-sdk/middleware-user-agent': 3.804.0 + '@aws-sdk/region-config-resolver': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@aws-sdk/util-endpoints': 3.804.0 + '@aws-sdk/util-user-agent-browser': 3.804.0 + '@aws-sdk/util-user-agent-node': 3.804.0 + '@smithy/config-resolver': 4.1.2 + '@smithy/core': 3.3.1 '@smithy/fetch-http-handler': 5.0.2 '@smithy/hash-node': 4.0.2 '@smithy/invalid-dependency': 4.0.2 '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.1 - '@smithy/middleware-retry': 4.1.2 + '@smithy/middleware-endpoint': 4.1.4 + '@smithy/middleware-retry': 4.1.5 '@smithy/middleware-serde': 4.0.3 '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.0.2 + '@smithy/node-config-provider': 4.1.1 '@smithy/node-http-handler': 4.0.4 '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.1 + '@smithy/smithy-client': 4.2.4 '@smithy/types': 4.2.0 '@smithy/url-parser': 4.0.2 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.9 - '@smithy/util-defaults-mode-node': 4.0.9 - '@smithy/util-endpoints': 3.0.2 + '@smithy/util-defaults-mode-browser': 4.0.12 + '@smithy/util-defaults-mode-node': 4.0.12 + '@smithy/util-endpoints': 3.0.4 '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.3 '@smithy/util-utf8': 4.0.0 @@ -8894,15 +9128,15 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.775.0': + '@aws-sdk/core@3.804.0': dependencies: - '@aws-sdk/types': 3.775.0 - '@smithy/core': 3.3.0 - '@smithy/node-config-provider': 4.0.2 + '@aws-sdk/types': 3.804.0 + '@smithy/core': 3.3.1 + '@smithy/node-config-provider': 4.1.1 '@smithy/property-provider': 4.0.2 '@smithy/protocol-http': 5.1.0 '@smithy/signature-v4': 5.1.0 - '@smithy/smithy-client': 4.2.1 + '@smithy/smithy-client': 4.2.4 '@smithy/types': 4.2.0 '@smithy/util-middleware': 4.0.2 fast-xml-parser: 4.4.1 @@ -8922,20 +9156,20 @@ snapshots: fast-xml-parser: 4.4.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-cognito-identity@3.787.0': + '@aws-sdk/credential-provider-cognito-identity@3.804.0': dependencies: - '@aws-sdk/client-cognito-identity': 3.787.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/client-cognito-identity': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-env@3.775.0': + '@aws-sdk/credential-provider-env@3.804.0': dependencies: - '@aws-sdk/core': 3.775.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 tslib: 2.8.1 @@ -8948,15 +9182,15 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.775.0': + '@aws-sdk/credential-provider-http@3.804.0': dependencies: - '@aws-sdk/core': 3.775.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/fetch-http-handler': 5.0.2 '@smithy/node-http-handler': 4.0.4 '@smithy/property-provider': 4.0.2 '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.1 + '@smithy/smithy-client': 4.2.4 '@smithy/types': 4.2.0 '@smithy/util-stream': 4.2.0 tslib: 2.8.1 @@ -8974,17 +9208,17 @@ snapshots: '@smithy/util-stream': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.787.0': + '@aws-sdk/credential-provider-ini@3.804.0': dependencies: - '@aws-sdk/core': 3.775.0 - '@aws-sdk/credential-provider-env': 3.775.0 - '@aws-sdk/credential-provider-http': 3.775.0 - '@aws-sdk/credential-provider-process': 3.775.0 - '@aws-sdk/credential-provider-sso': 3.787.0 - '@aws-sdk/credential-provider-web-identity': 3.787.0 - '@aws-sdk/nested-clients': 3.787.0 - '@aws-sdk/types': 3.775.0 - '@smithy/credential-provider-imds': 4.0.2 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/credential-provider-env': 3.804.0 + '@aws-sdk/credential-provider-http': 3.804.0 + '@aws-sdk/credential-provider-process': 3.804.0 + '@aws-sdk/credential-provider-sso': 3.804.0 + '@aws-sdk/credential-provider-web-identity': 3.804.0 + '@aws-sdk/nested-clients': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@smithy/credential-provider-imds': 4.0.4 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 @@ -9010,16 +9244,16 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.787.0': + '@aws-sdk/credential-provider-node@3.804.0': dependencies: - '@aws-sdk/credential-provider-env': 3.775.0 - '@aws-sdk/credential-provider-http': 3.775.0 - '@aws-sdk/credential-provider-ini': 3.787.0 - '@aws-sdk/credential-provider-process': 3.775.0 - '@aws-sdk/credential-provider-sso': 3.787.0 - '@aws-sdk/credential-provider-web-identity': 3.787.0 - '@aws-sdk/types': 3.775.0 - '@smithy/credential-provider-imds': 4.0.2 + '@aws-sdk/credential-provider-env': 3.804.0 + '@aws-sdk/credential-provider-http': 3.804.0 + '@aws-sdk/credential-provider-ini': 3.804.0 + '@aws-sdk/credential-provider-process': 3.804.0 + '@aws-sdk/credential-provider-sso': 3.804.0 + '@aws-sdk/credential-provider-web-identity': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@smithy/credential-provider-imds': 4.0.4 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 @@ -9044,10 +9278,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.775.0': + '@aws-sdk/credential-provider-process@3.804.0': dependencies: - '@aws-sdk/core': 3.775.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 @@ -9062,12 +9296,12 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.787.0': + '@aws-sdk/credential-provider-sso@3.804.0': dependencies: - '@aws-sdk/client-sso': 3.787.0 - '@aws-sdk/core': 3.775.0 - '@aws-sdk/token-providers': 3.787.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/client-sso': 3.804.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/token-providers': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 @@ -9088,11 +9322,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.787.0': + '@aws-sdk/credential-provider-web-identity@3.804.0': dependencies: - '@aws-sdk/core': 3.775.0 - '@aws-sdk/nested-clients': 3.787.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/nested-clients': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 tslib: 2.8.1 @@ -9110,37 +9344,30 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-providers@3.787.0': - dependencies: - '@aws-sdk/client-cognito-identity': 3.787.0 - '@aws-sdk/core': 3.775.0 - '@aws-sdk/credential-provider-cognito-identity': 3.787.0 - '@aws-sdk/credential-provider-env': 3.775.0 - '@aws-sdk/credential-provider-http': 3.775.0 - '@aws-sdk/credential-provider-ini': 3.787.0 - '@aws-sdk/credential-provider-node': 3.787.0 - '@aws-sdk/credential-provider-process': 3.775.0 - '@aws-sdk/credential-provider-sso': 3.787.0 - '@aws-sdk/credential-provider-web-identity': 3.787.0 - '@aws-sdk/nested-clients': 3.787.0 - '@aws-sdk/types': 3.775.0 - '@smithy/config-resolver': 4.1.0 - '@smithy/core': 3.3.0 - '@smithy/credential-provider-imds': 4.0.2 - '@smithy/node-config-provider': 4.0.2 + '@aws-sdk/credential-providers@3.804.0': + dependencies: + '@aws-sdk/client-cognito-identity': 3.804.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/credential-provider-cognito-identity': 3.804.0 + '@aws-sdk/credential-provider-env': 3.804.0 + '@aws-sdk/credential-provider-http': 3.804.0 + '@aws-sdk/credential-provider-ini': 3.804.0 + '@aws-sdk/credential-provider-node': 3.804.0 + '@aws-sdk/credential-provider-process': 3.804.0 + '@aws-sdk/credential-provider-sso': 3.804.0 + '@aws-sdk/credential-provider-web-identity': 3.804.0 + '@aws-sdk/nested-clients': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@smithy/config-resolver': 4.1.2 + '@smithy/core': 3.3.1 + '@smithy/credential-provider-imds': 4.0.4 + '@smithy/node-config-provider': 4.1.1 '@smithy/property-provider': 4.0.2 '@smithy/types': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/middleware-host-header@3.775.0': - dependencies: - '@aws-sdk/types': 3.775.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 @@ -9148,25 +9375,12 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.775.0': - dependencies: - '@aws-sdk/types': 3.775.0 - '@smithy/types': 4.2.0 - tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.775.0': - dependencies: - '@aws-sdk/types': 3.775.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 @@ -9184,12 +9398,12 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.787.0': + '@aws-sdk/middleware-user-agent@3.804.0': dependencies: - '@aws-sdk/core': 3.775.0 - '@aws-sdk/types': 3.775.0 - '@aws-sdk/util-endpoints': 3.787.0 - '@smithy/core': 3.3.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@aws-sdk/util-endpoints': 3.804.0 + '@smithy/core': 3.3.1 '@smithy/protocol-http': 5.1.0 '@smithy/types': 4.2.0 tslib: 2.8.1 @@ -9204,42 +9418,42 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.787.0': + '@aws-sdk/nested-clients@3.804.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.775.0 - '@aws-sdk/middleware-host-header': 3.775.0 - '@aws-sdk/middleware-logger': 3.775.0 - '@aws-sdk/middleware-recursion-detection': 3.775.0 - '@aws-sdk/middleware-user-agent': 3.787.0 - '@aws-sdk/region-config-resolver': 3.775.0 - '@aws-sdk/types': 3.775.0 - '@aws-sdk/util-endpoints': 3.787.0 - '@aws-sdk/util-user-agent-browser': 3.775.0 - '@aws-sdk/util-user-agent-node': 3.787.0 - '@smithy/config-resolver': 4.1.0 - '@smithy/core': 3.3.0 + '@aws-sdk/core': 3.804.0 + '@aws-sdk/middleware-host-header': 3.804.0 + '@aws-sdk/middleware-logger': 3.804.0 + '@aws-sdk/middleware-recursion-detection': 3.804.0 + '@aws-sdk/middleware-user-agent': 3.804.0 + '@aws-sdk/region-config-resolver': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@aws-sdk/util-endpoints': 3.804.0 + '@aws-sdk/util-user-agent-browser': 3.804.0 + '@aws-sdk/util-user-agent-node': 3.804.0 + '@smithy/config-resolver': 4.1.2 + '@smithy/core': 3.3.1 '@smithy/fetch-http-handler': 5.0.2 '@smithy/hash-node': 4.0.2 '@smithy/invalid-dependency': 4.0.2 '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.1 - '@smithy/middleware-retry': 4.1.2 + '@smithy/middleware-endpoint': 4.1.4 + '@smithy/middleware-retry': 4.1.5 '@smithy/middleware-serde': 4.0.3 '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.0.2 + '@smithy/node-config-provider': 4.1.1 '@smithy/node-http-handler': 4.0.4 '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.1 + '@smithy/smithy-client': 4.2.4 '@smithy/types': 4.2.0 '@smithy/url-parser': 4.0.2 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.9 - '@smithy/util-defaults-mode-node': 4.0.9 - '@smithy/util-endpoints': 3.0.2 + '@smithy/util-defaults-mode-browser': 4.0.12 + '@smithy/util-defaults-mode-node': 4.0.12 + '@smithy/util-endpoints': 3.0.4 '@smithy/util-middleware': 4.0.2 '@smithy/util-retry': 4.0.3 '@smithy/util-utf8': 4.0.0 @@ -9295,10 +9509,10 @@ snapshots: '@smithy/protocol-http': 1.2.0 tslib: 2.8.1 - '@aws-sdk/region-config-resolver@3.775.0': + '@aws-sdk/region-config-resolver@3.804.0': dependencies: - '@aws-sdk/types': 3.775.0 - '@smithy/node-config-provider': 4.0.2 + '@aws-sdk/types': 3.804.0 + '@smithy/node-config-provider': 4.1.1 '@smithy/types': 4.2.0 '@smithy/util-config-provider': 4.0.0 '@smithy/util-middleware': 4.0.2 @@ -9318,10 +9532,10 @@ snapshots: '@smithy/signature-v4': 1.1.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.787.0': + '@aws-sdk/token-providers@3.804.0': dependencies: - '@aws-sdk/nested-clients': 3.787.0 - '@aws-sdk/types': 3.775.0 + '@aws-sdk/nested-clients': 3.804.0 + '@aws-sdk/types': 3.804.0 '@smithy/property-provider': 4.0.2 '@smithy/shared-ini-file-loader': 4.0.2 '@smithy/types': 4.2.0 @@ -9340,21 +9554,16 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.775.0': - dependencies: - '@smithy/types': 4.2.0 - tslib: 2.8.1 - '@aws-sdk/types@3.804.0': dependencies: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.787.0': + '@aws-sdk/util-endpoints@3.804.0': dependencies: - '@aws-sdk/types': 3.775.0 + '@aws-sdk/types': 3.804.0 '@smithy/types': 4.2.0 - '@smithy/util-endpoints': 3.0.2 + '@smithy/util-endpoints': 3.0.4 tslib: 2.8.1 '@aws-sdk/util-endpoints@3.806.0': @@ -9375,13 +9584,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.775.0': - dependencies: - '@aws-sdk/types': 3.775.0 - '@smithy/types': 4.2.0 - bowser: 2.11.0 - tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 @@ -9389,11 +9591,11 @@ snapshots: bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.787.0': + '@aws-sdk/util-user-agent-node@3.804.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.787.0 - '@aws-sdk/types': 3.775.0 - '@smithy/node-config-provider': 4.0.2 + '@aws-sdk/middleware-user-agent': 3.804.0 + '@aws-sdk/types': 3.804.0 + '@smithy/node-config-provider': 4.1.1 '@smithy/types': 4.2.0 tslib: 2.8.1 @@ -9409,26 +9611,26 @@ snapshots: dependencies: tslib: 2.8.1 - '@babel/code-frame@7.26.2': + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.27.2': {} - '@babel/core@7.26.10': + '@babel/core@7.27.1': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.27.0 - '@babel/helper-compilation-targets': 7.27.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) - '@babel/helpers': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/template': 7.27.0 - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.1 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) + '@babel/helpers': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -9437,79 +9639,79 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.27.0': + '@babel/generator@7.27.1': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.27.0': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.26.8 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 + '@babel/compat-data': 7.27.2 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.24.5 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-module-imports@7.25.9': + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.0 - '@babel/types': 7.27.0 + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': + '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.27.0 + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.27.0': + '@babel/helpers@7.27.1': dependencies: - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 + '@babel/template': 7.27.2 + '@babel/types': 7.27.1 - '@babel/parser@7.27.0': + '@babel/parser@7.27.2': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.27.1 '@babel/runtime@7.26.10': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.27.0': + '@babel/template@7.27.2': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 - '@babel/traverse@7.27.0': + '@babel/traverse@7.27.1': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/template': 7.27.2 + '@babel/types': 7.27.1 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.27.0': + '@babel/types@7.27.1': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 '@balena/dockerignore@1.0.2': {} @@ -9570,78 +9772,153 @@ snapshots: '@esbuild/aix-ppc64@0.25.2': optional: true + '@esbuild/aix-ppc64@0.25.4': + optional: true + '@esbuild/android-arm64@0.25.2': optional: true + '@esbuild/android-arm64@0.25.4': + optional: true + '@esbuild/android-arm@0.25.2': optional: true + '@esbuild/android-arm@0.25.4': + optional: true + '@esbuild/android-x64@0.25.2': optional: true + '@esbuild/android-x64@0.25.4': + optional: true + '@esbuild/darwin-arm64@0.25.2': optional: true + '@esbuild/darwin-arm64@0.25.4': + optional: true + '@esbuild/darwin-x64@0.25.2': optional: true + '@esbuild/darwin-x64@0.25.4': + optional: true + '@esbuild/freebsd-arm64@0.25.2': optional: true + '@esbuild/freebsd-arm64@0.25.4': + optional: true + '@esbuild/freebsd-x64@0.25.2': optional: true + '@esbuild/freebsd-x64@0.25.4': + optional: true + '@esbuild/linux-arm64@0.25.2': optional: true + '@esbuild/linux-arm64@0.25.4': + optional: true + '@esbuild/linux-arm@0.25.2': optional: true + '@esbuild/linux-arm@0.25.4': + optional: true + '@esbuild/linux-ia32@0.25.2': optional: true + '@esbuild/linux-ia32@0.25.4': + optional: true + '@esbuild/linux-loong64@0.25.2': optional: true + '@esbuild/linux-loong64@0.25.4': + optional: true + '@esbuild/linux-mips64el@0.25.2': optional: true + '@esbuild/linux-mips64el@0.25.4': + optional: true + '@esbuild/linux-ppc64@0.25.2': optional: true + '@esbuild/linux-ppc64@0.25.4': + optional: true + '@esbuild/linux-riscv64@0.25.2': optional: true + '@esbuild/linux-riscv64@0.25.4': + optional: true + '@esbuild/linux-s390x@0.25.2': optional: true + '@esbuild/linux-s390x@0.25.4': + optional: true + '@esbuild/linux-x64@0.25.2': optional: true + '@esbuild/linux-x64@0.25.4': + optional: true + '@esbuild/netbsd-arm64@0.25.2': optional: true + '@esbuild/netbsd-arm64@0.25.4': + optional: true + '@esbuild/netbsd-x64@0.25.2': optional: true + '@esbuild/netbsd-x64@0.25.4': + optional: true + '@esbuild/openbsd-arm64@0.25.2': optional: true + '@esbuild/openbsd-arm64@0.25.4': + optional: true + '@esbuild/openbsd-x64@0.25.2': optional: true + '@esbuild/openbsd-x64@0.25.4': + optional: true + '@esbuild/sunos-x64@0.25.2': optional: true + '@esbuild/sunos-x64@0.25.4': + optional: true + '@esbuild/win32-arm64@0.25.2': optional: true + '@esbuild/win32-arm64@0.25.4': + optional: true + '@esbuild/win32-ia32@0.25.2': optional: true + '@esbuild/win32-ia32@0.25.4': + optional: true + '@esbuild/win32-x64@0.25.2': optional: true + '@esbuild/win32-x64@0.25.4': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0(jiti@2.4.2))': dependencies: eslint: 9.21.0(jiti@2.4.2) @@ -10039,7 +10316,7 @@ snapshots: dependencies: '@lukeed/csprng': 1.1.0 - '@mastra/core@0.9.3(react@19.1.0)(zod@3.24.4)': + '@mastra/core@0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4)': dependencies: '@libsql/client': 0.14.0 '@opentelemetry/api': 1.9.0 @@ -10056,12 +10333,12 @@ snapshots: '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.32.0 ai: 4.3.15(react@19.1.0)(zod@3.24.4) - cohere-ai: 7.16.0 + cohere-ai: 7.17.1 date-fns: 3.6.0 dotenv: 16.5.0 fastembed: 1.14.4 - hono: 4.7.6 - hono-openapi: 0.4.6(hono@4.7.6)(zod@3.24.4) + hono: 4.7.8 + hono-openapi: 0.4.8(hono@4.7.8)(openapi-types@12.1.3)(zod@3.24.4) json-schema: 0.4.0 json-schema-to-zod: 2.6.1 pino: 9.6.0 @@ -10091,30 +10368,30 @@ snapshots: - valibot - zod-openapi - '@mastra/deployer@0.3.3(react@19.1.0)(typescript@5.8.3)': + '@mastra/deployer@0.3.3(openapi-types@12.1.3)(react@19.1.0)(typescript@5.8.3)': dependencies: - '@babel/core': 7.26.10 - '@babel/helper-module-imports': 7.25.9 - '@mastra/core': 0.9.3(react@19.1.0)(zod@3.24.4) - '@mastra/server': 2.0.3(@mastra/core@0.9.3(react@19.1.0)(zod@3.24.4)) + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1 + '@mastra/core': 0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4) + '@mastra/server': 2.0.3(@mastra/core@0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4)) '@neon-rs/load': 0.1.82 - '@rollup/plugin-alias': 5.1.1(rollup@4.39.0) - '@rollup/plugin-commonjs': 28.0.3(rollup@4.39.0) - '@rollup/plugin-json': 6.1.0(rollup@4.39.0) - '@rollup/plugin-node-resolve': 16.0.1(rollup@4.39.0) - '@rollup/plugin-virtual': 3.0.2(rollup@4.39.0) + '@rollup/plugin-alias': 5.1.1(rollup@4.40.2) + '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2) + '@rollup/plugin-json': 6.1.0(rollup@4.40.2) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2) + '@rollup/plugin-virtual': 3.0.2(rollup@4.40.2) '@sindresorhus/slugify': 2.2.1 builtins: 5.1.0 - detect-libc: 2.0.3 + detect-libc: 2.0.4 dotenv: 16.5.0 - esbuild: 0.25.2 + esbuild: 0.25.4 find-workspaces: 0.3.1 fs-extra: 11.3.0 - hono: 4.7.6 + hono: 4.7.8 resolve-from: 5.0.0 - rollup: 4.39.0 - rollup-plugin-esbuild: 6.2.1(esbuild@0.25.2)(rollup@4.39.0) - rollup-plugin-node-externals: 8.0.0(rollup@4.39.0) + rollup: 4.40.2 + rollup-plugin-esbuild: 6.2.1(esbuild@0.25.4)(rollup@4.40.2) + rollup-plugin-node-externals: 8.0.0(rollup@4.40.2) typescript-paths: 1.5.1(typescript@5.8.3) zod: 3.24.4 transitivePeerDependencies: @@ -10138,9 +10415,9 @@ snapshots: - valibot - zod-openapi - '@mastra/evals@0.1.22(ai@4.3.15(react@19.1.0)(zod@3.24.4))(react@19.1.0)': + '@mastra/evals@0.1.22(ai@4.3.15(react@19.1.0)(zod@3.24.4))(openapi-types@12.1.3)(react@19.1.0)': dependencies: - '@mastra/core': 0.9.3(react@19.1.0)(zod@3.24.4) + '@mastra/core': 0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4) ai: 4.3.15(react@19.1.0)(zod@3.24.4) compromise: 14.14.4 difflib: 0.2.4 @@ -10169,9 +10446,9 @@ snapshots: - valibot - zod-openapi - '@mastra/server@2.0.3(@mastra/core@0.9.3(react@19.1.0)(zod@3.24.4))': + '@mastra/server@2.0.3(@mastra/core@0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4))': dependencies: - '@mastra/core': 0.9.3(react@19.1.0)(zod@3.24.4) + '@mastra/core': 0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4) '@modelcontextprotocol/sdk@1.11.0': dependencies: @@ -11603,7 +11880,7 @@ snapshots: '@panva/hkdf@1.2.1': {} - '@petamoriken/float16@3.9.1': + '@petamoriken/float16@3.9.2': optional: true '@playwright/test@1.50.1': @@ -12420,13 +12697,13 @@ snapshots: - '@types/react' - immer - '@rollup/plugin-alias@5.1.1(rollup@4.39.0)': + '@rollup/plugin-alias@5.1.1(rollup@4.40.2)': optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 - '@rollup/plugin-commonjs@28.0.3(rollup@4.39.0)': + '@rollup/plugin-commonjs@28.0.3(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.4(picomatch@4.0.2) @@ -12434,96 +12711,156 @@ snapshots: magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 - '@rollup/plugin-json@6.1.0(rollup@4.39.0)': + '@rollup/plugin-json@6.1.0(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.39.0)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.10 optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 - '@rollup/plugin-virtual@3.0.2(rollup@4.39.0)': + '@rollup/plugin-virtual@3.0.2(rollup@4.40.2)': optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 - '@rollup/pluginutils@5.1.4(rollup@4.39.0)': + '@rollup/pluginutils@5.1.4(rollup@4.40.2)': dependencies: '@types/estree': 1.0.7 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 '@rollup/rollup-android-arm-eabi@4.39.0': optional: true + '@rollup/rollup-android-arm-eabi@4.40.2': + optional: true + '@rollup/rollup-android-arm64@4.39.0': optional: true + '@rollup/rollup-android-arm64@4.40.2': + optional: true + '@rollup/rollup-darwin-arm64@4.39.0': optional: true + '@rollup/rollup-darwin-arm64@4.40.2': + optional: true + '@rollup/rollup-darwin-x64@4.39.0': optional: true + '@rollup/rollup-darwin-x64@4.40.2': + optional: true + '@rollup/rollup-freebsd-arm64@4.39.0': optional: true + '@rollup/rollup-freebsd-arm64@4.40.2': + optional: true + '@rollup/rollup-freebsd-x64@4.39.0': optional: true + '@rollup/rollup-freebsd-x64@4.40.2': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.39.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.39.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.40.2': + optional: true + '@rollup/rollup-linux-arm64-musl@4.39.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.40.2': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.39.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.39.0': optional: true + '@rollup/rollup-linux-riscv64-musl@4.40.2': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.39.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.40.2': + optional: true + '@rollup/rollup-linux-x64-gnu@4.39.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.40.2': + optional: true + '@rollup/rollup-linux-x64-musl@4.39.0': optional: true + '@rollup/rollup-linux-x64-musl@4.40.2': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.39.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.40.2': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.39.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.40.2': + optional: true + '@rollup/rollup-win32-x64-msvc@4.39.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.40.2': + optional: true + '@rtsao/scc@1.1.0': {} '@sec-ant/readable-stream@0.4.1': {} @@ -12579,14 +12916,6 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@smithy/config-resolver@4.1.0': - dependencies: - '@smithy/node-config-provider': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/util-config-provider': 4.0.0 - '@smithy/util-middleware': 4.0.2 - tslib: 2.8.1 - '@smithy/config-resolver@4.1.2': dependencies: '@smithy/node-config-provider': 4.1.1 @@ -12595,17 +12924,6 @@ snapshots: '@smithy/util-middleware': 4.0.2 tslib: 2.8.1 - '@smithy/core@3.3.0': - dependencies: - '@smithy/middleware-serde': 4.0.3 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-stream': 4.2.0 - '@smithy/util-utf8': 4.0.0 - tslib: 2.8.1 - '@smithy/core@3.3.1': dependencies: '@smithy/middleware-serde': 4.0.3 @@ -12691,17 +13009,6 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.1.1': - dependencies: - '@smithy/core': 3.3.0 - '@smithy/middleware-serde': 4.0.3 - '@smithy/node-config-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 - '@smithy/util-middleware': 4.0.2 - tslib: 2.8.1 - '@smithy/middleware-endpoint@4.1.4': dependencies: '@smithy/core': 3.3.1 @@ -12713,18 +13020,6 @@ snapshots: '@smithy/util-middleware': 4.0.2 tslib: 2.8.1 - '@smithy/middleware-retry@4.1.2': - dependencies: - '@smithy/node-config-provider': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/service-error-classification': 4.0.3 - '@smithy/smithy-client': 4.2.1 - '@smithy/types': 4.2.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 - tslib: 2.8.1 - uuid: 9.0.1 - '@smithy/middleware-retry@4.1.5': dependencies: '@smithy/node-config-provider': 4.1.1 @@ -12747,13 +13042,6 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.0.2': - dependencies: - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 - tslib: 2.8.1 - '@smithy/node-config-provider@4.1.1': dependencies: '@smithy/property-provider': 4.0.2 @@ -12826,16 +13114,6 @@ snapshots: '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/smithy-client@4.2.1': - dependencies: - '@smithy/core': 3.3.0 - '@smithy/middleware-endpoint': 4.1.1 - '@smithy/middleware-stack': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-stream': 4.2.0 - tslib: 2.8.1 - '@smithy/smithy-client@4.2.4': dependencies: '@smithy/core': 3.3.1 @@ -12901,14 +13179,6 @@ snapshots: bowser: 2.11.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.0.9': - dependencies: - '@smithy/property-provider': 4.0.2 - '@smithy/smithy-client': 4.2.1 - '@smithy/types': 4.2.0 - bowser: 2.11.0 - tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.0.12': dependencies: '@smithy/config-resolver': 4.1.2 @@ -12919,22 +13189,6 @@ snapshots: '@smithy/types': 4.2.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.0.9': - dependencies: - '@smithy/config-resolver': 4.1.0 - '@smithy/credential-provider-imds': 4.0.2 - '@smithy/node-config-provider': 4.0.2 - '@smithy/property-provider': 4.0.2 - '@smithy/smithy-client': 4.2.1 - '@smithy/types': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-endpoints@3.0.2': - dependencies: - '@smithy/node-config-provider': 4.0.2 - '@smithy/types': 4.2.0 - tslib: 2.8.1 - '@smithy/util-endpoints@3.0.4': dependencies: '@smithy/node-config-provider': 4.1.1 @@ -13681,7 +13935,7 @@ snapshots: axe-core@4.10.2: {} - axios@1.8.4: + axios@1.9.0: dependencies: follow-redirects: 1.15.9 form-data: 4.0.2 @@ -13749,6 +14003,13 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.2(browserslist@4.24.4) + browserslist@4.24.5: + dependencies: + caniuse-lite: 1.0.30001717 + electron-to-chromium: 1.5.150 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.5) + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} @@ -13811,6 +14072,8 @@ snapshots: caniuse-lite@1.0.30001703: {} + caniuse-lite@1.0.30001717: {} + canvas-confetti@1.9.3: {} ccount@2.0.1: {} @@ -13920,10 +14183,10 @@ snapshots: - '@types/react' - '@types/react-dom' - cohere-ai@7.16.0: + cohere-ai@7.17.1: dependencies: - '@aws-sdk/client-sagemaker': 3.787.0 - '@aws-sdk/credential-providers': 3.787.0 + '@aws-sdk/client-sagemaker': 3.805.0 + '@aws-sdk/credential-providers': 3.804.0 '@aws-sdk/protocol-http': 3.374.0 '@aws-sdk/signature-v4': 3.374.0 convict: 6.2.4 @@ -14289,6 +14552,8 @@ snapshots: efrt@2.7.0: {} + electron-to-chromium@1.5.150: {} + electron-to-chromium@1.5.87: {} emoji-regex-xs@1.0.0: {} @@ -14396,8 +14661,6 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 - es-module-lexer@1.6.0: {} - es-module-lexer@1.7.0: {} es-object-atoms@1.0.0: @@ -14462,6 +14725,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.2 '@esbuild/win32-x64': 0.25.2 + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -14804,6 +15095,14 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -14882,7 +15181,7 @@ snapshots: find-workspaces@0.3.1: dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.3 pkg-types: 1.3.1 yaml: 2.7.1 @@ -15008,13 +15307,13 @@ snapshots: transitivePeerDependencies: - supports-color - geist@1.4.2(next@15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)): + geist@1.4.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)): dependencies: - next: 15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) + next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) gel@2.0.0: dependencies: - '@petamoriken/float16': 3.9.1 + '@petamoriken/float16': 3.9.2 debug: 4.4.0 env-paths: 3.0.0 semver: 7.7.1 @@ -15281,14 +15580,15 @@ snapshots: highlightjs-vue@1.0.0: {} - hono-openapi@0.4.6(hono@4.7.6)(zod@3.24.4): + hono-openapi@0.4.8(hono@4.7.8)(openapi-types@12.1.3)(zod@3.24.4): dependencies: json-schema-walker: 2.0.0 + openapi-types: 12.1.3 optionalDependencies: - hono: 4.7.6 + hono: 4.7.8 zod: 3.24.4 - hono@4.7.6: {} + hono@4.7.8: {} html-entities@2.6.0: {} @@ -15429,6 +15729,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.2: dependencies: call-bound: 1.0.3 @@ -15836,12 +16140,12 @@ snapshots: markdown-table@3.0.4: {} - mastra@0.6.2(@opentelemetry/api@1.9.0)(react@19.1.0)(typescript@5.8.3): + mastra@0.6.2(@opentelemetry/api@1.9.0)(openapi-types@12.1.3)(react@19.1.0)(typescript@5.8.3): dependencies: '@clack/prompts': 0.8.2 '@lukeed/uuid': 2.0.1 - '@mastra/core': 0.9.3(react@19.1.0)(zod@3.24.4) - '@mastra/deployer': 0.3.3(react@19.1.0)(typescript@5.8.3) + '@mastra/core': 0.9.3(openapi-types@12.1.3)(react@19.1.0)(zod@3.24.4) + '@mastra/deployer': 0.3.3(openapi-types@12.1.3)(react@19.1.0)(typescript@5.8.3) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@webcontainer/env': 1.1.1 commander: 12.1.0 @@ -16331,10 +16635,10 @@ snapshots: negotiator@1.0.0: {} - next-auth@5.0.0-beta.25(next@15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0): + next-auth@5.0.0-beta.25(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0): dependencies: '@auth/core': 0.37.2 - next: 15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) + next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) react: 19.1.0 next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): @@ -16342,17 +16646,17 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - next@15.3.2(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4): + next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4): dependencies: '@next/env': 15.3.2 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 - caniuse-lite: 1.0.30001703 + caniuse-lite: 1.0.30001717 postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.2 '@next/swc-darwin-x64': 15.3.2 @@ -16491,6 +16795,8 @@ snapshots: onnxruntime-common: 1.21.0 tar: 7.4.3 + openapi-types@12.1.3: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -16727,7 +17033,7 @@ snapshots: posthog-node@4.16.0: dependencies: - axios: 1.8.4 + axios: 1.9.0 transitivePeerDependencies: - debug @@ -17196,6 +17502,12 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@1.22.8: dependencies: is-core-module: 2.15.1 @@ -17245,20 +17557,20 @@ snapshots: semver-compare: 1.0.0 sprintf-js: 1.1.3 - rollup-plugin-esbuild@6.2.1(esbuild@0.25.2)(rollup@4.39.0): + rollup-plugin-esbuild@6.2.1(esbuild@0.25.4)(rollup@4.40.2): dependencies: debug: 4.4.0 - es-module-lexer: 1.6.0 - esbuild: 0.25.2 + es-module-lexer: 1.7.0 + esbuild: 0.25.4 get-tsconfig: 4.10.0 - rollup: 4.39.0 + rollup: 4.40.2 unplugin-utils: 0.2.4 transitivePeerDependencies: - supports-color - rollup-plugin-node-externals@8.0.0(rollup@4.39.0): + rollup-plugin-node-externals@8.0.0(rollup@4.40.2): dependencies: - rollup: 4.39.0 + rollup: 4.40.2 rollup@4.39.0: dependencies: @@ -17286,6 +17598,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.39.0 fsevents: 2.3.3 + rollup@4.40.2: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 + fsevents: 2.3.3 + rope-sequence@1.3.4: {} router@2.2.0: @@ -17410,7 +17748,7 @@ snapshots: sharp@0.34.1: dependencies: color: 4.2.3 - detect-libc: 2.0.3 + detect-libc: 2.0.4 semver: 7.7.1 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.1 @@ -17671,12 +18009,12 @@ snapshots: dependencies: inline-style-parser: 0.2.4 - styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.27.1)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 optionalDependencies: - '@babel/core': 7.26.10 + '@babel/core': 7.27.1 stylis@4.3.5: {} @@ -17990,6 +18328,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.1.3(browserslist@4.24.5): + dependencies: + browserslist: 4.24.5 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 From 7de5a55eef7614995bfd5cddd1d22ffc31436187 Mon Sep 17 00:00:00 2001 From: urso Date: Wed, 14 May 2025 18:06:39 +0200 Subject: [PATCH 3/5] llmcheck --- .../chat/artifacts/server-document-handler.ts | 89 ++++++++++ .../src/components/chat/artifacts/server.ts | 90 +--------- .../components/chat/artifacts/sheet/server.ts | 8 +- .../components/chat/artifacts/text/server.ts | 2 +- apps/dbagent/src/evals/chat/judge.eval.ts | 156 ++++++++---------- apps/dbagent/src/evals/chat/tests.eval.ts | 68 ++++---- .../dbagent/src/evals/chat/toolChoice.eval.ts | 27 ++- apps/dbagent/src/evals/lib/chat-runner.ts | 101 +++++++++--- apps/dbagent/src/evals/lib/llmcheck/index.ts | 65 ++++++++ .../src/evals/lib/llmcheck/metrics/concise.ts | 28 ++++ .../lib/llmcheck/metrics/final-answer.ts | 24 +++ .../src/evals/lib/llmcheck/metrics/index.ts | 2 + .../lib/llmcheck/metrics/regex-response.ts | 32 ++++ .../evals/lib/llmcheck/metrics/toolchoice.ts | 51 ++++++ .../src/evals/lib/llmcheck/metrics/types.ts | 49 ++++++ .../src/evals/lib/llmcheck/metrics/util.ts | 98 +++++++++++ apps/dbagent/src/evals/lib/llmcheck/types.ts | 28 ++++ apps/dbagent/src/evals/lib/schemas.ts | 7 +- apps/dbagent/src/evals/lib/trace.ts | 28 +++- apps/dbagent/src/lib/ai/agent.ts | 59 ++++++- apps/dbagent/src/lib/ai/model.ts | 111 +++++++++---- apps/dbagent/src/lib/ai/tools/db.ts | 82 ++------- 22 files changed, 854 insertions(+), 351 deletions(-) create mode 100644 apps/dbagent/src/components/chat/artifacts/server-document-handler.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/index.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/concise.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/regex-response.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/types.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/metrics/util.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/types.ts diff --git a/apps/dbagent/src/components/chat/artifacts/server-document-handler.ts b/apps/dbagent/src/components/chat/artifacts/server-document-handler.ts new file mode 100644 index 00000000..18d21452 --- /dev/null +++ b/apps/dbagent/src/components/chat/artifacts/server-document-handler.ts @@ -0,0 +1,89 @@ +import { DataStreamWriter } from 'ai'; +import { saveDocument } from '~/lib/db/chats'; +import { DBAccess } from '~/lib/db/db'; +import { ArtifactDocument } from '~/lib/db/schema'; +import { ArtifactKind } from './artifact'; + +export interface SaveDocumentProps { + id: string; + title: string; + kind: ArtifactKind; + content: string; + userId: string; +} + +export interface CreateDocumentCallbackProps { + id: string; + title: string; + dataStream: DataStreamWriter; + userId: string; + projectId: string; + dbAccess: DBAccess; +} + +export interface UpdateDocumentCallbackProps { + document: ArtifactDocument; + description: string; + dataStream: DataStreamWriter; + userId: string; + projectId: string; + dbAccess: DBAccess; +} + +export interface DocumentHandler { + kind: T; + onCreateDocument: (args: CreateDocumentCallbackProps) => Promise; + onUpdateDocument: (args: UpdateDocumentCallbackProps) => Promise; +} + +export function createDocumentHandler(config: { + kind: T; + onCreateDocument: (params: CreateDocumentCallbackProps) => Promise; + onUpdateDocument: (params: UpdateDocumentCallbackProps) => Promise; +}): DocumentHandler { + return { + kind: config.kind, + onCreateDocument: async (args: CreateDocumentCallbackProps) => { + const draftContent = await config.onCreateDocument({ + id: args.id, + title: args.title, + dataStream: args.dataStream, + userId: args.userId, + projectId: args.projectId, + dbAccess: args.dbAccess + }); + + await saveDocument(args.dbAccess, { + id: args.id, + title: args.title, + content: draftContent, + kind: config.kind, + projectId: args.projectId, + userId: args.userId + }); + + return; + }, + onUpdateDocument: async (args: UpdateDocumentCallbackProps) => { + const draftContent = await config.onUpdateDocument({ + document: args.document, + description: args.description, + dataStream: args.dataStream, + userId: args.userId, + projectId: args.projectId, + dbAccess: args.dbAccess + }); + + await saveDocument(args.dbAccess, { + id: args.document.id, + title: args.document.title, + content: draftContent, + kind: config.kind, + projectId: args.projectId, + userId: args.userId + }); + + return; + } + }; +} diff --git a/apps/dbagent/src/components/chat/artifacts/server.ts b/apps/dbagent/src/components/chat/artifacts/server.ts index b020b289..f155068a 100644 --- a/apps/dbagent/src/components/chat/artifacts/server.ts +++ b/apps/dbagent/src/components/chat/artifacts/server.ts @@ -1,95 +1,7 @@ -import { DataStreamWriter } from 'ai'; -import { saveDocument } from '~/lib/db/chats'; -import { DBAccess } from '~/lib/db/db'; -import { ArtifactDocument } from '~/lib/db/schema'; -import { ArtifactKind } from './artifact'; +import { DocumentHandler } from './server-document-handler'; import { sheetDocumentHandler } from './sheet/server'; import { textDocumentHandler } from './text/server'; -export interface SaveDocumentProps { - id: string; - title: string; - kind: ArtifactKind; - content: string; - userId: string; -} - -export interface CreateDocumentCallbackProps { - id: string; - title: string; - dataStream: DataStreamWriter; - userId: string; - projectId: string; - dbAccess: DBAccess; -} - -export interface UpdateDocumentCallbackProps { - document: ArtifactDocument; - description: string; - dataStream: DataStreamWriter; - userId: string; - projectId: string; - dbAccess: DBAccess; -} - -export interface DocumentHandler { - kind: T; - onCreateDocument: (args: CreateDocumentCallbackProps) => Promise; - onUpdateDocument: (args: UpdateDocumentCallbackProps) => Promise; -} - -export function createDocumentHandler(config: { - kind: T; - onCreateDocument: (params: CreateDocumentCallbackProps) => Promise; - onUpdateDocument: (params: UpdateDocumentCallbackProps) => Promise; -}): DocumentHandler { - return { - kind: config.kind, - onCreateDocument: async (args: CreateDocumentCallbackProps) => { - const draftContent = await config.onCreateDocument({ - id: args.id, - title: args.title, - dataStream: args.dataStream, - userId: args.userId, - projectId: args.projectId, - dbAccess: args.dbAccess - }); - - await saveDocument(args.dbAccess, { - id: args.id, - title: args.title, - content: draftContent, - kind: config.kind, - projectId: args.projectId, - userId: args.userId - }); - - return; - }, - onUpdateDocument: async (args: UpdateDocumentCallbackProps) => { - const draftContent = await config.onUpdateDocument({ - document: args.document, - description: args.description, - dataStream: args.dataStream, - userId: args.userId, - projectId: args.projectId, - dbAccess: args.dbAccess - }); - - await saveDocument(args.dbAccess, { - id: args.document.id, - title: args.document.title, - content: draftContent, - kind: config.kind, - projectId: args.projectId, - userId: args.userId - }); - - return; - } - }; -} - /* * Use this array to define the document handlers for each artifact kind. */ diff --git a/apps/dbagent/src/components/chat/artifacts/sheet/server.ts b/apps/dbagent/src/components/chat/artifacts/sheet/server.ts index ce60c777..caad60ec 100644 --- a/apps/dbagent/src/components/chat/artifacts/sheet/server.ts +++ b/apps/dbagent/src/components/chat/artifacts/sheet/server.ts @@ -1,7 +1,13 @@ import { z } from 'zod'; import { commonModel } from '~/lib/ai/agent'; import { sheetPrompt, updateDocumentPrompt } from '~/lib/ai/prompts'; -import { createDocumentHandler } from '../server'; +import { createDocumentHandler } from '../server-document-handler'; + +// Note: To mock createDocumentHandler in tests, you can use a jest mock or a similar mocking library. +// For example, you can define a mock implementation like this: +// jest.mock('~/components/chat/artifacts/server', () => ({ +// createDocumentHandler: jest.fn().mockReturnValue(jest.fn()) +// })); export const sheetDocumentHandler = createDocumentHandler<'sheet'>({ kind: 'sheet', diff --git a/apps/dbagent/src/components/chat/artifacts/text/server.ts b/apps/dbagent/src/components/chat/artifacts/text/server.ts index 1be19db9..7a8967b3 100644 --- a/apps/dbagent/src/components/chat/artifacts/text/server.ts +++ b/apps/dbagent/src/components/chat/artifacts/text/server.ts @@ -1,7 +1,7 @@ import { AugmentedLanguageModel } from '~/lib/ai/model'; import { updateDocumentPrompt } from '~/lib/ai/prompts'; import { getProviderRegistry } from '~/lib/ai/providers'; -import { createDocumentHandler } from '../server'; +import { createDocumentHandler } from '../server-document-handler'; const titleModel = new AugmentedLanguageModel({ providerRegistry: getProviderRegistry, diff --git a/apps/dbagent/src/evals/chat/judge.eval.ts b/apps/dbagent/src/evals/chat/judge.eval.ts index a66206d2..5ad84d38 100644 --- a/apps/dbagent/src/evals/chat/judge.eval.ts +++ b/apps/dbagent/src/evals/chat/judge.eval.ts @@ -1,110 +1,94 @@ -import { generateObject } from 'ai'; import fs from 'fs'; import path from 'path'; import { afterAll, beforeAll, describe } from 'vitest'; -import { z } from 'zod'; -import { evalChat } from '~/evals/lib/chat-runner'; -import { getModelInstance } from '~/lib/ai/agent'; +import { AgentModelDeps, chatModel, getAgentMockDeps, getModelInstance } from '~/lib/ai/agent'; +import { Tool } from '~/lib/ai/model'; import { env } from '~/lib/env/eval'; -import { PostgresConfig, runSql, startPostgresContainer } from '../lib/eval-docker-db'; -import { mockGetConnectionInfo, mockGetProjectsById } from '../lib/mocking'; -import { evalResultEnum } from '../lib/schemas'; +import { TableStat } from '~/lib/targetdb/db'; +import { turnFromResponse } from '../lib/chat-runner'; +import { evalTurn, LLMTurn, setEvalModel } from '../lib/llmcheck'; +import { conciseAnswerMetric, Metric } from '../lib/llmcheck/metrics'; +import { finalAnswerMetric } from '../lib/llmcheck/metrics/final-answer'; import { ensureTraceFolderExistsExpect } from '../lib/test-id'; -import { stepToHuman } from '../lib/trace'; import { EvalCase, runEvals } from '../lib/vitest-helpers'; -let dbConfig: PostgresConfig; beforeAll(async () => { - mockGetProjectsById(); - mockGetConnectionInfo(); - try { - dbConfig = await startPostgresContainer({ port: 9888 }); - await runSql( - `create table dogs ( - id serial primary key, - name text - );`, - dbConfig - ); - } catch (error) { - console.error('Error starting postgres container', error); - throw error; - } + const model = await getModelInstance(env.JUDGE_MODEL); + setEvalModel(model); }); afterAll(async () => { - await dbConfig.close(); + setEvalModel(undefined); }); -type LLMJudgeConfig = { - prompt: (args: { input: string; steps: string[]; finalAnswer: string }) => string; - name: string; -}; - -const finalAnswerJudge = (expectedAnswer = ''): LLMJudgeConfig => ({ - name: 'final_answer', - prompt: ({ input, finalAnswer }) => ` - The following question was answered by an expert in PostgreSQL and database administration: - ${input} - ${finalAnswer} - ${expectedAnswer ? `${expectedAnswer}` : ''} - Please determine whether expert passed or failed at answering the question correctly and accurately. Please provied a critique of how the answer could be improved or does not match the response of an expert in PostgreSQl and database administration. - ` -}); +type LLMJudgeEval = EvalCase & { turn: LLMTurn; judge: Metric }; -const conciseAnswerJudge: LLMJudgeConfig = { - name: 'concise', - prompt: ({ input, finalAnswer }) => ` - The following question was answered by an expert in PostgreSQL and database administration: - ${input} - ${finalAnswer} +const conciseAnswer = conciseAnswerMetric(); - Please determine whether expert passed or failed at answering the question concisely with no extra information offered unless it's explicitly asked for in the question or it's almost certain from the question the user would want it. Please provied a critique of how the answer could be improved or does not match the resp` +// Mock the agent backend services. The getAgentMockDeps creates +// mocks that will raise an error when the tools are called. +// Set some default tool response for the eval via toolMocks. `toolMocks` gets +// the mocked tools as used by the agent and modifies the responsess for a +// selected set of tools. +const deps = getAgentMockDeps({}); +const toolMocks = (tools: Record>) => { + tools.getTablesInfo!.execute = async () => { + const data: TableStat[] = [ + { + name: 'dogs', + schema: 'public', + rows: 100, + size: '100', + seqScans: 100, + idxScans: 100, + nTupIns: 100, + nTupUpd: 100, + nTupDel: 100 + } + ]; + return data; + }; + return tools; }; -type LLMJudgeEval = EvalCase & { prompt: string; judge: LLMJudgeConfig }; +describe.concurrent('judge', async () => { + const evalCases: LLMJudgeEval[] = ( + await Promise.all( + [ + { + id: 'tables_in_db', + prompt: 'What tables do I have in my db?', + judges: [finalAnswerMetric('dogs'), conciseAnswer] + }, + { + id: 'tables_in_db_how_many', + prompt: 'How many tables do I have in my db?', + judges: [finalAnswerMetric('1'), conciseAnswer] + } + ].map(async (evalCase) => { + const result = await chatModel.generateText({ + prompt: evalCase.prompt, + deps, + maxSteps: 20, + tools: toolMocks + }); -describe.concurrent('judge', () => { - const evalCases: LLMJudgeEval[] = [ - { - id: 'tables_in_db', - prompt: 'What tables do I have in my db?', - judges: [finalAnswerJudge('dogs'), conciseAnswerJudge] - }, - { - id: 'tables_in_db_how_many', - prompt: 'How many tables do I have in my db?', - judges: [finalAnswerJudge('1'), conciseAnswerJudge] - } - ].flatMap((evalCase) => - evalCase.judges.map((judge) => ({ - id: `${judge.name}_${evalCase.id}`, - prompt: evalCase.prompt, - judge: judge - })) - ); + return evalCase.judges.map((judge) => ({ + id: `${judge.name()}_${evalCase.id}`, + turn: turnFromResponse(evalCase.prompt, result), + judge + })) as LLMJudgeEval[]; + }) + ) + ).flat(); - runEvals(evalCases, async ({ prompt, judge }, { expect }) => { + runEvals(evalCases, async ({ turn, judge }, { expect }) => { const traceFolder = ensureTraceFolderExistsExpect(expect); - const result = await evalChat({ - messages: [{ role: 'user', content: prompt }], - dbConnection: dbConfig.connectionString, - expect - }); - const humanSteps = result.steps.map(stepToHuman); - const finalAnswer = result.text; - const { object: judgeResponse } = await generateObject({ - model: await getModelInstance(env.JUDGE_MODEL), - schema: z.object({ - result: evalResultEnum, - critique: z.string() - }), - prompt: judge.prompt({ input: prompt, steps: humanSteps, finalAnswer }) - }); - const judgeResponseFile = path.join(traceFolder, 'judgeResponse.txt'); - fs.writeFileSync(judgeResponseFile, judgeResponse.critique); + const measure = await evalTurn(turn, judge); - expect(judgeResponse.result).toEqual('passed'); + const evalFile = path.join(traceFolder, 'eval.txt'); + fs.writeFileSync(evalFile, measure.reason ?? ''); + expect(measure.success).toBe(true); }); }); diff --git a/apps/dbagent/src/evals/chat/tests.eval.ts b/apps/dbagent/src/evals/chat/tests.eval.ts index becf04cd..6109ae5e 100644 --- a/apps/dbagent/src/evals/chat/tests.eval.ts +++ b/apps/dbagent/src/evals/chat/tests.eval.ts @@ -1,37 +1,38 @@ -import { afterAll, beforeAll, describe } from 'vitest'; -import { evalChat } from '~/evals/lib/chat-runner'; -import { PostgresConfig, runSql, startPostgresContainer } from '../lib/eval-docker-db'; -import { mockGetConnectionInfo, mockGetProjectsById } from '../lib/mocking'; +import { describe } from 'vitest'; +import { evalChat, turnFromResponse } from '~/evals/lib/chat-runner'; +import { TableStat } from '~/lib/targetdb/db'; +import { evalTurn } from '../lib/llmcheck'; +import { regexResponseMetric } from '../lib/llmcheck/metrics/regex-response'; import { EvalCase, runEvals } from '../lib/vitest-helpers'; -let dbConfig: PostgresConfig; -beforeAll(async () => { - mockGetProjectsById(); - mockGetConnectionInfo(); - try { - dbConfig = await startPostgresContainer({ port: 9889 }); - await runSql( - `create table dogs ( - id serial primary key, - name text - );`, - dbConfig - ); - } catch (error) { - console.error('Error starting postgres container', error); - throw error; - } -}); - -afterAll(async () => { - await dbConfig.close(); -}); - type Test = EvalCase & { prompt: string; finalAnswerRegex: RegExp; }; +const toolCalls = { + findTableSchema: async (_table: string) => { + return 'public'; + }, + + getTablesInfo: async () => { + const data: TableStat[] = [ + { + name: 'dogs', + schema: 'public', + rows: 100, + size: '24 kB', + seqScans: 100, + idxScans: 100, + nTupIns: 100, + nTupUpd: 100, + nTupDel: 100 + } + ]; + return data; + } +}; + describe.concurrent('test', () => { const evalCases: Test[] = [ { @@ -51,14 +52,11 @@ describe.concurrent('test', () => { } ]; runEvals(evalCases, async ({ prompt, finalAnswerRegex }, { expect }) => { - const result = await evalChat({ - messages: [{ role: 'user', content: prompt }], - dbConnection: dbConfig.connectionString, - expect - }); - - const finalAnswer = result.text; + const result = await evalChat({ prompt, expect, toolCalls }); + const turn = turnFromResponse(prompt, result); + const metric = regexResponseMetric(finalAnswerRegex); + const measure = await evalTurn(turn, metric); - expect(finalAnswer).toMatch(finalAnswerRegex); + expect(measure.success).toBe(true); }); }); diff --git a/apps/dbagent/src/evals/chat/toolChoice.eval.ts b/apps/dbagent/src/evals/chat/toolChoice.eval.ts index f645c3c3..c4d28c5c 100644 --- a/apps/dbagent/src/evals/chat/toolChoice.eval.ts +++ b/apps/dbagent/src/evals/chat/toolChoice.eval.ts @@ -1,6 +1,8 @@ import { afterAll, beforeAll, describe } from 'vitest'; -import { evalChat } from '~/evals/lib/chat-runner'; +import { evalChat, turnFromResponse } from '~/evals/lib/chat-runner'; import { PostgresConfig, runSql, startPostgresContainer } from '../lib/eval-docker-db'; +import { evalTurn } from '../lib/llmcheck'; +import { toolChoiceMetric } from '../lib/llmcheck/metrics/toolchoice'; import { mockGetConnectionInfo, mockGetProjectsById } from '../lib/mocking'; import { EvalCase, runEvals } from '../lib/vitest-helpers'; @@ -171,12 +173,21 @@ describe.concurrent('tool_choice', () => { expect }); - const allToolCalls = result.steps.flatMap((step) => step.toolCalls); - const toolCallNames = allToolCalls.map((toolCall) => toolCall.toolName); - expect(toolCallNames).toEqual(expect.arrayContaining(toolCalls)); - const unexpectedToolCalls = toolCallNames.filter((toolCall) => !toolCalls.includes(toolCall)); - if (!allowOtherTools) { - expect(unexpectedToolCalls).toEqual([]); - } + const turn = { + ...turnFromResponse(prompt, result), + expectedTools: toolCalls.map((toolCall) => ({ + name: toolCall + })) + }; + + const measure = await evalTurn( + turn, + toolChoiceMetric({ + allowMissing: false, + allowOthers: allowOtherTools + }) + ); + + expect(measure.success).toBe(true); }); }); diff --git a/apps/dbagent/src/evals/lib/chat-runner.ts b/apps/dbagent/src/evals/lib/chat-runner.ts index bc6ffb95..8f8b32e8 100644 --- a/apps/dbagent/src/evals/lib/chat-runner.ts +++ b/apps/dbagent/src/evals/lib/chat-runner.ts @@ -1,21 +1,49 @@ import { CoreMessage, Message as SDKMessage } from 'ai'; +import { Pool } from 'pg'; import { ExpectStatic } from 'vitest'; import { generateUUID } from '~/components/chat/utils'; -import { agentModelDeps, chatModel, getChatSystemPrompt } from '~/lib/ai/agent'; +import { AgentModelDeps, agentModelDeps, chatModel, getAgentMockDeps } from '~/lib/ai/agent'; +import { Tool } from '~/lib/ai/model'; import { getUserDBAccess } from '~/lib/db/db'; import { Connection, Project } from '~/lib/db/schema'; -import { env } from '~/lib/env/eval'; import { getTargetDbPool } from '~/lib/targetdb/db'; -import { traceVercelAiResponse } from './trace'; +import { LLMTurn } from './llmcheck'; +import { GenerateTextResponse, traceVercelAiResponse } from './trace'; + +export const turnFromResponse = (prompt: string, response: GenerateTextResponse): LLMTurn => { + const allToolCalls = response.steps.flatMap((step) => { + return step.toolResults.flatMap((toolResult) => { + return Object.values(toolResult).map((toolCall: any) => ({ + name: toolCall.toolName, + inputs: toolCall.args, + output: toolCall.result + })); + }); + }); + + return { + input: prompt, + output: response.text, + toolCalls: allToolCalls + }; +}; export const evalChat = async ({ + model, messages, + prompt, + deps, dbConnection, - expect + expect, + toolCalls }: { - messages: CoreMessage[] | Omit[]; - dbConnection: string; + model?: string; + prompt?: string; + messages?: CoreMessage[] | Omit[]; + deps?: AgentModelDeps; + dbConnection?: string; expect: ExpectStatic; + toolCalls?: Record PromiseLike>; }) => { const project: Project = { id: 'projectId', @@ -23,23 +51,21 @@ export const evalChat = async ({ cloudProvider: 'aws' }; - const connection: Connection = { - id: generateUUID(), - name: 'evaldb', - connectionString: dbConnection, - projectId: project.id, - isDefault: true - }; + let targetDb: Pool | undefined; + if (!deps) { + if (dbConnection) { + const connection: Connection = { + id: generateUUID(), + name: 'evaldb', + connectionString: dbConnection, + projectId: project.id, + isDefault: true + }; - const userId = 'evalUser'; - const targetDb = getTargetDbPool(connection.connectionString); - const dbAccess = await getUserDBAccess(userId); - try { - const response = await chatModel.generateText({ - model: env.CHAT_MODEL, - system: getChatSystemPrompt({ cloudProvider: project.cloudProvider }), - maxSteps: 20, - deps: agentModelDeps({ + targetDb = getTargetDbPool(connection.connectionString); + const userId = 'evalUser'; + const dbAccess = await getUserDBAccess(userId); + deps = agentModelDeps({ withMCP: true, targetDb, dbAccess, @@ -47,12 +73,37 @@ export const evalChat = async ({ cloudProvider: project.cloudProvider, userId, withArtifacts: false - }), - messages + }); + } else { + deps = getAgentMockDeps({}); + } + } + + try { + const response = await chatModel.generateText({ + model: model, + maxSteps: 20, + deps, + messages, + prompt, + tools: (tools: Record>) => { + if (toolCalls) { + for (const [name, execute] of Object.entries(toolCalls)) { + const tool = tools[name]; + if (!tool) { + throw new Error(`Tool ${name} not found`); + } + tool.execute = execute; + } + } + return tools; + } }); traceVercelAiResponse(response, expect); return response; } finally { - await targetDb.end(); + if (targetDb) { + await targetDb.end(); + } } }; diff --git a/apps/dbagent/src/evals/lib/llmcheck/index.ts b/apps/dbagent/src/evals/lib/llmcheck/index.ts new file mode 100644 index 00000000..0a3b4a3f --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/index.ts @@ -0,0 +1,65 @@ +import { LanguageModel } from 'ai'; +import { Measure, Metric } from './metrics/types'; +import { Chat, LLMTurn, LLMTurnField } from './types'; + +export * from './types'; + +let evalModel: LanguageModel | undefined = undefined; + +export function setEvalModel(model: LanguageModel | undefined) { + evalModel = model; +} + +export function getEvalModel(): LanguageModel { + if (!evalModel) { + throw new Error('Eval model not set'); + } + return evalModel; +} + +export async function evalAllTurn( + turn: LLMTurn, + metrics: Record | Metric[] +): Promise> { + const usedMetrics: [string, Metric][] = Array.isArray(metrics) + ? metrics.filter(isMeasureTurnMetric).map((metric) => [metric.name(), metric]) + : Object.entries(metrics).filter(([_, metric]) => isMeasureTurnMetric(metric)); + if (usedMetrics.length === 0) { + throw new Error('No metrics provided'); + } + + for (const [name, metric] of usedMetrics) { + if (!turnHasFields(turn, metric.requiredParams())) { + throw new Error(`Metric ${name} requires fields ${metric.requiredParams().join(', ')}`); + } + } + + const result: Record = {}; + for (const [name, metric] of usedMetrics) { + result[name] = await metric.measureTurn!(turn); + } + + return result; +} + +export async function evalTurn(turn: LLMTurn, metric: Metric): Promise { + if (!isMeasureTurnMetric(metric)) { + throw new Error(`Metric ${metric.name()} is not a measure turn metric`); + } + if (!turnHasFields(turn, metric.requiredParams())) { + throw new Error(`Metric ${metric.name()} requires fields ${metric.requiredParams().join(', ')}`); + } + return metric.measureTurn!(turn); +} + +function isMeasureTurnMetric(metric: Metric) { + return 'measureTurn' in metric && metric.measureTurn; +} + +export function turnHasFields(turn: LLMTurn, fields: LLMTurnField[]) { + return fields.every((field) => field in turn); +} + +export function chatHasFields(chat: Chat, fields: LLMTurnField[]) { + return chat.turns.every((turn) => turnHasFields(turn, fields)); +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/concise.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/concise.ts new file mode 100644 index 00000000..2450aff5 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/concise.ts @@ -0,0 +1,28 @@ +import { BoolMeasure, Metric } from './types'; +import { EvalPassResult, JudgeEvalModel, evalPassResultSchema, simpleJudgeMetric } from './util'; + +export function conciseAnswerMetric(model?: JudgeEvalModel): Metric { + return simpleJudgeMetric({ + model, + config: { + name: 'concise', + schema: evalPassResultSchema, + prompt: ({ input, output }) => ` + The following question was answered by an expert in PostgreSQL and database administration: + ${input} + ${output} + + Please determine whether expert passed or failed at answering the question + concisely with no extra information offered unless it's explicitly asked for + in the question or it's almost certain from the question the user would + want it. Please provide a critique of how the answer could be improved or + does not match the response of an expert in PostgreSQL and database + administration.`, + result: (result) => ({ + type: 'bool', + success: result.result === 'passed', + reason: result.critique + }) + } + }); +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts new file mode 100644 index 00000000..28cf21bc --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts @@ -0,0 +1,24 @@ +import { BoolMeasure, Metric } from './types'; +import { EvalPassResult, evalPassResultSchema, JudgeEvalModel, simpleJudgeMetric } from './util'; + +export function finalAnswerMetric(expectedResult?: string, model?: JudgeEvalModel): Metric { + return simpleJudgeMetric({ + model, + config: { + name: 'finalAnswer', + schema: evalPassResultSchema, + prompt: ({ input, output }) => ` + The following question was answered by an expert in PostgreSQL and database administration: + ${input} + ${output} + ${expectedResult ? `${expectedResult}` : ''} + Please determine whether expert passed or failed at answering the question correctly and accurately. Please provied a critique of how the answer could be improved or does not match the response of an expert in PostgreSQl and database administration. + `, + result: (result) => ({ + type: 'bool', + success: result.result === 'passed', + reason: result.critique + }) + } + }); +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts new file mode 100644 index 00000000..1cde7848 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts @@ -0,0 +1,2 @@ +export * from './concise'; +export * from './types'; diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/regex-response.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/regex-response.ts new file mode 100644 index 00000000..b069cb71 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/regex-response.ts @@ -0,0 +1,32 @@ +import { Measure, Metric, reportBool } from '../metrics'; +import { Chat, LLMTurn } from '../types'; + +export function regexResponseMetric(regex: RegExp): Metric { + return { + name: () => 'regexResponse', + requiredParams: () => ['output'], + measureTurn: async (turn: LLMTurn) => measureTurn(regex, turn), + measureChat: async (chat: Chat) => measureChat(regex, chat) + }; +} + +function measureTurn(regex: RegExp, turn: LLMTurn): Measure { + return measure(regex, turn.output); +} + +function measureChat(regex: RegExp, chat: Chat): Measure { + if (!chat.turns || chat.turns.length === 0) { + return reportBool(false, 'No turns in the chat'); + } + const lastTurn = chat.turns[chat.turns.length - 1]!; + return measure(regex, lastTurn.output); +} + +function measure(regex: RegExp, text: string): Measure { + const match = text.match(regex); + if (match) { + return reportBool(true, `The text matches the regex ${regex.source}`); + } else { + return reportBool(false, `The text does not match the regex ${regex.source}`); + } +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts new file mode 100644 index 00000000..268ae040 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts @@ -0,0 +1,51 @@ +import { LLMTurn, ToolCall } from '../types'; +import { Measure, Metric, reportScoreBool } from './types'; + +export type ToolChoiceProps = { + expectedToolCalls?: ToolCall[]; + allowOthers: boolean; + allowMissing: boolean; +}; + +export function toolChoiceMetric(props: ToolChoiceProps): Metric { + return { + name: () => 'toolChoice', + requiredParams: () => (props.expectedToolCalls ? ['toolCalls'] : ['toolCalls', 'expectedTools']), + measureTurn: async (turn: LLMTurn) => measureTurn(turn, props) + }; +} + +// toolChoice uses the expectedTools from the LLMTurn configuration +export const toolChoice = toolChoiceMetric({ allowOthers: false, allowMissing: false }); + +function measureTurn(turn: LLMTurn, { expectedToolCalls, allowOthers, allowMissing }: ToolChoiceProps): Measure { + const expectedToolsSet = new Set([ + ...(turn.expectedTools?.map((tool) => tool.name) ?? []), + ...(expectedToolCalls?.map((tool) => tool.inputs) ?? []) + ]); + const actualToolsSet = new Set(turn.toolCalls?.map((tool) => tool.name) ?? []); + + const missing = [...expectedToolsSet].filter((tool) => !actualToolsSet.has(tool)); + const others = [...actualToolsSet].filter((tool) => !expectedToolsSet.has(tool)); + + const errorReport: string[] = []; + let success = true; + if (missing.length > 0) { + errorReport.push(`Missing tools: ${missing.join(', ')}`); + if (!allowMissing) { + success = false; + } + } + if (others.length > 0) { + errorReport.push(`Unexpected tools: ${others.join(', ')}`); + if (!allowOthers) { + success = false; + } + } + + const total = expectedToolsSet.size; + const expectedCount = total - errorReport.length; + const score = total > 0 ? expectedCount / total : 0; + + return reportScoreBool(score, success, errorReport.join('\n')); +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/types.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/types.ts new file mode 100644 index 00000000..481c2028 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/types.ts @@ -0,0 +1,49 @@ +import { Chat, LLMTurn, LLMTurnField } from '../types'; + +export type Metric = { + name: () => string; + requiredParams: () => LLMTurnField[]; + measureTurn?: (turn: LLMTurn) => PromiseLike; + measureChat?: (chat: Chat) => PromiseLike; +}; + +export type Measure = ScoreMeasure | BoolMeasure; + +export type BoolMeasure = { + type: 'bool'; + success: boolean; + reason?: string; +}; + +export function reportBool(success: boolean, reason?: string): Measure { + return { + type: 'bool', + success, + reason + }; +} + +export type ScoreMeasure = { + type: 'score'; + success: boolean; + score: number; + reason?: string; +}; + +export function reportScore(score: number, min_success: number, reason?: string): Measure { + return { + type: 'score', + success: score >= min_success, + score, + reason + }; +} + +export function reportScoreBool(score: number, success: boolean, reason?: string): Measure { + return { + type: 'score', + success, + score, + reason + }; +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/util.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/util.ts new file mode 100644 index 00000000..3e3e7396 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/util.ts @@ -0,0 +1,98 @@ +import { generateObject, LanguageModel } from 'ai'; + +import { z } from 'zod'; +import { getEvalModel } from '..'; +import { Chat, LLMTurn } from '../types'; +import { Measure, Metric } from './types'; + +export const evalPassResultSchema = z.object({ + result: z.enum(['passed', 'failed']), + critique: z.string() +}); + +export type EvalPassResult = z.infer; + +export const evalScoreResultSchema = z.object({ + score: z.number(), + critique: z.string() +}); + +export type EvalScoreResult = z.infer; + +export type LLMJudgeConfig = { + name: string; + schema: z.ZodTypeAny; + prompt: (args: { input: string; steps: string[]; output: string; expectedOutput?: string }) => string; + result?: (result: T) => R; +}; + +export type JudgeEvalModel = LanguageModel | (() => Promise); + +export function simpleJudgeMetric({ + model, + config +}: { + model?: JudgeEvalModel; + config: LLMJudgeConfig; +}): Metric { + return { + name: () => config.name, + requiredParams: () => ['input', 'output'], + measureTurn: (turn) => measureTurn({ model, config, turn }), + measureChat: (chat) => measureChat({ model, config, chat }) + }; +} + +async function measureTurn({ + model, + config, + turn +}: { + model?: JudgeEvalModel; + config: LLMJudgeConfig; + turn: LLMTurn; +}): Promise { + return await measure({ model, config, input: turn.input, output: turn.output }); +} + +async function measureChat({ + model, + config, + chat +}: { + model?: JudgeEvalModel; + config: LLMJudgeConfig; + chat: Chat; +}): Promise { + if (chat.turns.length === 0) { + throw new Error('Chat has no turns'); + } + const initialTurn = chat.turns[0]!; + const finalTurn = chat.turns[chat.turns.length - 1]!; + return await measure({ model, config, input: initialTurn.input, output: finalTurn.output }); +} + +async function measure({ + model, + config, + input, + output, + expectedOutput +}: { + model?: JudgeEvalModel; + config: LLMJudgeConfig; + input: string; + output: string; + expectedOutput?: string; +}): Promise { + const prompt = config.prompt({ input, steps: [], output, expectedOutput }); + + const evalModel = model ? (typeof model === 'function' ? await model() : model) : getEvalModel(); + const { object: judgeResponse } = await generateObject({ + model: evalModel, + schema: config.schema, + prompt + }); + + return config.result ? config.result(judgeResponse) : (judgeResponse as R); +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/types.ts b/apps/dbagent/src/evals/lib/llmcheck/types.ts new file mode 100644 index 00000000..da49aca9 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/types.ts @@ -0,0 +1,28 @@ +export type Chat = { + turns: LLMTurn[]; + name?: string; +}; + +export type LLMTurnField = keyof LLMTurn; + +export type LLMTurn = { + name?: string; + input: string; + output: string; + expectedOutput?: string; + reasoning?: string; + tokenCost?: number; + durationMs?: number; + context?: LLMTurn[]; + toolCalls?: ToolCall[]; + expectedTools?: ToolCall[]; +}; + +export type ToolCallFields = keyof ToolCall; + +export type ToolCall = { + name: string; + description?: string; + inputs?: any[]; + output?: any; +}; diff --git a/apps/dbagent/src/evals/lib/schemas.ts b/apps/dbagent/src/evals/lib/schemas.ts index 08e59d4a..c6d200fc 100644 --- a/apps/dbagent/src/evals/lib/schemas.ts +++ b/apps/dbagent/src/evals/lib/schemas.ts @@ -2,8 +2,13 @@ import { z } from 'zod'; import { evalEnvSchema } from '../../lib/env/eval'; export const evalResultSchema = z - .object({ id: z.string(), result: z.enum(['passed', 'failed']), env: evalEnvSchema }) + .object({ + id: z.string(), + result: z.enum(['passed', 'failed']), + env: evalEnvSchema + }) .strict(); + export type EvalResult = z.infer; export const evalResultEnum = z.enum(['passed', 'failed']); diff --git a/apps/dbagent/src/evals/lib/trace.ts b/apps/dbagent/src/evals/lib/trace.ts index e32fd3bd..1454d262 100644 --- a/apps/dbagent/src/evals/lib/trace.ts +++ b/apps/dbagent/src/evals/lib/trace.ts @@ -4,7 +4,7 @@ import path from 'path'; import { ExpectStatic } from 'vitest'; import { ensureTraceFolderExistsExpect } from './test-id'; -type GenerateTextResponse = Awaited>; +export type GenerateTextResponse = Awaited>; const toolResultToHuman = (toolResult: any) => { return ` @@ -31,15 +31,37 @@ const parseRequestBody = (response: GenerateTextResponse) => { const getSystemPromptFromResponse = (response: GenerateTextResponse) => { const body = parseRequestBody(response); - return body.system[0].text; + const system = + body.system && body.system.length > 0 + ? getResponseMessageText(body.system[0]) + : body.messages[0].role === 'system' + ? getResponseMessageText(body.messages[0].content) + : ''; + return system; }; const getUserPromptFromResponse = (response: GenerateTextResponse) => { const body = parseRequestBody(response); - return body.messages[0].content[0].text; + for (const message of body.messages) { + if (message.role === 'user') { + return getResponseMessageText(message.content); + } + } + return ''; +}; + +const getResponseMessageText = (content: any) => { + if (typeof content === 'string') { + return content; + } else if (Array.isArray(content)) { + return content[0].text; + } + return ''; }; export const traceVercelAiResponse = (response: GenerateTextResponse, expect: ExpectStatic) => { + console.log(response); + const traceFolder = ensureTraceFolderExistsExpect(expect); const humanTraceFile = path.join(traceFolder, 'human.txt'); const humanTrace = ` diff --git a/apps/dbagent/src/lib/ai/agent.ts b/apps/dbagent/src/lib/ai/agent.ts index 247c68a8..a8320141 100644 --- a/apps/dbagent/src/lib/ai/agent.ts +++ b/apps/dbagent/src/lib/ai/agent.ts @@ -63,9 +63,66 @@ export function agentModelDeps({ }; } +export function getAgentMockDeps({ + withMCP = false, + useArtifacts = false, + targetDbService, + playbookService, + clusterService, + artifactsService +}: { + withMCP?: boolean; + useArtifacts?: boolean; + targetDbService?: DBService; + playbookService?: PlaybookService; + clusterService?: ClusterService | CloudProvider; + artifactsService?: ArtifactsService; +}): AgentModelDeps { + const mockHandler = { + get(_target: any, prop: any, _receiver: any) { + console.trace(`Stack trace for property access: ${prop}`); + throw new Error(`Not implemented: ${prop}`); + } + }; + + const useClusterService = clusterService + ? typeof clusterService === 'string' + ? getMockClusterService(clusterService) + : clusterService + : getMockClusterService('aws'); + + return { + withMCP: withMCP ?? false, + targetDb: targetDbService || new Proxy({} as DBService, mockHandler), + playbookService: playbookService || new Proxy({} as PlaybookService, mockHandler), + clusterService: useClusterService, + artifactsService: + artifactsService || (useArtifacts ? new Proxy({} as ArtifactsService, mockHandler) : undefined) + }; +} + +export function getMockClusterService(cloudProvider: CloudProvider): ClusterService { + const mockData = { + type: cloudProvider + }; + + return new Proxy(mockData as unknown as ClusterService, { + get(target, prop): any { + if (!(prop in target)) { + console.trace(`Stack trace for property access: ${String(prop)}`); + throw new Error(`Property ${String(prop)} is not implemented in mockData.`); + } + return target[prop as keyof typeof target]; + } + }); +} + export const toolsets = [ { - active: (deps?: { withMCP: boolean }) => deps?.withMCP || true, + active: (deps?: { withMCP: boolean }) => { + const enabled = !deps || deps.withMCP !== false; + return enabled; + }, tools: mcpToolset.listMCPTools }, { tools: commonToolset }, diff --git a/apps/dbagent/src/lib/ai/model.ts b/apps/dbagent/src/lib/ai/model.ts index dafc4d44..5e17c449 100644 --- a/apps/dbagent/src/lib/ai/model.ts +++ b/apps/dbagent/src/lib/ai/model.ts @@ -41,7 +41,7 @@ interface Telemetry { metadata?: Record; } -type ModelSettings = { +type ModelOptions = { maxTokens?: number; temperature?: number; topP?: number; @@ -54,10 +54,16 @@ type ModelSettings = { abortSignal?: AbortSignal; }; +type ToolOptions = { + // Add additional tools or modify existing tools + tools?: Record> | ((tools: Record>, deps?: DEPS) => Record>); +}; + type StreamTextOptions = PromptOptions & + ToolOptions & Telemetry & { model?: LanguageModel | string; - modelSettings?: ModelSettings; + modelSettings?: ModelOptions; deps?: DEPS; maxSteps?: number; onChunk?: StreamTextOnChunkCallback; @@ -70,9 +76,10 @@ type StreamTextOptions = PromptOptions & type StreamResult = Awaited>; type GenerateTextOptions = PromptOptions & + ToolOptions & Telemetry & { model?: LanguageModel | string; - modelSettings?: ModelSettings; + modelSettings?: ModelOptions; deps?: DEPS; maxSteps?: number; }; @@ -80,7 +87,7 @@ type GenerateTextOptions = PromptOptions & type GenerateObjectOptions = PromptOptions & Telemetry & { model?: LanguageModel | string; - modelSettings?: ModelSettings; + modelSettings?: ModelOptions; deps?: DEPS; maxSteps?: number; schema: z.Schema; @@ -92,7 +99,7 @@ type GenerateObjectOptions = PromptOptions & type StreamObjectOptions = PromptOptions & Telemetry & { model?: LanguageModel | string; - modelSettings?: ModelSettings; + modelSettings?: ModelOptions; deps?: DEPS; maxSteps?: number; schema: z.Schema | Schema; @@ -191,11 +198,14 @@ export class AugmentedLanguageModel implements Model { } async getTools(deps?: DEPS): Promise { - const { tools } = await this.getToolsWithPrompt(deps); + const { tools } = await this.getToolsWithPrompt({}, deps); return tools; } - private async getToolsWithPrompt(deps?: DEPS): Promise<{ tools: SDKToolSet; systemPrompts: Prompt[] }> { + private async getToolsWithPrompt( + options: ToolOptions, + deps?: DEPS + ): Promise<{ tools: SDKToolSet; systemPrompts: Prompt[] }> { const systemPrompts: Prompt[] = []; const toolsetTools: Record> = ( await Promise.all( @@ -215,25 +225,18 @@ export class AugmentedLanguageModel implements Model { return { ...acc, ...tools }; }, {}); - const allTools = { ...toolsetTools, ...this.#tools }; - if (!deps) { - return { - systemPrompts, - tools: Object.fromEntries( - Object.entries(allTools) - .filter(([_, t]) => t.type !== 'function-deps') - .map(([key, t]) => [key, t as SDKTool]) - ) - }; + let allTools = { ...toolsetTools, ...this.#tools }; + if (options.tools) { + if (typeof options.tools === 'function') { + allTools = options.tools(allTools, deps); + } else { + allTools = { ...allTools, ...options.tools }; + } } return { systemPrompts, - tools: Object.fromEntries( - Object.entries(allTools) - .map(([key, t]) => [key, toSDKTool(t, deps)] as [string, SDKTool | null]) - .filter(([, tool]) => tool !== null) - ) as SDKToolSet + tools: toSDKTools(allTools, deps) }; } @@ -258,6 +261,7 @@ export class AugmentedLanguageModel implements Model { } }; } + async streamText(options: StreamTextOptions): Promise { const { model, @@ -271,10 +275,11 @@ export class AugmentedLanguageModel implements Model { onChunk, onError, onFinish, - onStepFinish + onStepFinish, + tools: toolsOptions } = options; - const { tools, systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); + const { tools, systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt({ tools: toolsOptions }, deps); const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); const llm = await this.getModel(model); const telemetry = this.getTelemetryConfig(metadata); @@ -299,8 +304,8 @@ export class AugmentedLanguageModel implements Model { } async generateText(options: GenerateTextOptions): Promise> { - const { model, modelSettings, deps, system, prompt, messages, metadata, maxSteps } = options; - const { tools, systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); + const { model, modelSettings, deps, system, prompt, messages, metadata, maxSteps, tools: toolsOptions } = options; + const { tools, systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt({ tools: toolsOptions }, deps); const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); const llm = await this.getModel(model); const telemetry = this.getTelemetryConfig(metadata); @@ -331,8 +336,7 @@ export class AugmentedLanguageModel implements Model { schemaDescription, mode } = options; - const { systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); - const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); + const systemPrompt = this.getSystemPrompt(deps, system, []); const llm = await this.getModel(model); const telemetry = this.getTelemetryConfig(metadata); @@ -384,8 +388,7 @@ export class AugmentedLanguageModel implements Model { schemaDescription, mode } = options; - const { systemPrompts: toolSystemPrompts } = await this.getToolsWithPrompt(deps); - const systemPrompt = this.getSystemPrompt(deps, system, toolSystemPrompts); + const systemPrompt = this.getSystemPrompt(deps, system, []); const llm = await this.getModel(model); const telemetry = this.getTelemetryConfig(metadata); @@ -435,7 +438,7 @@ import { ProviderRegistry } from './providers'; type ToolParameters = z.ZodTypeAny; -type Tool = +export type Tool = | SDKTool | { type: 'function-deps'; @@ -501,3 +504,49 @@ export function tool(tool } return sdkTool(tool); } + +export function toSDKTools(tools: Record>, deps?: DEPS): Record { + return Object.fromEntries(toolsWithErrorCatch(generateSDKTools(tools, deps))); +} + +function* generateSDKTools(tools: Record>, deps?: DEPS): Generator<[string, SDKTool]> { + if (deps) { + for (const [key, tool] of Object.entries(tools)) { + const sdkToolInstance = toSDKTool(tool, deps); + if (sdkToolInstance !== null) { + yield [key, sdkToolInstance]; + } + } + } else { + for (const [key, tool] of Object.entries(tools)) { + if (tool.type !== 'function-deps') { + yield [key, tool]; + } + } + } +} + +export function* toolsWithErrorCatch(tools: Iterable<[string, SDKTool]>): Generator<[string, SDKTool]> { + for (const [key, tool] of tools) { + if (!tool.execute) { + yield [key, tool]; + continue; + } + const errorMessage = tool.description ? `Error: ${tool.description.split('.')[0]}` : 'Error, operation failed'; + tool.execute = executeWithErrorHandling(tool.execute, errorMessage); + yield [key, tool]; + } +} + +export function executeWithErrorHandling( + fn: (...args: T) => PromiseLike, + errorMessage?: string +): (...args: T) => Promise { + return (...args: T): Promise => { + return Promise.resolve(fn(...args)) + .then((result) => result) + .catch((error) => { + return errorMessage ? `${errorMessage}: ${error}` : `Error: ${error}`; + }); + }; +} diff --git a/apps/dbagent/src/lib/ai/tools/db.ts b/apps/dbagent/src/lib/ai/tools/db.ts index 3c1e1119..cf0e3af4 100644 --- a/apps/dbagent/src/lib/ai/tools/db.ts +++ b/apps/dbagent/src/lib/ai/tools/db.ts @@ -77,13 +77,7 @@ function getSlowQueries({ db }: DBToolsProps): Tool { the max execution time in seconds, the mean execution time in seconds, the total execution time (all calls together) in seconds, and the query itself.`, parameters: z.object({}), - execute: async () => { - try { - return await db.slowQueries(2000); - } catch (error) { - return `Error getting slow queries: ${error}`; - } - } + execute: async () => await db.slowQueries(2000) }); } @@ -98,13 +92,9 @@ If you know the schema, pass it in as well.`, query: z.string() }), execute: async ({ schema = 'public', query }) => { - try { - const explain = await db.explainQuery(schema, query); - if (!explain) return 'Could not run EXPLAIN on the query'; - return explain; - } catch (error) { - return `Error running EXPLAIN on the query: ${error}`; - } + const explain = await db.explainQuery(schema, query); + if (!explain) return 'Could not run EXPLAIN on the query'; + return explain; } }); } @@ -116,13 +106,7 @@ function describeTable({ db }: DBToolsProps): Tool { schema: z.string(), table: z.string() }), - execute: async ({ schema = 'public', table }) => { - try { - return await db.describeTable(schema, table); - } catch (error) { - return `Error describing table: ${error}`; - } - } + execute: async ({ schema = 'public', table }) => await db.describeTable(schema, table) }); } @@ -132,13 +116,7 @@ function findTableSchema({ db }: DBToolsProps): Tool { parameters: z.object({ table: z.string() }), - execute: async ({ table }) => { - try { - return await db.findTableSchema(table); - } catch (error) { - return `Error finding table schema: ${error}`; - } - } + execute: async ({ table }) => await db.findTableSchema(table) }); } @@ -146,13 +124,7 @@ function getCurrentActiveQueries({ db }: DBToolsProps): Tool { return tool({ description: `Get the currently active queries.`, parameters: z.object({}), - execute: async () => { - try { - return await db.currentActiveQueries(); - } catch (error) { - return `Error getting current active queries: ${error}`; - } - } + execute: async () => await db.currentActiveQueries() }); } @@ -160,13 +132,7 @@ function getQueriesWaitingOnLocks({ db }: DBToolsProps): Tool { return tool({ description: `Get the queries that are currently blocked waiting on locks.`, parameters: z.object({}), - execute: async () => { - try { - return await db.queriesWaitingOnLocks(); - } catch (error) { - return `Error getting queries waiting on locks: ${error}`; - } - } + execute: async () => await db.queriesWaitingOnLocks() }); } @@ -174,13 +140,7 @@ function getVacuumStats({ db }: DBToolsProps): Tool { return tool({ description: `Get the vacuum stats for the top tables in the database. They are sorted by the number of dead tuples descending.`, parameters: z.object({}), - execute: async () => { - try { - return await db.vacuumStats(); - } catch (error) { - return `Error getting vacuum stats: ${error}`; - } - } + execute: async () => await db.vacuumStats() }); } @@ -188,13 +148,7 @@ function getConnectionsStats({ db }: DBToolsProps): Tool { return tool({ description: `Get the connections stats for the database.`, parameters: z.object({}), - execute: async () => { - try { - return await db.connectionsStats(); - } catch (error) { - return `Error getting connections stats: ${error}`; - } - } + execute: async () => await db.connectionsStats() }); } @@ -202,13 +156,7 @@ function getConnectionsGroups({ db }: DBToolsProps): Tool { return tool({ description: `Get the connections groups for the database. This is a view in the pg_stat_activity table, grouped by (state, user, application_name, client_addr, wait_event_type, wait_event).`, parameters: z.object({}), - execute: async () => { - try { - return await db.connectionsGroups(); - } catch (error) { - return `Error getting connections groups: ${error}`; - } - } + execute: async () => await db.connectionsGroups() }); } @@ -216,12 +164,6 @@ function getPerformanceAndVacuumSettings({ db }: DBToolsProps): Tool { return tool({ description: `Get the performance and vacuum settings for the database.`, parameters: z.object({}), - execute: async () => { - try { - return await db.getPerformanceAndVacuumSettings(); - } catch (error) { - return `Error getting performance and vacuum settings: ${error}`; - } - } + execute: async () => await db.getPerformanceAndVacuumSettings() }); } From de1d40eecb7429a4a25d415e81afa629de147c9b Mon Sep 17 00:00:00 2001 From: urso Date: Fri, 16 May 2025 17:24:10 +0200 Subject: [PATCH 4/5] langfuse eval runner --- apps/dbagent/package.json | 4 + apps/dbagent/scripts/eval/simple.ts | 14 + apps/dbagent/scripts/lftool/env.ts | 8 + apps/dbagent/scripts/lftool/lftool.ts | 431 ++++++++++++++++++ apps/dbagent/scripts/lftool/loader.mjs | 15 + apps/dbagent/scripts/lftool/mocks/empty.ts | 2 + apps/dbagent/src/evals/chat/judge.eval.ts | 3 +- apps/dbagent/src/evals/chat/tests.eval.ts | 2 +- .../dbagent/src/evals/chat/toolChoice.eval.ts | 2 +- apps/dbagent/src/evals/lib/chat-runner.ts | 30 +- .../dbagent/src/evals/lib/llmcheck/dataset.ts | 32 ++ .../lib/llmcheck/metrics/final-answer.ts | 10 +- .../src/evals/lib/llmcheck/metrics/index.ts | 3 + .../evals/lib/llmcheck/metrics/toolchoice.ts | 6 +- apps/dbagent/src/evals/lib/llmcheck/types.ts | 3 + apps/dbagent/src/evals/lib/trace.ts | 2 - apps/dbagent/src/lib/ai/agent.ts | 3 +- apps/dbagent/src/lib/ai/tools/artifacts.ts | 201 +------- apps/dbagent/src/lib/env/server.ts | 1 + apps/dbagent/src/lib/tools/artifacts.ts | 199 ++++++++ pnpm-lock.yaml | 71 +++ 21 files changed, 825 insertions(+), 217 deletions(-) create mode 100644 apps/dbagent/scripts/eval/simple.ts create mode 100644 apps/dbagent/scripts/lftool/env.ts create mode 100644 apps/dbagent/scripts/lftool/lftool.ts create mode 100644 apps/dbagent/scripts/lftool/loader.mjs create mode 100644 apps/dbagent/scripts/lftool/mocks/empty.ts create mode 100644 apps/dbagent/src/evals/lib/llmcheck/dataset.ts create mode 100644 apps/dbagent/src/lib/tools/artifacts.ts diff --git a/apps/dbagent/package.json b/apps/dbagent/package.json index f2773d38..ba0abc10 100644 --- a/apps/dbagent/package.json +++ b/apps/dbagent/package.json @@ -55,6 +55,7 @@ "@opentelemetry/semantic-conventions": "^1.32.0", "@tailwindcss/postcss": "^4.1.6", "@tanstack/react-query": "^5.75.7", + "@types/ndjson": "^2.0.4", "@vercel/functions": "^2.0.3", "ai": "^4.3.15", "bytes": "^3.1.2", @@ -72,9 +73,11 @@ "geist": "^1.4.2", "import-in-the-middle": "^1.13.1", "kysely": "^0.28.2", + "langfuse": "^3.37.2", "langfuse-vercel": "^3.37.2", "litellm-api": "^0.0.3", "lucide-react": "^0.509.0", + "ndjson": "^2.0.0", "next": "^15.3.2", "next-auth": "5.0.0-beta.25", "next-themes": "^0.4.6", @@ -118,6 +121,7 @@ "@types/react-dom": "^19.1.3", "@types/react-syntax-highlighter": "^15.5.13", "autoprefixer": "^10.4.21", + "cmd-ts": "^0.13.0", "dockerode": "^4.0.6", "dotenv": "^16.5.0", "filenamify": "^6.0.0", diff --git a/apps/dbagent/scripts/eval/simple.ts b/apps/dbagent/scripts/eval/simple.ts new file mode 100644 index 00000000..c4f00cbd --- /dev/null +++ b/apps/dbagent/scripts/eval/simple.ts @@ -0,0 +1,14 @@ +'use server'; + +import Langfuse from 'langfuse'; +import { evalAllTurn, LLMTurn } from '~/evals/lib/llmcheck'; +import { conciseAnswerMetric, finalAnswerMetric, Measure } from '~/evals/lib/llmcheck/metrics'; + +const metrics = [conciseAnswerMetric(), finalAnswerMetric()]; + +export async function evalFunction(langfuse: Langfuse, turn: LLMTurn): Promise> { + console.log(turn); + + const evals = await evalAllTurn(turn, metrics); + return evals; +} diff --git a/apps/dbagent/scripts/lftool/env.ts b/apps/dbagent/scripts/lftool/env.ts new file mode 100644 index 00000000..3b7f8e3b --- /dev/null +++ b/apps/dbagent/scripts/lftool/env.ts @@ -0,0 +1,8 @@ +import { config } from '@dotenvx/dotenvx'; + +// Load environment variables first +config({ + path: ['.env', '.env.local'], + strict: false, + ignore: ['MISSING_ENV_FILE'] +}); diff --git a/apps/dbagent/scripts/lftool/lftool.ts b/apps/dbagent/scripts/lftool/lftool.ts new file mode 100644 index 00000000..ed61b2fd --- /dev/null +++ b/apps/dbagent/scripts/lftool/lftool.ts @@ -0,0 +1,431 @@ +// Import env configuration first +import { evalFunction } from '../eval/simple'; +import './env'; + +import { WriteStream } from 'fs'; +import { ApiTraceWithDetails, ApiUtilsMetaResponse, Langfuse } from 'langfuse'; +import { z } from 'zod'; + +import { command, number, option, optional, positional, restPositionals, run, string, subcommands } from 'cmd-ts'; +import { promises as fs } from 'fs'; +import { FileHandle } from 'fs/promises'; +import { turnFromResponse } from '~/evals/lib/chat-runner'; +import { setEvalModel } from '~/evals/lib/llmcheck'; +import { AgentModelDeps, chatModel, getAgentMockDeps, getModelInstance } from '~/lib/ai/agent'; +import { Tool } from '~/lib/ai/model'; + +const envSchema = z.object({ + LANGFUSE_HOST: z.string().optional(), + LANGFUSE_PUBLIC_KEY: z.string().optional(), + LANGFUSE_SECRET_KEY: z.string().optional(), + LANGFUSE_DEBUG: z.string(z.enum(['true', 'false'])).default('false') +}); + +/* eslint-disable-next-line no-process-env */ +const env = envSchema.parse(process.env); + +function getLangfuse(): Langfuse { + return new Langfuse({ + baseUrl: env.LANGFUSE_HOST, + publicKey: env.LANGFUSE_PUBLIC_KEY, + secretKey: env.LANGFUSE_SECRET_KEY + }); +} + +const cmdSessionsList = command({ + name: 'list', + args: { + limit: option({ type: optional(number), long: 'limit', short: 'l' }) + }, + handler: async ({ limit }) => { + const langfuse = getLangfuse(); + const sessions = await langfuse.fetchSessions({ + limit: limit + }); + console.log(sessions.data); + } +}); + +const cmdSessionDownload = command({ + name: 'dlsession', + args: { + sessionIds: restPositionals({ type: string }), + output: option({ type: optional(string), long: 'output', short: 'o' }) + }, + handler: async ({ sessionIds, output }) => { + const langfuse = getLangfuse(); + + const sessionPages = async function* (sessionId: string, data: ApiTraceWithDetails[], meta: ApiUtilsMetaResponse) { + while (true) { + yield data; + if (meta.page == meta.totalPages) { + break; + } + + ({ data, meta } = await langfuse.fetchTraces({ + sessionId: sessionId, + limit: 20, + page: meta.page + 1 + })); + } + }; + + const sessionItems = async function* (sessionId: string, data: ApiTraceWithDetails[], meta: ApiUtilsMetaResponse) { + for await (const page of sessionPages(sessionId, data, meta)) { + for (const item of page) { + yield item; + } + } + }; + + const sessions = async function* () { + let ids = sessionIds; + if (ids.length == 0) { + const sessions = await langfuse.fetchSessions({ + limit: 20 + }); + ids = sessions.data.map((session) => session.id); + } + + for (const sessionId of ids) { + const { data, meta } = await langfuse.fetchTraces({ + sessionId: sessionId, + limit: 20 + }); + + yield { + sessionId: sessionId, + items: sessionItems(sessionId, data, meta) + }; + } + }; + + const header = { + type: 'langfuse-session', + version: 1 + }; + + let file: FileHandle | undefined; + if (output) { + file = await fs.open(output, 'w'); + } + + const outputStream: WriteStream = (file ? file.createWriteStream() : process.stdout) as WriteStream; + try { + outputStream.write(JSON.stringify(header)); + outputStream.write('\n'); + + for await (const { sessionId, items } of sessions()) { + outputStream.write(`{"sessionId": "${sessionId}", items:[`); + let count = 0; + for await (const item of items) { + if (count > 0) outputStream.write(', '); + outputStream.write(JSON.stringify(item)); + count++; + } + outputStream.write(']}\n'); + } + } finally { + outputStream.end(); + await file?.close(); + } + } +}); + +const cmdSessions = subcommands({ + name: 'session', + cmds: { + list: cmdSessionsList, + download: cmdSessionDownload + } +}); + +const cmdDatasetsList = command({ + name: 'list', + args: {}, + handler: async () => { + const langfuse = getLangfuse(); + const url = `${langfuse.baseUrl}/api/public/v2/datasets`; + + const fetchDatasets = async ({ limit = 50, page = 1 }: { limit?: number; page?: number }) => { + const params = new URLSearchParams({ limit: limit.toString(), page: page.toString() }); + const response = await langfuse.fetch( + `${url}?${params.toString()}`, + langfuse._getFetchOptions({ + method: 'GET' + }) + ); + if (response.status !== 200) { + throw new Error(`Failed to fetch datasets: ${response.status}`); + } + return await response.json(); + }; + + const datasetPages = async function* () { + let { data, meta } = await fetchDatasets({}); + + while (true) { + yield data; + if (meta.page == meta.totalPages) { + break; + } + + ({ data, meta } = await fetchDatasets({ + limit: 50, + page: meta.page + 1 + })); + } + }; + + for await (const page of datasetPages()) { + for (const item of page) { + console.log(item); + } + } + } +}); + +const cmdDatasetDownload = command({ + name: 'download', + args: { + datasetName: positional({ type: string }), + output: option({ type: optional(string), long: 'output', short: 'o' }) + }, + handler: async ({ datasetName: datasetId, output }) => { + const langfuse = getLangfuse(); + const response = await langfuse.getDataset(datasetId); + const { items, ...rest } = response; + const header = { + type: 'langfuse-dataset', + version: 1, + ...rest + }; + + let file: FileHandle | undefined; + if (output) { + file = await fs.open(output, 'w'); + } + const outputStream: WriteStream = (file ? file.createWriteStream() : process.stdout) as WriteStream; + try { + outputStream.write(JSON.stringify(header)); + outputStream.write('\n'); + for (const item of items) { + outputStream.write(JSON.stringify(item)); + outputStream.write('\n'); + } + } finally { + outputStream.end(); + await file?.close(); + } + } +}); + +const cmdDatasetEval = command({ + name: 'eval', + args: { + datasetName: positional({ type: string, displayName: 'dataset-name' }) + }, + handler: async ({ datasetName }) => { + const langfuse = getLangfuse(); + const response = await langfuse.getDataset(datasetName); + const { items } = response; + + setEvalModel(await getModelInstance(process.env.EVAL_MODEL ?? 'chat')); + + const runName = `${datasetName}-${new Date().toISOString()}`; + const model = process.env.CHAT_MODEL; + + for (const item of items) { + type ToolCall = { input: any; output: any }; + type ToolCallTable = Record; + type ToolCallItem = ToolCall & { toolName: string }; + + const trace = langfuse.trace({ + name: runName + }); + + const toolCalls: ToolCallTable = (item.metadata as any).toolCalls.reduce( + (acc: ToolCallTable, toolCall: ToolCallItem) => { + acc[toolCall.toolName] = [...(acc[toolCall.toolName] || []), toolCall]; + return acc; + }, + {} as ToolCallTable + ); + console.log(toolCalls); + + const inputEqual = (a: any, b: any) => { + return Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((key) => a[key] === b[key]); + }; + + const messages = (item.input as any).messages; + const response = await chatModel.generateText({ + model, + maxSteps: 20, + deps: getAgentMockDeps({}), + messages, + tools: (tools: Record>) => { + for (const [name, toolCallVariants] of Object.entries(toolCalls)) { + const tool = tools[name]; + if (!tool) { + console.log(`Tool ${name} not found`); + continue; + } + tool.execute = async (input: any) => { + console.log(`tool call: ${name}(${JSON.stringify(input)})`); + + const toolCall = toolCallVariants.find((toolCall) => inputEqual(toolCall.input, input)); + if (!toolCall) { + console.log(`Error: tool call ${name} for input ${JSON.stringify(input)} not found`); + return null; + } + + const output = toolCall.output; + console.log(` => ${name}: ${JSON.stringify(output)}`); + return output; + }; + } + return tools; + } + }); + + // find prompt in messages + let prompt = ''; + for (const message of messages) { + if (message.role === 'user') { + prompt = message.content; + break; + } + } + + const turn = { + ...turnFromResponse(prompt, response), + expectedOutput: item.expectedOutput as any + }; + const evals = await evalFunction(langfuse, turn); + console.log(evals); + + const runMetadata = { + output: turn.output, + evals + }; + + await item.link(trace, runName, { + metadata: runMetadata + }); + + // try to set scores + try { + for (const [metricName, measure] of Object.entries(evals)) { + const value = measure.type === 'bool' ? (measure.success ? 'PASS' : 'FAIL') : measure.score; + console.log(`${metricName}: ${value}`); + + langfuse.score({ + name: metricName, + value, + traceId: trace.id + }); + } + } catch (e) { + console.error(e); + } + } + + await langfuse.flushAsync(); + } +}); + +async function* traceSpans(langfuse: Langfuse, traceId: string) { + const pages = async function* () { + let { data, meta } = await langfuse.fetchObservations({ traceId, type: 'SPAN' }); + while (true) { + yield data; + if (meta.page == meta.totalPages) { + break; + } + + ({ data, meta } = await langfuse.fetchObservations({ traceId, type: 'SPAN', page: meta.page + 1 })); + } + }; + + for await (const page of pages()) { + for (const item of page) { + yield item; + } + } +} + +async function* traceToolCalls(langfuse: Langfuse, traceId: string) { + const spans = traceSpans(langfuse, traceId); + for await (const span of spans) { + if (!span.name || !span.name || !span.name.startsWith('ai.toolCall')) { + continue; + } + + const toolName = span.name.split(' ')[1]; + const { input, output } = span; + yield { toolName, input, output }; + } +} + +const cmdDatasetPromote = command({ + name: 'enhance', + args: { + datasetName: positional({ type: string, displayName: 'dataset-name' }), + staging: option({ type: optional(string), long: 'staging', short: 's' }) + }, + handler: async ({ datasetName, staging }) => { + const langfuse = getLangfuse(); + const response = await langfuse.getDataset(staging || 'staging'); + const { items } = response; + + // make sure the target dataset exists + await langfuse.createDataset({ name: datasetName }); + for (const item of items) { + const { sourceTraceId } = item; + if (!sourceTraceId) { + console.log(`Skipping item ${item.id} because it has no sourceTraceId`); + continue; + } + + const toolCalls = []; + for await (const toolCall of traceToolCalls(langfuse, sourceTraceId)) { + toolCalls.push(toolCall); + } + + const metadata = { + ...(item.metadata ?? {}), + toolCalls + }; + + await langfuse.createDatasetItem({ + id: item.id + '-enhanced', + datasetName, + expectedOutput: item.expectedOutput, + input: item.input, + metadata, + sourceTraceId: item.id + }); + } + } +}); + +const cmdDatasets = subcommands({ + name: 'datasets', + cmds: { + list: cmdDatasetsList, + download: cmdDatasetDownload, + promote: cmdDatasetPromote, + eval: cmdDatasetEval + } +}); + +const tool = subcommands({ + name: 'tool', + cmds: { + session: cmdSessions, + dataset: cmdDatasets, + ds: cmdDatasets + } +}); + +run(tool, process.argv.slice(2)).catch(console.error); diff --git a/apps/dbagent/scripts/lftool/loader.mjs b/apps/dbagent/scripts/lftool/loader.mjs new file mode 100644 index 00000000..1162b894 --- /dev/null +++ b/apps/dbagent/scripts/lftool/loader.mjs @@ -0,0 +1,15 @@ +import { resolve as resolvePath, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export async function resolve(specifier, context, nextResolve) { + if (specifier === 'server-only') { + return { + shortCircuit: true, + url: `file://${resolvePath(__dirname, './mocks/empty.ts')}` + }; + } + + return nextResolve(specifier); +} \ No newline at end of file diff --git a/apps/dbagent/scripts/lftool/mocks/empty.ts b/apps/dbagent/scripts/lftool/mocks/empty.ts new file mode 100644 index 00000000..2c076590 --- /dev/null +++ b/apps/dbagent/scripts/lftool/mocks/empty.ts @@ -0,0 +1,2 @@ +// Empty mock for server-only module +export {}; diff --git a/apps/dbagent/src/evals/chat/judge.eval.ts b/apps/dbagent/src/evals/chat/judge.eval.ts index 5ad84d38..b00334bd 100644 --- a/apps/dbagent/src/evals/chat/judge.eval.ts +++ b/apps/dbagent/src/evals/chat/judge.eval.ts @@ -7,8 +7,7 @@ import { env } from '~/lib/env/eval'; import { TableStat } from '~/lib/targetdb/db'; import { turnFromResponse } from '../lib/chat-runner'; import { evalTurn, LLMTurn, setEvalModel } from '../lib/llmcheck'; -import { conciseAnswerMetric, Metric } from '../lib/llmcheck/metrics'; -import { finalAnswerMetric } from '../lib/llmcheck/metrics/final-answer'; +import { conciseAnswerMetric, finalAnswerMetric, Metric } from '../lib/llmcheck/metrics'; import { ensureTraceFolderExistsExpect } from '../lib/test-id'; import { EvalCase, runEvals } from '../lib/vitest-helpers'; diff --git a/apps/dbagent/src/evals/chat/tests.eval.ts b/apps/dbagent/src/evals/chat/tests.eval.ts index 6109ae5e..ef07289a 100644 --- a/apps/dbagent/src/evals/chat/tests.eval.ts +++ b/apps/dbagent/src/evals/chat/tests.eval.ts @@ -2,7 +2,7 @@ import { describe } from 'vitest'; import { evalChat, turnFromResponse } from '~/evals/lib/chat-runner'; import { TableStat } from '~/lib/targetdb/db'; import { evalTurn } from '../lib/llmcheck'; -import { regexResponseMetric } from '../lib/llmcheck/metrics/regex-response'; +import { regexResponseMetric } from '../lib/llmcheck/metrics'; import { EvalCase, runEvals } from '../lib/vitest-helpers'; type Test = EvalCase & { diff --git a/apps/dbagent/src/evals/chat/toolChoice.eval.ts b/apps/dbagent/src/evals/chat/toolChoice.eval.ts index c4d28c5c..c8933092 100644 --- a/apps/dbagent/src/evals/chat/toolChoice.eval.ts +++ b/apps/dbagent/src/evals/chat/toolChoice.eval.ts @@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe } from 'vitest'; import { evalChat, turnFromResponse } from '~/evals/lib/chat-runner'; import { PostgresConfig, runSql, startPostgresContainer } from '../lib/eval-docker-db'; import { evalTurn } from '../lib/llmcheck'; -import { toolChoiceMetric } from '../lib/llmcheck/metrics/toolchoice'; +import { toolChoiceMetric } from '../lib/llmcheck/metrics'; import { mockGetConnectionInfo, mockGetProjectsById } from '../lib/mocking'; import { EvalCase, runEvals } from '../lib/vitest-helpers'; diff --git a/apps/dbagent/src/evals/lib/chat-runner.ts b/apps/dbagent/src/evals/lib/chat-runner.ts index 8f8b32e8..4806dd24 100644 --- a/apps/dbagent/src/evals/lib/chat-runner.ts +++ b/apps/dbagent/src/evals/lib/chat-runner.ts @@ -12,12 +12,30 @@ import { GenerateTextResponse, traceVercelAiResponse } from './trace'; export const turnFromResponse = (prompt: string, response: GenerateTextResponse): LLMTurn => { const allToolCalls = response.steps.flatMap((step) => { - return step.toolResults.flatMap((toolResult) => { - return Object.values(toolResult).map((toolCall: any) => ({ - name: toolCall.toolName, - inputs: toolCall.args, - output: toolCall.result - })); + console.log(`step ${step.usage.totalTokens}`); + + return step.toolResults.flatMap((toolResult: any) => { + console.log(` toolResult: ${JSON.stringify(toolResult, null, 2)}`); + + if ('type' in toolResult && toolResult.type === 'tool-result') { + // generateText format + return [ + { + name: toolResult.toolName, + inputs: toolResult.args, + output: toolResult.result + } + ]; + } + return Object.values(toolResult).map((toolCall: any) => { + const transformed = { + name: toolCall.toolName, + inputs: toolCall.args, + output: toolCall.result + }; + console.log(` transformed: ${JSON.stringify(transformed)}`); + return transformed; + }); }); }); diff --git a/apps/dbagent/src/evals/lib/llmcheck/dataset.ts b/apps/dbagent/src/evals/lib/llmcheck/dataset.ts new file mode 100644 index 00000000..7e9e5963 --- /dev/null +++ b/apps/dbagent/src/evals/lib/llmcheck/dataset.ts @@ -0,0 +1,32 @@ +import { Chat, LLMTurn } from './types'; + +export type DatasetItem = LLMTurn | Chat; +export type Dataset = AsyncIterable; + +import { promises as fs } from 'fs'; + +import ndjson from 'ndjson'; + +export async function writeToJSONFile(dataset: Dataset, path: string) { + const file = await fs.open(path, 'w'); + try { + const writer = file.createWriteStream().pipe(ndjson.stringify()); + for await (const item of dataset) { + writer.write(item); + } + } finally { + await file.close(); + } +} + +export async function* readFromJSONFile(path: string): AsyncGenerator { + const file = await fs.open(path, 'r'); + try { + const reader = file.createReadStream().pipe(ndjson.parse()); + for await (const item of reader) { + yield item; + } + } finally { + await file.close(); + } +} diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts index 28cf21bc..108b6142 100644 --- a/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/final-answer.ts @@ -7,13 +7,17 @@ export function finalAnswerMetric(expectedResult?: string, model?: JudgeEvalMode config: { name: 'finalAnswer', schema: evalPassResultSchema, - prompt: ({ input, output }) => ` + prompt: ({ input, output, expectedOutput }) => { + const expected = expectedOutput ?? expectedResult; + + return ` The following question was answered by an expert in PostgreSQL and database administration: ${input} ${output} - ${expectedResult ? `${expectedResult}` : ''} + ${expected ? `${expected}` : ''} Please determine whether expert passed or failed at answering the question correctly and accurately. Please provied a critique of how the answer could be improved or does not match the response of an expert in PostgreSQl and database administration. - `, + `; + }, result: (result) => ({ type: 'bool', success: result.result === 'passed', diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts index 1cde7848..bef16271 100644 --- a/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/index.ts @@ -1,2 +1,5 @@ export * from './concise'; +export * from './final-answer'; +export * from './regex-response'; +export * from './toolchoice'; export * from './types'; diff --git a/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts b/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts index 268ae040..0f25079a 100644 --- a/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts +++ b/apps/dbagent/src/evals/lib/llmcheck/metrics/toolchoice.ts @@ -21,12 +21,12 @@ export const toolChoice = toolChoiceMetric({ allowOthers: false, allowMissing: f function measureTurn(turn: LLMTurn, { expectedToolCalls, allowOthers, allowMissing }: ToolChoiceProps): Measure { const expectedToolsSet = new Set([ ...(turn.expectedTools?.map((tool) => tool.name) ?? []), - ...(expectedToolCalls?.map((tool) => tool.inputs) ?? []) + ...(expectedToolCalls?.map((tool) => tool.name) ?? []) ]); const actualToolsSet = new Set(turn.toolCalls?.map((tool) => tool.name) ?? []); - const missing = [...expectedToolsSet].filter((tool) => !actualToolsSet.has(tool)); - const others = [...actualToolsSet].filter((tool) => !expectedToolsSet.has(tool)); + const missing = [...expectedToolsSet].filter((tool) => !actualToolsSet.has(tool)).sort(); + const others = [...actualToolsSet].filter((tool) => !expectedToolsSet.has(tool)).sort(); const errorReport: string[] = []; let success = true; diff --git a/apps/dbagent/src/evals/lib/llmcheck/types.ts b/apps/dbagent/src/evals/lib/llmcheck/types.ts index da49aca9..f81e851d 100644 --- a/apps/dbagent/src/evals/lib/llmcheck/types.ts +++ b/apps/dbagent/src/evals/lib/llmcheck/types.ts @@ -1,4 +1,5 @@ export type Chat = { + id?: string; turns: LLMTurn[]; name?: string; }; @@ -6,6 +7,7 @@ export type Chat = { export type LLMTurnField = keyof LLMTurn; export type LLMTurn = { + id?: string; name?: string; input: string; output: string; @@ -21,6 +23,7 @@ export type LLMTurn = { export type ToolCallFields = keyof ToolCall; export type ToolCall = { + id?: string; name: string; description?: string; inputs?: any[]; diff --git a/apps/dbagent/src/evals/lib/trace.ts b/apps/dbagent/src/evals/lib/trace.ts index 1454d262..777928ce 100644 --- a/apps/dbagent/src/evals/lib/trace.ts +++ b/apps/dbagent/src/evals/lib/trace.ts @@ -60,8 +60,6 @@ const getResponseMessageText = (content: any) => { }; export const traceVercelAiResponse = (response: GenerateTextResponse, expect: ExpectStatic) => { - console.log(response); - const traceFolder = ensureTraceFolderExistsExpect(expect); const humanTraceFile = path.join(traceFolder, 'human.txt'); const humanTrace = ` diff --git a/apps/dbagent/src/lib/ai/agent.ts b/apps/dbagent/src/lib/ai/agent.ts index a8320141..231bf4a6 100644 --- a/apps/dbagent/src/lib/ai/agent.ts +++ b/apps/dbagent/src/lib/ai/agent.ts @@ -1,5 +1,6 @@ import { DataStreamWriter, LanguageModel } from 'ai'; import { Pool } from 'pg'; +import { ArtifactsService, projectArtifactService } from '~/lib/tools/artifacts'; import { DBAccess } from '../db/db'; import { CloudProvider, Connection } from '../db/schema'; import { AugmentedLanguageModel, ToolSet } from './model'; @@ -13,7 +14,7 @@ import { } from './prompts'; import { getLanguageModel, getLanguageModelWithFallback, getProviderRegistry, ModelWithFallback } from './providers'; import { commonToolset } from './tools'; -import { ArtifactsService, getArtifactTools, projectArtifactService } from './tools/artifacts'; +import { getArtifactTools } from './tools/artifacts'; import { ClusterService, getDBClusterTools, getProjectClusterService } from './tools/cluster'; import { DBService, getDBSQLTools, targetDBService } from './tools/db'; import { getPlaybookToolset, PlaybookService, projectPlaybookService } from './tools/playbook'; diff --git a/apps/dbagent/src/lib/ai/tools/artifacts.ts b/apps/dbagent/src/lib/ai/tools/artifacts.ts index 1eb2f99d..2147ae55 100644 --- a/apps/dbagent/src/lib/ai/tools/artifacts.ts +++ b/apps/dbagent/src/lib/ai/tools/artifacts.ts @@ -1,33 +1,12 @@ -import { DataStreamWriter, tool, Tool } from 'ai'; +import { tool, Tool } from 'ai'; import { z } from 'zod'; -import { ArtifactKind } from '~/components/chat/artifacts/artifact'; -import { artifactKinds, documentHandlersByArtifactKind } from '~/components/chat/artifacts/server'; -import { generateUUID } from '~/components/chat/utils'; -import { getDocumentById, saveSuggestions } from '~/lib/db/chats'; -import { DBAccess } from '~/lib/db/db'; -import { ArtifactSuggestion } from '~/lib/db/schema'; -import { commonModel } from '../agent'; +import { artifactKinds } from '~/components/chat/artifacts/server'; +import { ArtifactsService } from '~/lib/tools/artifacts'; interface ArtifactToolProps { tools: ArtifactsService; } -export type DocumentOpResult = - | { - id: string; - title: string; - kind: ArtifactKind; - } - | { - error: string; - }; - -export interface ArtifactsService { - createDocument({ title, kind }: { title: string; kind: (typeof artifactKinds)[number] }): Promise; - updateDocument({ id, description }: { id: string; description: string }): Promise; - requestSuggestions({ documentId }: { documentId: string }): Promise; -} - export function getArtifactTools({ tools }: ArtifactToolProps): Record { return { createDocument: createDocument({ tools }), @@ -36,21 +15,6 @@ export function getArtifactTools({ tools }: ArtifactToolProps): Record await execCreateDocument(project, { title, kind }), - updateDocument: async ({ id, description }) => await execUpdateDocument(project, { id, description }), - requestSuggestions: async ({ documentId }) => await execRequestSuggestions(project, { documentId }) - }; -} - export const createDocument = ({ tools }: ArtifactToolProps) => tool({ description: @@ -68,58 +32,6 @@ export const createDocument = ({ tools }: ArtifactToolProps) => } }); -const execCreateDocument = async ( - { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, - { title, kind }: { title: string; kind: ArtifactKind } -): Promise => { - const id = generateUUID(); - - dataStream.writeData({ - type: 'kind', - content: kind - }); - - dataStream.writeData({ - type: 'id', - content: id - }); - - dataStream.writeData({ - type: 'title', - content: title - }); - - dataStream.writeData({ - type: 'clear', - content: '' - }); - - const documentHandler = documentHandlersByArtifactKind.find( - (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === kind - ); - - if (!documentHandler) { - throw new Error(`No document handler found for kind: ${kind}`); - } - - await documentHandler.onCreateDocument({ - id, - title, - dataStream, - userId, - projectId, - dbAccess - }); - - dataStream.writeData({ type: 'finish', content: '' }); - - return { - id, - title, - kind - }; -}; - export const updateDocument = ({ tools }: ArtifactToolProps) => tool({ description: 'Update a document with the given description.', @@ -139,49 +51,6 @@ export const updateDocument = ({ tools }: ArtifactToolProps) => } }); -const execUpdateDocument = async ( - { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, - { id, description }: { id: string; description: string } -): Promise => { - const document = await getDocumentById(dbAccess, { id }); - - if (!document) { - return { - error: 'Document not found' - }; - } - - dataStream.writeData({ - type: 'clear', - content: document.title - }); - - const documentHandler = documentHandlersByArtifactKind.find( - (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === document.kind - ); - - if (!documentHandler) { - throw new Error(`No document handler found for kind: ${document.kind}`); - } - - await documentHandler.onUpdateDocument({ - document, - description, - dataStream, - userId, - projectId, - dbAccess - }); - - dataStream.writeData({ type: 'finish', content: '' }); - - return { - id, - title: document.title, - kind: document.kind - }; -}; - export const requestSuggestions = ({ tools }: ArtifactToolProps) => tool({ description: 'Request suggestions for a document', @@ -199,67 +68,3 @@ export const requestSuggestions = ({ tools }: ArtifactToolProps) => }; } }); - -export const execRequestSuggestions = async ( - { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, - { documentId }: { documentId: string } -): Promise => { - const document = await getDocumentById(dbAccess, { id: documentId }); - if (!document || !document.content) { - return { - error: 'Document not found' - }; - } - - const suggestions: Array> = []; - - const { elementStream } = await commonModel.streamObject({ - system: - 'You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.', - prompt: document.content, - output: 'array', - schema: z.object({ - originalSentence: z.string().describe('The original sentence'), - suggestedSentence: z.string().describe('The suggested sentence'), - description: z.string().describe('The description of the suggestion') - }), - metadata: { - tags: ['artifact', 'suggestion'] - } - }); - - for await (const element of elementStream) { - const suggestion = { - originalText: element.originalSentence, - suggestedText: element.suggestedSentence, - description: element.description, - id: generateUUID(), - documentId: documentId, - isResolved: false, - projectId - }; - - dataStream.writeData({ - type: 'suggestion', - content: suggestion - }); - - suggestions.push(suggestion); - } - - await saveSuggestions(dbAccess, { - suggestions: suggestions.map((suggestion) => ({ - ...suggestion, - userId, - projectId, - createdAt: new Date(), - documentCreatedAt: document.createdAt - })) - }); - - return { - id: documentId, - title: document.title, - kind: document.kind - }; -}; diff --git a/apps/dbagent/src/lib/env/server.ts b/apps/dbagent/src/lib/env/server.ts index 3daf17b7..2e485a39 100644 --- a/apps/dbagent/src/lib/env/server.ts +++ b/apps/dbagent/src/lib/env/server.ts @@ -38,5 +38,6 @@ const schema = z.object({ }); const serverEnv = schema.parse(process.env); +console.log('serverEnv', serverEnv); export const env = { ...clientEnv, ...serverEnv }; diff --git a/apps/dbagent/src/lib/tools/artifacts.ts b/apps/dbagent/src/lib/tools/artifacts.ts new file mode 100644 index 00000000..3ea31e6e --- /dev/null +++ b/apps/dbagent/src/lib/tools/artifacts.ts @@ -0,0 +1,199 @@ +import { DataStreamWriter } from 'ai'; +import { z } from 'zod'; +import { ArtifactKind } from '~/components/chat/artifacts/artifact'; +import { artifactKinds, documentHandlersByArtifactKind } from '~/components/chat/artifacts/server'; +import { generateUUID } from '~/components/chat/utils'; +import { getDocumentById, saveSuggestions } from '~/lib/db/chats'; +import { ArtifactSuggestion } from '~/lib/db/schema'; +import { commonModel } from '../ai/agent'; +import { DBAccess } from '../db/db'; + +export type ArtifactProjectProps = { + userId: string; + projectId: string; + dataStream: DataStreamWriter; + dbAccess: DBAccess; +}; + +export interface ArtifactsService { + createDocument({ title, kind }: { title: string; kind: (typeof artifactKinds)[number] }): Promise; + updateDocument({ id, description }: { id: string; description: string }): Promise; + requestSuggestions({ documentId }: { documentId: string }): Promise; +} + +export type DocumentOpResult = + | { + id: string; + title: string; + kind: ArtifactKind; + } + | { + error: string; + }; + +export function projectArtifactService(project: ArtifactProjectProps): ArtifactsService { + return { + createDocument: async ({ title, kind }) => await execCreateDocument(project, { title, kind }), + updateDocument: async ({ id, description }) => await execUpdateDocument(project, { id, description }), + requestSuggestions: async ({ documentId }) => await execRequestSuggestions(project, { documentId }) + }; +} + +export const execCreateDocument = async ( + { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, + { title, kind }: { title: string; kind: ArtifactKind } +): Promise => { + const id = generateUUID(); + + dataStream.writeData({ + type: 'kind', + content: kind + }); + + dataStream.writeData({ + type: 'id', + content: id + }); + + dataStream.writeData({ + type: 'title', + content: title + }); + + dataStream.writeData({ + type: 'clear', + content: '' + }); + + const documentHandler = documentHandlersByArtifactKind.find( + (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === kind + ); + + if (!documentHandler) { + throw new Error(`No document handler found for kind: ${kind}`); + } + + await documentHandler.onCreateDocument({ + id, + title, + dataStream, + userId, + projectId, + dbAccess + }); + + dataStream.writeData({ type: 'finish', content: '' }); + + return { + id, + title, + kind + }; +}; + +export const execUpdateDocument = async ( + { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, + { id, description }: { id: string; description: string } +): Promise => { + const document = await getDocumentById(dbAccess, { id }); + + if (!document) { + return { + error: 'Document not found' + }; + } + + dataStream.writeData({ + type: 'clear', + content: document.title + }); + + const documentHandler = documentHandlersByArtifactKind.find( + (documentHandlerByArtifactKind) => documentHandlerByArtifactKind.kind === document.kind + ); + + if (!documentHandler) { + throw new Error(`No document handler found for kind: ${document.kind}`); + } + + await documentHandler.onUpdateDocument({ + document, + description, + dataStream, + userId, + projectId, + dbAccess + }); + + dataStream.writeData({ type: 'finish', content: '' }); + + return { + id, + title: document.title, + kind: document.kind + }; +}; + +export const execRequestSuggestions = async ( + { dataStream, userId, projectId, dbAccess }: ArtifactProjectProps, + { documentId }: { documentId: string } +): Promise => { + const document = await getDocumentById(dbAccess, { id: documentId }); + if (!document || !document.content) { + return { + error: 'Document not found' + }; + } + + const suggestions: Array> = []; + + const { elementStream } = await commonModel.streamObject({ + system: + 'You are a help writing assistant. Given a piece of writing, please offer suggestions to improve the piece of writing and describe the change. It is very important for the edits to contain full sentences instead of just words. Max 5 suggestions.', + prompt: document.content, + output: 'array', + schema: z.object({ + originalSentence: z.string().describe('The original sentence'), + suggestedSentence: z.string().describe('The suggested sentence'), + description: z.string().describe('The description of the suggestion') + }), + metadata: { + tags: ['artifact', 'suggestion'] + } + }); + + for await (const element of elementStream) { + const suggestion = { + originalText: element.originalSentence, + suggestedText: element.suggestedSentence, + description: element.description, + id: generateUUID(), + documentId: documentId, + isResolved: false, + projectId + }; + + dataStream.writeData({ + type: 'suggestion', + content: suggestion + }); + + suggestions.push(suggestion); + } + + await saveSuggestions(dbAccess, { + suggestions: suggestions.map((suggestion) => ({ + ...suggestion, + userId, + projectId, + createdAt: new Date(), + documentCreatedAt: document.createdAt + })) + }); + + return { + id: documentId, + title: document.title, + kind: document.kind + }; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56d57f71..21d597b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,6 +171,9 @@ importers: '@tanstack/react-query': specifier: ^5.75.7 version: 5.76.0(react@19.1.0) + '@types/ndjson': + specifier: ^2.0.4 + version: 2.0.4 '@vercel/functions': specifier: ^2.0.3 version: 2.0.3(@aws-sdk/credential-provider-web-identity@3.806.0) @@ -222,6 +225,9 @@ importers: kysely: specifier: ^0.28.2 version: 0.28.2 + langfuse: + specifier: ^3.37.2 + version: 3.37.2 langfuse-vercel: specifier: ^3.37.2 version: 3.37.2(ai@4.3.15(react@19.1.0)(zod@3.24.4)) @@ -231,6 +237,9 @@ importers: lucide-react: specifier: ^0.509.0 version: 0.509.0(react@19.1.0) + ndjson: + specifier: ^2.0.0 + version: 2.0.0 next: specifier: ^15.3.2 version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4) @@ -355,6 +364,9 @@ importers: autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.3) + cmd-ts: + specifier: ^0.13.0 + version: 0.13.0 dockerode: specifier: ^4.0.6 version: 4.0.6 @@ -4312,6 +4324,9 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} + '@types/ndjson@2.0.4': + resolution: {integrity: sha512-ajAl7AjhFstF6waORYNSS49GL5iBKisqJlgvXuprXFKCX9fto4ordlNU3+XMgkMddgeR0WoQQBmKUk0v0dJ4pw==} + '@types/node@18.19.80': resolution: {integrity: sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==} @@ -4356,6 +4371,9 @@ packages: '@types/tedious@4.0.14': resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -4849,6 +4867,9 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cmd-ts@0.13.0: + resolution: {integrity: sha512-nsnxf6wNIM/JAS7T/x/1JmbEsjH0a8tezXqqpaL0O6+eV0/aDEnRxwjxpu0VzDdRcaC1ixGSbRlUuf/IU59I4g==} + cmdk@1.1.1: resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} peerDependencies: @@ -5163,6 +5184,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} @@ -6903,6 +6927,11 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + ndjson@2.0.0: + resolution: {integrity: sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==} + engines: {node: '>=10'} + hasBin: true + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -7971,6 +8000,9 @@ packages: split-ca@1.0.1: resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -8180,6 +8212,9 @@ packages: resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} engines: {node: '>=18'} + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -13560,6 +13595,11 @@ snapshots: dependencies: '@types/node': 22.15.17 + '@types/ndjson@2.0.4': + dependencies: + '@types/node': 22.15.17 + '@types/through': 0.0.33 + '@types/node@18.19.80': dependencies: undici-types: 5.26.5 @@ -13619,6 +13659,10 @@ snapshots: dependencies: '@types/node': 22.15.17 + '@types/through@0.0.33': + dependencies: + '@types/node': 22.15.17 + '@types/tough-cookie@4.0.5': {} '@types/unist@2.0.11': {} @@ -14171,6 +14215,15 @@ snapshots: clsx@2.1.1: {} + cmd-ts@0.13.0: + dependencies: + chalk: 4.1.2 + debug: 4.4.0 + didyoumean: 1.2.2 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + cmdk@1.1.1(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.1.3)(react@19.1.0) @@ -14459,6 +14512,8 @@ snapshots: dependencies: dequal: 2.0.3 + didyoumean@1.2.2: {} + diff-match-patch@1.0.5: {} difflib@0.2.4: @@ -16633,6 +16688,14 @@ snapshots: natural-compare@1.4.0: {} + ndjson@2.0.0: + dependencies: + json-stringify-safe: 5.0.1 + minimist: 1.2.8 + readable-stream: 3.6.2 + split2: 3.2.2 + through2: 4.0.2 + negotiator@1.0.0: {} next-auth@5.0.0-beta.25(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0): @@ -17872,6 +17935,10 @@ snapshots: split-ca@1.0.1: {} + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + split2@4.2.0: {} sprintf-js@1.1.3: {} @@ -18120,6 +18187,10 @@ snapshots: throttleit@2.1.0: {} + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} From b1135a563b4b3d3920ed2aaa746bd425e316b6f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 May 2025 15:24:55 +0000 Subject: [PATCH 5/5] [Bot] Code fixes --- apps/dbagent/scripts/lftool/loader.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dbagent/scripts/lftool/loader.mjs b/apps/dbagent/scripts/lftool/loader.mjs index 1162b894..21a45019 100644 --- a/apps/dbagent/scripts/lftool/loader.mjs +++ b/apps/dbagent/scripts/lftool/loader.mjs @@ -1,4 +1,4 @@ -import { resolve as resolvePath, dirname } from 'path'; +import { dirname, resolve as resolvePath } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -12,4 +12,4 @@ export async function resolve(specifier, context, nextResolve) { } return nextResolve(specifier); -} \ No newline at end of file +}