From 011724286c72561be84ebc6048ad66e6b7b19449 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 23 Jul 2025 16:20:34 -0600 Subject: [PATCH 01/11] Fix broken font size --- .../src/components/ui/charts/MetricChart/BusterMetricChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/ui/charts/MetricChart/BusterMetricChart.tsx b/apps/web/src/components/ui/charts/MetricChart/BusterMetricChart.tsx index 0c82521a1..ac57a39c4 100644 --- a/apps/web/src/components/ui/charts/MetricChart/BusterMetricChart.tsx +++ b/apps/web/src/components/ui/charts/MetricChart/BusterMetricChart.tsx @@ -163,7 +163,7 @@ export const BusterMetricChart: React.FC = React.memo( {...memoizedAnimation}>
-
{formattedValue}
+

{formattedValue}

From 42d1da5389fc0f256fb61ac2e33f9542ed41d8b4 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 16:56:06 -0600 Subject: [PATCH 02/11] context fixes on messages --- .../helpers/message-fetchers.ts | 28 ++++++++++- .../messages/chatConversationHistory.ts | 49 +++++++++++++------ 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/apps/trigger/src/tasks/message-post-processing/helpers/message-fetchers.ts b/apps/trigger/src/tasks/message-post-processing/helpers/message-fetchers.ts index 265dfc7d6..fef10645f 100644 --- a/apps/trigger/src/tasks/message-post-processing/helpers/message-fetchers.ts +++ b/apps/trigger/src/tasks/message-post-processing/helpers/message-fetchers.ts @@ -1,5 +1,16 @@ import { getPermissionedDatasets } from '@buster/access-controls'; -import { and, chats, eq, getDb, isNotNull, isNull, lte, messages, users } from '@buster/database'; +import { + and, + chats, + eq, + getChatConversationHistory, + getDb, + isNotNull, + isNull, + lte, + messages, + users, +} from '@buster/database'; import type { CoreMessage } from 'ai'; import { DataFetchError, @@ -37,12 +48,25 @@ export async function fetchMessageWithContext(messageId: string): Promise > { let chatMessages: Array<{ id: string; rawLlmMessages: unknown; createdAt: string; + isCompleted: boolean; }>; try { chatMessages = await db @@ -48,6 +50,7 @@ async function getAllMessagesForChat(chatId: string): Promise< id: messages.id, rawLlmMessages: messages.rawLlmMessages, createdAt: messages.createdAt, + isCompleted: messages.isCompleted, }) .from(messages) .where(and(eq(messages.chatId, chatId), isNull(messages.deletedAt))) @@ -61,25 +64,42 @@ async function getAllMessagesForChat(chatId: string): Promise< return chatMessages; } -// Helper function to combine raw LLM messages -function combineRawLlmMessages(chatMessages: Array<{ rawLlmMessages: unknown }>): CoreMessage[] { - const conversationHistory: CoreMessage[] = []; - +// Helper function to get the most recent raw LLM messages +function getMostRecentRawLlmMessages( + chatMessages: Array<{ rawLlmMessages: unknown; isCompleted: boolean }> +): CoreMessage[] { try { - for (const message of chatMessages) { - if (message.rawLlmMessages && Array.isArray(message.rawLlmMessages)) { - // Preserve the exact message structure from the database - // Each rawLlmMessages array should contain properly unbundled messages - conversationHistory.push(...(message.rawLlmMessages as CoreMessage[])); + // Find the most recent completed message with valid rawLlmMessages + // We iterate backwards to find the most recent valid message + for (let i = chatMessages.length - 1; i >= 0; i--) { + const message = chatMessages[i]; + + // Skip if message is not completed + if (!message?.isCompleted) { + continue; + } + + // Skip if rawLlmMessages is empty, null, or default empty object + const rawMessages = message.rawLlmMessages; + if (!rawMessages || + (typeof rawMessages === 'object' && Object.keys(rawMessages).length === 0) || + rawMessages === '{}') { + continue; + } + + // Check if it's a valid array + if (Array.isArray(rawMessages) && rawMessages.length > 0) { + return rawMessages as CoreMessage[]; } } + + // No valid messages found + return []; } catch (processingError) { throw new Error( `Failed to process conversation history: ${processingError instanceof Error ? processingError.message : 'Unknown processing error'}` ); } - - return conversationHistory; } // Zod schemas for validation @@ -94,7 +114,8 @@ export type ChatConversationHistoryOutput = z.infer Date: Wed, 23 Jul 2025 16:56:32 -0600 Subject: [PATCH 03/11] lint fixes --- .../queries/messages/chatConversationHistory.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/database/src/queries/messages/chatConversationHistory.ts b/packages/database/src/queries/messages/chatConversationHistory.ts index 328620088..732be81e6 100644 --- a/packages/database/src/queries/messages/chatConversationHistory.ts +++ b/packages/database/src/queries/messages/chatConversationHistory.ts @@ -73,20 +73,22 @@ function getMostRecentRawLlmMessages( // We iterate backwards to find the most recent valid message for (let i = chatMessages.length - 1; i >= 0; i--) { const message = chatMessages[i]; - + // Skip if message is not completed if (!message?.isCompleted) { continue; } - + // Skip if rawLlmMessages is empty, null, or default empty object const rawMessages = message.rawLlmMessages; - if (!rawMessages || - (typeof rawMessages === 'object' && Object.keys(rawMessages).length === 0) || - rawMessages === '{}') { + if ( + !rawMessages || + (typeof rawMessages === 'object' && Object.keys(rawMessages).length === 0) || + rawMessages === '{}' + ) { continue; } - + // Check if it's a valid array if (Array.isArray(rawMessages) && rawMessages.length > 0) { return rawMessages as CoreMessage[]; From 5f43b2d0744ba5ac6e8b820c1415109ded0bcf9a Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 17:39:03 -0600 Subject: [PATCH 04/11] new structure for chat and values --- .../steps/extract-values-search-step.test.ts | 36 +++++++++---------- .../src/steps/extract-values-search-step.ts | 29 +++++++++------ .../ai/src/steps/generate-chat-title-step.ts | 32 +++++++++++------ 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/packages/ai/src/steps/extract-values-search-step.test.ts b/packages/ai/src/steps/extract-values-search-step.test.ts index 1492fe09e..31722a570 100644 --- a/packages/ai/src/steps/extract-values-search-step.test.ts +++ b/packages/ai/src/steps/extract-values-search-step.test.ts @@ -19,17 +19,16 @@ vi.mock('braintrust', () => ({ wrapAISDKModel: vi.fn((model) => model), })); -// Create a ref object to hold the mock generate function -const mockGenerateRef = { current: vi.fn() }; +// Mock the AI SDK +vi.mock('ai', () => ({ + generateObject: vi.fn(), +})); -// Mock the Agent class from Mastra with the generate function +// Mock Mastra vi.mock('@mastra/core', async () => { const actual = await vi.importActual('@mastra/core'); return { ...actual, - Agent: vi.fn().mockImplementation(() => ({ - generate: (...args: any[]) => mockGenerateRef.current(...args), - })), createStep: actual.createStep, }; }); @@ -41,18 +40,17 @@ import { extractValuesSearchStep } from './extract-values-search-step'; // Import the mocked functions import { generateEmbedding, searchValuesByEmbedding } from '@buster/stored-values/search'; +import { generateObject } from 'ai'; const mockGenerateEmbedding = generateEmbedding as ReturnType; const mockSearchValuesByEmbedding = searchValuesByEmbedding as ReturnType; - -// Access the mock generate function through the ref -const mockGenerate = mockGenerateRef.current; +const mockGenerateObject = generateObject as ReturnType; describe('extractValuesSearchStep', () => { beforeEach(() => { vi.clearAllMocks(); // Set default mock behavior - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: [] }, }); }); @@ -72,7 +70,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock the LLM response for keyword extraction - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['Red Bull', 'California'] }, }); @@ -141,7 +139,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock empty keyword extraction - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: [] }, }); @@ -195,7 +193,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock successful keyword extraction - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['Red Bull'] }, }); @@ -226,7 +224,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock LLM extraction success but embedding failure - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['test keyword'] }, }); @@ -254,7 +252,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock successful keyword extraction - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['test keyword'] }, }); @@ -284,7 +282,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock two keywords: one succeeds, one fails - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['keyword1', 'keyword2'] }, }); @@ -327,7 +325,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock everything to fail - mockGenerate.mockRejectedValue(new Error('LLM failure')); + mockGenerateObject.mockRejectedValue(new Error('LLM failure')); mockGenerateEmbedding.mockRejectedValue(new Error('Embedding failure')); mockSearchValuesByEmbedding.mockRejectedValue(new Error('Database failure')); @@ -378,7 +376,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock successful keyword extraction - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['Red Bull'] }, }); @@ -437,7 +435,7 @@ describe('extractValuesSearchStep', () => { runtimeContext.set('dataSourceId', 'test-datasource-id'); // Mock successful keyword extraction - mockGenerate.mockResolvedValue({ + mockGenerateObject.mockResolvedValue({ object: { values: ['test'] }, }); diff --git a/packages/ai/src/steps/extract-values-search-step.ts b/packages/ai/src/steps/extract-values-search-step.ts index ed7e49894..c5e38aae7 100644 --- a/packages/ai/src/steps/extract-values-search-step.ts +++ b/packages/ai/src/steps/extract-values-search-step.ts @@ -1,7 +1,8 @@ import type { StoredValueResult } from '@buster/stored-values'; import { generateEmbedding, searchValuesByEmbedding } from '@buster/stored-values/search'; -import { Agent, createStep } from '@mastra/core'; +import { createStep } from '@mastra/core'; import type { RuntimeContext } from '@mastra/core/runtime-context'; +import { generateObject } from 'ai'; import type { CoreMessage } from 'ai'; import { wrapTraced } from 'braintrust'; import { z } from 'zod'; @@ -12,6 +13,11 @@ import type { AnalystRuntimeContext } from '../workflows/analyst-workflow'; const inputSchema = thinkAndPrepWorkflowInputSchema; +// Schema for what the LLM returns +const llmOutputSchema = z.object({ + values: z.array(z.string()).describe('The values that the agent will search for.'), +}); + // Step output schema - what the step returns after performing the search export const extractValuesSearchOutputSchema = z.object({ values: z.array(z.string()).describe('The values that the agent will search for.'), @@ -231,12 +237,6 @@ async function searchStoredValues( } } -const valuesAgent = new Agent({ - name: 'Extract Values', - instructions: extractValuesInstructions, - model: Haiku35, -}); - const extractValuesSearchStepExecution = async ({ inputData, runtimeContext, @@ -264,12 +264,19 @@ const extractValuesSearchStepExecution = async ({ try { const tracedValuesExtraction = wrapTraced( async () => { - const response = await valuesAgent.generate(messages, { - maxSteps: 0, - output: extractValuesSearchOutputSchema, + const { object } = await generateObject({ + model: Haiku35, + schema: llmOutputSchema, + messages: [ + { + role: 'system', + content: extractValuesInstructions, + }, + ...messages, + ], }); - return response.object; + return object; }, { name: 'Extract Values', diff --git a/packages/ai/src/steps/generate-chat-title-step.ts b/packages/ai/src/steps/generate-chat-title-step.ts index 728a36094..b43ae9f11 100644 --- a/packages/ai/src/steps/generate-chat-title-step.ts +++ b/packages/ai/src/steps/generate-chat-title-step.ts @@ -1,6 +1,7 @@ import { updateChat, updateMessage } from '@buster/database'; -import { Agent, createStep } from '@mastra/core'; +import { createStep } from '@mastra/core'; import type { RuntimeContext } from '@mastra/core/runtime-context'; +import { generateObject } from 'ai'; import type { CoreMessage } from 'ai'; import { wrapTraced } from 'braintrust'; import { z } from 'zod'; @@ -11,6 +12,12 @@ import type { AnalystRuntimeContext } from '../workflows/analyst-workflow'; const inputSchema = thinkAndPrepWorkflowInputSchema; +// Schema for what the LLM returns +const llmOutputSchema = z.object({ + title: z.string().describe('The title for the chat.'), +}); + +// Schema for what the step returns (includes pass-through data) export const generateChatTitleOutputSchema = z.object({ title: z.string().describe('The title for the chat.'), // Pass through dashboard context @@ -28,13 +35,9 @@ export const generateChatTitleOutputSchema = z.object({ const generateChatTitleInstructions = ` I am a chat title generator that is responsible for generating a title for the chat. -`; -const todosAgent = new Agent({ - name: 'Extract Values', - instructions: generateChatTitleInstructions, - model: Haiku35, -}); +The title should be 3-8 words, capturing the main topic or intent of the conversation. +`; const generateChatTitleExecution = async ({ inputData, @@ -63,12 +66,19 @@ const generateChatTitleExecution = async ({ try { const tracedChatTitle = wrapTraced( async () => { - const response = await todosAgent.generate(messages, { - maxSteps: 0, - output: generateChatTitleOutputSchema, + const { object } = await generateObject({ + model: Haiku35, + schema: llmOutputSchema, + messages: [ + { + role: 'system', + content: generateChatTitleInstructions, + }, + ...messages, + ], }); - return response.object; + return object; }, { name: 'Generate Chat Title', From b8b26af1f135aaa1476612b1d6e448a02f7ee693 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 20:55:20 -0600 Subject: [PATCH 05/11] update trigger package? --- apps/trigger/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/trigger/package.json b/apps/trigger/package.json index a1ede1a13..335183339 100644 --- a/apps/trigger/package.json +++ b/apps/trigger/package.json @@ -27,6 +27,7 @@ "@buster/test-utils": "workspace:*", "@buster/typescript-config": "workspace:*", "@buster/vitest-config": "workspace:*", + "@buster-tools/web-tools": "workspace:*", "@mastra/core": "catalog:", "@trigger.dev/sdk": "catalog:", "ai": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ed2886f1..872b43113 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -181,6 +181,9 @@ importers: apps/trigger: dependencies: + '@buster-tools/web-tools': + specifier: workspace:* + version: link:../../packages/web-tools '@buster/access-controls': specifier: workspace:* version: link:../../packages/access-controls From 2140f081637094a6c79f60cd79e56a59e0e43157 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 21:06:42 -0600 Subject: [PATCH 06/11] package renamo --- packages/web-tools/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-tools/package.json b/packages/web-tools/package.json index 49d661eaa..3e5b895e9 100644 --- a/packages/web-tools/package.json +++ b/packages/web-tools/package.json @@ -1,5 +1,5 @@ { - "name": "@buster-tools/web-tools", + "name": "@buster/web-tools", "version": "0.1.0", "description": "Web scraping and research tools using Firecrawl and other services", "type": "module", From 3396f9fb8aff5e2332654872b302e78ae02b21fe Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 21:09:53 -0600 Subject: [PATCH 07/11] Refactor package references: rename '@buster-tools/web-tools' to '@buster/web-tools' in package.json and pnpm-lock.yaml --- apps/trigger/package.json | 2 +- packages/ai/package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/trigger/package.json b/apps/trigger/package.json index 335183339..a4ef5871b 100644 --- a/apps/trigger/package.json +++ b/apps/trigger/package.json @@ -27,7 +27,7 @@ "@buster/test-utils": "workspace:*", "@buster/typescript-config": "workspace:*", "@buster/vitest-config": "workspace:*", - "@buster-tools/web-tools": "workspace:*", + "@buster/web-tools": "workspace:*", "@mastra/core": "catalog:", "@trigger.dev/sdk": "catalog:", "ai": "catalog:", diff --git a/packages/ai/package.json b/packages/ai/package.json index 670a63da6..2668d3d9d 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -45,7 +45,7 @@ "@buster/test-utils": "workspace:*", "@buster/typescript-config": "workspace:*", "@buster/vitest-config": "workspace:*", - "@buster-tools/web-tools": "workspace:*", + "@buster/web-tools": "workspace:*", "@mastra/core": "catalog:", "@mastra/loggers": "^0.10.3", "ai": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 872b43113..ee57d2f26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -181,9 +181,6 @@ importers: apps/trigger: dependencies: - '@buster-tools/web-tools': - specifier: workspace:* - version: link:../../packages/web-tools '@buster/access-controls': specifier: workspace:* version: link:../../packages/access-controls @@ -211,6 +208,9 @@ importers: '@buster/vitest-config': specifier: workspace:* version: link:../../packages/vitest-config + '@buster/web-tools': + specifier: workspace:* + version: link:../../packages/web-tools '@mastra/core': specifier: 'catalog:' version: 0.10.8(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.1) @@ -684,9 +684,6 @@ importers: '@ai-sdk/provider': specifier: ^1.1.3 version: 1.1.3 - '@buster-tools/web-tools': - specifier: workspace:* - version: link:../web-tools '@buster/access-controls': specifier: workspace:* version: link:../access-controls @@ -717,6 +714,9 @@ importers: '@buster/vitest-config': specifier: workspace:* version: link:../vitest-config + '@buster/web-tools': + specifier: workspace:* + version: link:../web-tools '@mastra/core': specifier: 'catalog:' version: 0.10.8(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.1) From ab374dc465579901ee19725ee8ef7883369a1658 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 03:09:56 +0000 Subject: [PATCH 08/11] BUS-1524: Enhance error messages for tool availability and SQL wildcards - Update createWorkflowAwareHealingMessage to include next workflow step info and transition descriptions - Replace SQL wildcard error message with more user-friendly text - Maintain existing function signatures and return structures Co-Authored-By: Dallin Bentley --- .../ai/src/utils/retry/retry-agent-stream.ts | 40 ++++++++++++------- .../sql-permissions/sql-parser-helpers.ts | 3 +- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/packages/ai/src/utils/retry/retry-agent-stream.ts b/packages/ai/src/utils/retry/retry-agent-stream.ts index eb7d0d0de..42d9aae75 100644 --- a/packages/ai/src/utils/retry/retry-agent-stream.ts +++ b/packages/ai/src/utils/retry/retry-agent-stream.ts @@ -14,7 +14,7 @@ import type { RetryableError, WorkflowContext } from './types'; * Creates a workflow-aware healing message for NoSuchToolError */ function createWorkflowAwareHealingMessage(toolName: string, context?: WorkflowContext): string { - const baseMessage = `Tool "${toolName}" is not available in the current mode.`; + const baseMessage = `error: Tool "${toolName}" is not available.`; if (!context) { return `${baseMessage} Please use one of the available tools instead.`; @@ -22,28 +22,40 @@ function createWorkflowAwareHealingMessage(toolName: string, context?: WorkflowC const { currentStep, availableTools } = context; + const nextMode = currentStep === 'think-and-prep' ? 'analyst' : 'think-and-prep'; + const transitionDescription = currentStep === 'think-and-prep' + ? 'after thinking, understanding the data, and submitting your thoughts' + : 'after completing your analysis'; + // Use actual available tools if provided if (availableTools && availableTools.size > 0) { - const toolList = Array.from(availableTools).sort().join(', '); - return `${baseMessage} + const currentToolList = Array.from(availableTools).sort().join(', '); + + const nextModeTools = nextMode === 'analyst' + ? 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool' + : 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion'; -You are currently in ${currentStep} mode. The available tools are: ${toolList}. + return `${baseMessage} For reference you are currently in ${currentStep} mode which has access to the following tools: +${currentToolList} -Please use one of these tools to continue with your task. Make sure to use the exact tool name as listed above.`; +The next mode that you'll transition to ${transitionDescription} will be the ${nextMode} mode which has access to the following tools: +${nextModeTools}`; } // Fallback to static message if tools not provided - const pipelineContext = ` + const currentModeTools = currentStep === 'think-and-prep' + ? 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion' + : 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool'; + + const nextModeTools = nextMode === 'analyst' + ? 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool' + : 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion'; -This workflow has two steps: -1. think-and-prep mode: Available tools are sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion -2. analyst mode: Available tools are createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool + return `${baseMessage} For reference you are currently in ${currentStep} mode which has access to the following tools: +${currentModeTools} -You are currently in ${currentStep} mode. Please use one of the tools available in your current mode. - -You should proceed with the proper tool calls in the context of the current step. There is a chance you might be a little confused about where you are in the workflow. or the tools available to you.`; - - return baseMessage + pipelineContext; +The next mode that you'll transition to ${transitionDescription} will be the ${nextMode} mode which has access to the following tools: +${nextModeTools}`; } /** diff --git a/packages/ai/src/utils/sql-permissions/sql-parser-helpers.ts b/packages/ai/src/utils/sql-permissions/sql-parser-helpers.ts index ef6d4e2dc..55c2f27c1 100644 --- a/packages/ai/src/utils/sql-permissions/sql-parser-helpers.ts +++ b/packages/ai/src/utils/sql-permissions/sql-parser-helpers.ts @@ -410,10 +410,9 @@ export function validateWildcardUsage( } if (blockedTables.length > 0) { - const tableList = blockedTables.join(', '); return { isValid: false, - error: `Wildcard usage on physical tables is not allowed: ${tableList}. Please specify explicit column names.`, + error: `You're not allowed to use a wildcard on physical tables, please be specific about which columns you'd like to work with`, blockedTables, }; } From acee19e981efd427a17c377b542cf6dca2099c7a Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 21:13:56 -0600 Subject: [PATCH 09/11] reolve to @butster/web-tools --- packages/ai/src/tools/web-tools/web-search-tool.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/ai/src/tools/web-tools/web-search-tool.ts b/packages/ai/src/tools/web-tools/web-search-tool.ts index ca8b29dfe..52a1e944d 100644 --- a/packages/ai/src/tools/web-tools/web-search-tool.ts +++ b/packages/ai/src/tools/web-tools/web-search-tool.ts @@ -1,8 +1,4 @@ -import { - FirecrawlService, - type WebSearchOptions, - type WebSearchResult, -} from '@buster-tools/web-tools'; +import { FirecrawlService, type WebSearchOptions, type WebSearchResult } from '@buster/web-tools'; import type { RuntimeContext } from '@mastra/core/runtime-context'; import { createTool } from '@mastra/core/tools'; import { wrapTraced } from 'braintrust'; From ae9f0c4f7e67e0e1014a3086dddabc99791606db Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 03:20:46 +0000 Subject: [PATCH 10/11] Fix incorrect import paths from '@buster-tools/web-tools' to '@buster/web-tools' - Update package.json dependencies in ai and trigger packages - Fix import statements in web-search-tool.ts and web-search-tool.test.ts - Update pnpm-lock.yaml to reflect correct package references - Resolves CI build failure due to missing module '@buster-tools/web-tools' Co-Authored-By: Dallin Bentley --- apps/trigger/package.json | 2 +- packages/ai/package.json | 2 +- .../ai/src/tools/web-tools/web-search-tool.test.ts | 4 ++-- packages/ai/src/tools/web-tools/web-search-tool.ts | 2 +- pnpm-lock.yaml | 12 ++++++------ 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/trigger/package.json b/apps/trigger/package.json index 335183339..a4ef5871b 100644 --- a/apps/trigger/package.json +++ b/apps/trigger/package.json @@ -27,7 +27,7 @@ "@buster/test-utils": "workspace:*", "@buster/typescript-config": "workspace:*", "@buster/vitest-config": "workspace:*", - "@buster-tools/web-tools": "workspace:*", + "@buster/web-tools": "workspace:*", "@mastra/core": "catalog:", "@trigger.dev/sdk": "catalog:", "ai": "catalog:", diff --git a/packages/ai/package.json b/packages/ai/package.json index 670a63da6..2668d3d9d 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -45,7 +45,7 @@ "@buster/test-utils": "workspace:*", "@buster/typescript-config": "workspace:*", "@buster/vitest-config": "workspace:*", - "@buster-tools/web-tools": "workspace:*", + "@buster/web-tools": "workspace:*", "@mastra/core": "catalog:", "@mastra/loggers": "^0.10.3", "ai": "catalog:", diff --git a/packages/ai/src/tools/web-tools/web-search-tool.test.ts b/packages/ai/src/tools/web-tools/web-search-tool.test.ts index 606dbfa01..02ed2607a 100644 --- a/packages/ai/src/tools/web-tools/web-search-tool.test.ts +++ b/packages/ai/src/tools/web-tools/web-search-tool.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { webSearch } from './web-search-tool'; -vi.mock('@buster-tools/web-tools', () => { +vi.mock('@buster/web-tools', () => { const mockFirecrawlService = { webSearch: vi.fn(), }; @@ -18,7 +18,7 @@ describe('webSearch tool', () => { beforeEach(async () => { vi.clearAllMocks(); const { mockFirecrawlService: mock } = vi.mocked( - await import('@buster-tools/web-tools') + await import('@buster/web-tools') ) as any; mockFirecrawlService = mock; }); diff --git a/packages/ai/src/tools/web-tools/web-search-tool.ts b/packages/ai/src/tools/web-tools/web-search-tool.ts index ca8b29dfe..055dd3d2c 100644 --- a/packages/ai/src/tools/web-tools/web-search-tool.ts +++ b/packages/ai/src/tools/web-tools/web-search-tool.ts @@ -2,7 +2,7 @@ import { FirecrawlService, type WebSearchOptions, type WebSearchResult, -} from '@buster-tools/web-tools'; +} from '@buster/web-tools'; import type { RuntimeContext } from '@mastra/core/runtime-context'; import { createTool } from '@mastra/core/tools'; import { wrapTraced } from 'braintrust'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 872b43113..ee57d2f26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -181,9 +181,6 @@ importers: apps/trigger: dependencies: - '@buster-tools/web-tools': - specifier: workspace:* - version: link:../../packages/web-tools '@buster/access-controls': specifier: workspace:* version: link:../../packages/access-controls @@ -211,6 +208,9 @@ importers: '@buster/vitest-config': specifier: workspace:* version: link:../../packages/vitest-config + '@buster/web-tools': + specifier: workspace:* + version: link:../../packages/web-tools '@mastra/core': specifier: 'catalog:' version: 0.10.8(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.1) @@ -684,9 +684,6 @@ importers: '@ai-sdk/provider': specifier: ^1.1.3 version: 1.1.3 - '@buster-tools/web-tools': - specifier: workspace:* - version: link:../web-tools '@buster/access-controls': specifier: workspace:* version: link:../access-controls @@ -717,6 +714,9 @@ importers: '@buster/vitest-config': specifier: workspace:* version: link:../vitest-config + '@buster/web-tools': + specifier: workspace:* + version: link:../web-tools '@mastra/core': specifier: 'catalog:' version: 0.10.8(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.1) From 7cd3aed6fafe43d60cc550bf59f33ae6e35303f2 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 23 Jul 2025 21:46:51 -0600 Subject: [PATCH 11/11] Refactor import statements and enhance error messages in SQL validation tests - Simplified import statements in web-search-tool.ts and web-search-tool.test.ts for better readability. - Updated error messages in sql-parser-helpers.test.ts to provide clearer feedback regarding wildcard usage on physical tables. --- .../tools/web-tools/web-search-tool.test.ts | 4 +-- .../ai/src/tools/web-tools/web-search-tool.ts | 6 +--- .../ai/src/utils/retry/retry-agent-stream.ts | 32 +++++++++++-------- .../sql-parser-helpers.test.ts | 8 ++--- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/ai/src/tools/web-tools/web-search-tool.test.ts b/packages/ai/src/tools/web-tools/web-search-tool.test.ts index 02ed2607a..101911845 100644 --- a/packages/ai/src/tools/web-tools/web-search-tool.test.ts +++ b/packages/ai/src/tools/web-tools/web-search-tool.test.ts @@ -17,9 +17,7 @@ describe('webSearch tool', () => { beforeEach(async () => { vi.clearAllMocks(); - const { mockFirecrawlService: mock } = vi.mocked( - await import('@buster/web-tools') - ) as any; + const { mockFirecrawlService: mock } = vi.mocked(await import('@buster/web-tools')) as any; mockFirecrawlService = mock; }); diff --git a/packages/ai/src/tools/web-tools/web-search-tool.ts b/packages/ai/src/tools/web-tools/web-search-tool.ts index 055dd3d2c..52a1e944d 100644 --- a/packages/ai/src/tools/web-tools/web-search-tool.ts +++ b/packages/ai/src/tools/web-tools/web-search-tool.ts @@ -1,8 +1,4 @@ -import { - FirecrawlService, - type WebSearchOptions, - type WebSearchResult, -} from '@buster/web-tools'; +import { FirecrawlService, type WebSearchOptions, type WebSearchResult } from '@buster/web-tools'; import type { RuntimeContext } from '@mastra/core/runtime-context'; import { createTool } from '@mastra/core/tools'; import { wrapTraced } from 'braintrust'; diff --git a/packages/ai/src/utils/retry/retry-agent-stream.ts b/packages/ai/src/utils/retry/retry-agent-stream.ts index 42d9aae75..179410953 100644 --- a/packages/ai/src/utils/retry/retry-agent-stream.ts +++ b/packages/ai/src/utils/retry/retry-agent-stream.ts @@ -23,17 +23,19 @@ function createWorkflowAwareHealingMessage(toolName: string, context?: WorkflowC const { currentStep, availableTools } = context; const nextMode = currentStep === 'think-and-prep' ? 'analyst' : 'think-and-prep'; - const transitionDescription = currentStep === 'think-and-prep' - ? 'after thinking, understanding the data, and submitting your thoughts' - : 'after completing your analysis'; + const transitionDescription = + currentStep === 'think-and-prep' + ? 'after thinking, understanding the data, and submitting your thoughts' + : 'after completing your analysis'; // Use actual available tools if provided if (availableTools && availableTools.size > 0) { const currentToolList = Array.from(availableTools).sort().join(', '); - - const nextModeTools = nextMode === 'analyst' - ? 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool' - : 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion'; + + const nextModeTools = + nextMode === 'analyst' + ? 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool' + : 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion'; return `${baseMessage} For reference you are currently in ${currentStep} mode which has access to the following tools: ${currentToolList} @@ -43,13 +45,15 @@ ${nextModeTools}`; } // Fallback to static message if tools not provided - const currentModeTools = currentStep === 'think-and-prep' - ? 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion' - : 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool'; - - const nextModeTools = nextMode === 'analyst' - ? 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool' - : 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion'; + const currentModeTools = + currentStep === 'think-and-prep' + ? 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion' + : 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool'; + + const nextModeTools = + nextMode === 'analyst' + ? 'createMetrics, modifyMetrics, createDashboards, modifyDashboards, doneTool' + : 'sequentialThinking, executeSql, respondWithoutAssetCreation, submitThoughts, messageUserClarifyingQuestion'; return `${baseMessage} For reference you are currently in ${currentStep} mode which has access to the following tools: ${currentModeTools} diff --git a/packages/ai/src/utils/sql-permissions/sql-parser-helpers.test.ts b/packages/ai/src/utils/sql-permissions/sql-parser-helpers.test.ts index 22371bdc8..a6308ec88 100644 --- a/packages/ai/src/utils/sql-permissions/sql-parser-helpers.test.ts +++ b/packages/ai/src/utils/sql-permissions/sql-parser-helpers.test.ts @@ -426,7 +426,7 @@ models: const sql = 'SELECT * FROM users'; const result = validateWildcardUsage(sql); expect(result.isValid).toBe(false); - expect(result.error).toContain('Wildcard usage on physical tables is not allowed'); + expect(result.error).toContain("You're not allowed to use a wildcard on physical tables"); expect(result.blockedTables).toContain('users'); }); @@ -434,7 +434,7 @@ models: const sql = 'SELECT u.* FROM users u'; const result = validateWildcardUsage(sql); expect(result.isValid).toBe(false); - expect(result.error).toContain('Wildcard usage on physical tables is not allowed'); + expect(result.error).toContain("You're not allowed to use a wildcard on physical tables"); expect(result.blockedTables).toContain('u'); }); @@ -470,7 +470,7 @@ models: `; const result = validateWildcardUsage(sql); expect(result.isValid).toBe(false); - expect(result.error).toContain('Wildcard usage on physical tables is not allowed'); + expect(result.error).toContain("You're not allowed to use a wildcard on physical tables"); expect(result.blockedTables).toContain('users'); }); @@ -514,7 +514,7 @@ models: const sql = 'SELECT * FROM public.users'; const result = validateWildcardUsage(sql); expect(result.isValid).toBe(false); - expect(result.error).toContain('Wildcard usage on physical tables is not allowed'); + expect(result.error).toContain("You're not allowed to use a wildcard on physical tables"); }); it('should handle invalid SQL gracefully', () => {