@sqlrooms/ai 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AiSlice.d.ts +90 -75
- package/dist/AiSlice.d.ts.map +1 -1
- package/dist/AiSlice.js +105 -61
- package/dist/AiSlice.js.map +1 -1
- package/dist/AnalysisResult.d.ts +16 -2
- package/dist/AnalysisResult.d.ts.map +1 -1
- package/dist/AnalysisResult.js +43 -7
- package/dist/AnalysisResult.js.map +1 -1
- package/dist/QueryResult.d.ts +9 -0
- package/dist/QueryResult.d.ts.map +1 -0
- package/dist/QueryResult.js +46 -0
- package/dist/QueryResult.js.map +1 -0
- package/dist/ToolCall.d.ts +64 -5
- package/dist/ToolCall.d.ts.map +1 -1
- package/dist/ToolCall.js +53 -10
- package/dist/ToolCall.js.map +1 -1
- package/dist/ToolResult.d.ts +3 -0
- package/dist/ToolResult.d.ts.map +1 -1
- package/dist/ToolResult.js +19 -2
- package/dist/ToolResult.js.map +1 -1
- package/dist/analysis.d.ts +48 -174
- package/dist/analysis.d.ts.map +1 -1
- package/dist/analysis.js +219 -61
- package/dist/analysis.js.map +1 -1
- package/dist/schemas.d.ts +84 -110
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +13 -11
- package/dist/schemas.js.map +1 -1
- package/package.json +9 -7
package/dist/analysis.d.ts
CHANGED
|
@@ -1,180 +1,54 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { StepResult } from 'ai';
|
|
2
|
+
import { ToolCallMessage, VercelToolSet } from '@openassistant/core';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param prompt - The prompt for the analysis
|
|
6
|
-
* @param abortSignal - An optional abort signal to cancel the analysis
|
|
7
|
-
* @returns The tool calls and the final answer
|
|
4
|
+
* Configuration options for running an AI analysis session
|
|
8
5
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
model
|
|
6
|
+
export type AnalysisConfig = {
|
|
7
|
+
/** Assistant instance identifier (default: 'sqlrooms-ai') */
|
|
8
|
+
name?: string;
|
|
9
|
+
/** AI model provider (e.g., 'openai', 'anthropic') */
|
|
10
|
+
modelProvider: string;
|
|
11
|
+
/** Model identifier (e.g., 'gpt-4', 'claude-3') */
|
|
12
|
+
model: string;
|
|
13
|
+
/** Authentication key for the model provider's API */
|
|
14
|
+
apiKey: string;
|
|
15
|
+
/** Analysis prompt or question to be processed */
|
|
16
|
+
prompt: string;
|
|
17
|
+
/** Optional controller for canceling the analysis operation */
|
|
18
|
+
abortController?: AbortController;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired after each analysis step completion
|
|
21
|
+
* @param event - Current step result containing tool execution details. See Vercel AI SDK documentation for more details.
|
|
22
|
+
* Specifically, it contains the array of tool calls and the results of the tool calls (toolResults).
|
|
23
|
+
* @param toolCallMessages - Collection of messages generated during tool calls. They are linked to the tool call by the toolCallId.
|
|
24
|
+
*/
|
|
25
|
+
onStepFinish?: (event: StepResult<typeof TOOLS>, toolCallMessages: ToolCallMessage[]) => Promise<void> | void;
|
|
26
|
+
/** Maximum number of analysis steps allowed (default: 100) */
|
|
13
27
|
maxSteps?: number;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}, "strip", import("zod").ZodTypeAny, {
|
|
21
|
-
type: "query";
|
|
22
|
-
sqlQuery: string;
|
|
23
|
-
reasoning: string;
|
|
24
|
-
}, {
|
|
25
|
-
type: "query";
|
|
26
|
-
sqlQuery: string;
|
|
27
|
-
reasoning: string;
|
|
28
|
-
}>, {
|
|
29
|
-
success: false;
|
|
30
|
-
error: string;
|
|
31
|
-
} | {
|
|
32
|
-
success: true;
|
|
33
|
-
data: Record<string, any>;
|
|
34
|
-
}> & {
|
|
35
|
-
execute: (args: {
|
|
36
|
-
type: "query";
|
|
37
|
-
sqlQuery: string;
|
|
38
|
-
reasoning: string;
|
|
39
|
-
}, options: ToolExecutionOptions) => PromiseLike<{
|
|
40
|
-
success: false;
|
|
41
|
-
error: string;
|
|
42
|
-
} | {
|
|
43
|
-
success: true;
|
|
44
|
-
data: Record<string, any>;
|
|
45
|
-
}>;
|
|
46
|
-
};
|
|
47
|
-
answer: import("ai").Tool<import("zod").ZodObject<{
|
|
48
|
-
type: import("zod").ZodLiteral<"answer">;
|
|
49
|
-
answer: import("zod").ZodString;
|
|
50
|
-
chart: import("zod").ZodUnion<[import("zod").ZodObject<{
|
|
51
|
-
sqlQuery: import("zod").ZodString;
|
|
52
|
-
vegaLiteSpec: import("zod").ZodString;
|
|
53
|
-
}, "strip", import("zod").ZodTypeAny, {
|
|
54
|
-
sqlQuery: string;
|
|
55
|
-
vegaLiteSpec: string;
|
|
56
|
-
}, {
|
|
57
|
-
sqlQuery: string;
|
|
58
|
-
vegaLiteSpec: string;
|
|
59
|
-
}>, import("zod").ZodNull]>;
|
|
60
|
-
}, "strip", import("zod").ZodTypeAny, {
|
|
61
|
-
type: "answer";
|
|
62
|
-
answer: string;
|
|
63
|
-
chart: {
|
|
64
|
-
sqlQuery: string;
|
|
65
|
-
vegaLiteSpec: string;
|
|
66
|
-
} | null;
|
|
67
|
-
}, {
|
|
68
|
-
type: "answer";
|
|
69
|
-
answer: string;
|
|
70
|
-
chart: {
|
|
71
|
-
sqlQuery: string;
|
|
72
|
-
vegaLiteSpec: string;
|
|
73
|
-
} | null;
|
|
74
|
-
}>, {
|
|
75
|
-
success: false;
|
|
76
|
-
error: string;
|
|
77
|
-
} | {
|
|
78
|
-
success: true;
|
|
79
|
-
data: Record<string, any>;
|
|
80
|
-
}> & {
|
|
81
|
-
execute: (args: {
|
|
82
|
-
type: "answer";
|
|
83
|
-
answer: string;
|
|
84
|
-
chart: {
|
|
85
|
-
sqlQuery: string;
|
|
86
|
-
vegaLiteSpec: string;
|
|
87
|
-
} | null;
|
|
88
|
-
}, options: ToolExecutionOptions) => PromiseLike<{
|
|
89
|
-
success: false;
|
|
90
|
-
error: string;
|
|
91
|
-
} | {
|
|
92
|
-
success: true;
|
|
93
|
-
data: Record<string, any>;
|
|
94
|
-
}>;
|
|
95
|
-
};
|
|
96
|
-
}, never>>;
|
|
97
|
-
declare const TOOLS: {
|
|
98
|
-
query: import("ai").Tool<import("zod").ZodObject<{
|
|
99
|
-
type: import("zod").ZodLiteral<"query">;
|
|
100
|
-
sqlQuery: import("zod").ZodString;
|
|
101
|
-
reasoning: import("zod").ZodString;
|
|
102
|
-
}, "strip", import("zod").ZodTypeAny, {
|
|
103
|
-
type: "query";
|
|
104
|
-
sqlQuery: string;
|
|
105
|
-
reasoning: string;
|
|
106
|
-
}, {
|
|
107
|
-
type: "query";
|
|
108
|
-
sqlQuery: string;
|
|
109
|
-
reasoning: string;
|
|
110
|
-
}>, {
|
|
111
|
-
success: false;
|
|
112
|
-
error: string;
|
|
113
|
-
} | {
|
|
114
|
-
success: true;
|
|
115
|
-
data: Record<string, any>;
|
|
116
|
-
}> & {
|
|
117
|
-
execute: (args: {
|
|
118
|
-
type: "query";
|
|
119
|
-
sqlQuery: string;
|
|
120
|
-
reasoning: string;
|
|
121
|
-
}, options: ToolExecutionOptions) => PromiseLike<{
|
|
122
|
-
success: false;
|
|
123
|
-
error: string;
|
|
124
|
-
} | {
|
|
125
|
-
success: true;
|
|
126
|
-
data: Record<string, any>;
|
|
127
|
-
}>;
|
|
128
|
-
};
|
|
129
|
-
answer: import("ai").Tool<import("zod").ZodObject<{
|
|
130
|
-
type: import("zod").ZodLiteral<"answer">;
|
|
131
|
-
answer: import("zod").ZodString;
|
|
132
|
-
chart: import("zod").ZodUnion<[import("zod").ZodObject<{
|
|
133
|
-
sqlQuery: import("zod").ZodString;
|
|
134
|
-
vegaLiteSpec: import("zod").ZodString;
|
|
135
|
-
}, "strip", import("zod").ZodTypeAny, {
|
|
136
|
-
sqlQuery: string;
|
|
137
|
-
vegaLiteSpec: string;
|
|
138
|
-
}, {
|
|
139
|
-
sqlQuery: string;
|
|
140
|
-
vegaLiteSpec: string;
|
|
141
|
-
}>, import("zod").ZodNull]>;
|
|
142
|
-
}, "strip", import("zod").ZodTypeAny, {
|
|
143
|
-
type: "answer";
|
|
144
|
-
answer: string;
|
|
145
|
-
chart: {
|
|
146
|
-
sqlQuery: string;
|
|
147
|
-
vegaLiteSpec: string;
|
|
148
|
-
} | null;
|
|
149
|
-
}, {
|
|
150
|
-
type: "answer";
|
|
151
|
-
answer: string;
|
|
152
|
-
chart: {
|
|
153
|
-
sqlQuery: string;
|
|
154
|
-
vegaLiteSpec: string;
|
|
155
|
-
} | null;
|
|
156
|
-
}>, {
|
|
157
|
-
success: false;
|
|
158
|
-
error: string;
|
|
159
|
-
} | {
|
|
160
|
-
success: true;
|
|
161
|
-
data: Record<string, any>;
|
|
162
|
-
}> & {
|
|
163
|
-
execute: (args: {
|
|
164
|
-
type: "answer";
|
|
165
|
-
answer: string;
|
|
166
|
-
chart: {
|
|
167
|
-
sqlQuery: string;
|
|
168
|
-
vegaLiteSpec: string;
|
|
169
|
-
} | null;
|
|
170
|
-
}, options: ToolExecutionOptions) => PromiseLike<{
|
|
171
|
-
success: false;
|
|
172
|
-
error: string;
|
|
173
|
-
} | {
|
|
174
|
-
success: true;
|
|
175
|
-
data: Record<string, any>;
|
|
176
|
-
}>;
|
|
177
|
-
};
|
|
28
|
+
/**
|
|
29
|
+
* Callback for handling streaming results
|
|
30
|
+
* @param message - Current message content being streamed
|
|
31
|
+
* @param isCompleted - Indicates if this is the final message in the stream
|
|
32
|
+
*/
|
|
33
|
+
onStreamResult: (message: string, isCompleted: boolean) => void;
|
|
178
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Executes an AI analysis session on the project data
|
|
37
|
+
*
|
|
38
|
+
* @param config - Analysis configuration options. See {@link AnalysisConfig} for more details.
|
|
39
|
+
* @returns Object containing tool calls executed and the final analysis result
|
|
40
|
+
*/
|
|
41
|
+
export declare function runAnalysis({ name, modelProvider, model, apiKey, prompt, abortController, onStepFinish, onStreamResult, maxSteps, }: AnalysisConfig): Promise<{
|
|
42
|
+
messages: import("ai").Message[];
|
|
43
|
+
outputToolResults: never[][] | undefined;
|
|
44
|
+
outputToolCalls: import("ai").ToolCall<string, unknown>[] | undefined;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Collection of tools available to the AI assistant for data analysis
|
|
48
|
+
* Includes:
|
|
49
|
+
* - query: Executes SQL queries against DuckDB
|
|
50
|
+
* - chart: Creates VegaLite visualizations
|
|
51
|
+
*/
|
|
52
|
+
declare const TOOLS: VercelToolSet;
|
|
179
53
|
export {};
|
|
180
54
|
//# sourceMappingURL=analysis.d.ts.map
|
package/dist/analysis.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,UAAU,EAAC,MAAM,IAAI,CAAC;AAE9B,OAAO,EAGL,eAAe,EACf,aAAa,EACd,MAAM,qBAAqB,CAAC;AAsF7B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IAEtB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IAEd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IAEf,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IAEf,+DAA+D;IAC/D,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CACb,KAAK,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,EAC/B,gBAAgB,EAAE,eAAe,EAAE,KAChC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAE1B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;CACjE,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,EAChC,IAAoB,EACpB,aAAa,EACb,KAAK,EACL,MAAM,EACN,MAAM,EACN,eAAe,EACf,YAAY,EACZ,cAAc,EACd,QAAY,GACb,EAAE,cAAc;;;;GA6BhB;AAiBD;;;;;GAKG;AACH,QAAA,MAAM,KAAK,EAAE,aAwHZ,CAAC"}
|
package/dist/analysis.js
CHANGED
|
@@ -1,88 +1,246 @@
|
|
|
1
|
-
import { arrowTableToJson, getDuckDb } from '@sqlrooms/duckdb';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { arrowTableToJson, getDuckDb, getDuckTableSchemas, } from '@sqlrooms/duckdb';
|
|
2
|
+
import { createAssistant, } from '@openassistant/core';
|
|
3
|
+
import { ChartToolParameters, QueryToolParameters } from './schemas';
|
|
4
|
+
import { queryMessage } from './QueryResult';
|
|
5
|
+
import { isChartToolParameters, isQueryToolParameters } from './ToolCall';
|
|
4
6
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* System prompt template for the AI assistant that provides instructions for:
|
|
8
|
+
* - Using DuckDB-specific SQL syntax and functions
|
|
9
|
+
* - Handling query results and error cases
|
|
10
|
+
* - Creating visualizations with VegaLite
|
|
11
|
+
* - Formatting final answers
|
|
9
12
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const SYSTEM_PROMPT = `
|
|
14
|
+
You are analyzing tables in DuckDB database in the context of a project.
|
|
15
|
+
|
|
16
|
+
Instructions for analysis:
|
|
17
|
+
- Use DuckDB-specific SQL syntax and functions (not Oracle, PostgreSQL, or other SQL dialects)
|
|
18
|
+
- Some key DuckDB-specific functions to use:
|
|
19
|
+
* regexp_matches() for regex (not regexp_like)
|
|
20
|
+
* strftime() for date formatting (not to_char)
|
|
21
|
+
* list_aggregate() for array operations
|
|
22
|
+
* unnest() for array expansion
|
|
23
|
+
* regr_sxy()
|
|
24
|
+
* corr()
|
|
25
|
+
* skewness()
|
|
26
|
+
- Please always try to use SQL queries to answer users questions
|
|
27
|
+
- Please run tool calls sequentially, don't run multiple tool calls in parallel
|
|
28
|
+
- IMPORTANT: Do not list out raw query results in your response. Instead:
|
|
29
|
+
* Describe the results in natural language
|
|
30
|
+
* Provide summary statistics
|
|
31
|
+
* Use comparisons and relative terms
|
|
32
|
+
* Include only the most relevant values if necessary
|
|
33
|
+
- Break down complex problems into smaller steps
|
|
34
|
+
- Use "SUMMARIZE table_name"for quick overview of the table
|
|
35
|
+
- Please don't modify data
|
|
36
|
+
- IMPORTANT: When you receive an error response from a tool call (where success: false):
|
|
37
|
+
* Stop making any further tool calls immediately
|
|
38
|
+
* Return a final answer that includes the error message
|
|
39
|
+
* Explain what went wrong and suggest possible fixes if applicable
|
|
40
|
+
|
|
41
|
+
When creating visualizations:
|
|
42
|
+
- Follow VegaLite syntax
|
|
43
|
+
- Choose appropriate chart types based on the data and analysis goals
|
|
44
|
+
- Use clear titles and axis labels
|
|
45
|
+
- Consider color schemes for better readability
|
|
46
|
+
- Add meaningful tooltips when relevant
|
|
47
|
+
- Format numbers and dates appropriately
|
|
48
|
+
- Use aggregations when dealing with large datasets
|
|
49
|
+
|
|
50
|
+
For your final answer:
|
|
51
|
+
- Provide an explanation for how you got it
|
|
52
|
+
- Explain your reasoning step by step
|
|
53
|
+
- Include relevant statistics or metrics
|
|
54
|
+
- For each prompt, please alwasy provide the final answer.
|
|
55
|
+
|
|
56
|
+
Please use the following schema for the tables:
|
|
57
|
+
`;
|
|
58
|
+
/**
|
|
59
|
+
* Generates summary statistics for a SQL query result
|
|
60
|
+
* @param conn - DuckDB connection instance
|
|
61
|
+
* @param sqlQuery - SQL SELECT query to analyze
|
|
62
|
+
* @returns Summary statistics as JSON object, or null if the query is not a SELECT statement or if summary generation fails
|
|
63
|
+
*/
|
|
64
|
+
async function getQuerySummary(conn, sqlQuery) {
|
|
65
|
+
if (!sqlQuery.toLowerCase().trim().startsWith('select')) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const viewName = `temp_result_${Date.now()}`; // unique view name to avoid conflicts
|
|
70
|
+
await conn.query(`CREATE TEMPORARY VIEW ${viewName} AS ${sqlQuery}`);
|
|
71
|
+
const summaryResult = await conn.query(`SUMMARIZE ${viewName}`);
|
|
72
|
+
const summaryData = arrowTableToJson(summaryResult);
|
|
73
|
+
await conn.query(`DROP VIEW IF EXISTS ${viewName}`);
|
|
74
|
+
return summaryData;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.warn('Failed to get summary:', error);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Executes an AI analysis session on the project data
|
|
83
|
+
*
|
|
84
|
+
* @param config - Analysis configuration options. See {@link AnalysisConfig} for more details.
|
|
85
|
+
* @returns Object containing tool calls executed and the final analysis result
|
|
86
|
+
*/
|
|
87
|
+
export async function runAnalysis({ name = 'sqlrooms-ai', modelProvider, model, apiKey, prompt, abortController, onStepFinish, onStreamResult, maxSteps = 5, }) {
|
|
88
|
+
const tablesSchema = await getDuckTableSchemas();
|
|
89
|
+
// get the singlton assistant instance
|
|
90
|
+
const assistant = await createAssistant({
|
|
91
|
+
name,
|
|
92
|
+
modelProvider,
|
|
14
93
|
model,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
94
|
+
apiKey,
|
|
95
|
+
version: 'v1',
|
|
96
|
+
instructions: `${SYSTEM_PROMPT}\n${JSON.stringify(tablesSchema)}`,
|
|
97
|
+
vercelFunctions: TOOLS,
|
|
98
|
+
temperature: 0,
|
|
99
|
+
toolChoice: 'auto', // this will enable streaming
|
|
20
100
|
maxSteps,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
101
|
+
...(abortController ? { abortController } : {}),
|
|
102
|
+
});
|
|
103
|
+
// process the prompt
|
|
104
|
+
const result = await assistant.processTextMessage({
|
|
105
|
+
textMessage: prompt,
|
|
106
|
+
streamMessageCallback: (message) => {
|
|
107
|
+
// the final result (before the answer) can be streamed back here
|
|
108
|
+
onStreamResult(message.deltaMessage, message.isCompleted ?? false);
|
|
109
|
+
},
|
|
26
110
|
onStepFinish,
|
|
27
111
|
});
|
|
28
|
-
// const answer = result.toolCalls.find((t) => t.toolName === 'answer');
|
|
29
|
-
// if (!answer) {
|
|
30
|
-
// console.error('No answer tool call found', {result});
|
|
31
|
-
// throw new Error('No answer tool call found');
|
|
32
|
-
// }
|
|
33
|
-
// return answer.args;
|
|
34
112
|
return result;
|
|
35
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Extracts a readable error message from an error object
|
|
116
|
+
* @param error - Error object or unknown value
|
|
117
|
+
* @returns Formatted error message string
|
|
118
|
+
*/
|
|
119
|
+
function getErrorMessage(error) {
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
if (error.cause instanceof Error) {
|
|
122
|
+
return error.cause.message;
|
|
123
|
+
}
|
|
124
|
+
return error.message;
|
|
125
|
+
}
|
|
126
|
+
return String(error);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Collection of tools available to the AI assistant for data analysis
|
|
130
|
+
* Includes:
|
|
131
|
+
* - query: Executes SQL queries against DuckDB
|
|
132
|
+
* - chart: Creates VegaLite visualizations
|
|
133
|
+
*/
|
|
36
134
|
const TOOLS = {
|
|
37
|
-
query:
|
|
38
|
-
description:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'Include VegaLite charts in your response if the data is suitable for it. ' +
|
|
44
|
-
'Omit the data from the chart.vegaLiteSpec in the response, provide an sql query in chart.sqlQuery instead. ' +
|
|
45
|
-
'To obtain stats, use the `SUMMARIZE table_name` query. ' +
|
|
46
|
-
"Don't execute queries that modify data unless explicitly asked. ",
|
|
135
|
+
query: {
|
|
136
|
+
description: `A tool for executing SQL queries in DuckDB that is embedded in browser using duckdb-wasm.
|
|
137
|
+
Query results are returned as a json object "{success: boolean, data: object[], error?: string}"
|
|
138
|
+
Please only analyze tables which are in the main schema.
|
|
139
|
+
To obtain stats, use the "SUMMARIZE table_name" query.
|
|
140
|
+
Don't execute queries that modify data unless explicitly asked.`,
|
|
47
141
|
parameters: QueryToolParameters,
|
|
48
|
-
|
|
142
|
+
executeWithContext: async (props) => {
|
|
143
|
+
if (!isQueryToolParameters(props.functionArgs)) {
|
|
144
|
+
return {
|
|
145
|
+
name: 'query',
|
|
146
|
+
result: {
|
|
147
|
+
success: false,
|
|
148
|
+
error: 'Invalid query parameters',
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const { type, sqlQuery } = props.functionArgs;
|
|
49
153
|
try {
|
|
50
154
|
const { conn } = await getDuckDb();
|
|
51
155
|
// TODO use options.abortSignal: maybe call db.cancelPendingQuery
|
|
52
156
|
const result = await conn.query(sqlQuery);
|
|
53
|
-
// if
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
157
|
+
// Only get summary if the query isn't already a SUMMARIZE query
|
|
158
|
+
const summaryData = sqlQuery.toLowerCase().includes('summarize')
|
|
159
|
+
? arrowTableToJson(result)
|
|
160
|
+
: await getQuerySummary(conn, sqlQuery);
|
|
161
|
+
// Get first 2 rows of the result as a json object
|
|
162
|
+
const subResult = result.slice(0, 2);
|
|
163
|
+
const firstTwoRows = arrowTableToJson(subResult);
|
|
164
|
+
// create result object sent back to LLM for tool call
|
|
165
|
+
const llmResult = {
|
|
166
|
+
type,
|
|
57
167
|
success: true,
|
|
58
|
-
data:
|
|
168
|
+
data: {
|
|
169
|
+
// only summary and first two rows will be sent back to LLM as context
|
|
170
|
+
summary: summaryData,
|
|
171
|
+
firstTwoRows,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
// data object of the raw query result, which is NOT sent back to LLM
|
|
175
|
+
// we can use it to visualize the arrow table in the callback function `message()` below
|
|
176
|
+
const data = { sqlQuery };
|
|
177
|
+
return {
|
|
178
|
+
name: 'query',
|
|
179
|
+
result: llmResult,
|
|
180
|
+
data,
|
|
59
181
|
};
|
|
60
182
|
}
|
|
61
183
|
catch (error) {
|
|
62
|
-
console.error('SQL query error:', error);
|
|
63
|
-
const errorMessage = error instanceof Error
|
|
64
|
-
? error.cause instanceof Error
|
|
65
|
-
? error.cause.message
|
|
66
|
-
: error.message
|
|
67
|
-
: String(error);
|
|
68
184
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
185
|
+
name: 'query',
|
|
186
|
+
result: {
|
|
187
|
+
success: false,
|
|
188
|
+
description: 'Failed to execute the query. Please stop tool call and return error message.',
|
|
189
|
+
error: getErrorMessage(error),
|
|
190
|
+
},
|
|
71
191
|
};
|
|
72
192
|
}
|
|
73
193
|
},
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
194
|
+
message: queryMessage,
|
|
195
|
+
},
|
|
196
|
+
chart: {
|
|
197
|
+
description: `A tool for creating VegaLite charts based on the schema of the SQL query result from the "query" tool.
|
|
198
|
+
In the response:
|
|
199
|
+
- omit the data from the vegaLiteSpec
|
|
200
|
+
- provide an sql query in sqlQuery instead.`,
|
|
201
|
+
parameters: ChartToolParameters,
|
|
202
|
+
executeWithContext: async (props) => {
|
|
203
|
+
if (!isChartToolParameters(props.functionArgs)) {
|
|
204
|
+
return {
|
|
205
|
+
name: 'chart',
|
|
206
|
+
result: {
|
|
207
|
+
success: false,
|
|
208
|
+
error: 'Invalid chart parameters',
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const { sqlQuery, vegaLiteSpec } = props.functionArgs;
|
|
213
|
+
const llmResult = {
|
|
82
214
|
success: true,
|
|
83
|
-
|
|
215
|
+
details: 'Chart created successfully.',
|
|
216
|
+
};
|
|
217
|
+
// data object of the vegaLiteSpec and sqlQuery
|
|
218
|
+
// it is not used yet, but we can use it to create a JSON editor for user to edit the vegaLiteSpec so that chart can be updated
|
|
219
|
+
const data = {
|
|
220
|
+
sqlQuery,
|
|
221
|
+
vegaLiteSpec,
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
name: 'chart',
|
|
225
|
+
result: llmResult,
|
|
226
|
+
data,
|
|
84
227
|
};
|
|
85
228
|
},
|
|
86
|
-
}
|
|
229
|
+
},
|
|
230
|
+
// answer tool: the LLM will provide a structured answer
|
|
231
|
+
// answer: {
|
|
232
|
+
// description: 'A tool for providing the final answer.',
|
|
233
|
+
// parameters: AnswerToolParameters,
|
|
234
|
+
// executeWithContext: async (props: CallbackFunctionProps) => {
|
|
235
|
+
// const {answer} = props.functionArgs;
|
|
236
|
+
// return {
|
|
237
|
+
// name: 'answer',
|
|
238
|
+
// result: {
|
|
239
|
+
// success: true,
|
|
240
|
+
// data: answer,
|
|
241
|
+
// },
|
|
242
|
+
// };
|
|
243
|
+
// },
|
|
244
|
+
// },
|
|
87
245
|
};
|
|
88
246
|
//# sourceMappingURL=analysis.js.map
|
package/dist/analysis.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAC,YAAY,EAAc,IAAI,EAAuB,MAAM,IAAI,CAAC;AACxE,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAEpB,MAAM,WAAW,CAAC;AAEnB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAChC,KAAK;AACL,UAAU;AACV,WAAW,EACX,YAAY,EACZ,QAAQ,GAAG,GAAG,EACd,QAAQ,GAQT;IACC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,KAAK;QAEL,WAAW;QACX,UAAU;QACV,QAAQ;QAER,KAAK,EAAE,KAAK;QAEZ,UAAU,EAAE,MAAM;QAClB,QAAQ;QACR,UAAU,EAAE,CAAC;QAEb,MAAM,EACJ,2EAA2E;YAC3E,oEAAoE;YACpE,uBAAuB;YACvB,4EAA4E;QAE9E,YAAY;KACb,CAAC,CAAC;IAEH,wEAAwE;IACxE,iBAAiB;IACjB,0DAA0D;IAC1D,kDAAkD;IAClD,IAAI;IACJ,sBAAsB;IAEtB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,KAAK,GAAG;IACZ,KAAK,EAAE,IAAI,CAAC;QACV,WAAW,EACT,4FAA4F;YAC5F,4FAA4F;YAC5F,oGAAoG;YACpG,+DAA+D;YAC/D,8EAA8E;YAC9E,2EAA2E;YAC3E,6GAA6G;YAC7G,yDAAyD;YACzD,kEAAkE;QACpE,UAAU,EAAE,mBAAmB;QAE/B,OAAO,EAAE,KAAK,EACZ,EAAC,QAAQ,EAAC,EACV,OAA6B,EACQ,EAAE;YACvC,IAAI,CAAC;gBACH,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,SAAS,EAAE,CAAC;gBACjC,iEAAiE;gBACjE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC1C,sCAAsC;gBACtC,sCAAsC;gBACtC,IAAI;gBACJ,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;iBAC/B,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;gBACzC,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK;oBACpB,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK;wBAC5B,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO;wBACrB,CAAC,CAAC,KAAK,CAAC,OAAO;oBACjB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,YAAY;iBACpB,CAAC;YACJ,CAAC;QACH,CAAC;QACD,wEAAwE;KACzE,CAAC;IAEF,wDAAwD;IACxD,MAAM,EAAE,IAAI,CAAC;QACX,WAAW,EAAE,wCAAwC;QACrD,UAAU,EAAE,oBAAoB;QAEhC,OAAO,EAAE,KAAK,EAAE,EAAC,MAAM,EAAC,EAAuC,EAAE;YAC/D,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,EAAE;aACT,CAAC;QACJ,CAAC;KACF,CAAC;CACH,CAAC","sourcesContent":["import type {LanguageModelV1} from '@ai-sdk/provider';\nimport {arrowTableToJson, getDuckDb} from '@sqlrooms/duckdb';\nimport {generateText, StepResult, tool, ToolExecutionOptions} from 'ai';\nimport {\n AnswerToolParameters,\n QueryToolParameters,\n ToolResultSchema,\n} from './schemas';\n\n/**\n * Run analysis on the project data\n * @param prompt - The prompt for the analysis\n * @param abortSignal - An optional abort signal to cancel the analysis\n * @returns The tool calls and the final answer\n */\nexport async function runAnalysis({\n model,\n // prompt,\n abortSignal,\n onStepFinish,\n maxSteps = 100,\n messages,\n}: {\n // prompt: string;\n abortSignal?: AbortSignal;\n onStepFinish?: (event: StepResult<typeof TOOLS>) => Promise<void> | void;\n model: LanguageModelV1;\n maxSteps?: number;\n messages?: any[];\n}) {\n const result = await generateText({\n model,\n\n abortSignal,\n // prompt,\n messages,\n\n tools: TOOLS,\n\n toolChoice: 'auto',\n maxSteps,\n maxRetries: 1,\n\n system:\n 'You are analyzing tables in DuckDB database in the context of a project. ' +\n 'You can run SQL queries to perform analysis and answer questions. ' +\n 'Reason step by step. ' +\n 'When you give the final answer, provide an explanation for how you got it.',\n\n onStepFinish,\n });\n\n // const answer = result.toolCalls.find((t) => t.toolName === 'answer');\n // if (!answer) {\n // console.error('No answer tool call found', {result});\n // throw new Error('No answer tool call found');\n // }\n // return answer.args;\n\n return result;\n}\n\nconst TOOLS = {\n query: tool({\n description:\n 'A tool for executing SQL queries in DuckDB that is embedded in browser using duckdb-wasm. ' +\n 'You can obtain the structures of all tables and their column types by running `DESCRIBE`. ' +\n 'Query results are returned as a json object `{success: boolean, data: object[], error?: string}`. ' +\n 'You should only analyze tables which are in the main schema. ' +\n 'Avoid queries returning too much data to prevent the browser from crashing. ' +\n 'Include VegaLite charts in your response if the data is suitable for it. ' +\n 'Omit the data from the chart.vegaLiteSpec in the response, provide an sql query in chart.sqlQuery instead. ' +\n 'To obtain stats, use the `SUMMARIZE table_name` query. ' +\n \"Don't execute queries that modify data unless explicitly asked. \",\n parameters: QueryToolParameters,\n\n execute: async (\n {sqlQuery},\n options: ToolExecutionOptions,\n ): Promise<ToolResultSchema['result']> => {\n try {\n const {conn} = await getDuckDb();\n // TODO use options.abortSignal: maybe call db.cancelPendingQuery\n const result = await conn.query(sqlQuery);\n // if (options.abortSignal?.aborted) {\n // throw new Error('Query aborted');\n // }\n return {\n success: true,\n data: arrowTableToJson(result),\n };\n } catch (error) {\n console.error('SQL query error:', error);\n const errorMessage =\n error instanceof Error\n ? error.cause instanceof Error\n ? error.cause.message\n : error.message\n : String(error);\n return {\n success: false,\n error: errorMessage,\n };\n }\n },\n // TODO: consider experimental_toToolResultContent() for returning Arrow\n }),\n\n // answer tool: the LLM will provide a structured answer\n answer: tool({\n description: 'A tool for providing the final answer.',\n parameters: AnswerToolParameters,\n\n execute: async ({answer}): Promise<ToolResultSchema['result']> => {\n return {\n success: true,\n data: [],\n };\n },\n }),\n};\n"]}
|
|
1
|
+
{"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n arrowTableToJson,\n getDuckDb,\n getDuckTableSchemas,\n} from '@sqlrooms/duckdb';\nimport {StepResult} from 'ai';\nimport * as duckdb from '@duckdb/duckdb-wasm';\nimport {\n CallbackFunctionProps,\n createAssistant,\n ToolCallMessage,\n VercelToolSet,\n} from '@openassistant/core';\n\nimport {ChartToolParameters, QueryToolParameters} from './schemas';\nimport {queryMessage} from './QueryResult';\nimport {isChartToolParameters, isQueryToolParameters} from './ToolCall';\n\n/**\n * System prompt template for the AI assistant that provides instructions for:\n * - Using DuckDB-specific SQL syntax and functions\n * - Handling query results and error cases\n * - Creating visualizations with VegaLite\n * - Formatting final answers\n */\nconst SYSTEM_PROMPT = `\nYou are analyzing tables in DuckDB database in the context of a project.\n\nInstructions for analysis:\n- Use DuckDB-specific SQL syntax and functions (not Oracle, PostgreSQL, or other SQL dialects)\n- Some key DuckDB-specific functions to use:\n * regexp_matches() for regex (not regexp_like)\n * strftime() for date formatting (not to_char)\n * list_aggregate() for array operations\n * unnest() for array expansion\n * regr_sxy()\n * corr()\n * skewness()\n- Please always try to use SQL queries to answer users questions\n- Please run tool calls sequentially, don't run multiple tool calls in parallel\n- IMPORTANT: Do not list out raw query results in your response. Instead:\n * Describe the results in natural language\n * Provide summary statistics\n * Use comparisons and relative terms\n * Include only the most relevant values if necessary\n- Break down complex problems into smaller steps\n- Use \"SUMMARIZE table_name\"for quick overview of the table\n- Please don't modify data\n- IMPORTANT: When you receive an error response from a tool call (where success: false):\n * Stop making any further tool calls immediately\n * Return a final answer that includes the error message\n * Explain what went wrong and suggest possible fixes if applicable\n\nWhen creating visualizations:\n- Follow VegaLite syntax\n- Choose appropriate chart types based on the data and analysis goals\n- Use clear titles and axis labels\n- Consider color schemes for better readability\n- Add meaningful tooltips when relevant\n- Format numbers and dates appropriately\n- Use aggregations when dealing with large datasets\n\nFor your final answer:\n- Provide an explanation for how you got it\n- Explain your reasoning step by step\n- Include relevant statistics or metrics\n- For each prompt, please alwasy provide the final answer.\n\nPlease use the following schema for the tables:\n`;\n\n/**\n * Generates summary statistics for a SQL query result\n * @param conn - DuckDB connection instance\n * @param sqlQuery - SQL SELECT query to analyze\n * @returns Summary statistics as JSON object, or null if the query is not a SELECT statement or if summary generation fails\n */\nasync function getQuerySummary(\n conn: duckdb.AsyncDuckDBConnection,\n sqlQuery: string,\n) {\n if (!sqlQuery.toLowerCase().trim().startsWith('select')) {\n return null;\n }\n\n try {\n const viewName = `temp_result_${Date.now()}`; // unique view name to avoid conflicts\n await conn.query(`CREATE TEMPORARY VIEW ${viewName} AS ${sqlQuery}`);\n const summaryResult = await conn.query(`SUMMARIZE ${viewName}`);\n const summaryData = arrowTableToJson(summaryResult);\n await conn.query(`DROP VIEW IF EXISTS ${viewName}`);\n return summaryData;\n } catch (error) {\n console.warn('Failed to get summary:', error);\n return null;\n }\n}\n\n/**\n * Configuration options for running an AI analysis session\n */\nexport type AnalysisConfig = {\n /** Assistant instance identifier (default: 'sqlrooms-ai') */\n name?: string;\n\n /** AI model provider (e.g., 'openai', 'anthropic') */\n modelProvider: string;\n\n /** Model identifier (e.g., 'gpt-4', 'claude-3') */\n model: string;\n\n /** Authentication key for the model provider's API */\n apiKey: string;\n\n /** Analysis prompt or question to be processed */\n prompt: string;\n\n /** Optional controller for canceling the analysis operation */\n abortController?: AbortController;\n\n /**\n * Callback fired after each analysis step completion\n * @param event - Current step result containing tool execution details. See Vercel AI SDK documentation for more details.\n * Specifically, it contains the array of tool calls and the results of the tool calls (toolResults).\n * @param toolCallMessages - Collection of messages generated during tool calls. They are linked to the tool call by the toolCallId.\n */\n onStepFinish?: (\n event: StepResult<typeof TOOLS>,\n toolCallMessages: ToolCallMessage[],\n ) => Promise<void> | void;\n\n /** Maximum number of analysis steps allowed (default: 100) */\n maxSteps?: number;\n\n /**\n * Callback for handling streaming results\n * @param message - Current message content being streamed\n * @param isCompleted - Indicates if this is the final message in the stream\n */\n onStreamResult: (message: string, isCompleted: boolean) => void;\n};\n\n/**\n * Executes an AI analysis session on the project data\n *\n * @param config - Analysis configuration options. See {@link AnalysisConfig} for more details.\n * @returns Object containing tool calls executed and the final analysis result\n */\nexport async function runAnalysis({\n name = 'sqlrooms-ai',\n modelProvider,\n model,\n apiKey,\n prompt,\n abortController,\n onStepFinish,\n onStreamResult,\n maxSteps = 5,\n}: AnalysisConfig) {\n const tablesSchema = await getDuckTableSchemas();\n\n // get the singlton assistant instance\n const assistant = await createAssistant({\n name,\n modelProvider,\n model,\n apiKey,\n version: 'v1',\n instructions: `${SYSTEM_PROMPT}\\n${JSON.stringify(tablesSchema)}`,\n vercelFunctions: TOOLS,\n temperature: 0,\n toolChoice: 'auto', // this will enable streaming\n maxSteps,\n ...(abortController ? {abortController} : {}),\n });\n\n // process the prompt\n const result = await assistant.processTextMessage({\n textMessage: prompt,\n streamMessageCallback: (message) => {\n // the final result (before the answer) can be streamed back here\n onStreamResult(message.deltaMessage, message.isCompleted ?? false);\n },\n onStepFinish,\n });\n\n return result;\n}\n\n/**\n * Extracts a readable error message from an error object\n * @param error - Error object or unknown value\n * @returns Formatted error message string\n */\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n if (error.cause instanceof Error) {\n return error.cause.message;\n }\n return error.message;\n }\n return String(error);\n}\n\n/**\n * Collection of tools available to the AI assistant for data analysis\n * Includes:\n * - query: Executes SQL queries against DuckDB\n * - chart: Creates VegaLite visualizations\n */\nconst TOOLS: VercelToolSet = {\n query: {\n description: `A tool for executing SQL queries in DuckDB that is embedded in browser using duckdb-wasm.\nQuery results are returned as a json object \"{success: boolean, data: object[], error?: string}\"\nPlease only analyze tables which are in the main schema.\nTo obtain stats, use the \"SUMMARIZE table_name\" query.\nDon't execute queries that modify data unless explicitly asked.`,\n parameters: QueryToolParameters,\n executeWithContext: async (props: CallbackFunctionProps) => {\n if (!isQueryToolParameters(props.functionArgs)) {\n return {\n name: 'query',\n result: {\n success: false,\n error: 'Invalid query parameters',\n },\n };\n }\n\n const {type, sqlQuery} = props.functionArgs;\n try {\n const {conn} = await getDuckDb();\n // TODO use options.abortSignal: maybe call db.cancelPendingQuery\n const result = await conn.query(sqlQuery);\n // Only get summary if the query isn't already a SUMMARIZE query\n const summaryData = sqlQuery.toLowerCase().includes('summarize')\n ? arrowTableToJson(result)\n : await getQuerySummary(conn, sqlQuery);\n\n // Get first 2 rows of the result as a json object\n const subResult = result.slice(0, 2);\n const firstTwoRows = arrowTableToJson(subResult);\n\n // create result object sent back to LLM for tool call\n const llmResult = {\n type,\n success: true,\n data: {\n // only summary and first two rows will be sent back to LLM as context\n summary: summaryData,\n firstTwoRows,\n },\n };\n\n // data object of the raw query result, which is NOT sent back to LLM\n // we can use it to visualize the arrow table in the callback function `message()` below\n const data = {sqlQuery};\n\n return {\n name: 'query',\n result: llmResult,\n data,\n };\n } catch (error) {\n return {\n name: 'query',\n result: {\n success: false,\n description:\n 'Failed to execute the query. Please stop tool call and return error message.',\n error: getErrorMessage(error),\n },\n };\n }\n },\n message: queryMessage,\n },\n\n chart: {\n description: `A tool for creating VegaLite charts based on the schema of the SQL query result from the \"query\" tool.\nIn the response:\n- omit the data from the vegaLiteSpec\n- provide an sql query in sqlQuery instead.`,\n parameters: ChartToolParameters,\n executeWithContext: async (props: CallbackFunctionProps) => {\n if (!isChartToolParameters(props.functionArgs)) {\n return {\n name: 'chart',\n result: {\n success: false,\n error: 'Invalid chart parameters',\n },\n };\n }\n const {sqlQuery, vegaLiteSpec} = props.functionArgs;\n const llmResult = {\n success: true,\n details: 'Chart created successfully.',\n };\n\n // data object of the vegaLiteSpec and sqlQuery\n // it is not used yet, but we can use it to create a JSON editor for user to edit the vegaLiteSpec so that chart can be updated\n const data = {\n sqlQuery,\n vegaLiteSpec,\n };\n\n return {\n name: 'chart',\n result: llmResult,\n data,\n };\n },\n },\n\n // answer tool: the LLM will provide a structured answer\n // answer: {\n // description: 'A tool for providing the final answer.',\n // parameters: AnswerToolParameters,\n // executeWithContext: async (props: CallbackFunctionProps) => {\n // const {answer} = props.functionArgs;\n // return {\n // name: 'answer',\n // result: {\n // success: true,\n // data: answer,\n // },\n // };\n // },\n // },\n};\n"]}
|