sql tools

This commit is contained in:
dal 2025-08-11 14:54:43 -06:00
parent 82c1ea7f46
commit 71d862e51b
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
3 changed files with 105 additions and 94 deletions

View File

@ -76,9 +76,7 @@ const ExecuteSqlOutputSchema = z.object({
export type ExecuteSqlOutput = z.infer<typeof ExecuteSqlOutputSchema>;
// Factory function to create the execute-sql tool
export function createExecuteSqlTool<TAgentContext extends ExecuteSqlContext = ExecuteSqlContext>(
context: TAgentContext
) {
export function createExecuteSqlTool(context: ExecuteSqlContext) {
// Initialize state for streaming
const state: ExecuteSqlState = {
toolCallId: undefined,
@ -110,40 +108,3 @@ export function createExecuteSqlTool<TAgentContext extends ExecuteSqlContext = E
onInputAvailable,
});
}
// Legacy export for backward compatibility
export const executeSql = tool({
description: `Use this to run lightweight, validation queries to understand values in columns, date ranges, etc.
Please limit your queries to 50 rows for performance.
Query results will be limited to 50 rows for performance.
You must use the <SCHEMA_NAME>.<TABLE_NAME> syntax/qualifier for all table names.
Otherwise the queries wont run successfully.`,
inputSchema: ExecuteSqlInputSchema,
outputSchema: ExecuteSqlOutputSchema,
execute: async (input, { experimental_context: context }) => {
const rawContext = context as AnalystAgentOptions;
const executeSqlContext = ExecuteSqlContextSchema.parse({
dataSourceId: rawContext.dataSourceId,
userId: rawContext.userId,
dataSourceSyntax: rawContext.dataSourceSyntax,
messageId: rawContext.messageId || '',
});
// Create temporary state for this execution
const state: ExecuteSqlState = {
toolCallId: undefined,
args: JSON.stringify(input),
statements: input.statements,
isComplete: true,
startTime: Date.now(),
executionTime: undefined,
executionResults: undefined,
};
const executeFunction = createExecuteSqlExecute(state, executeSqlContext);
return await executeFunction(input);
},
});
export default executeSql;

View File

@ -72,9 +72,7 @@ const SuperExecuteSqlOutputSchema = z.object({
export type SuperExecuteSqlOutput = z.infer<typeof SuperExecuteSqlOutputSchema>;
// Factory function to create the super-execute-sql tool
export function createSuperExecuteSqlTool<
TAgentContext extends SuperExecuteSqlContext = SuperExecuteSqlContext,
>(context: TAgentContext) {
export function createSuperExecuteSqlTool(context: SuperExecuteSqlContext) {
// Initialize state
const state: SuperExecuteSqlState = {
startTime: Date.now(),

View File

@ -21,11 +21,60 @@ import type {
CreateDashboardsState,
} from './create-dashboards-tool';
import {
createDashboardsRawLlMessageEntry,
createDashboardsReasoningMessage,
createDashboardsResponseMessage,
createCreateDashboardsRawLlmMessageEntry,
createCreateDashboardsReasoningEntry,
} from './helpers/create-dashboards-tool-transform-helper';
// Type definitions
interface DashboardFileContent {
rows: Array<{
items: Array<{
id: string;
[key: string]: unknown;
}>;
[key: string]: unknown;
}>;
[key: string]: unknown;
}
interface FileWithId {
id: string;
name: string;
file_type: string;
result_message?: string;
results?: Record<string, unknown>[];
created_at: string;
updated_at: string;
version_number: number;
content?: DashboardFileContent;
}
interface FailedFileCreation {
name: string;
error: string;
}
type CreateDashboardFilesParams = CreateDashboardsInput;
type CreateDashboardFilesOutput = CreateDashboardsOutput;
// Dashboard YAML schema
const dashboardYmlSchema = z.object({
name: z.string().min(1, 'Dashboard name is required'),
description: z.string().optional(),
rows: z.array(
z.object({
id: z.string(),
items: z.array(
z.object({
id: z.string(),
}).passthrough()
),
column_sizes: z.array(z.number()).optional(),
rowHeight: z.number().optional(),
}).passthrough()
).min(1, 'Dashboard must have at least one row'),
});
// Parse and validate dashboard YAML content
function parseAndValidateYaml(ymlContent: string): {
success: boolean;
@ -96,7 +145,7 @@ async function validateMetricIds(
}
// Process a dashboard file creation request
async function processDashboardFile(file: CreateDashboardsInput): Promise<{
async function processDashboardFile(file: { name: string; yml_content: string }): Promise<{
success: boolean;
dashboardFile?: FileWithId;
dashboardYml?: DashboardYml;
@ -445,34 +494,34 @@ export function createCreateDashboardsExecute(
const finalStatus = typedResult.failed_files?.length ? 'failed' : 'completed';
const toolCallId = state.toolCallId || `tool-${Date.now()}`;
const reasoningEntry = createDashboardsReasoningMessage(
toolCallId,
state.files ?? [],
finalStatus
);
const responseEntry = createDashboardsResponseMessage(
toolCallId,
typedResult.message
);
const rawLlmMessage: ModelMessage = {
role: 'assistant',
content: [
{
type: 'tool-call',
toolCallId,
toolName: 'create-dashboards',
input: state.parsedArgs || (input as Partial<CreateDashboardFilesParams>),
},
],
// Update state for final status
if (state.files) {
state.files.forEach(f => {
if (!f.status || f.status === 'processing') {
f.status = finalStatus === 'failed' ? 'failed' : 'completed';
}
});
}
const reasoningEntry = createCreateDashboardsReasoningEntry(state, toolCallId);
const rawLlmMessage = createCreateDashboardsRawLlmMessageEntry(state, toolCallId);
const updates: Parameters<typeof updateMessageEntries>[0] = {
messageId: context.messageId,
mode: 'update',
};
await updateMessageEntries({
messageId: context.messageId,
reasoningEntry,
responseEntry,
rawLlmMessage,
mode: 'update',
});
if (reasoningEntry) {
updates.responseEntry = reasoningEntry;
}
if (rawLlmMessage) {
updates.rawLlmMessageEntry = rawLlmMessage;
}
if (reasoningEntry || rawLlmMessage) {
await updateMessageEntries(updates);
}
console.info('[create-dashboards] Updated last entries with final results', {
messageId: context.messageId,
@ -505,29 +554,32 @@ export function createCreateDashboardsExecute(
if (context.messageId) {
try {
const toolCallId = state.toolCallId || `tool-${Date.now()}`;
const reasoningEntry = createDashboardsReasoningMessage(
toolCallId,
(state.files ?? []).map((f) => ({ ...f, status: 'failed' })),
'failed'
);
const rawLlmMessage: ModelMessage = {
role: 'assistant',
content: [
{
type: 'tool-call',
toolCallId,
toolName: 'create-dashboards',
input: state.parsedArgs || {},
},
],
// Update state files to failed status
if (state.files) {
state.files.forEach(f => {
f.status = 'failed';
});
}
const reasoningEntry = createCreateDashboardsReasoningEntry(state, toolCallId);
const rawLlmMessage = createCreateDashboardsRawLlmMessageEntry(state, toolCallId);
const updates: Parameters<typeof updateMessageEntries>[0] = {
messageId: context.messageId,
mode: 'update',
};
await updateMessageEntries({
messageId: context.messageId,
reasoningEntry,
rawLlmMessage,
mode: 'update',
});
if (reasoningEntry) {
updates.responseEntry = reasoningEntry;
}
if (rawLlmMessage) {
updates.rawLlmMessageEntry = rawLlmMessage;
}
if (reasoningEntry || rawLlmMessage) {
await updateMessageEntries(updates);
}
} catch (updateError) {
console.error('[create-dashboards] Error updating entries on failure:', updateError);
}