@deepagents/text2sql 0.6.0 → 0.7.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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1121 -453
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/mysql/column-stats.mysql.grounding.d.ts +14 -0
- package/dist/lib/adapters/mysql/column-stats.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/column-values.mysql.grounding.d.ts +22 -0
- package/dist/lib/adapters/mysql/column-values.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/constraint.mysql.grounding.d.ts +13 -0
- package/dist/lib/adapters/mysql/constraint.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/index.d.ts +44 -0
- package/dist/lib/adapters/mysql/index.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/indexes.mysql.grounding.d.ts +13 -0
- package/dist/lib/adapters/mysql/indexes.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/info.mysql.grounding.d.ts +13 -0
- package/dist/lib/adapters/mysql/info.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/mysql.d.ts +33 -0
- package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/row-count.mysql.grounding.d.ts +13 -0
- package/dist/lib/adapters/mysql/row-count.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/table.mysql.grounding.d.ts +21 -0
- package/dist/lib/adapters/mysql/table.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/mysql/view.mysql.grounding.d.ts +18 -0
- package/dist/lib/adapters/mysql/view.mysql.grounding.d.ts.map +1 -0
- package/dist/lib/agents/bi.agent.d.ts +14 -0
- package/dist/lib/agents/bi.agent.d.ts.map +1 -0
- package/dist/lib/agents/chat1.agent.d.ts.map +1 -1
- package/dist/lib/agents/chat2.agent.d.ts.map +1 -1
- package/dist/lib/agents/developer.agent.d.ts +31 -0
- package/dist/lib/agents/developer.agent.d.ts.map +1 -0
- package/dist/lib/agents/question.agent.d.ts +1 -1
- package/dist/lib/agents/question.agent.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts +14 -32
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/agents/text2sql.agent.d.ts.map +1 -1
- package/dist/lib/checkpoint.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +42 -0
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/extractors/sql-extractor.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +267 -164
- package/dist/lib/synthesis/index.js.map +3 -3
- package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts.map +1 -1
- package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
- package/dist/lib/synthesis/synthesizers/schema-synthesizer.d.ts.map +1 -1
- package/dist/lib/synthesis/synthesizers/styles.d.ts +2 -2
- package/dist/lib/synthesis/synthesizers/styles.d.ts.map +1 -1
- package/dist/lib/teach/teachings.d.ts.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -149,64 +149,58 @@ function getTablesWithRelated(allTables, relationships, filter) {
|
|
|
149
149
|
return Array.from(result);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
// packages/text2sql/src/lib/agents/
|
|
152
|
+
// packages/text2sql/src/lib/agents/developer.agent.ts
|
|
153
|
+
import { groq as groq3 } from "@ai-sdk/groq";
|
|
154
|
+
import { tool } from "ai";
|
|
155
|
+
import dedent2 from "dedent";
|
|
156
|
+
import z3 from "zod";
|
|
157
|
+
import { agent as agent3, generate as generate2, toState } from "@deepagents/agent";
|
|
158
|
+
import { scratchpad_tool } from "@deepagents/toolbox";
|
|
159
|
+
|
|
160
|
+
// packages/text2sql/src/lib/agents/explainer.agent.ts
|
|
153
161
|
import { groq } from "@ai-sdk/groq";
|
|
154
162
|
import dedent from "dedent";
|
|
155
163
|
import z from "zod";
|
|
156
|
-
import { agent
|
|
157
|
-
var
|
|
158
|
-
name: "
|
|
164
|
+
import { agent } from "@deepagents/agent";
|
|
165
|
+
var explainerAgent = agent({
|
|
166
|
+
name: "explainer",
|
|
159
167
|
model: groq("openai/gpt-oss-20b"),
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
sql: z.string().describe("The SQL statement needed to answer the question."),
|
|
165
|
-
businessValue: z.string().describe("Why the question matters to stakeholders.")
|
|
166
|
-
})
|
|
167
|
-
).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
|
|
168
|
-
}),
|
|
169
|
-
prompt: (state) => {
|
|
170
|
-
return dedent`
|
|
171
|
-
${thirdPersonPrompt()}
|
|
172
|
-
|
|
173
|
-
<identity>
|
|
174
|
-
You are a senior analytics strategist who proposes ambitious business questions
|
|
175
|
-
and drafts the SQL needed to answer them. You specialize in identifying ideas
|
|
176
|
-
that combine multiple tables, apply segmentation or time analysis, and surface
|
|
177
|
-
metrics that drive executive decisions.
|
|
178
|
-
</identity>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
<instructions>
|
|
182
|
-
- Recommend one or two UNIQUE questions that go beyond simple counts or listings.
|
|
183
|
-
- Favor questions that require joins, aggregates, time comparisons, cohort analysis,
|
|
184
|
-
or window functions.
|
|
185
|
-
- For each question, explain the business reason stakeholders care about it.
|
|
186
|
-
- Provide the complete SQL query that could answer the question in the given schema.
|
|
187
|
-
- Keep result sets scoped with LIMIT clauses (max 50 rows) when returning raw rows.
|
|
188
|
-
- Ensure table/column names match the provided schema exactly.
|
|
189
|
-
- Use columns marked [LowCardinality: ...] to identify meaningful categorical filters or segmentations.
|
|
190
|
-
- Leverage table [rows / size] hints to determine whether to aggregate (large tables) or inspect detailed data (tiny tables).
|
|
191
|
-
- Reference PK/Indexed annotations and the Indexes list to recommend queries that use efficient join/filter paths.
|
|
192
|
-
- Column annotations may expose ranges/null percentages—use them to suggest realistic thresholds or quality checks.
|
|
193
|
-
- Consult <relationship_examples> to anchor your recommendations in the actual join paths between tables.
|
|
194
|
-
- Output only information grounded in the schema/context provided.
|
|
195
|
-
</instructions>
|
|
168
|
+
prompt: (state) => dedent`
|
|
169
|
+
You are an expert SQL tutor.
|
|
170
|
+
Explain the following SQL query in plain English to a non-technical user.
|
|
171
|
+
Focus on the intent and logic, not the syntax.
|
|
196
172
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
173
|
+
<sql>
|
|
174
|
+
${state?.sql}
|
|
175
|
+
</sql>
|
|
176
|
+
`,
|
|
177
|
+
output: z.object({
|
|
178
|
+
explanation: z.string().describe("The explanation of the SQL query.")
|
|
179
|
+
})
|
|
202
180
|
});
|
|
203
181
|
|
|
204
|
-
// packages/text2sql/src/lib/agents/
|
|
182
|
+
// packages/text2sql/src/lib/agents/sql.agent.ts
|
|
205
183
|
import { groq as groq2 } from "@ai-sdk/groq";
|
|
206
|
-
import {
|
|
184
|
+
import {
|
|
185
|
+
APICallError,
|
|
186
|
+
JSONParseError,
|
|
187
|
+
NoContentGeneratedError,
|
|
188
|
+
NoObjectGeneratedError,
|
|
189
|
+
NoOutputGeneratedError,
|
|
190
|
+
TypeValidationError,
|
|
191
|
+
defaultSettingsMiddleware,
|
|
192
|
+
wrapLanguageModel
|
|
193
|
+
} from "ai";
|
|
194
|
+
import { Console } from "node:console";
|
|
195
|
+
import { createWriteStream } from "node:fs";
|
|
196
|
+
import pRetry from "p-retry";
|
|
207
197
|
import z2 from "zod";
|
|
208
|
-
import {
|
|
209
|
-
|
|
198
|
+
import {
|
|
199
|
+
agent as agent2,
|
|
200
|
+
generate,
|
|
201
|
+
toOutput,
|
|
202
|
+
user
|
|
203
|
+
} from "@deepagents/agent";
|
|
210
204
|
|
|
211
205
|
// packages/text2sql/src/lib/teach/xml.ts
|
|
212
206
|
function wrapBlock(tag, children) {
|
|
@@ -581,6 +575,384 @@ function toTeachables(generated) {
|
|
|
581
575
|
});
|
|
582
576
|
}
|
|
583
577
|
|
|
578
|
+
// packages/text2sql/src/lib/agents/sql.agent.ts
|
|
579
|
+
var logger = new Console({
|
|
580
|
+
stdout: createWriteStream("./sql-agent.log", { flags: "a" }),
|
|
581
|
+
stderr: createWriteStream("./sql-agent-error.log", { flags: "a" }),
|
|
582
|
+
inspectOptions: { depth: null }
|
|
583
|
+
});
|
|
584
|
+
var RETRY_TEMPERATURES = [0, 0.2, 0.3];
|
|
585
|
+
var sqlQueryAgent = agent2({
|
|
586
|
+
name: "text2sql",
|
|
587
|
+
model: groq2("openai/gpt-oss-20b"),
|
|
588
|
+
logging: process.env.AGENT_LOGGING === "true",
|
|
589
|
+
output: z2.union([
|
|
590
|
+
z2.object({
|
|
591
|
+
sql: z2.string().describe("The SQL query that answers the question"),
|
|
592
|
+
reasoning: z2.string().optional().describe("The reasoning steps taken to generate the SQL")
|
|
593
|
+
}),
|
|
594
|
+
z2.object({
|
|
595
|
+
error: z2.string().describe(
|
|
596
|
+
"Error message explaining why the question cannot be answered with the given schema"
|
|
597
|
+
)
|
|
598
|
+
})
|
|
599
|
+
]),
|
|
600
|
+
prompt: (state) => {
|
|
601
|
+
return `
|
|
602
|
+
${state?.teachings || ""}
|
|
603
|
+
${state?.introspection || ""}
|
|
604
|
+
`;
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
function extractSql(output) {
|
|
608
|
+
const match = output.match(/```sql\n?([\s\S]*?)```/);
|
|
609
|
+
return match ? match[1].trim() : output.trim();
|
|
610
|
+
}
|
|
611
|
+
var marker = Symbol("SQLValidationError");
|
|
612
|
+
var SQLValidationError = class _SQLValidationError extends Error {
|
|
613
|
+
[marker];
|
|
614
|
+
constructor(message) {
|
|
615
|
+
super(message);
|
|
616
|
+
this.name = "SQLValidationError";
|
|
617
|
+
this[marker] = true;
|
|
618
|
+
}
|
|
619
|
+
static isInstance(error) {
|
|
620
|
+
return error instanceof _SQLValidationError && error[marker] === true;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
|
|
624
|
+
constructor(message) {
|
|
625
|
+
super(message);
|
|
626
|
+
this.name = "UnanswerableSQLError";
|
|
627
|
+
}
|
|
628
|
+
static isInstance(error) {
|
|
629
|
+
return error instanceof _UnanswerableSQLError;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
async function toSql(options) {
|
|
633
|
+
const { maxRetries = 3 } = options;
|
|
634
|
+
return withRetry(
|
|
635
|
+
async (attemptNumber, errors, attempts) => {
|
|
636
|
+
const agentInstance = sqlQueryAgent.clone({
|
|
637
|
+
model: wrapLanguageModel({
|
|
638
|
+
model: options.model ?? sqlQueryAgent.model,
|
|
639
|
+
middleware: defaultSettingsMiddleware({
|
|
640
|
+
settings: {
|
|
641
|
+
temperature: RETRY_TEMPERATURES[attemptNumber - 1] ?? 0.3,
|
|
642
|
+
topP: 1
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
})
|
|
646
|
+
});
|
|
647
|
+
const messages = errors.length ? [
|
|
648
|
+
user(options.input),
|
|
649
|
+
user(
|
|
650
|
+
`<validation_error>Your previous SQL query had the following error: ${errors.at(-1)?.message}. Please fix the query.</validation_error>`
|
|
651
|
+
)
|
|
652
|
+
] : [user(options.input)];
|
|
653
|
+
const output = await toOutput(
|
|
654
|
+
generate(agentInstance, messages, {
|
|
655
|
+
introspection: options.introspection,
|
|
656
|
+
teachings: toInstructions(
|
|
657
|
+
"instructions",
|
|
658
|
+
persona({
|
|
659
|
+
name: "Freya",
|
|
660
|
+
role: "You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema."
|
|
661
|
+
}),
|
|
662
|
+
...options.instructions
|
|
663
|
+
)
|
|
664
|
+
})
|
|
665
|
+
);
|
|
666
|
+
if ("error" in output) {
|
|
667
|
+
throw new UnanswerableSQLError(output.error);
|
|
668
|
+
}
|
|
669
|
+
const sql = extractSql(output.sql);
|
|
670
|
+
const validationError = await options.adapter.validate(sql);
|
|
671
|
+
if (validationError) {
|
|
672
|
+
throw new SQLValidationError(validationError);
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
attempts,
|
|
676
|
+
sql,
|
|
677
|
+
errors: errors.length ? errors.map(formatErrorMessage) : void 0
|
|
678
|
+
};
|
|
679
|
+
},
|
|
680
|
+
{ retries: maxRetries - 1 }
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
function formatErrorMessage(error) {
|
|
684
|
+
if (APICallError.isInstance(error)) {
|
|
685
|
+
if (error.message.startsWith("Failed to validate JSON")) {
|
|
686
|
+
return `Schema validation failed: ${error.message}`;
|
|
687
|
+
}
|
|
688
|
+
return error.message;
|
|
689
|
+
}
|
|
690
|
+
if (SQLValidationError.isInstance(error)) {
|
|
691
|
+
return `SQL Validation Error: ${error.message}`;
|
|
692
|
+
}
|
|
693
|
+
return error.message;
|
|
694
|
+
}
|
|
695
|
+
async function withRetry(computation, options = { retries: 3 }) {
|
|
696
|
+
const errors = [];
|
|
697
|
+
let attempts = 0;
|
|
698
|
+
return pRetry(
|
|
699
|
+
(attemptNumber) => {
|
|
700
|
+
return computation(attemptNumber, errors, ++attempts);
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
retries: options.retries,
|
|
704
|
+
shouldRetry: (context2) => {
|
|
705
|
+
if (UnanswerableSQLError.isInstance(context2.error)) {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
if (SQLValidationError.isInstance(context2.error)) {
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
console.log({
|
|
712
|
+
NoObjectGeneratedError: NoObjectGeneratedError.isInstance(
|
|
713
|
+
context2.error
|
|
714
|
+
),
|
|
715
|
+
NoOutputGeneratedError: NoOutputGeneratedError.isInstance(
|
|
716
|
+
context2.error
|
|
717
|
+
),
|
|
718
|
+
APICallError: APICallError.isInstance(context2.error),
|
|
719
|
+
JSONParseError: JSONParseError.isInstance(context2.error),
|
|
720
|
+
TypeValidationError: TypeValidationError.isInstance(context2.error),
|
|
721
|
+
NoContentGeneratedError: NoContentGeneratedError.isInstance(
|
|
722
|
+
context2.error
|
|
723
|
+
)
|
|
724
|
+
});
|
|
725
|
+
return APICallError.isInstance(context2.error) || JSONParseError.isInstance(context2.error) || TypeValidationError.isInstance(context2.error) || NoObjectGeneratedError.isInstance(context2.error) || NoOutputGeneratedError.isInstance(context2.error) || NoContentGeneratedError.isInstance(context2.error);
|
|
726
|
+
},
|
|
727
|
+
onFailedAttempt(context2) {
|
|
728
|
+
logger.error(`toSQL`, context2.error);
|
|
729
|
+
console.log(
|
|
730
|
+
`Attempt ${context2.attemptNumber} failed. There are ${context2.retriesLeft} retries left.`
|
|
731
|
+
);
|
|
732
|
+
errors.push(context2.error);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// packages/text2sql/src/lib/agents/developer.agent.ts
|
|
739
|
+
var tools = {
|
|
740
|
+
/**
|
|
741
|
+
* Generate SQL from natural language question.
|
|
742
|
+
* Uses the toSql function with retry logic and validation.
|
|
743
|
+
*/
|
|
744
|
+
generate_sql: tool({
|
|
745
|
+
description: dedent2`
|
|
746
|
+
Generate a validated SQL query from a natural language question.
|
|
747
|
+
The query is automatically validated against the database schema.
|
|
748
|
+
Use this when the user asks a question that requires data retrieval.
|
|
749
|
+
|
|
750
|
+
Returns the SQL query along with generation metadata (attempts, any errors encountered).
|
|
751
|
+
`,
|
|
752
|
+
inputSchema: z3.object({
|
|
753
|
+
question: z3.string().min(1).describe("The natural language question to convert to SQL")
|
|
754
|
+
}),
|
|
755
|
+
execute: async ({ question }, options) => {
|
|
756
|
+
const state = toState(options);
|
|
757
|
+
try {
|
|
758
|
+
const result = await toSql({
|
|
759
|
+
input: question,
|
|
760
|
+
adapter: state.adapter,
|
|
761
|
+
introspection: state.introspection,
|
|
762
|
+
instructions: state.instructions
|
|
763
|
+
});
|
|
764
|
+
return {
|
|
765
|
+
success: true,
|
|
766
|
+
sql: result.sql,
|
|
767
|
+
attempts: result.attempts,
|
|
768
|
+
errors: result.errors
|
|
769
|
+
};
|
|
770
|
+
} catch (error) {
|
|
771
|
+
return {
|
|
772
|
+
success: false,
|
|
773
|
+
error: error instanceof Error ? error.message : String(error)
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}),
|
|
778
|
+
/**
|
|
779
|
+
* Get plain-English explanation of a SQL query.
|
|
780
|
+
*/
|
|
781
|
+
explain_sql: tool({
|
|
782
|
+
description: dedent2`
|
|
783
|
+
Get a plain-English explanation of a SQL query.
|
|
784
|
+
Use this to help the user understand what a query does.
|
|
785
|
+
|
|
786
|
+
The explanation focuses on intent and logic, not syntax.
|
|
787
|
+
`,
|
|
788
|
+
inputSchema: z3.object({
|
|
789
|
+
sql: z3.string().min(1).describe("The SQL query to explain")
|
|
790
|
+
}),
|
|
791
|
+
execute: async ({ sql }) => {
|
|
792
|
+
const { experimental_output } = await generate2(explainerAgent, [], {
|
|
793
|
+
sql
|
|
794
|
+
});
|
|
795
|
+
return { explanation: experimental_output.explanation };
|
|
796
|
+
}
|
|
797
|
+
}),
|
|
798
|
+
/**
|
|
799
|
+
* Show database schema introspection.
|
|
800
|
+
*/
|
|
801
|
+
show_schema: tool({
|
|
802
|
+
description: dedent2`
|
|
803
|
+
Display the database schema introspection.
|
|
804
|
+
Use this when the user wants to see available tables, columns, or relationships.
|
|
805
|
+
|
|
806
|
+
Optionally filter by table name to reduce output.
|
|
807
|
+
`,
|
|
808
|
+
inputSchema: z3.object({
|
|
809
|
+
table: z3.string().optional().describe(
|
|
810
|
+
"Optional: filter to show only a specific table. If omitted, shows full schema."
|
|
811
|
+
)
|
|
812
|
+
}),
|
|
813
|
+
execute: async ({ table }, options) => {
|
|
814
|
+
const state = toState(options);
|
|
815
|
+
if (!table) {
|
|
816
|
+
return { schema: state.introspection };
|
|
817
|
+
}
|
|
818
|
+
const lines = state.introspection.split("\n");
|
|
819
|
+
const tableLines = [];
|
|
820
|
+
let inTable = false;
|
|
821
|
+
let depth = 0;
|
|
822
|
+
for (const line of lines) {
|
|
823
|
+
const lowerLine = line.toLowerCase();
|
|
824
|
+
if (lowerLine.includes(`name="${table.toLowerCase()}"`) || lowerLine.includes(`table="${table.toLowerCase()}"`)) {
|
|
825
|
+
inTable = true;
|
|
826
|
+
depth = 1;
|
|
827
|
+
tableLines.push(line);
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (inTable) {
|
|
831
|
+
tableLines.push(line);
|
|
832
|
+
if (line.includes("</")) {
|
|
833
|
+
depth--;
|
|
834
|
+
}
|
|
835
|
+
if (line.includes("<") && !line.includes("</") && !line.includes("/>")) {
|
|
836
|
+
depth++;
|
|
837
|
+
}
|
|
838
|
+
if (depth <= 0) {
|
|
839
|
+
break;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (tableLines.length === 0) {
|
|
844
|
+
return {
|
|
845
|
+
schema: `Table "${table}" not found in schema. Use show_schema without a table filter to see all available tables.`
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
return { schema: tableLines.join("\n") };
|
|
849
|
+
}
|
|
850
|
+
}),
|
|
851
|
+
/**
|
|
852
|
+
* Developer scratchpad for notes and reasoning.
|
|
853
|
+
*/
|
|
854
|
+
scratchpad: scratchpad_tool
|
|
855
|
+
};
|
|
856
|
+
var developerAgent = agent3({
|
|
857
|
+
model: groq3("gpt-oss-20b"),
|
|
858
|
+
tools,
|
|
859
|
+
name: "developer_agent",
|
|
860
|
+
prompt: (state) => {
|
|
861
|
+
return dedent2`
|
|
862
|
+
You are an expert SQL developer assistant helping power users build and refine queries.
|
|
863
|
+
|
|
864
|
+
## Your Capabilities
|
|
865
|
+
|
|
866
|
+
You have access to the following tools:
|
|
867
|
+
|
|
868
|
+
1. **generate_sql**: Convert natural language questions to validated SQL queries
|
|
869
|
+
- Automatically validates against the database schema
|
|
870
|
+
- Returns generation metadata (attempts, errors if any)
|
|
871
|
+
|
|
872
|
+
2. **explain_sql**: Get a plain-English explanation of any SQL query
|
|
873
|
+
- Helps users understand complex queries
|
|
874
|
+
- Focuses on intent and logic, not syntax
|
|
875
|
+
|
|
876
|
+
3. **show_schema**: Display database schema information
|
|
877
|
+
- Can show full schema or filter by table name
|
|
878
|
+
- Use to explore available tables and columns
|
|
879
|
+
|
|
880
|
+
4. **scratchpad**: Record your reasoning and notes
|
|
881
|
+
|
|
882
|
+
## Guidelines
|
|
883
|
+
|
|
884
|
+
- Be transparent: show the SQL you generate before explaining it
|
|
885
|
+
- Be precise: provide exact column names and table references
|
|
886
|
+
- Be helpful: suggest refinements and alternatives when appropriate
|
|
887
|
+
- Support both natural language questions AND raw SQL input
|
|
888
|
+
- When validating user SQL, explain any errors clearly
|
|
889
|
+
- Use show_schema proactively when you need to verify table/column names
|
|
890
|
+
|
|
891
|
+
${state?.teachings || ""}
|
|
892
|
+
${state?.introspection || ""}
|
|
893
|
+
`;
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// packages/text2sql/src/lib/agents/suggestions.agents.ts
|
|
898
|
+
import { groq as groq4 } from "@ai-sdk/groq";
|
|
899
|
+
import dedent3 from "dedent";
|
|
900
|
+
import z4 from "zod";
|
|
901
|
+
import { agent as agent4, thirdPersonPrompt } from "@deepagents/agent";
|
|
902
|
+
var suggestionsAgent = agent4({
|
|
903
|
+
name: "text2sql-suggestions",
|
|
904
|
+
model: groq4("openai/gpt-oss-20b"),
|
|
905
|
+
output: z4.object({
|
|
906
|
+
suggestions: z4.array(
|
|
907
|
+
z4.object({
|
|
908
|
+
question: z4.string().describe("A complex, high-impact business question."),
|
|
909
|
+
sql: z4.string().describe("The SQL statement needed to answer the question."),
|
|
910
|
+
businessValue: z4.string().describe("Why the question matters to stakeholders.")
|
|
911
|
+
})
|
|
912
|
+
).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
|
|
913
|
+
}),
|
|
914
|
+
prompt: (state) => {
|
|
915
|
+
return dedent3`
|
|
916
|
+
${thirdPersonPrompt()}
|
|
917
|
+
|
|
918
|
+
<identity>
|
|
919
|
+
You are a senior analytics strategist who proposes ambitious business questions
|
|
920
|
+
and drafts the SQL needed to answer them. You specialize in identifying ideas
|
|
921
|
+
that combine multiple tables, apply segmentation or time analysis, and surface
|
|
922
|
+
metrics that drive executive decisions.
|
|
923
|
+
</identity>
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
<instructions>
|
|
927
|
+
- Recommend one or two UNIQUE questions that go beyond simple counts or listings.
|
|
928
|
+
- Favor questions that require joins, aggregates, time comparisons, cohort analysis,
|
|
929
|
+
or window functions.
|
|
930
|
+
- For each question, explain the business reason stakeholders care about it.
|
|
931
|
+
- Provide the complete SQL query that could answer the question in the given schema.
|
|
932
|
+
- Keep result sets scoped with LIMIT clauses (max 50 rows) when returning raw rows.
|
|
933
|
+
- Ensure table/column names match the provided schema exactly.
|
|
934
|
+
- Use columns marked [LowCardinality: ...] to identify meaningful categorical filters or segmentations.
|
|
935
|
+
- Leverage table [rows / size] hints to determine whether to aggregate (large tables) or inspect detailed data (tiny tables).
|
|
936
|
+
- Reference PK/Indexed annotations and the Indexes list to recommend queries that use efficient join/filter paths.
|
|
937
|
+
- Column annotations may expose ranges/null percentages—use them to suggest realistic thresholds or quality checks.
|
|
938
|
+
- Consult <relationship_examples> to anchor your recommendations in the actual join paths between tables.
|
|
939
|
+
- Output only information grounded in the schema/context provided.
|
|
940
|
+
</instructions>
|
|
941
|
+
|
|
942
|
+
<response-format>
|
|
943
|
+
Return valid JSON that satisfies the defined output schema.
|
|
944
|
+
</response-format>
|
|
945
|
+
`;
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
// packages/text2sql/src/lib/agents/text2sql.agent.ts
|
|
950
|
+
import { groq as groq5 } from "@ai-sdk/groq";
|
|
951
|
+
import { tool as tool2 } from "ai";
|
|
952
|
+
import z5 from "zod";
|
|
953
|
+
import { agent as agent5, toState as toState2 } from "@deepagents/agent";
|
|
954
|
+
import { scratchpad_tool as scratchpad_tool2 } from "@deepagents/toolbox";
|
|
955
|
+
|
|
584
956
|
// packages/text2sql/src/lib/memory/memory.prompt.ts
|
|
585
957
|
var memory_prompt_default = toInstructions(
|
|
586
958
|
"memory_guidelines",
|
|
@@ -710,14 +1082,14 @@ var memory_prompt_default = toInstructions(
|
|
|
710
1082
|
);
|
|
711
1083
|
|
|
712
1084
|
// packages/text2sql/src/lib/agents/text2sql.agent.ts
|
|
713
|
-
var
|
|
714
|
-
validate_query:
|
|
1085
|
+
var tools2 = {
|
|
1086
|
+
validate_query: tool2({
|
|
715
1087
|
description: `Validate SQL query syntax before execution. Use this to check if your SQL is valid before running db_query. This helps catch errors early and allows you to correct the query if needed.`,
|
|
716
|
-
inputSchema:
|
|
717
|
-
sql:
|
|
1088
|
+
inputSchema: z5.object({
|
|
1089
|
+
sql: z5.string().describe("The SQL query to validate.")
|
|
718
1090
|
}),
|
|
719
1091
|
execute: async ({ sql }, options) => {
|
|
720
|
-
const state =
|
|
1092
|
+
const state = toState2(options);
|
|
721
1093
|
const result = await state.adapter.validate(sql);
|
|
722
1094
|
if (typeof result === "string") {
|
|
723
1095
|
return `Validation Error: ${result}`;
|
|
@@ -725,22 +1097,22 @@ var tools = {
|
|
|
725
1097
|
return "Query is valid.";
|
|
726
1098
|
}
|
|
727
1099
|
}),
|
|
728
|
-
get_sample_rows:
|
|
1100
|
+
get_sample_rows: tool2({
|
|
729
1101
|
description: `Sample rows from a table to understand data formatting, codes, and value patterns. Use BEFORE writing queries when:
|
|
730
1102
|
- Column types in schema don't reveal format (e.g., "status" could be 'active'/'inactive' or 1/0)
|
|
731
1103
|
- Date/time formats are unclear (ISO, Unix timestamp, locale-specific)
|
|
732
1104
|
- You need to understand lookup table codes or enum values
|
|
733
1105
|
- Column names are ambiguous (e.g., "type", "category", "code")`,
|
|
734
|
-
inputSchema:
|
|
735
|
-
tableName:
|
|
736
|
-
columns:
|
|
1106
|
+
inputSchema: z5.object({
|
|
1107
|
+
tableName: z5.string().describe("The name of the table to sample."),
|
|
1108
|
+
columns: z5.array(z5.string()).optional().describe(
|
|
737
1109
|
"Specific columns to sample. If omitted, samples all columns."
|
|
738
1110
|
),
|
|
739
|
-
limit:
|
|
1111
|
+
limit: z5.number().min(1).max(10).default(3).optional().describe("Number of rows to sample (1-10, default 3).")
|
|
740
1112
|
}),
|
|
741
1113
|
execute: ({ tableName, columns, limit = 3 }, options) => {
|
|
742
1114
|
const safeLimit = Math.min(Math.max(1, limit), 10);
|
|
743
|
-
const state =
|
|
1115
|
+
const state = toState2(options);
|
|
744
1116
|
const sql = state.adapter.buildSampleRowsQuery(
|
|
745
1117
|
tableName,
|
|
746
1118
|
columns,
|
|
@@ -749,13 +1121,13 @@ var tools = {
|
|
|
749
1121
|
return state.adapter.execute(sql);
|
|
750
1122
|
}
|
|
751
1123
|
}),
|
|
752
|
-
db_query:
|
|
1124
|
+
db_query: tool2({
|
|
753
1125
|
description: `Internal tool to fetch data from the store's database. Write a SQL query to retrieve the information needed to answer the user's question. The results will be returned as data that you can then present to the user in natural language.`,
|
|
754
|
-
inputSchema:
|
|
755
|
-
reasoning:
|
|
1126
|
+
inputSchema: z5.object({
|
|
1127
|
+
reasoning: z5.string().describe(
|
|
756
1128
|
"Your reasoning for why this SQL query is relevant to the user request."
|
|
757
1129
|
),
|
|
758
|
-
sql:
|
|
1130
|
+
sql: z5.string().min(1, { message: "SQL query cannot be empty." }).refine(
|
|
759
1131
|
(sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
|
|
760
1132
|
{
|
|
761
1133
|
message: "Only read-only SELECT or WITH queries are allowed."
|
|
@@ -763,11 +1135,11 @@ var tools = {
|
|
|
763
1135
|
).describe("The SQL query to execute against the database.")
|
|
764
1136
|
}),
|
|
765
1137
|
execute: ({ sql }, options) => {
|
|
766
|
-
const state =
|
|
1138
|
+
const state = toState2(options);
|
|
767
1139
|
return state.adapter.execute(sql);
|
|
768
1140
|
}
|
|
769
1141
|
}),
|
|
770
|
-
scratchpad:
|
|
1142
|
+
scratchpad: scratchpad_tool2
|
|
771
1143
|
};
|
|
772
1144
|
var userMemoryTypes = [
|
|
773
1145
|
"identity",
|
|
@@ -776,61 +1148,61 @@ var userMemoryTypes = [
|
|
|
776
1148
|
"context",
|
|
777
1149
|
"correction"
|
|
778
1150
|
];
|
|
779
|
-
var userMemorySchema =
|
|
780
|
-
|
|
781
|
-
type:
|
|
782
|
-
description:
|
|
1151
|
+
var userMemorySchema = z5.discriminatedUnion("type", [
|
|
1152
|
+
z5.object({
|
|
1153
|
+
type: z5.literal("identity"),
|
|
1154
|
+
description: z5.string().describe("The user's identity: role or/and name")
|
|
783
1155
|
}),
|
|
784
|
-
|
|
785
|
-
type:
|
|
786
|
-
term:
|
|
787
|
-
meaning:
|
|
1156
|
+
z5.object({
|
|
1157
|
+
type: z5.literal("alias"),
|
|
1158
|
+
term: z5.string().describe("The term the user uses"),
|
|
1159
|
+
meaning: z5.string().describe("What the user means by this term")
|
|
788
1160
|
}),
|
|
789
|
-
|
|
790
|
-
type:
|
|
791
|
-
aspect:
|
|
792
|
-
value:
|
|
1161
|
+
z5.object({
|
|
1162
|
+
type: z5.literal("preference"),
|
|
1163
|
+
aspect: z5.string().describe("What aspect of output this preference applies to"),
|
|
1164
|
+
value: z5.string().describe("The user's preference")
|
|
793
1165
|
}),
|
|
794
|
-
|
|
795
|
-
type:
|
|
796
|
-
description:
|
|
1166
|
+
z5.object({
|
|
1167
|
+
type: z5.literal("context"),
|
|
1168
|
+
description: z5.string().describe("What the user is currently working on")
|
|
797
1169
|
}),
|
|
798
|
-
|
|
799
|
-
type:
|
|
800
|
-
subject:
|
|
801
|
-
clarification:
|
|
1170
|
+
z5.object({
|
|
1171
|
+
type: z5.literal("correction"),
|
|
1172
|
+
subject: z5.string().describe("What was misunderstood"),
|
|
1173
|
+
clarification: z5.string().describe("The correct understanding")
|
|
802
1174
|
})
|
|
803
1175
|
]);
|
|
804
1176
|
var memoryTools = {
|
|
805
|
-
remember_memory:
|
|
1177
|
+
remember_memory: tool2({
|
|
806
1178
|
description: "Store something about the user for future conversations. Use silently when user shares facts, preferences, vocabulary, corrections, or context.",
|
|
807
|
-
inputSchema:
|
|
1179
|
+
inputSchema: z5.object({ memory: userMemorySchema }),
|
|
808
1180
|
execute: async ({ memory }, options) => {
|
|
809
|
-
const state =
|
|
1181
|
+
const state = toState2(
|
|
810
1182
|
options
|
|
811
1183
|
);
|
|
812
1184
|
await state.memory.remember(state.userId, memory);
|
|
813
1185
|
return "Remembered.";
|
|
814
1186
|
}
|
|
815
1187
|
}),
|
|
816
|
-
forget_memory:
|
|
1188
|
+
forget_memory: tool2({
|
|
817
1189
|
description: "Forget a specific memory. Use when user asks to remove something.",
|
|
818
|
-
inputSchema:
|
|
819
|
-
id:
|
|
1190
|
+
inputSchema: z5.object({
|
|
1191
|
+
id: z5.string().describe("The ID of the teachable to forget")
|
|
820
1192
|
}),
|
|
821
1193
|
execute: async ({ id }, options) => {
|
|
822
|
-
const state =
|
|
1194
|
+
const state = toState2(options);
|
|
823
1195
|
await state.memory.forget(id);
|
|
824
1196
|
return "Forgotten.";
|
|
825
1197
|
}
|
|
826
1198
|
}),
|
|
827
|
-
recall_memory:
|
|
1199
|
+
recall_memory: tool2({
|
|
828
1200
|
description: "List stored memories for the current user. Use when user asks what you remember about them or wants to see their stored preferences.",
|
|
829
|
-
inputSchema:
|
|
830
|
-
type:
|
|
1201
|
+
inputSchema: z5.object({
|
|
1202
|
+
type: z5.enum(userMemoryTypes).optional().catch(void 0).describe("Optional: filter by memory type")
|
|
831
1203
|
}),
|
|
832
1204
|
execute: async ({ type }, options) => {
|
|
833
|
-
const state =
|
|
1205
|
+
const state = toState2(
|
|
834
1206
|
options
|
|
835
1207
|
);
|
|
836
1208
|
const memories = await state.memory.recall(state.userId, type);
|
|
@@ -845,30 +1217,117 @@ var memoryTools = {
|
|
|
845
1217
|
}));
|
|
846
1218
|
}
|
|
847
1219
|
}),
|
|
848
|
-
update_memory:
|
|
1220
|
+
update_memory: tool2({
|
|
849
1221
|
description: "Update an existing memory. Use when user wants to modify something you previously stored.",
|
|
850
|
-
inputSchema:
|
|
1222
|
+
inputSchema: z5.object({
|
|
851
1223
|
memory: userMemorySchema,
|
|
852
|
-
id:
|
|
1224
|
+
id: z5.string().describe("The ID of the memory to update")
|
|
853
1225
|
}),
|
|
854
1226
|
execute: async ({ id, memory }, options) => {
|
|
855
|
-
const state =
|
|
1227
|
+
const state = toState2(options);
|
|
856
1228
|
await state.memory.update(id, memory);
|
|
857
1229
|
return "Updated.";
|
|
858
1230
|
}
|
|
859
1231
|
})
|
|
860
1232
|
};
|
|
861
|
-
var
|
|
862
|
-
|
|
863
|
-
|
|
1233
|
+
var chainOfThoughtPrompt = `
|
|
1234
|
+
## Query Reasoning Process
|
|
1235
|
+
|
|
1236
|
+
Let's think step by step before writing SQL:
|
|
1237
|
+
|
|
1238
|
+
1. **Schema Link**: Which tables and columns are relevant? Verify they exist in the schema.
|
|
1239
|
+
2. **Join Path**: If multiple tables, what relationships connect them?
|
|
1240
|
+
3. **Filters**: What WHERE conditions are needed?
|
|
1241
|
+
4. **Aggregation**: Is COUNT, SUM, AVG, GROUP BY, or HAVING required?
|
|
1242
|
+
5. **Output**: What columns to SELECT and any ORDER BY or LIMIT?
|
|
1243
|
+
6. **Verify**: Do all referenced tables and columns exist in the schema above?
|
|
1244
|
+
|
|
1245
|
+
For simple queries, steps 2-4 may not apply\u2014skip them.
|
|
1246
|
+
|
|
1247
|
+
For complex queries requiring multiple data points, decompose into sub-questions:
|
|
1248
|
+
- Break the question into simpler parts (Q1, Q2, ...)
|
|
1249
|
+
- Determine if each part needs a subquery or CTE
|
|
1250
|
+
- Combine the parts into the final query
|
|
1251
|
+
|
|
1252
|
+
Keep reasoning brief. Verbose explanations cause errors.
|
|
1253
|
+
`;
|
|
1254
|
+
var fewShotExamples = `
|
|
1255
|
+
## Examples
|
|
1256
|
+
|
|
1257
|
+
### Example 1: Simple Filter
|
|
1258
|
+
Question: "How many records in table_a have column_x equal to 'value'?"
|
|
1259
|
+
Reasoning:
|
|
1260
|
+
- Schema Link: table_a, column_x
|
|
1261
|
+
- Filters: column_x = 'value'
|
|
1262
|
+
- Aggregation: COUNT(*)
|
|
1263
|
+
SQL: SELECT COUNT(*) FROM table_a WHERE column_x = 'value'
|
|
1264
|
+
|
|
1265
|
+
### Example 2: JOIN Query
|
|
1266
|
+
Question: "Show column_y from table_b for each record in table_a where column_x is 'value'"
|
|
1267
|
+
Reasoning:
|
|
1268
|
+
- Schema Link: table_a (column_x, id), table_b (column_y, fk_a)
|
|
1269
|
+
- Join Path: table_a.id \u2192 table_b.fk_a
|
|
1270
|
+
- Filters: column_x = 'value'
|
|
1271
|
+
- Output: column_y from table_b
|
|
1272
|
+
SQL: SELECT b.column_y FROM table_a a JOIN table_b b ON b.fk_a = a.id WHERE a.column_x = 'value'
|
|
1273
|
+
|
|
1274
|
+
### Example 3: Aggregation with GROUP BY
|
|
1275
|
+
Question: "What is the total of column_y grouped by column_x?"
|
|
1276
|
+
Reasoning:
|
|
1277
|
+
- Schema Link: table_a (column_x, column_y)
|
|
1278
|
+
- Aggregation: SUM(column_y), GROUP BY column_x
|
|
1279
|
+
- Output: column_x, sum
|
|
1280
|
+
SQL: SELECT column_x, SUM(column_y) as total FROM table_a GROUP BY column_x
|
|
1281
|
+
|
|
1282
|
+
### Example 4: Complex Aggregation
|
|
1283
|
+
Question: "Which values of column_x have more than 10 records, sorted by count descending?"
|
|
1284
|
+
Reasoning:
|
|
1285
|
+
- Schema Link: table_a (column_x)
|
|
1286
|
+
- Aggregation: COUNT(*), GROUP BY column_x, HAVING > 10
|
|
1287
|
+
- Output: column_x, count, ORDER BY count DESC
|
|
1288
|
+
SQL: SELECT column_x, COUNT(*) as cnt FROM table_a GROUP BY column_x HAVING COUNT(*) > 10 ORDER BY cnt DESC
|
|
1289
|
+
|
|
1290
|
+
### Example 5: Subquery (Decomposition)
|
|
1291
|
+
Question: "Show records from table_a where column_y is above average"
|
|
1292
|
+
Reasoning:
|
|
1293
|
+
- Decompose:
|
|
1294
|
+
- Q1: What is the average of column_y?
|
|
1295
|
+
- Q2: Which records have column_y above that value?
|
|
1296
|
+
- Schema Link: table_a (column_y)
|
|
1297
|
+
- Filters: column_y > (result of Q1)
|
|
1298
|
+
- Output: all columns from matching records
|
|
1299
|
+
- Verify: table_a and column_y exist \u2713
|
|
1300
|
+
SQL: SELECT * FROM table_a WHERE column_y > (SELECT AVG(column_y) FROM table_a)
|
|
1301
|
+
|
|
1302
|
+
### Example 6: Complex Multi-Join (Decomposition)
|
|
1303
|
+
Question: "Find the top 3 categories by total sales amount for orders placed last month"
|
|
1304
|
+
Reasoning:
|
|
1305
|
+
- Decompose:
|
|
1306
|
+
- Q1: Which orders were placed last month?
|
|
1307
|
+
- Q2: What is the total sales per category for those orders?
|
|
1308
|
+
- Q3: Which 3 categories have the highest totals?
|
|
1309
|
+
- Schema Link: orders (order_date, id), order_items (order_id, amount, product_id), products (id, category_id), categories (id, name)
|
|
1310
|
+
- Join Path: orders \u2192 order_items \u2192 products \u2192 categories
|
|
1311
|
+
- Filters: order_date within last month
|
|
1312
|
+
- Aggregation: SUM(amount), GROUP BY category
|
|
1313
|
+
- Output: category name, total, ORDER BY total DESC, LIMIT 3
|
|
1314
|
+
- Verify: all tables and columns exist \u2713
|
|
1315
|
+
SQL: SELECT c.name, SUM(oi.amount) as total FROM orders o JOIN order_items oi ON oi.order_id = o.id JOIN products p ON p.id = oi.product_id JOIN categories c ON c.id = p.category_id WHERE o.order_date >= DATE('now', '-1 month') GROUP BY c.id, c.name ORDER BY total DESC LIMIT 3
|
|
1316
|
+
`;
|
|
1317
|
+
var t_a_g = agent5({
|
|
1318
|
+
model: groq5("openai/gpt-oss-20b"),
|
|
1319
|
+
tools: tools2,
|
|
864
1320
|
name: "text2sql",
|
|
865
1321
|
prompt: (state) => {
|
|
866
1322
|
const hasMemory = !!state?.memory;
|
|
867
1323
|
return `
|
|
868
|
-
|
|
869
1324
|
${state?.teachings || ""}
|
|
870
1325
|
${state?.introspection || ""}
|
|
871
1326
|
|
|
1327
|
+
${chainOfThoughtPrompt}
|
|
1328
|
+
|
|
1329
|
+
${fewShotExamples}
|
|
1330
|
+
|
|
872
1331
|
${hasMemory ? memory_prompt_default : ""}
|
|
873
1332
|
`;
|
|
874
1333
|
}
|
|
@@ -939,9 +1398,7 @@ var Checkpoint = class _Checkpoint {
|
|
|
939
1398
|
* @param step - Unique identifier for this checkpoint point
|
|
940
1399
|
*/
|
|
941
1400
|
point(step) {
|
|
942
|
-
|
|
943
|
-
this.points[step] = { committed: false, entries: [] };
|
|
944
|
-
}
|
|
1401
|
+
this.points[step] ??= { committed: false, entries: [] };
|
|
945
1402
|
return new Point(this.points[step], () => this.save());
|
|
946
1403
|
}
|
|
947
1404
|
/**
|
|
@@ -1279,156 +1736,338 @@ import {
|
|
|
1279
1736
|
} from "ai";
|
|
1280
1737
|
import { v7 as v72 } from "uuid";
|
|
1281
1738
|
import {
|
|
1282
|
-
generate as
|
|
1739
|
+
generate as generate5,
|
|
1283
1740
|
stream,
|
|
1284
1741
|
user as user4
|
|
1285
1742
|
} from "@deepagents/agent";
|
|
1286
1743
|
|
|
1287
|
-
// packages/text2sql/src/lib/agents/
|
|
1288
|
-
import { groq as
|
|
1289
|
-
import { tool as
|
|
1290
|
-
import
|
|
1291
|
-
import
|
|
1292
|
-
import {
|
|
1744
|
+
// packages/text2sql/src/lib/agents/bi.agent.ts
|
|
1745
|
+
import { groq as groq6 } from "@ai-sdk/groq";
|
|
1746
|
+
import { tool as tool3 } from "ai";
|
|
1747
|
+
import dedent4 from "dedent";
|
|
1748
|
+
import z6 from "zod";
|
|
1749
|
+
import { agent as agent6, toState as toState3 } from "@deepagents/agent";
|
|
1750
|
+
import { scratchpad_tool as scratchpad_tool3 } from "@deepagents/toolbox";
|
|
1751
|
+
var tools3 = {
|
|
1752
|
+
/**
|
|
1753
|
+
* Validate SQL query syntax without executing.
|
|
1754
|
+
* Use this to verify queries are correct before embedding in dashboard components.
|
|
1755
|
+
*/
|
|
1756
|
+
validate_query: tool3({
|
|
1757
|
+
description: dedent4`
|
|
1758
|
+
Validate SQL query syntax before embedding in dashboard components.
|
|
1759
|
+
Use this to verify your queries are syntactically correct and reference valid tables/columns.
|
|
1293
1760
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
reasoning: z3.string().optional().describe("The reasoning steps taken to generate the SQL")
|
|
1761
|
+
This tool does NOT execute the query or return data.
|
|
1762
|
+
Only SELECT or WITH statements are allowed.
|
|
1763
|
+
`,
|
|
1764
|
+
inputSchema: z6.object({
|
|
1765
|
+
reasoning: z6.string().describe(
|
|
1766
|
+
"Why this query helps understand the data for dashboard design."
|
|
1767
|
+
),
|
|
1768
|
+
sql: z6.string().min(1, { message: "SQL query cannot be empty." }).refine(
|
|
1769
|
+
(sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
|
|
1770
|
+
{
|
|
1771
|
+
message: "Only read-only SELECT or WITH queries are allowed."
|
|
1772
|
+
}
|
|
1773
|
+
).describe("The SQL query to validate.")
|
|
1308
1774
|
}),
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
)
|
|
1313
|
-
|
|
1314
|
-
]),
|
|
1315
|
-
prompt: (state) => {
|
|
1316
|
-
return `
|
|
1317
|
-
${state?.teachings || ""}
|
|
1318
|
-
${state?.introspection || ""}
|
|
1319
|
-
`;
|
|
1320
|
-
}
|
|
1321
|
-
});
|
|
1322
|
-
function extractSql(output) {
|
|
1323
|
-
const match = output.match(/```sql\n?([\s\S]*?)```/);
|
|
1324
|
-
return match ? match[1].trim() : output.trim();
|
|
1325
|
-
}
|
|
1326
|
-
async function generateSql(params) {
|
|
1327
|
-
const {
|
|
1328
|
-
input,
|
|
1329
|
-
model,
|
|
1330
|
-
temperature,
|
|
1331
|
-
introspection,
|
|
1332
|
-
instructions,
|
|
1333
|
-
previousError
|
|
1334
|
-
} = params;
|
|
1335
|
-
const agentInstance = sqlQueryAgent.clone({
|
|
1336
|
-
model: wrapLanguageModel({
|
|
1337
|
-
model,
|
|
1338
|
-
middleware: defaultSettingsMiddleware({
|
|
1339
|
-
settings: { temperature, topP: 1 }
|
|
1340
|
-
})
|
|
1341
|
-
})
|
|
1342
|
-
});
|
|
1343
|
-
const messages = previousError ? [
|
|
1344
|
-
user(input),
|
|
1345
|
-
user(
|
|
1346
|
-
`<validation_error>Your previous SQL query had the following error: ${previousError}. Please fix the query.</validation_error>`
|
|
1347
|
-
)
|
|
1348
|
-
] : [user(input)];
|
|
1349
|
-
try {
|
|
1350
|
-
const { experimental_output: output } = await generate(
|
|
1351
|
-
agentInstance,
|
|
1352
|
-
messages,
|
|
1353
|
-
{
|
|
1354
|
-
teachings: toInstructions(
|
|
1355
|
-
"instructions",
|
|
1356
|
-
persona({
|
|
1357
|
-
name: "Freya",
|
|
1358
|
-
role: "You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema."
|
|
1359
|
-
}),
|
|
1360
|
-
...instructions
|
|
1361
|
-
),
|
|
1362
|
-
introspection
|
|
1775
|
+
execute: async ({ sql }, options) => {
|
|
1776
|
+
const state = toState3(options);
|
|
1777
|
+
const result = await state.adapter.validate(sql);
|
|
1778
|
+
if (typeof result === "string") {
|
|
1779
|
+
return { valid: false, error: result };
|
|
1363
1780
|
}
|
|
1364
|
-
|
|
1365
|
-
if ("error" in output) {
|
|
1366
|
-
return { success: false, error: output.error, isUnanswerable: true };
|
|
1367
|
-
}
|
|
1368
|
-
return { success: true, sql: extractSql(output.sql) };
|
|
1369
|
-
} catch (error) {
|
|
1370
|
-
if (error instanceof Error && (error.message.includes("Failed to validate JSON") || error.message.includes("response did not match schema"))) {
|
|
1371
|
-
return {
|
|
1372
|
-
success: false,
|
|
1373
|
-
error: `Schema validation failed: ${error.message}`
|
|
1374
|
-
};
|
|
1781
|
+
return { valid: true };
|
|
1375
1782
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1783
|
+
}),
|
|
1784
|
+
/**
|
|
1785
|
+
* Record insights and reasoning during schema analysis and dashboard design.
|
|
1786
|
+
*/
|
|
1787
|
+
scratchpad: scratchpad_tool3
|
|
1381
1788
|
};
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1789
|
+
var COMPONENTS_DOC = dedent4`
|
|
1790
|
+
## Available Components
|
|
1791
|
+
|
|
1792
|
+
You output markdown with embedded HTML custom elements. Use kebab-case tags with closing tags.
|
|
1793
|
+
|
|
1794
|
+
### Chart Components
|
|
1795
|
+
|
|
1796
|
+
#### Area Charts
|
|
1797
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1798
|
+
|-----------|----------------|----------------|----------|
|
|
1799
|
+
| \`<area-chart>\` | \`title\`, \`sql\` | \`x-key\`, \`y-key\`, \`variant\` | Cumulative values, trends with volume |
|
|
1800
|
+
|
|
1801
|
+
**Variants** (use \`variant\` prop):
|
|
1802
|
+
- \`default\` - Basic area with smooth curves
|
|
1803
|
+
- \`linear\` - Sharp-edged lines showing precise changes
|
|
1804
|
+
- \`step\` - Step-based segments for discrete data
|
|
1805
|
+
- \`stacked\` - Multiple series stacked on top of each other
|
|
1806
|
+
- \`stacked-expand\` - Normalized to 100% showing percentage contribution
|
|
1807
|
+
- \`gradient\` - Filled with gradient for visual depth
|
|
1808
|
+
|
|
1809
|
+
#### Bar Charts
|
|
1810
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1811
|
+
|-----------|----------------|----------------|----------|
|
|
1812
|
+
| \`<bar-chart>\` | \`title\`, \`sql\` | \`x-key\`, \`y-key\`, \`variant\`, \`orientation\` | Categorical comparisons, grouped data |
|
|
1813
|
+
|
|
1814
|
+
**Variants** (use \`variant\` prop):
|
|
1815
|
+
- \`default\` - Basic vertical bars
|
|
1816
|
+
- \`multiple\` - Multiple series side by side
|
|
1817
|
+
- \`stacked\` - Multiple series stacked
|
|
1818
|
+
- \`labeled\` - With value labels on bars
|
|
1819
|
+
- \`negative\` - Supports positive/negative values with conditional coloring
|
|
1820
|
+
- \`mixed\` - Different colors per category
|
|
1821
|
+
|
|
1822
|
+
**Orientation** (use \`orientation\` prop):
|
|
1823
|
+
- \`vertical\` (default) - Vertical bars
|
|
1824
|
+
- \`horizontal\` - Horizontal bars (good for long category names)
|
|
1825
|
+
|
|
1826
|
+
#### Line Charts
|
|
1827
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1828
|
+
|-----------|----------------|----------------|----------|
|
|
1829
|
+
| \`<line-chart>\` | \`title\`, \`sql\` | \`x-key\`, \`y-key\`, \`variant\` | Trends over time, continuous data |
|
|
1830
|
+
|
|
1831
|
+
**Variants** (use \`variant\` prop):
|
|
1832
|
+
- \`default\` - Smooth curved lines
|
|
1833
|
+
- \`linear\` - Straight lines between points
|
|
1834
|
+
- \`step\` - Step-based transitions
|
|
1835
|
+
- \`dots\` - Lines with visible data point markers
|
|
1836
|
+
- \`multiple\` - Multiple series for comparisons (A/B testing, etc.)
|
|
1837
|
+
- \`interactive\` - With metric switching capability
|
|
1838
|
+
- \`labeled\` - With value labels at each point
|
|
1839
|
+
|
|
1840
|
+
#### Pie & Donut Charts
|
|
1841
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1842
|
+
|-----------|----------------|----------------|----------|
|
|
1843
|
+
| \`<pie-chart>\` | \`title\`, \`sql\` | \`label-key\`, \`value-key\`, \`variant\` | Part-to-whole relationships, distributions |
|
|
1844
|
+
| \`<donut-chart>\` | \`title\`, \`sql\` | \`label-key\`, \`value-key\`, \`variant\` | Same as pie but with center space for text/KPI |
|
|
1845
|
+
|
|
1846
|
+
**Variants** (use \`variant\` prop):
|
|
1847
|
+
- \`default\` - Basic pie/donut
|
|
1848
|
+
- \`labeled\` - With labels on segments
|
|
1849
|
+
- \`legend\` - With external legend
|
|
1850
|
+
- \`interactive\` - With hover highlighting and selection
|
|
1851
|
+
- \`stacked\` - Multiple concentric rings for comparison
|
|
1852
|
+
|
|
1853
|
+
#### Radar Charts
|
|
1854
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1855
|
+
|-----------|----------------|----------------|----------|
|
|
1856
|
+
| \`<radar-chart>\` | \`title\`, \`sql\` | \`label-key\`, \`value-key\`, \`variant\` | Multi-dimensional comparisons, skill assessments |
|
|
1857
|
+
|
|
1858
|
+
**Variants** (use \`variant\` prop):
|
|
1859
|
+
- \`default\` - Basic radar with polygon grid
|
|
1860
|
+
- \`dots\` - With visible data point markers
|
|
1861
|
+
- \`filled\` - With filled area
|
|
1862
|
+
- \`multiple\` - Multiple series overlapping
|
|
1863
|
+
- \`circle\` - Circular grid instead of polygon
|
|
1864
|
+
- \`legend\` - With integrated legend
|
|
1865
|
+
|
|
1866
|
+
#### Radial Charts
|
|
1867
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1868
|
+
|-----------|----------------|----------------|----------|
|
|
1869
|
+
| \`<radial-chart>\` | \`title\`, \`sql\` | \`value-key\`, \`variant\` | Progress indicators, gauges, circular metrics |
|
|
1870
|
+
|
|
1871
|
+
**Variants** (use \`variant\` prop):
|
|
1872
|
+
- \`default\` - Basic radial bars from center outward
|
|
1873
|
+
- \`text\` - With centered value/caption text
|
|
1874
|
+
- \`shape\` - Gauge-style arc (not full circle)
|
|
1875
|
+
- \`stacked\` - Concentric arcs for multiple metrics
|
|
1876
|
+
- \`grid\` - With background grid rings
|
|
1877
|
+
|
|
1878
|
+
#### KPI Component
|
|
1879
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1880
|
+
|-----------|----------------|----------------|----------|
|
|
1881
|
+
| \`<kpi>\` | \`title\`, \`sql\` | \`variant\`, \`format\`, \`trend-sql\`, \`target\`, \`icon\`, \`description\`, \`color\` | Rich metric displays with trends, progress, sparklines |
|
|
1882
|
+
|
|
1883
|
+
**Variants** (use \`variant\` prop):
|
|
1884
|
+
- \`default\` - Simple value card
|
|
1885
|
+
- \`trend\` - Value with change indicator (↑12.5% or ↓3.2%)
|
|
1886
|
+
- \`comparison\` - Value with previous period value shown
|
|
1887
|
+
- \`progress\` - Value with horizontal progress bar toward target
|
|
1888
|
+
- \`ring\` - Value with circular progress gauge toward target
|
|
1889
|
+
- \`sparkline\` - Value with mini area chart showing recent trend
|
|
1890
|
+
|
|
1891
|
+
**Props Reference**:
|
|
1892
|
+
- \`title\` (required) - Display label
|
|
1893
|
+
- \`sql\` (required) - Query returning \`{ value: number }\`
|
|
1894
|
+
- \`variant\` - Display style (see above)
|
|
1895
|
+
- \`format\` - Value format: \`currency\`, \`percent\`, \`number\`, \`compact\`, \`duration\`
|
|
1896
|
+
- \`trend-sql\` - Query for trend data:
|
|
1897
|
+
- For \`trend\`/\`comparison\`: returns \`{ change: number }\` or \`{ previous: number }\`
|
|
1898
|
+
- For \`sparkline\`: returns time-series \`[{ date, value }]\`
|
|
1899
|
+
- \`target\` - Target value for \`progress\`/\`ring\` variants
|
|
1900
|
+
- \`icon\` - Icon identifier: \`dollar\`, \`users\`, \`cart\`, \`chart\`, \`percent\`, \`clock\`
|
|
1901
|
+
- \`description\` - Subtitle/context text
|
|
1902
|
+
- \`color\` - Accent color: \`positive\` (green), \`negative\` (red), \`neutral\`, \`primary\`
|
|
1903
|
+
|
|
1904
|
+
#### Data Table
|
|
1905
|
+
| Component | Required Props | Optional Props | Use Case |
|
|
1906
|
+
|-----------|----------------|----------------|----------|
|
|
1907
|
+
| \`<data-table>\` | \`title\`, \`sql\` | \`columns\` | Detailed data, lists, rankings |
|
|
1908
|
+
|
|
1909
|
+
### Layout Components
|
|
1910
|
+
| Component | Props | Description |
|
|
1911
|
+
|-----------|-------|-------------|
|
|
1912
|
+
| \`<row>\` | \`gap?\` | Horizontal flex container (small, medium, or large) |
|
|
1913
|
+
| \`<column>\` | \`span\` (1-12) | Column within row, 12-column grid |
|
|
1914
|
+
| \`<grid>\` | \`cols\`, \`gap?\` | CSS Grid container |
|
|
1915
|
+
|
|
1916
|
+
### Chart Selection Guide
|
|
1917
|
+
- **Time series / Trends with volume**: Use \`<area-chart>\` (shows magnitude over time)
|
|
1918
|
+
- **Time series / Precise trends**: Use \`<line-chart>\` (clean trend lines)
|
|
1919
|
+
- **Categories / Comparisons**: Use \`<bar-chart>\`
|
|
1920
|
+
- **Part-to-whole / Proportions**: Use \`<pie-chart>\` or \`<donut-chart>\`
|
|
1921
|
+
- **Multi-dimensional comparisons**: Use \`<radar-chart>\` (e.g., comparing skills, features)
|
|
1922
|
+
- **Progress / Gauges**: Use \`<radial-chart>\` (circular progress indicators)
|
|
1923
|
+
- **Detailed data / Rankings**: Use \`<data-table>\`
|
|
1924
|
+
- **Single metrics**: Use \`<kpi>\` with appropriate variant:
|
|
1925
|
+
- Simple value → \`default\`
|
|
1926
|
+
- Value with change indicator → \`trend\`
|
|
1927
|
+
- Value vs previous period → \`comparison\`
|
|
1928
|
+
- Value toward goal → \`progress\` or \`ring\`
|
|
1929
|
+
- Value with recent history → \`sparkline\`
|
|
1930
|
+
|
|
1931
|
+
### Example Output
|
|
1932
|
+
\`\`\`markdown
|
|
1933
|
+
## Sales Dashboard
|
|
1934
|
+
|
|
1935
|
+
<row>
|
|
1936
|
+
<column span="3">
|
|
1937
|
+
<kpi
|
|
1938
|
+
title="Total Revenue"
|
|
1939
|
+
sql="SELECT SUM(amount) as value FROM orders"
|
|
1940
|
+
trend-sql="SELECT ((SUM(CASE WHEN created_at >= NOW() - INTERVAL '30 days' THEN amount END) - SUM(CASE WHEN created_at >= NOW() - INTERVAL '60 days' AND created_at < NOW() - INTERVAL '30 days' THEN amount END)) / NULLIF(SUM(CASE WHEN created_at >= NOW() - INTERVAL '60 days' AND created_at < NOW() - INTERVAL '30 days' THEN amount END), 0) * 100) as change FROM orders"
|
|
1941
|
+
variant="trend"
|
|
1942
|
+
format="currency"
|
|
1943
|
+
icon="dollar">
|
|
1944
|
+
</kpi>
|
|
1945
|
+
</column>
|
|
1946
|
+
<column span="3">
|
|
1947
|
+
<kpi
|
|
1948
|
+
title="Orders Today"
|
|
1949
|
+
sql="SELECT COUNT(*) as value FROM orders WHERE DATE(created_at) = CURRENT_DATE"
|
|
1950
|
+
trend-sql="SELECT DATE(created_at) as date, COUNT(*) as value FROM orders WHERE created_at >= NOW() - INTERVAL '7 days' GROUP BY 1 ORDER BY 1"
|
|
1951
|
+
variant="sparkline"
|
|
1952
|
+
icon="cart">
|
|
1953
|
+
</kpi>
|
|
1954
|
+
</column>
|
|
1955
|
+
<column span="3">
|
|
1956
|
+
<kpi
|
|
1957
|
+
title="Sales Target"
|
|
1958
|
+
sql="SELECT SUM(amount) as value FROM orders WHERE EXTRACT(QUARTER FROM created_at) = EXTRACT(QUARTER FROM NOW())"
|
|
1959
|
+
target="100000"
|
|
1960
|
+
variant="progress"
|
|
1961
|
+
format="currency"
|
|
1962
|
+
description="Q4 2024 Goal">
|
|
1963
|
+
</kpi>
|
|
1964
|
+
</column>
|
|
1965
|
+
<column span="3">
|
|
1966
|
+
<kpi
|
|
1967
|
+
title="Conversion Rate"
|
|
1968
|
+
sql="SELECT (COUNT(DISTINCT buyer_id)::float / COUNT(DISTINCT visitor_id) * 100) as value FROM sessions"
|
|
1969
|
+
variant="ring"
|
|
1970
|
+
target="100"
|
|
1971
|
+
format="percent"
|
|
1972
|
+
color="primary">
|
|
1973
|
+
</kpi>
|
|
1974
|
+
</column>
|
|
1975
|
+
</row>
|
|
1976
|
+
|
|
1977
|
+
<row>
|
|
1978
|
+
<column span="8">
|
|
1979
|
+
<area-chart
|
|
1980
|
+
title="Revenue Over Time"
|
|
1981
|
+
sql="SELECT DATE_TRUNC('month', created_at) as month, SUM(amount) as revenue FROM orders GROUP BY 1 ORDER BY 1"
|
|
1982
|
+
x-key="month"
|
|
1983
|
+
y-key="revenue"
|
|
1984
|
+
variant="gradient">
|
|
1985
|
+
</area-chart>
|
|
1986
|
+
</column>
|
|
1987
|
+
<column span="4">
|
|
1988
|
+
<donut-chart
|
|
1989
|
+
title="Revenue by Category"
|
|
1990
|
+
sql="SELECT category, SUM(amount) as revenue FROM orders GROUP BY category"
|
|
1991
|
+
label-key="category"
|
|
1992
|
+
value-key="revenue"
|
|
1993
|
+
variant="interactive">
|
|
1994
|
+
</donut-chart>
|
|
1995
|
+
</column>
|
|
1996
|
+
</row>
|
|
1997
|
+
|
|
1998
|
+
<row>
|
|
1999
|
+
<column span="6">
|
|
2000
|
+
<bar-chart
|
|
2001
|
+
title="Monthly Comparison"
|
|
2002
|
+
sql="SELECT DATE_TRUNC('month', created_at) as month, SUM(CASE WHEN EXTRACT(YEAR FROM created_at) = 2024 THEN amount END) as this_year, SUM(CASE WHEN EXTRACT(YEAR FROM created_at) = 2023 THEN amount END) as last_year FROM orders GROUP BY 1"
|
|
2003
|
+
variant="multiple"
|
|
2004
|
+
x-key="month">
|
|
2005
|
+
</bar-chart>
|
|
2006
|
+
</column>
|
|
2007
|
+
<column span="6">
|
|
2008
|
+
<radar-chart
|
|
2009
|
+
title="Product Performance"
|
|
2010
|
+
sql="SELECT metric, score FROM product_metrics WHERE product_id = 1"
|
|
2011
|
+
label-key="metric"
|
|
2012
|
+
value-key="score"
|
|
2013
|
+
variant="filled">
|
|
2014
|
+
</radar-chart>
|
|
2015
|
+
</column>
|
|
2016
|
+
</row>
|
|
2017
|
+
|
|
2018
|
+
<data-table
|
|
2019
|
+
title="Top 10 Products"
|
|
2020
|
+
sql="SELECT name, SUM(quantity) as sold, SUM(amount) as revenue FROM order_items GROUP BY name ORDER BY revenue DESC LIMIT 10">
|
|
2021
|
+
</data-table>
|
|
2022
|
+
\`\`\`
|
|
2023
|
+
`;
|
|
2024
|
+
var biAgent = agent6({
|
|
2025
|
+
model: groq6("gpt-oss-20b"),
|
|
2026
|
+
tools: tools3,
|
|
2027
|
+
name: "bi_agent",
|
|
2028
|
+
prompt: (state) => {
|
|
2029
|
+
return dedent4`
|
|
2030
|
+
You are an expert BI analyst that creates dashboard specifications using HTML custom elements.
|
|
2031
|
+
|
|
2032
|
+
${COMPONENTS_DOC}
|
|
2033
|
+
|
|
2034
|
+
## Your Workflow
|
|
2035
|
+
|
|
2036
|
+
1. **PLAN**: Analyze the request and schema to determine what metrics/visualizations to create
|
|
2037
|
+
2. **VALIDATE**: Use \`validate_query\` to verify SQL syntax is correct before embedding
|
|
2038
|
+
3. **OUTPUT**: Generate the dashboard using layout and chart components
|
|
2039
|
+
|
|
2040
|
+
## Critical Rules
|
|
2041
|
+
|
|
2042
|
+
- **Design from schema**: Use the provided schema introspection to understand available tables, columns, and relationships
|
|
2043
|
+
- **Validate all queries**: Use \`validate_query\` to ensure SQL is syntactically correct before embedding in components
|
|
2044
|
+
- **Use kebab-case HTML tags** with closing tags (e.g., \`<bar-chart></bar-chart>\`)
|
|
2045
|
+
- Use \`scratchpad\` to record schema analysis insights and design decisions
|
|
2046
|
+
- Choose chart types based on column types (dates → line/area, categories → bar/pie, numbers → KPI)
|
|
2047
|
+
- Use layout components (row, column, grid) to organize the dashboard
|
|
2048
|
+
- Include a text introduction explaining what the dashboard shows
|
|
2049
|
+
|
|
2050
|
+
## SQL Rules
|
|
2051
|
+
|
|
2052
|
+
- Only SELECT or WITH statements
|
|
2053
|
+
- Use proper date/time functions for the database
|
|
2054
|
+
- Include appropriate GROUP BY, ORDER BY clauses
|
|
2055
|
+
- Use aliases for calculated columns
|
|
2056
|
+
|
|
2057
|
+
${state?.teachings || ""}
|
|
2058
|
+
${state?.introspection || ""}
|
|
2059
|
+
`;
|
|
1425
2060
|
}
|
|
1426
|
-
|
|
1427
|
-
}
|
|
2061
|
+
});
|
|
1428
2062
|
|
|
1429
2063
|
// packages/text2sql/src/lib/agents/chat1.agent.ts
|
|
1430
|
-
|
|
1431
|
-
|
|
2064
|
+
import { groq as groq7 } from "@ai-sdk/groq";
|
|
2065
|
+
import { tool as tool4 } from "ai";
|
|
2066
|
+
import z7 from "zod";
|
|
2067
|
+
import { agent as agent7, toState as toState4 } from "@deepagents/agent";
|
|
2068
|
+
import { scratchpad_tool as scratchpad_tool4 } from "@deepagents/toolbox";
|
|
2069
|
+
var tools4 = {
|
|
2070
|
+
query_database: tool4({
|
|
1432
2071
|
description: `Query the database to answer a question. Provide your question in natural language and this tool will:
|
|
1433
2072
|
1. Generate the appropriate SQL query
|
|
1434
2073
|
2. Validate the SQL syntax
|
|
@@ -1436,16 +2075,16 @@ var tools2 = {
|
|
|
1436
2075
|
4. Return the results
|
|
1437
2076
|
|
|
1438
2077
|
Use this tool when you need to retrieve data to answer the user's question.`,
|
|
1439
|
-
inputSchema:
|
|
1440
|
-
question:
|
|
2078
|
+
inputSchema: z7.object({
|
|
2079
|
+
question: z7.string().min(1).describe(
|
|
1441
2080
|
"The question to answer, expressed in natural language. Be specific about what data you need."
|
|
1442
2081
|
),
|
|
1443
|
-
reasoning:
|
|
2082
|
+
reasoning: z7.string().optional().describe(
|
|
1444
2083
|
"Your reasoning for why this query is needed to answer the user."
|
|
1445
2084
|
)
|
|
1446
2085
|
}),
|
|
1447
2086
|
execute: async ({ question }, options) => {
|
|
1448
|
-
const state =
|
|
2087
|
+
const state = toState4(options);
|
|
1449
2088
|
try {
|
|
1450
2089
|
const sqlResult = await toSql({
|
|
1451
2090
|
input: question,
|
|
@@ -1456,16 +2095,14 @@ Use this tool when you need to retrieve data to answer the user's question.`,
|
|
|
1456
2095
|
if (!sqlResult.sql) {
|
|
1457
2096
|
return {
|
|
1458
2097
|
success: false,
|
|
1459
|
-
error: sqlResult.errors?.join("; ") || "Failed to generate SQL"
|
|
1460
|
-
attempts: sqlResult.attempts
|
|
2098
|
+
error: sqlResult.errors?.join("; ") || "Failed to generate SQL"
|
|
1461
2099
|
};
|
|
1462
2100
|
}
|
|
1463
2101
|
const data = await state.adapter.execute(sqlResult.sql);
|
|
1464
2102
|
return {
|
|
1465
2103
|
success: true,
|
|
1466
2104
|
sql: sqlResult.sql,
|
|
1467
|
-
data
|
|
1468
|
-
attempts: sqlResult.attempts
|
|
2105
|
+
data
|
|
1469
2106
|
};
|
|
1470
2107
|
} catch (error) {
|
|
1471
2108
|
return {
|
|
@@ -1475,12 +2112,12 @@ Use this tool when you need to retrieve data to answer the user's question.`,
|
|
|
1475
2112
|
}
|
|
1476
2113
|
}
|
|
1477
2114
|
}),
|
|
1478
|
-
scratchpad:
|
|
2115
|
+
scratchpad: scratchpad_tool4
|
|
1479
2116
|
};
|
|
1480
|
-
var chat1Agent =
|
|
2117
|
+
var chat1Agent = agent7({
|
|
1481
2118
|
name: "chat1-combined",
|
|
1482
|
-
model:
|
|
1483
|
-
tools:
|
|
2119
|
+
model: groq7("openai/gpt-oss-20b"),
|
|
2120
|
+
tools: tools4,
|
|
1484
2121
|
prompt: (state) => {
|
|
1485
2122
|
return `
|
|
1486
2123
|
${state?.teachings || ""}
|
|
@@ -1490,13 +2127,13 @@ ${state?.introspection || ""}
|
|
|
1490
2127
|
});
|
|
1491
2128
|
|
|
1492
2129
|
// packages/text2sql/src/lib/agents/chat2.agent.ts
|
|
1493
|
-
import { groq as
|
|
1494
|
-
import { tool as
|
|
1495
|
-
import
|
|
1496
|
-
import { agent as
|
|
1497
|
-
import { scratchpad_tool as
|
|
1498
|
-
var
|
|
1499
|
-
generate_sql:
|
|
2130
|
+
import { groq as groq8 } from "@ai-sdk/groq";
|
|
2131
|
+
import { tool as tool5 } from "ai";
|
|
2132
|
+
import z8 from "zod";
|
|
2133
|
+
import { agent as agent8, toState as toState5 } from "@deepagents/agent";
|
|
2134
|
+
import { scratchpad_tool as scratchpad_tool5 } from "@deepagents/toolbox";
|
|
2135
|
+
var tools5 = {
|
|
2136
|
+
generate_sql: tool5({
|
|
1500
2137
|
description: `Generate a SQL query from a natural language question. This tool will:
|
|
1501
2138
|
1. Translate your question into SQL
|
|
1502
2139
|
2. Validate the SQL syntax
|
|
@@ -1507,14 +2144,14 @@ Use this BEFORE execute_sql to see what query will be run. You can then:
|
|
|
1507
2144
|
- Explain the approach to the user
|
|
1508
2145
|
- Decide if the SQL looks correct
|
|
1509
2146
|
- Refine your question and regenerate if needed`,
|
|
1510
|
-
inputSchema:
|
|
1511
|
-
question:
|
|
2147
|
+
inputSchema: z8.object({
|
|
2148
|
+
question: z8.string().min(1).describe(
|
|
1512
2149
|
"The question to translate into SQL. Be specific about what data you need."
|
|
1513
2150
|
),
|
|
1514
|
-
reasoning:
|
|
2151
|
+
reasoning: z8.string().optional().describe("Your reasoning for why this data is needed.")
|
|
1515
2152
|
}),
|
|
1516
2153
|
execute: async ({ question }, options) => {
|
|
1517
|
-
const state =
|
|
2154
|
+
const state = toState5(options);
|
|
1518
2155
|
try {
|
|
1519
2156
|
const sqlResult = await toSql({
|
|
1520
2157
|
input: question,
|
|
@@ -1526,14 +2163,12 @@ Use this BEFORE execute_sql to see what query will be run. You can then:
|
|
|
1526
2163
|
return {
|
|
1527
2164
|
success: false,
|
|
1528
2165
|
error: sqlResult.errors?.join("; ") || "Failed to generate SQL",
|
|
1529
|
-
attempts: sqlResult.attempts,
|
|
1530
2166
|
validationErrors: sqlResult.errors
|
|
1531
2167
|
};
|
|
1532
2168
|
}
|
|
1533
2169
|
return {
|
|
1534
2170
|
success: true,
|
|
1535
2171
|
sql: sqlResult.sql,
|
|
1536
|
-
attempts: sqlResult.attempts,
|
|
1537
2172
|
validationErrors: sqlResult.errors
|
|
1538
2173
|
};
|
|
1539
2174
|
} catch (error) {
|
|
@@ -1544,21 +2179,21 @@ Use this BEFORE execute_sql to see what query will be run. You can then:
|
|
|
1544
2179
|
}
|
|
1545
2180
|
}
|
|
1546
2181
|
}),
|
|
1547
|
-
execute_sql:
|
|
2182
|
+
execute_sql: tool5({
|
|
1548
2183
|
description: `Execute a SQL query and return the results. Use this AFTER generate_sql to run the query.
|
|
1549
2184
|
|
|
1550
2185
|
Only SELECT and WITH (CTE) queries are allowed - no data modification.`,
|
|
1551
|
-
inputSchema:
|
|
1552
|
-
sql:
|
|
2186
|
+
inputSchema: z8.object({
|
|
2187
|
+
sql: z8.string().min(1).refine(
|
|
1553
2188
|
(sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
|
|
1554
2189
|
{
|
|
1555
2190
|
message: "Only read-only SELECT or WITH queries are allowed."
|
|
1556
2191
|
}
|
|
1557
2192
|
).describe("The SQL query to execute (must be SELECT or WITH)."),
|
|
1558
|
-
reasoning:
|
|
2193
|
+
reasoning: z8.string().optional().describe("Brief explanation of what this query retrieves.")
|
|
1559
2194
|
}),
|
|
1560
2195
|
execute: async ({ sql }, options) => {
|
|
1561
|
-
const state =
|
|
2196
|
+
const state = toState5(options);
|
|
1562
2197
|
try {
|
|
1563
2198
|
const data = await state.adapter.execute(sql);
|
|
1564
2199
|
return {
|
|
@@ -1574,12 +2209,12 @@ Only SELECT and WITH (CTE) queries are allowed - no data modification.`,
|
|
|
1574
2209
|
}
|
|
1575
2210
|
}
|
|
1576
2211
|
}),
|
|
1577
|
-
scratchpad:
|
|
2212
|
+
scratchpad: scratchpad_tool5
|
|
1578
2213
|
};
|
|
1579
|
-
var chat2Agent =
|
|
2214
|
+
var chat2Agent = agent8({
|
|
1580
2215
|
name: "chat2-with-peek",
|
|
1581
|
-
model:
|
|
1582
|
-
tools:
|
|
2216
|
+
model: groq8("openai/gpt-oss-20b"),
|
|
2217
|
+
tools: tools5,
|
|
1583
2218
|
prompt: (state) => {
|
|
1584
2219
|
return `
|
|
1585
2220
|
${state?.teachings || ""}
|
|
@@ -1597,34 +2232,34 @@ If the generated SQL doesn't look right, you can refine your question and regene
|
|
|
1597
2232
|
});
|
|
1598
2233
|
|
|
1599
2234
|
// packages/text2sql/src/lib/agents/chat3.agent.ts
|
|
1600
|
-
import { groq as
|
|
1601
|
-
import { defaultSettingsMiddleware as defaultSettingsMiddleware2, tool as
|
|
1602
|
-
import
|
|
1603
|
-
import { agent as
|
|
1604
|
-
import { scratchpad_tool as
|
|
1605
|
-
var collaborativeSqlOutputSchema =
|
|
1606
|
-
|
|
1607
|
-
status:
|
|
1608
|
-
sql:
|
|
1609
|
-
confidence:
|
|
1610
|
-
assumptions:
|
|
1611
|
-
reasoning:
|
|
2235
|
+
import { groq as groq9 } from "@ai-sdk/groq";
|
|
2236
|
+
import { defaultSettingsMiddleware as defaultSettingsMiddleware2, tool as tool6, wrapLanguageModel as wrapLanguageModel2 } from "ai";
|
|
2237
|
+
import z9 from "zod";
|
|
2238
|
+
import { agent as agent9, generate as generate3, toState as toState6, user as user2 } from "@deepagents/agent";
|
|
2239
|
+
import { scratchpad_tool as scratchpad_tool6 } from "@deepagents/toolbox";
|
|
2240
|
+
var collaborativeSqlOutputSchema = z9.discriminatedUnion("status", [
|
|
2241
|
+
z9.object({
|
|
2242
|
+
status: z9.literal("success"),
|
|
2243
|
+
sql: z9.string().describe("The generated SQL query"),
|
|
2244
|
+
confidence: z9.enum(["high", "medium", "low"]).describe("Confidence level in this SQL being correct"),
|
|
2245
|
+
assumptions: z9.array(z9.string()).optional().describe("Assumptions made during SQL generation"),
|
|
2246
|
+
reasoning: z9.string().optional().describe("Brief explanation of the query approach")
|
|
1612
2247
|
}),
|
|
1613
|
-
|
|
1614
|
-
status:
|
|
1615
|
-
question:
|
|
1616
|
-
context:
|
|
1617
|
-
options:
|
|
2248
|
+
z9.object({
|
|
2249
|
+
status: z9.literal("clarification_needed"),
|
|
2250
|
+
question: z9.string().describe("Question to clarify the request"),
|
|
2251
|
+
context: z9.string().optional().describe("Why this clarification is needed"),
|
|
2252
|
+
options: z9.array(z9.string()).optional().describe("Possible options if applicable")
|
|
1618
2253
|
}),
|
|
1619
|
-
|
|
1620
|
-
status:
|
|
1621
|
-
reason:
|
|
1622
|
-
suggestions:
|
|
2254
|
+
z9.object({
|
|
2255
|
+
status: z9.literal("unanswerable"),
|
|
2256
|
+
reason: z9.string().describe("Why this question cannot be answered"),
|
|
2257
|
+
suggestions: z9.array(z9.string()).optional().describe("Alternative questions that could be answered")
|
|
1623
2258
|
})
|
|
1624
2259
|
]);
|
|
1625
|
-
var collaborativeSqlAgent =
|
|
2260
|
+
var collaborativeSqlAgent = agent9({
|
|
1626
2261
|
name: "collaborative-sql",
|
|
1627
|
-
model:
|
|
2262
|
+
model: groq9("openai/gpt-oss-20b"),
|
|
1628
2263
|
output: collaborativeSqlOutputSchema,
|
|
1629
2264
|
prompt: (state) => {
|
|
1630
2265
|
return `
|
|
@@ -1658,8 +2293,8 @@ Prefer asking for clarification over making low-confidence guesses.
|
|
|
1658
2293
|
`;
|
|
1659
2294
|
}
|
|
1660
2295
|
});
|
|
1661
|
-
var
|
|
1662
|
-
consult_sql_agent:
|
|
2296
|
+
var tools6 = {
|
|
2297
|
+
consult_sql_agent: tool6({
|
|
1663
2298
|
description: `Consult the SQL specialist agent to generate a query. The SQL agent may:
|
|
1664
2299
|
- Return a SQL query with confidence level and assumptions
|
|
1665
2300
|
- Ask for clarification if the question is ambiguous
|
|
@@ -1669,15 +2304,15 @@ Based on the response:
|
|
|
1669
2304
|
- If clarification is needed, you can provide context or ask the user
|
|
1670
2305
|
- If assumptions were made, verify them with the user for important queries
|
|
1671
2306
|
- If unanswerable, relay the suggestions to the user`,
|
|
1672
|
-
inputSchema:
|
|
1673
|
-
question:
|
|
1674
|
-
context:
|
|
1675
|
-
previousClarification:
|
|
2307
|
+
inputSchema: z9.object({
|
|
2308
|
+
question: z9.string().min(1).describe("The question to translate into SQL."),
|
|
2309
|
+
context: z9.string().optional().describe("Additional context from the conversation that might help."),
|
|
2310
|
+
previousClarification: z9.string().optional().describe(
|
|
1676
2311
|
"Answer to a previous clarification question from the SQL agent."
|
|
1677
2312
|
)
|
|
1678
2313
|
}),
|
|
1679
2314
|
execute: async ({ question, context: context2, previousClarification }, options) => {
|
|
1680
|
-
const state =
|
|
2315
|
+
const state = toState6(options);
|
|
1681
2316
|
try {
|
|
1682
2317
|
let fullQuestion = question;
|
|
1683
2318
|
if (context2) {
|
|
@@ -1698,7 +2333,7 @@ Clarification provided: ${previousClarification}`;
|
|
|
1698
2333
|
})
|
|
1699
2334
|
})
|
|
1700
2335
|
});
|
|
1701
|
-
const { experimental_output: output } = await
|
|
2336
|
+
const { experimental_output: output } = await generate3(
|
|
1702
2337
|
agentInstance,
|
|
1703
2338
|
[user2(fullQuestion)],
|
|
1704
2339
|
state
|
|
@@ -1747,11 +2382,11 @@ Clarification provided: ${previousClarification}`;
|
|
|
1747
2382
|
}
|
|
1748
2383
|
}
|
|
1749
2384
|
}),
|
|
1750
|
-
execute_sql:
|
|
2385
|
+
execute_sql: tool6({
|
|
1751
2386
|
description: `Execute a SQL query directly. Use this when you have SQL that you want to run
|
|
1752
2387
|
(e.g., after receiving SQL from consult_sql_agent or for follow-up queries).`,
|
|
1753
|
-
inputSchema:
|
|
1754
|
-
sql:
|
|
2388
|
+
inputSchema: z9.object({
|
|
2389
|
+
sql: z9.string().min(1).refine(
|
|
1755
2390
|
(sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
|
|
1756
2391
|
{
|
|
1757
2392
|
message: "Only read-only SELECT or WITH queries are allowed."
|
|
@@ -1759,7 +2394,7 @@ Clarification provided: ${previousClarification}`;
|
|
|
1759
2394
|
).describe("The SQL query to execute.")
|
|
1760
2395
|
}),
|
|
1761
2396
|
execute: async ({ sql }, options) => {
|
|
1762
|
-
const state =
|
|
2397
|
+
const state = toState6(options);
|
|
1763
2398
|
try {
|
|
1764
2399
|
const validationError = await state.adapter.validate(sql);
|
|
1765
2400
|
if (validationError) {
|
|
@@ -1782,12 +2417,12 @@ Clarification provided: ${previousClarification}`;
|
|
|
1782
2417
|
}
|
|
1783
2418
|
}
|
|
1784
2419
|
}),
|
|
1785
|
-
scratchpad:
|
|
2420
|
+
scratchpad: scratchpad_tool6
|
|
1786
2421
|
};
|
|
1787
|
-
var chat3Agent =
|
|
2422
|
+
var chat3Agent = agent9({
|
|
1788
2423
|
name: "chat3-collaborative",
|
|
1789
|
-
model:
|
|
1790
|
-
tools:
|
|
2424
|
+
model: groq9("openai/gpt-oss-20b"),
|
|
2425
|
+
tools: tools6,
|
|
1791
2426
|
prompt: (state) => {
|
|
1792
2427
|
return `
|
|
1793
2428
|
${state?.teachings || ""}
|
|
@@ -1807,39 +2442,39 @@ For clarification requests, try to answer from conversation context first before
|
|
|
1807
2442
|
});
|
|
1808
2443
|
|
|
1809
2444
|
// packages/text2sql/src/lib/agents/chat4.agent.ts
|
|
1810
|
-
import { groq as
|
|
1811
|
-
import { defaultSettingsMiddleware as defaultSettingsMiddleware3, tool as
|
|
1812
|
-
import
|
|
1813
|
-
import { agent as
|
|
1814
|
-
import { scratchpad_tool as
|
|
1815
|
-
var questionDecompositionSchema =
|
|
1816
|
-
originalQuestion:
|
|
1817
|
-
breakdown:
|
|
2445
|
+
import { groq as groq10 } from "@ai-sdk/groq";
|
|
2446
|
+
import { defaultSettingsMiddleware as defaultSettingsMiddleware3, tool as tool7, wrapLanguageModel as wrapLanguageModel3 } from "ai";
|
|
2447
|
+
import z10 from "zod";
|
|
2448
|
+
import { agent as agent10, generate as generate4, toState as toState7, user as user3 } from "@deepagents/agent";
|
|
2449
|
+
import { scratchpad_tool as scratchpad_tool7 } from "@deepagents/toolbox";
|
|
2450
|
+
var questionDecompositionSchema = z10.object({
|
|
2451
|
+
originalQuestion: z10.string().describe("The original question being decomposed"),
|
|
2452
|
+
breakdown: z10.array(z10.string()).min(1).describe(
|
|
1818
2453
|
"Semantic breakdown of the question into its component parts. Each part describes an aspect of what is being asked, NOT how to implement it."
|
|
1819
2454
|
),
|
|
1820
|
-
entities:
|
|
2455
|
+
entities: z10.array(z10.string()).optional().describe(
|
|
1821
2456
|
"Key entities/concepts mentioned (e.g., customers, orders, products)"
|
|
1822
2457
|
),
|
|
1823
|
-
filters:
|
|
2458
|
+
filters: z10.array(z10.string()).optional().describe(
|
|
1824
2459
|
'Filtering criteria mentioned (e.g., "last quarter", "above $100")'
|
|
1825
2460
|
),
|
|
1826
|
-
aggregation:
|
|
2461
|
+
aggregation: z10.string().optional().describe(
|
|
1827
2462
|
'Type of aggregation if any (e.g., "count", "sum", "average", "top N")'
|
|
1828
2463
|
),
|
|
1829
|
-
ambiguities:
|
|
2464
|
+
ambiguities: z10.array(z10.string()).optional().describe("Any ambiguous parts that might need clarification")
|
|
1830
2465
|
});
|
|
1831
|
-
var decompositionSqlOutputSchema =
|
|
1832
|
-
|
|
1833
|
-
sql:
|
|
1834
|
-
reasoning:
|
|
2466
|
+
var decompositionSqlOutputSchema = z10.union([
|
|
2467
|
+
z10.object({
|
|
2468
|
+
sql: z10.string().describe("The SQL query that answers the decomposed question"),
|
|
2469
|
+
reasoning: z10.string().optional().describe("How each breakdown component was addressed")
|
|
1835
2470
|
}),
|
|
1836
|
-
|
|
1837
|
-
error:
|
|
2471
|
+
z10.object({
|
|
2472
|
+
error: z10.string().describe("Error message if the question cannot be answered")
|
|
1838
2473
|
})
|
|
1839
2474
|
]);
|
|
1840
|
-
var decompositionSqlAgent =
|
|
2475
|
+
var decompositionSqlAgent = agent10({
|
|
1841
2476
|
name: "decomposition-sql",
|
|
1842
|
-
model:
|
|
2477
|
+
model: groq10("openai/gpt-oss-20b"),
|
|
1843
2478
|
output: decompositionSqlOutputSchema,
|
|
1844
2479
|
prompt: (state) => {
|
|
1845
2480
|
return `
|
|
@@ -1866,8 +2501,8 @@ If there are ambiguities, make reasonable assumptions and note them in your reas
|
|
|
1866
2501
|
}
|
|
1867
2502
|
});
|
|
1868
2503
|
var RETRY_TEMPERATURES2 = [0, 0.2, 0.3];
|
|
1869
|
-
var
|
|
1870
|
-
query_with_decomposition:
|
|
2504
|
+
var tools7 = {
|
|
2505
|
+
query_with_decomposition: tool7({
|
|
1871
2506
|
description: `Query the database using question decomposition. This tool:
|
|
1872
2507
|
1. Breaks down your question into semantic components (entities, filters, aggregations)
|
|
1873
2508
|
2. Passes the decomposition to the SQL specialist
|
|
@@ -1875,22 +2510,22 @@ var tools5 = {
|
|
|
1875
2510
|
4. Executes and returns results
|
|
1876
2511
|
|
|
1877
2512
|
This approach helps ensure all aspects of the question are addressed in the query.`,
|
|
1878
|
-
inputSchema:
|
|
1879
|
-
question:
|
|
1880
|
-
breakdown:
|
|
2513
|
+
inputSchema: z10.object({
|
|
2514
|
+
question: z10.string().min(1).describe("The question to answer."),
|
|
2515
|
+
breakdown: z10.array(z10.string()).min(1).describe(
|
|
1881
2516
|
'Break down the question into its semantic parts. Each part should describe an ASPECT of what is being asked, not instructions. Example for "top customers by revenue last month": ["customers who made purchases", "revenue from those purchases", "time period: last month", "ranking: top by total revenue"]'
|
|
1882
2517
|
),
|
|
1883
|
-
entities:
|
|
2518
|
+
entities: z10.array(z10.string()).optional().describe(
|
|
1884
2519
|
'Key entities mentioned (e.g., ["customers", "orders", "products"])'
|
|
1885
2520
|
),
|
|
1886
|
-
filters:
|
|
1887
|
-
aggregation:
|
|
2521
|
+
filters: z10.array(z10.string()).optional().describe('Filter criteria (e.g., ["last month", "status = active"])'),
|
|
2522
|
+
aggregation: z10.string().optional().describe(
|
|
1888
2523
|
'Aggregation type if any (e.g., "sum revenue", "count orders", "top 10")'
|
|
1889
2524
|
),
|
|
1890
|
-
ambiguities:
|
|
2525
|
+
ambiguities: z10.array(z10.string()).optional().describe("Note any ambiguous parts you identified")
|
|
1891
2526
|
}),
|
|
1892
2527
|
execute: async ({ question, breakdown, entities, filters, aggregation, ambiguities }, options) => {
|
|
1893
|
-
const state =
|
|
2528
|
+
const state = toState7(options);
|
|
1894
2529
|
const decomposition = {
|
|
1895
2530
|
originalQuestion: question,
|
|
1896
2531
|
breakdown,
|
|
@@ -1915,7 +2550,7 @@ This approach helps ensure all aspects of the question are addressed in the quer
|
|
|
1915
2550
|
const prompt = lastError ? `${decomposedPrompt}
|
|
1916
2551
|
|
|
1917
2552
|
Previous attempt failed with: ${lastError}. Please fix the query.` : decomposedPrompt;
|
|
1918
|
-
const { experimental_output: output } = await
|
|
2553
|
+
const { experimental_output: output } = await generate4(
|
|
1919
2554
|
agentInstance,
|
|
1920
2555
|
[user3(prompt)],
|
|
1921
2556
|
state
|
|
@@ -1962,10 +2597,10 @@ Previous attempt failed with: ${lastError}. Please fix the query.` : decomposedP
|
|
|
1962
2597
|
}
|
|
1963
2598
|
}
|
|
1964
2599
|
}),
|
|
1965
|
-
execute_sql:
|
|
2600
|
+
execute_sql: tool7({
|
|
1966
2601
|
description: `Execute a SQL query directly. Use for follow-up queries or when you already have SQL.`,
|
|
1967
|
-
inputSchema:
|
|
1968
|
-
sql:
|
|
2602
|
+
inputSchema: z10.object({
|
|
2603
|
+
sql: z10.string().min(1).refine(
|
|
1969
2604
|
(sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
|
|
1970
2605
|
{
|
|
1971
2606
|
message: "Only read-only SELECT or WITH queries are allowed."
|
|
@@ -1973,7 +2608,7 @@ Previous attempt failed with: ${lastError}. Please fix the query.` : decomposedP
|
|
|
1973
2608
|
).describe("The SQL query to execute.")
|
|
1974
2609
|
}),
|
|
1975
2610
|
execute: async ({ sql }, options) => {
|
|
1976
|
-
const state =
|
|
2611
|
+
const state = toState7(options);
|
|
1977
2612
|
try {
|
|
1978
2613
|
const validationError = await state.adapter.validate(sql);
|
|
1979
2614
|
if (validationError) {
|
|
@@ -1996,7 +2631,7 @@ Previous attempt failed with: ${lastError}. Please fix the query.` : decomposedP
|
|
|
1996
2631
|
}
|
|
1997
2632
|
}
|
|
1998
2633
|
}),
|
|
1999
|
-
scratchpad:
|
|
2634
|
+
scratchpad: scratchpad_tool7
|
|
2000
2635
|
};
|
|
2001
2636
|
function formatDecomposition(decomposition) {
|
|
2002
2637
|
const parts = [
|
|
@@ -2027,10 +2662,10 @@ function formatDecomposition(decomposition) {
|
|
|
2027
2662
|
);
|
|
2028
2663
|
return parts.join("\n");
|
|
2029
2664
|
}
|
|
2030
|
-
var chat4Agent =
|
|
2665
|
+
var chat4Agent = agent10({
|
|
2031
2666
|
name: "chat4-decomposition",
|
|
2032
|
-
model:
|
|
2033
|
-
tools:
|
|
2667
|
+
model: groq10("openai/gpt-oss-20b"),
|
|
2668
|
+
tools: tools7,
|
|
2034
2669
|
prompt: (state) => {
|
|
2035
2670
|
return `
|
|
2036
2671
|
${state?.teachings || ""}
|
|
@@ -2056,28 +2691,6 @@ Break the question into its semantic aspects, and let the SQL specialist figure
|
|
|
2056
2691
|
}
|
|
2057
2692
|
});
|
|
2058
2693
|
|
|
2059
|
-
// packages/text2sql/src/lib/agents/explainer.agent.ts
|
|
2060
|
-
import { groq as groq8 } from "@ai-sdk/groq";
|
|
2061
|
-
import dedent2 from "dedent";
|
|
2062
|
-
import z8 from "zod";
|
|
2063
|
-
import { agent as agent8 } from "@deepagents/agent";
|
|
2064
|
-
var explainerAgent = agent8({
|
|
2065
|
-
name: "explainer",
|
|
2066
|
-
model: groq8("openai/gpt-oss-20b"),
|
|
2067
|
-
prompt: (state) => dedent2`
|
|
2068
|
-
You are an expert SQL tutor.
|
|
2069
|
-
Explain the following SQL query in plain English to a non-technical user.
|
|
2070
|
-
Focus on the intent and logic, not the syntax.
|
|
2071
|
-
|
|
2072
|
-
<sql>
|
|
2073
|
-
${state?.sql}
|
|
2074
|
-
</sql>
|
|
2075
|
-
`,
|
|
2076
|
-
output: z8.object({
|
|
2077
|
-
explanation: z8.string().describe("The explanation of the SQL query.")
|
|
2078
|
-
})
|
|
2079
|
-
});
|
|
2080
|
-
|
|
2081
2694
|
// packages/text2sql/src/lib/synthesis/types.ts
|
|
2082
2695
|
async function toPairs(producer) {
|
|
2083
2696
|
const pairs = [];
|
|
@@ -2139,7 +2752,8 @@ function guidelines(options = {}) {
|
|
|
2139
2752
|
),
|
|
2140
2753
|
// Style and readability
|
|
2141
2754
|
styleGuide({
|
|
2142
|
-
prefer: "
|
|
2755
|
+
prefer: 'For table aliases, use the full table name (e.g., "FROM users AS users", "JOIN order_items AS order_items"). For column aliases, use descriptive names that reflect the data (e.g., "COUNT(*) AS total_orders").',
|
|
2756
|
+
never: "Use abbreviated table aliases (u, oi, ca) or generic positional aliases (t1, t2, a, b)."
|
|
2143
2757
|
}),
|
|
2144
2758
|
styleGuide({
|
|
2145
2759
|
prefer: "Summaries should be concise, business-friendly, highlight key comparisons, and add a short helpful follow-up when useful."
|
|
@@ -2151,7 +2765,7 @@ function guidelines(options = {}) {
|
|
|
2151
2765
|
action: "Validate syntax and schema references before returning."
|
|
2152
2766
|
}),
|
|
2153
2767
|
guardrail({
|
|
2154
|
-
rule: "Only generate SELECT statements (read-only queries).",
|
|
2768
|
+
rule: "Only generate SELECT/WITH statements (read-only queries).",
|
|
2155
2769
|
reason: "Prevents accidental data modification.",
|
|
2156
2770
|
action: "Never generate INSERT, UPDATE, DELETE, DROP, or other DDL/DML statements."
|
|
2157
2771
|
}),
|
|
@@ -2239,7 +2853,7 @@ var Text2Sql = class {
|
|
|
2239
2853
|
};
|
|
2240
2854
|
}
|
|
2241
2855
|
async explain(sql) {
|
|
2242
|
-
const { experimental_output } = await
|
|
2856
|
+
const { experimental_output } = await generate5(
|
|
2243
2857
|
explainerAgent,
|
|
2244
2858
|
[user4("Explain this SQL.")],
|
|
2245
2859
|
{ sql }
|
|
@@ -2260,7 +2874,7 @@ var Text2Sql = class {
|
|
|
2260
2874
|
instruct(...dataset) {
|
|
2261
2875
|
this.#config.instructions.push(...dataset);
|
|
2262
2876
|
}
|
|
2263
|
-
async inspect(
|
|
2877
|
+
async inspect(agent11) {
|
|
2264
2878
|
const [grounding] = await Promise.all([this.index()]);
|
|
2265
2879
|
const renderToolNames = Object.keys(this.#config.tools ?? {}).filter(
|
|
2266
2880
|
(name) => name.startsWith("render_")
|
|
@@ -2280,14 +2894,14 @@ var Text2Sql = class {
|
|
|
2280
2894
|
})
|
|
2281
2895
|
] : []
|
|
2282
2896
|
];
|
|
2283
|
-
const
|
|
2284
|
-
...
|
|
2897
|
+
const tools8 = Object.keys({
|
|
2898
|
+
...agent11.handoff.tools,
|
|
2285
2899
|
...this.#config.memory ? memoryTools : {},
|
|
2286
2900
|
...this.#config.tools
|
|
2287
2901
|
});
|
|
2288
2902
|
return {
|
|
2289
|
-
tools:
|
|
2290
|
-
prompt:
|
|
2903
|
+
tools: tools8,
|
|
2904
|
+
prompt: agent11.instructions({
|
|
2291
2905
|
introspection: grounding,
|
|
2292
2906
|
teachings: toInstructions("instructions", ...allInstructions)
|
|
2293
2907
|
})
|
|
@@ -2401,46 +3015,12 @@ var Text2Sql = class {
|
|
|
2401
3015
|
userId: params.userId
|
|
2402
3016
|
}
|
|
2403
3017
|
);
|
|
2404
|
-
return
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
} else if (ToolCallRepairError.isInstance(error)) {
|
|
2411
|
-
return "The model tried to call a tool with invalid arguments, but it was repaired.";
|
|
2412
|
-
} else {
|
|
2413
|
-
return "An unknown error occurred.";
|
|
2414
|
-
}
|
|
2415
|
-
},
|
|
2416
|
-
sendStart: true,
|
|
2417
|
-
sendFinish: true,
|
|
2418
|
-
sendReasoning: true,
|
|
2419
|
-
sendSources: true,
|
|
2420
|
-
originalMessages: originalMessage,
|
|
2421
|
-
generateMessageId: generateId,
|
|
2422
|
-
onFinish: async ({ responseMessage, isContinuation }) => {
|
|
2423
|
-
const userMessage = messages.at(-1);
|
|
2424
|
-
if (!isContinuation && userMessage) {
|
|
2425
|
-
console.log(
|
|
2426
|
-
"Saving user message to history:",
|
|
2427
|
-
JSON.stringify(userMessage)
|
|
2428
|
-
);
|
|
2429
|
-
await this.#config.history.addMessage({
|
|
2430
|
-
id: v72(),
|
|
2431
|
-
chatId: params.chatId,
|
|
2432
|
-
role: userMessage.role,
|
|
2433
|
-
content: userMessage
|
|
2434
|
-
});
|
|
2435
|
-
}
|
|
2436
|
-
await this.#config.history.addMessage({
|
|
2437
|
-
id: v72(),
|
|
2438
|
-
chatId: params.chatId,
|
|
2439
|
-
role: responseMessage.role,
|
|
2440
|
-
content: responseMessage
|
|
2441
|
-
});
|
|
2442
|
-
}
|
|
2443
|
-
});
|
|
3018
|
+
return this.#createUIMessageStream(
|
|
3019
|
+
result,
|
|
3020
|
+
messages,
|
|
3021
|
+
params,
|
|
3022
|
+
originalMessage
|
|
3023
|
+
);
|
|
2444
3024
|
}
|
|
2445
3025
|
/**
|
|
2446
3026
|
* Chat1 - Combined tool, no peek.
|
|
@@ -2484,7 +3064,7 @@ var Text2Sql = class {
|
|
|
2484
3064
|
chat1Agent.clone({
|
|
2485
3065
|
model: this.#config.model,
|
|
2486
3066
|
tools: {
|
|
2487
|
-
...
|
|
3067
|
+
...tools4,
|
|
2488
3068
|
...this.#config.memory ? memoryTools : {},
|
|
2489
3069
|
...this.#config.tools
|
|
2490
3070
|
}
|
|
@@ -2493,11 +3073,6 @@ var Text2Sql = class {
|
|
|
2493
3073
|
{
|
|
2494
3074
|
teachings: toInstructions(
|
|
2495
3075
|
"instructions",
|
|
2496
|
-
persona({
|
|
2497
|
-
name: "Freya",
|
|
2498
|
-
role: "You are an expert SQL query generator, answering business questions with accurate queries.",
|
|
2499
|
-
tone: "Your tone should be concise and business-friendly."
|
|
2500
|
-
}),
|
|
2501
3076
|
...instructions,
|
|
2502
3077
|
teachable("user_profile", ...userTeachables)
|
|
2503
3078
|
),
|
|
@@ -2555,7 +3130,7 @@ var Text2Sql = class {
|
|
|
2555
3130
|
chat2Agent.clone({
|
|
2556
3131
|
model: this.#config.model,
|
|
2557
3132
|
tools: {
|
|
2558
|
-
...
|
|
3133
|
+
...tools5,
|
|
2559
3134
|
...this.#config.memory ? memoryTools : {},
|
|
2560
3135
|
...this.#config.tools
|
|
2561
3136
|
}
|
|
@@ -2564,11 +3139,6 @@ var Text2Sql = class {
|
|
|
2564
3139
|
{
|
|
2565
3140
|
teachings: toInstructions(
|
|
2566
3141
|
"instructions",
|
|
2567
|
-
persona({
|
|
2568
|
-
name: "Freya",
|
|
2569
|
-
role: "You are an expert SQL query generator, answering business questions with accurate queries.",
|
|
2570
|
-
tone: "Your tone should be concise and business-friendly."
|
|
2571
|
-
}),
|
|
2572
3142
|
...instructions,
|
|
2573
3143
|
teachable("user_profile", ...userTeachables)
|
|
2574
3144
|
),
|
|
@@ -2625,7 +3195,7 @@ var Text2Sql = class {
|
|
|
2625
3195
|
chat3Agent.clone({
|
|
2626
3196
|
model: this.#config.model,
|
|
2627
3197
|
tools: {
|
|
2628
|
-
...
|
|
3198
|
+
...tools6,
|
|
2629
3199
|
...this.#config.memory ? memoryTools : {},
|
|
2630
3200
|
...this.#config.tools
|
|
2631
3201
|
}
|
|
@@ -2634,11 +3204,6 @@ var Text2Sql = class {
|
|
|
2634
3204
|
{
|
|
2635
3205
|
teachings: toInstructions(
|
|
2636
3206
|
"instructions",
|
|
2637
|
-
persona({
|
|
2638
|
-
name: "Freya",
|
|
2639
|
-
role: "You are an expert SQL query generator, answering business questions with accurate queries.",
|
|
2640
|
-
tone: "Your tone should be concise and business-friendly."
|
|
2641
|
-
}),
|
|
2642
3207
|
...instructions,
|
|
2643
3208
|
teachable("user_profile", ...userTeachables)
|
|
2644
3209
|
),
|
|
@@ -2698,7 +3263,7 @@ var Text2Sql = class {
|
|
|
2698
3263
|
chat4Agent.clone({
|
|
2699
3264
|
model: this.#config.model,
|
|
2700
3265
|
tools: {
|
|
2701
|
-
...
|
|
3266
|
+
...tools7,
|
|
2702
3267
|
...this.#config.memory ? memoryTools : {},
|
|
2703
3268
|
...this.#config.tools
|
|
2704
3269
|
}
|
|
@@ -2707,11 +3272,6 @@ var Text2Sql = class {
|
|
|
2707
3272
|
{
|
|
2708
3273
|
teachings: toInstructions(
|
|
2709
3274
|
"instructions",
|
|
2710
|
-
persona({
|
|
2711
|
-
name: "Freya",
|
|
2712
|
-
role: "You are an expert SQL query generator, answering business questions with accurate queries.",
|
|
2713
|
-
tone: "Your tone should be concise and business-friendly."
|
|
2714
|
-
}),
|
|
2715
3275
|
...instructions,
|
|
2716
3276
|
teachable("user_profile", ...userTeachables)
|
|
2717
3277
|
),
|
|
@@ -2729,10 +3289,117 @@ var Text2Sql = class {
|
|
|
2729
3289
|
originalMessage
|
|
2730
3290
|
);
|
|
2731
3291
|
}
|
|
3292
|
+
/**
|
|
3293
|
+
* Business intelligence focused chat agent.
|
|
3294
|
+
*
|
|
3295
|
+
* Creates dashboards using MDX components with embedded SQL queries.
|
|
3296
|
+
* The agent explores data, validates SQL, and outputs markdown with
|
|
3297
|
+
* JSX chart components that the frontend renders via MDX.
|
|
3298
|
+
*
|
|
3299
|
+
* @example
|
|
3300
|
+
* ```typescript
|
|
3301
|
+
* const result = await text2sql.bi(
|
|
3302
|
+
* [user("Show me a sales dashboard for last 30 days")],
|
|
3303
|
+
* { chatId: 'dashboard-1', userId: 'user-1' }
|
|
3304
|
+
* );
|
|
3305
|
+
* // Result contains markdown with <BarChart sql="..." />, <KPI sql="..." />, etc.
|
|
3306
|
+
* ```
|
|
3307
|
+
*/
|
|
3308
|
+
async bi(messages, params) {
|
|
3309
|
+
const [introspection, userTeachables] = await Promise.all([
|
|
3310
|
+
this.index({ onProgress: console.log }),
|
|
3311
|
+
this.#config.memory ? this.#config.memory.toTeachables(params.userId) : []
|
|
3312
|
+
]);
|
|
3313
|
+
const chat = await this.#config.history.upsertChat({
|
|
3314
|
+
id: params.chatId,
|
|
3315
|
+
userId: params.userId,
|
|
3316
|
+
title: "Chat " + params.chatId
|
|
3317
|
+
});
|
|
3318
|
+
const originalMessages = [
|
|
3319
|
+
...chat.messages.map((it) => it.content),
|
|
3320
|
+
...messages
|
|
3321
|
+
];
|
|
3322
|
+
const result = stream(
|
|
3323
|
+
biAgent.clone({
|
|
3324
|
+
model: this.#config.model
|
|
3325
|
+
}),
|
|
3326
|
+
originalMessages,
|
|
3327
|
+
{
|
|
3328
|
+
teachings: toInstructions(
|
|
3329
|
+
"instructions",
|
|
3330
|
+
...this.#config.instructions,
|
|
3331
|
+
teachable("user_profile", ...userTeachables)
|
|
3332
|
+
),
|
|
3333
|
+
adapter: this.#config.adapter,
|
|
3334
|
+
introspection
|
|
3335
|
+
}
|
|
3336
|
+
);
|
|
3337
|
+
return this.#createUIMessageStream(
|
|
3338
|
+
result,
|
|
3339
|
+
messages,
|
|
3340
|
+
params,
|
|
3341
|
+
originalMessages
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
/**
|
|
3345
|
+
* Developer-focused conversational interface for SQL generation.
|
|
3346
|
+
*
|
|
3347
|
+
* Provides power-user tools for query building without execution:
|
|
3348
|
+
* - generate_sql: Convert natural language to validated SQL
|
|
3349
|
+
* - validate_sql: Check SQL syntax
|
|
3350
|
+
* - explain_sql: Get plain-English explanations
|
|
3351
|
+
* - show_schema: Explore database schema on demand
|
|
3352
|
+
*
|
|
3353
|
+
* @example
|
|
3354
|
+
* ```typescript
|
|
3355
|
+
* const result = await text2sql.developer(
|
|
3356
|
+
* [user("Generate a query to find top customers by revenue")],
|
|
3357
|
+
* { chatId: 'dev-session-1', userId: 'dev-1' }
|
|
3358
|
+
* );
|
|
3359
|
+
* // Agent responds with SQL, can validate, explain, or refine iteratively
|
|
3360
|
+
* ```
|
|
3361
|
+
*/
|
|
3362
|
+
async developer(messages, params) {
|
|
3363
|
+
const [introspection, userTeachables] = await Promise.all([
|
|
3364
|
+
this.index({ onProgress: console.log }),
|
|
3365
|
+
this.#config.memory ? this.#config.memory.toTeachables(params.userId) : []
|
|
3366
|
+
]);
|
|
3367
|
+
const chat = await this.#config.history.upsertChat({
|
|
3368
|
+
id: params.chatId,
|
|
3369
|
+
userId: params.userId,
|
|
3370
|
+
title: "Chat " + params.chatId
|
|
3371
|
+
});
|
|
3372
|
+
const originalMessages = [
|
|
3373
|
+
...chat.messages.map((it) => it.content),
|
|
3374
|
+
...messages
|
|
3375
|
+
];
|
|
3376
|
+
const result = stream(
|
|
3377
|
+
developerAgent.clone({
|
|
3378
|
+
model: this.#config.model
|
|
3379
|
+
}),
|
|
3380
|
+
originalMessages,
|
|
3381
|
+
{
|
|
3382
|
+
teachings: toInstructions(
|
|
3383
|
+
"instructions",
|
|
3384
|
+
...this.#config.instructions,
|
|
3385
|
+
teachable("user_profile", ...userTeachables)
|
|
3386
|
+
),
|
|
3387
|
+
adapter: this.#config.adapter,
|
|
3388
|
+
introspection,
|
|
3389
|
+
instructions: this.#config.instructions
|
|
3390
|
+
}
|
|
3391
|
+
);
|
|
3392
|
+
return this.#createUIMessageStream(
|
|
3393
|
+
result,
|
|
3394
|
+
messages,
|
|
3395
|
+
params,
|
|
3396
|
+
originalMessages
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
2732
3399
|
/**
|
|
2733
3400
|
* Helper to create UI message stream with common error handling and persistence.
|
|
2734
3401
|
*/
|
|
2735
|
-
#createUIMessageStream(result, messages, params,
|
|
3402
|
+
#createUIMessageStream(result, messages, params, originalMessages) {
|
|
2736
3403
|
return result.toUIMessageStream({
|
|
2737
3404
|
onError: (error) => {
|
|
2738
3405
|
if (NoSuchToolError.isInstance(error)) {
|
|
@@ -2749,7 +3416,7 @@ var Text2Sql = class {
|
|
|
2749
3416
|
sendFinish: true,
|
|
2750
3417
|
sendReasoning: true,
|
|
2751
3418
|
sendSources: true,
|
|
2752
|
-
originalMessages
|
|
3419
|
+
originalMessages,
|
|
2753
3420
|
generateMessageId: generateId,
|
|
2754
3421
|
onFinish: async ({ responseMessage, isContinuation }) => {
|
|
2755
3422
|
const userMessage = messages.at(-1);
|
|
@@ -2789,6 +3456,7 @@ export {
|
|
|
2789
3456
|
TeachablesStore,
|
|
2790
3457
|
Text2Sql,
|
|
2791
3458
|
applyTablesFilter,
|
|
3459
|
+
developerAgent,
|
|
2792
3460
|
filterRelationshipsByTables,
|
|
2793
3461
|
filterTablesByName,
|
|
2794
3462
|
getTablesWithRelated,
|