2025-10-01 10:49:25 +08:00
|
|
|
import type { LanguageModelV2 } from '@ai-sdk/provider';
|
2025-10-01 10:59:53 +08:00
|
|
|
import type { Sandbox } from '@buster/sandbox';
|
2025-08-06 12:11:48 +08:00
|
|
|
import { type ModelMessage, hasToolCall, stepCountIs, streamText } from 'ai';
|
|
|
|
import { wrapTraced } from 'braintrust';
|
|
|
|
import z from 'zod';
|
2025-09-03 11:21:33 +08:00
|
|
|
import { DEFAULT_ANTHROPIC_OPTIONS } from '../../llm/providers/gateway';
|
2025-08-08 22:03:22 +08:00
|
|
|
import { Sonnet4 } from '../../llm/sonnet-4';
|
2025-10-01 12:15:38 +08:00
|
|
|
import { createIdleTool } from '../../tools';
|
2025-10-01 12:44:15 +08:00
|
|
|
import { createEditFileTool, createLsTool, createMultiEditFileTool, createWriteFileTool } from '../../tools/file-tools';
|
2025-10-01 12:15:38 +08:00
|
|
|
import { createBashTool } from '../../tools/file-tools/bash-tool/bash-tool';
|
|
|
|
import { createGrepTool } from '../../tools/file-tools/grep-tool/grep-tool';
|
|
|
|
import { createReadFileTool } from '../../tools/file-tools/read-file-tool/read-file-tool';
|
2025-08-21 00:22:35 +08:00
|
|
|
import { type AgentContext, repairToolCall } from '../../utils/tool-call-repair';
|
2025-10-03 06:45:08 +08:00
|
|
|
import { getDocsAgentSystemPrompt as getAnalyticsEngineerAgentSystemPrompt } from './get-analytics-engineer-agent-system-prompt';
|
2025-10-03 22:09:09 +08:00
|
|
|
import type { ToolEventCallback } from './tool-events';
|
2025-07-24 00:55:03 +08:00
|
|
|
|
2025-10-03 06:45:08 +08:00
|
|
|
export const ANALYST_ENGINEER_AGENT_NAME = 'analyticsEngineerAgent';
|
2025-08-21 00:22:35 +08:00
|
|
|
|
2025-10-03 06:45:08 +08:00
|
|
|
const STOP_CONDITIONS = [stepCountIs(100), hasToolCall('idleTool')];
|
2025-08-06 12:11:48 +08:00
|
|
|
|
2025-10-03 06:45:08 +08:00
|
|
|
const AnalyticsEngineerAgentOptionsSchema = z.object({
|
2025-08-06 12:11:48 +08:00
|
|
|
folder_structure: z.string().describe('The file structure of the dbt repository'),
|
2025-08-07 07:10:25 +08:00
|
|
|
userId: z.string(),
|
|
|
|
chatId: z.string(),
|
|
|
|
dataSourceId: z.string(),
|
|
|
|
organizationId: z.string(),
|
2025-08-16 05:24:05 +08:00
|
|
|
messageId: z.string(),
|
2025-08-08 06:09:45 +08:00
|
|
|
sandbox: z
|
|
|
|
.custom<Sandbox>(
|
|
|
|
(val) => {
|
|
|
|
return val && typeof val === 'object' && 'id' in val && 'fs' in val;
|
|
|
|
},
|
|
|
|
{ message: 'Invalid Sandbox instance' }
|
|
|
|
)
|
|
|
|
.optional(),
|
2025-10-01 10:59:53 +08:00
|
|
|
model: z
|
|
|
|
.custom<LanguageModelV2>()
|
|
|
|
.optional()
|
|
|
|
.describe('Custom language model to use (defaults to Sonnet4)'),
|
2025-08-06 12:11:48 +08:00
|
|
|
});
|
|
|
|
|
2025-10-03 06:45:08 +08:00
|
|
|
const AnalyticsEngineerAgentStreamOptionsSchema = z.object({
|
2025-08-06 12:11:48 +08:00
|
|
|
messages: z.array(z.custom<ModelMessage>()).describe('The messages to send to the docs agent'),
|
2025-07-24 00:55:03 +08:00
|
|
|
});
|
2025-08-06 12:11:48 +08:00
|
|
|
|
2025-10-03 22:09:09 +08:00
|
|
|
export type AnalyticsEngineerAgentOptions = z.infer<typeof AnalyticsEngineerAgentOptionsSchema> & {
|
|
|
|
onToolEvent?: ToolEventCallback;
|
|
|
|
};
|
2025-10-03 06:45:08 +08:00
|
|
|
export type AnalyticsEngineerAgentStreamOptions = z.infer<typeof AnalyticsEngineerAgentStreamOptionsSchema>;
|
2025-08-06 12:11:48 +08:00
|
|
|
|
2025-08-07 23:58:28 +08:00
|
|
|
// Extended type for passing to tools (includes sandbox)
|
2025-10-03 06:45:08 +08:00
|
|
|
export type DocsAgentContextWithSandbox = AnalyticsEngineerAgentOptions & { sandbox: Sandbox };
|
2025-08-07 23:58:28 +08:00
|
|
|
|
2025-10-03 06:45:08 +08:00
|
|
|
export function createAnalyticsEngineerAgent(analyticsEngineerAgentOptions: AnalyticsEngineerAgentOptions) {
|
2025-08-06 12:11:48 +08:00
|
|
|
const systemMessage = {
|
|
|
|
role: 'system',
|
2025-10-03 06:45:08 +08:00
|
|
|
content: getAnalyticsEngineerAgentSystemPrompt(analyticsEngineerAgentOptions.folder_structure),
|
2025-09-03 11:21:33 +08:00
|
|
|
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
2025-08-06 12:11:48 +08:00
|
|
|
} as ModelMessage;
|
|
|
|
|
2025-10-03 22:09:09 +08:00
|
|
|
const idleTool = createIdleTool({
|
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
|
|
|
});
|
2025-10-01 11:39:29 +08:00
|
|
|
const writeFileTool = createWriteFileTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 22:34:18 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 11:39:29 +08:00
|
|
|
});
|
2025-10-01 12:15:38 +08:00
|
|
|
const grepTool = createGrepTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 22:09:09 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 12:15:38 +08:00
|
|
|
});
|
|
|
|
const readFileTool = createReadFileTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 23:20:29 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 12:15:38 +08:00
|
|
|
});
|
|
|
|
const bashTool = createBashTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 22:09:09 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 12:15:38 +08:00
|
|
|
});
|
2025-10-01 12:37:14 +08:00
|
|
|
const editFileTool = createEditFileTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 23:07:20 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 12:37:14 +08:00
|
|
|
});
|
|
|
|
const multiEditFileTool = createMultiEditFileTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 23:07:20 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 12:37:14 +08:00
|
|
|
});
|
2025-10-01 12:44:15 +08:00
|
|
|
const lsTool = createLsTool({
|
2025-10-03 06:45:08 +08:00
|
|
|
messageId: analyticsEngineerAgentOptions.messageId,
|
|
|
|
projectDirectory: analyticsEngineerAgentOptions.folder_structure,
|
2025-10-03 22:09:09 +08:00
|
|
|
onToolEvent: analyticsEngineerAgentOptions.onToolEvent,
|
2025-10-01 12:44:15 +08:00
|
|
|
});
|
2025-08-08 12:09:58 +08:00
|
|
|
|
2025-08-12 00:28:26 +08:00
|
|
|
// Create planning tools with simple context
|
2025-10-03 06:45:08 +08:00
|
|
|
async function stream({ messages }: AnalyticsEngineerAgentStreamOptions) {
|
2025-08-21 00:22:35 +08:00
|
|
|
// Collect available tools dynamically based on what's enabled
|
|
|
|
const availableTools: string[] = ['sequentialThinking'];
|
|
|
|
availableTools.push('executeSql');
|
|
|
|
availableTools.push('updateClarificationsFile', 'checkOffTodoList', 'idleTool', 'webSearch');
|
|
|
|
|
|
|
|
const agentContext: AgentContext = {
|
2025-10-03 06:45:08 +08:00
|
|
|
agentName: ANALYST_ENGINEER_AGENT_NAME,
|
2025-08-21 00:22:35 +08:00
|
|
|
availableTools,
|
|
|
|
};
|
|
|
|
|
2025-08-06 12:11:48 +08:00
|
|
|
return wrapTraced(
|
|
|
|
() =>
|
|
|
|
streamText({
|
2025-10-03 06:45:08 +08:00
|
|
|
model: analyticsEngineerAgentOptions.model || Sonnet4,
|
2025-09-03 11:21:33 +08:00
|
|
|
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
2025-08-06 12:11:48 +08:00
|
|
|
tools: {
|
|
|
|
idleTool,
|
2025-10-01 12:15:38 +08:00
|
|
|
grepTool,
|
2025-10-01 11:39:29 +08:00
|
|
|
writeFileTool,
|
2025-10-01 12:15:38 +08:00
|
|
|
readFileTool,
|
|
|
|
bashTool,
|
2025-10-01 12:37:14 +08:00
|
|
|
editFileTool,
|
|
|
|
multiEditFileTool,
|
2025-10-01 12:44:15 +08:00
|
|
|
lsTool,
|
2025-08-06 12:11:48 +08:00
|
|
|
},
|
|
|
|
messages: [systemMessage, ...messages],
|
|
|
|
stopWhen: STOP_CONDITIONS,
|
|
|
|
toolChoice: 'required',
|
|
|
|
maxOutputTokens: 10000,
|
|
|
|
temperature: 0,
|
2025-10-03 06:45:08 +08:00
|
|
|
experimental_context: analyticsEngineerAgentOptions,
|
2025-08-21 00:22:35 +08:00
|
|
|
experimental_repairToolCall: async (repairContext) => {
|
|
|
|
return repairToolCall({
|
|
|
|
toolCall: repairContext.toolCall,
|
|
|
|
tools: repairContext.tools,
|
|
|
|
error: repairContext.error,
|
|
|
|
messages: repairContext.messages,
|
|
|
|
...(repairContext.system && { system: repairContext.system }),
|
|
|
|
...(repairContext.inputSchema && { inputSchema: repairContext.inputSchema }),
|
|
|
|
agentContext,
|
|
|
|
});
|
|
|
|
},
|
2025-08-06 12:11:48 +08:00
|
|
|
}),
|
|
|
|
{
|
|
|
|
name: 'Docs Agent',
|
|
|
|
}
|
|
|
|
)();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
stream,
|
|
|
|
};
|
|
|
|
}
|