@deepagents/text2sql 0.3.1 → 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.
Files changed (167) hide show
  1. package/README.md +167 -0
  2. package/dist/finetune/convert-to-gguf.d.ts +18 -0
  3. package/dist/finetune/convert-to-gguf.d.ts.map +1 -0
  4. package/dist/finetune/run-finetune.d.ts +23 -0
  5. package/dist/finetune/run-finetune.d.ts.map +1 -0
  6. package/dist/finetune/run-mlx.d.ts +22 -0
  7. package/dist/finetune/run-mlx.d.ts.map +1 -0
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2500 -318
  11. package/dist/index.js.map +4 -4
  12. package/dist/lib/adapters/adapter.d.ts +3 -3
  13. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  14. package/dist/lib/adapters/{grounding.ticket.d.ts → groundings/abstract.grounding.d.ts} +2 -2
  15. package/dist/lib/adapters/groundings/abstract.grounding.d.ts.map +1 -0
  16. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts +1 -1
  17. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts.map +1 -1
  18. package/dist/lib/adapters/groundings/column-values.grounding.d.ts +76 -0
  19. package/dist/lib/adapters/groundings/column-values.grounding.d.ts.map +1 -0
  20. package/dist/lib/adapters/groundings/constraint.grounding.d.ts +1 -1
  21. package/dist/lib/adapters/groundings/constraint.grounding.d.ts.map +1 -1
  22. package/dist/lib/adapters/groundings/context.d.ts +1 -1
  23. package/dist/lib/adapters/groundings/context.d.ts.map +1 -1
  24. package/dist/lib/adapters/groundings/{grounding.d.ts → index.d.ts} +8 -5
  25. package/dist/lib/adapters/groundings/index.d.ts.map +1 -0
  26. package/dist/lib/adapters/groundings/{grounding.js → index.js} +411 -206
  27. package/dist/lib/adapters/groundings/index.js.map +7 -0
  28. package/dist/lib/adapters/groundings/indexes.grounding.d.ts +1 -1
  29. package/dist/lib/adapters/groundings/indexes.grounding.d.ts.map +1 -1
  30. package/dist/lib/adapters/groundings/info.grounding.d.ts +1 -1
  31. package/dist/lib/adapters/groundings/info.grounding.d.ts.map +1 -1
  32. package/dist/lib/adapters/groundings/report.grounding.d.ts +1 -1
  33. package/dist/lib/adapters/groundings/report.grounding.d.ts.map +1 -1
  34. package/dist/lib/adapters/groundings/row-count.grounding.d.ts +1 -1
  35. package/dist/lib/adapters/groundings/row-count.grounding.d.ts.map +1 -1
  36. package/dist/lib/adapters/groundings/table.grounding.d.ts +1 -1
  37. package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
  38. package/dist/lib/adapters/groundings/view.grounding.d.ts +1 -1
  39. package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
  40. package/dist/lib/adapters/mysql/column-stats.mysql.grounding.d.ts +14 -0
  41. package/dist/lib/adapters/mysql/column-stats.mysql.grounding.d.ts.map +1 -0
  42. package/dist/lib/adapters/mysql/column-values.mysql.grounding.d.ts +22 -0
  43. package/dist/lib/adapters/mysql/column-values.mysql.grounding.d.ts.map +1 -0
  44. package/dist/lib/adapters/mysql/constraint.mysql.grounding.d.ts +13 -0
  45. package/dist/lib/adapters/mysql/constraint.mysql.grounding.d.ts.map +1 -0
  46. package/dist/lib/adapters/mysql/index.d.ts +44 -0
  47. package/dist/lib/adapters/mysql/index.d.ts.map +1 -0
  48. package/dist/lib/adapters/mysql/indexes.mysql.grounding.d.ts +13 -0
  49. package/dist/lib/adapters/mysql/indexes.mysql.grounding.d.ts.map +1 -0
  50. package/dist/lib/adapters/mysql/info.mysql.grounding.d.ts +13 -0
  51. package/dist/lib/adapters/mysql/info.mysql.grounding.d.ts.map +1 -0
  52. package/dist/lib/adapters/mysql/mysql.d.ts +33 -0
  53. package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -0
  54. package/dist/lib/adapters/mysql/row-count.mysql.grounding.d.ts +13 -0
  55. package/dist/lib/adapters/mysql/row-count.mysql.grounding.d.ts.map +1 -0
  56. package/dist/lib/adapters/mysql/table.mysql.grounding.d.ts +21 -0
  57. package/dist/lib/adapters/mysql/table.mysql.grounding.d.ts.map +1 -0
  58. package/dist/lib/adapters/mysql/view.mysql.grounding.d.ts +18 -0
  59. package/dist/lib/adapters/mysql/view.mysql.grounding.d.ts.map +1 -0
  60. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts.map +1 -1
  61. package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts +17 -0
  62. package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts.map +1 -0
  63. package/dist/lib/adapters/postgres/index.d.ts +4 -4
  64. package/dist/lib/adapters/postgres/index.d.ts.map +1 -1
  65. package/dist/lib/adapters/postgres/index.js +233 -33
  66. package/dist/lib/adapters/postgres/index.js.map +4 -4
  67. package/dist/lib/adapters/sqlite/column-values.sqlite.grounding.d.ts +17 -0
  68. package/dist/lib/adapters/sqlite/column-values.sqlite.grounding.d.ts.map +1 -0
  69. package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts.map +1 -1
  70. package/dist/lib/adapters/sqlite/index.d.ts +4 -4
  71. package/dist/lib/adapters/sqlite/index.d.ts.map +1 -1
  72. package/dist/lib/adapters/sqlite/index.js +214 -46
  73. package/dist/lib/adapters/sqlite/index.js.map +4 -4
  74. package/dist/lib/adapters/sqlserver/column-values.sqlserver.grounding.d.ts +17 -0
  75. package/dist/lib/adapters/sqlserver/column-values.sqlserver.grounding.d.ts.map +1 -0
  76. package/dist/lib/adapters/sqlserver/index.d.ts +4 -4
  77. package/dist/lib/adapters/sqlserver/index.d.ts.map +1 -1
  78. package/dist/lib/adapters/sqlserver/index.js +179 -32
  79. package/dist/lib/adapters/sqlserver/index.js.map +4 -4
  80. package/dist/lib/agents/bi.agent.d.ts +14 -0
  81. package/dist/lib/agents/bi.agent.d.ts.map +1 -0
  82. package/dist/lib/agents/chat1.agent.d.ts +50 -0
  83. package/dist/lib/agents/chat1.agent.d.ts.map +1 -0
  84. package/dist/lib/agents/chat2.agent.d.ts +68 -0
  85. package/dist/lib/agents/chat2.agent.d.ts.map +1 -0
  86. package/dist/lib/agents/chat3.agent.d.ts +80 -0
  87. package/dist/lib/agents/chat3.agent.d.ts.map +1 -0
  88. package/dist/lib/agents/chat4.agent.d.ts +88 -0
  89. package/dist/lib/agents/chat4.agent.d.ts.map +1 -0
  90. package/dist/lib/agents/developer.agent.d.ts +31 -0
  91. package/dist/lib/agents/developer.agent.d.ts.map +1 -0
  92. package/dist/lib/agents/question.agent.d.ts +23 -0
  93. package/dist/lib/agents/question.agent.d.ts.map +1 -0
  94. package/dist/lib/agents/sql.agent.d.ts +44 -0
  95. package/dist/lib/agents/sql.agent.d.ts.map +1 -0
  96. package/dist/lib/agents/teachables.agent.d.ts +8 -9
  97. package/dist/lib/agents/teachables.agent.d.ts.map +1 -1
  98. package/dist/lib/agents/text2sql.agent.d.ts +0 -1
  99. package/dist/lib/agents/text2sql.agent.d.ts.map +1 -1
  100. package/dist/lib/checkpoint.d.ts +99 -0
  101. package/dist/lib/checkpoint.d.ts.map +1 -0
  102. package/dist/lib/instructions.js +50 -21
  103. package/dist/lib/instructions.js.map +2 -2
  104. package/dist/lib/sql.d.ts +125 -3
  105. package/dist/lib/sql.d.ts.map +1 -1
  106. package/dist/lib/syntheize.d.ts +2 -0
  107. package/dist/lib/syntheize.d.ts.map +1 -0
  108. package/dist/lib/synthesis/decorators/deduplicated-producer.d.ts +26 -0
  109. package/dist/lib/synthesis/decorators/deduplicated-producer.d.ts.map +1 -0
  110. package/dist/lib/synthesis/decorators/filtered-producer.d.ts +26 -0
  111. package/dist/lib/synthesis/decorators/filtered-producer.d.ts.map +1 -0
  112. package/dist/lib/synthesis/decorators/index.d.ts +7 -0
  113. package/dist/lib/synthesis/decorators/index.d.ts.map +1 -0
  114. package/dist/lib/synthesis/decorators/validated-producer.d.ts +33 -0
  115. package/dist/lib/synthesis/decorators/validated-producer.d.ts.map +1 -0
  116. package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts +76 -0
  117. package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts.map +1 -0
  118. package/dist/lib/synthesis/extractors/full-context-extractor.d.ts +25 -0
  119. package/dist/lib/synthesis/extractors/full-context-extractor.d.ts.map +1 -0
  120. package/dist/lib/synthesis/extractors/index.d.ts +8 -0
  121. package/dist/lib/synthesis/extractors/index.d.ts.map +1 -0
  122. package/dist/lib/synthesis/extractors/last-query-extractor.d.ts +30 -0
  123. package/dist/lib/synthesis/extractors/last-query-extractor.d.ts.map +1 -0
  124. package/dist/lib/synthesis/extractors/message-extractor.d.ts +27 -0
  125. package/dist/lib/synthesis/extractors/message-extractor.d.ts.map +1 -0
  126. package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts +48 -0
  127. package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts.map +1 -0
  128. package/dist/lib/synthesis/extractors/sql-extractor.d.ts +27 -0
  129. package/dist/lib/synthesis/extractors/sql-extractor.d.ts.map +1 -0
  130. package/dist/lib/synthesis/extractors/windowed-context-extractor.d.ts +30 -0
  131. package/dist/lib/synthesis/extractors/windowed-context-extractor.d.ts.map +1 -0
  132. package/dist/lib/synthesis/index.d.ts +6 -0
  133. package/dist/lib/synthesis/index.d.ts.map +1 -0
  134. package/dist/lib/synthesis/index.js +2172 -0
  135. package/dist/lib/synthesis/index.js.map +7 -0
  136. package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts +34 -0
  137. package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts.map +1 -0
  138. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts +41 -0
  139. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -0
  140. package/dist/lib/synthesis/synthesizers/index.d.ts +7 -0
  141. package/dist/lib/synthesis/synthesizers/index.d.ts.map +1 -0
  142. package/dist/lib/synthesis/synthesizers/persona-generator.d.ts +34 -0
  143. package/dist/lib/synthesis/synthesizers/persona-generator.d.ts.map +1 -0
  144. package/dist/lib/synthesis/synthesizers/schema-synthesizer.d.ts +39 -0
  145. package/dist/lib/synthesis/synthesizers/schema-synthesizer.d.ts.map +1 -0
  146. package/dist/lib/synthesis/synthesizers/styles.d.ts +8 -0
  147. package/dist/lib/synthesis/synthesizers/styles.d.ts.map +1 -0
  148. package/dist/lib/synthesis/synthesizers/teachings-generator.d.ts +32 -0
  149. package/dist/lib/synthesis/synthesizers/teachings-generator.d.ts.map +1 -0
  150. package/dist/lib/synthesis/types.d.ts +26 -0
  151. package/dist/lib/synthesis/types.d.ts.map +1 -0
  152. package/dist/lib/teach/teachables.d.ts +18 -3
  153. package/dist/lib/teach/teachables.d.ts.map +1 -1
  154. package/dist/lib/teach/teachings.d.ts +9 -2
  155. package/dist/lib/teach/teachings.d.ts.map +1 -1
  156. package/package.json +38 -15
  157. package/dist/lib/adapters/grounding.ticket.d.ts.map +0 -1
  158. package/dist/lib/adapters/groundings/grounding.d.ts.map +0 -1
  159. package/dist/lib/adapters/groundings/grounding.js.map +0 -7
  160. package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts +0 -35
  161. package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts.map +0 -1
  162. package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts +0 -14
  163. package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts.map +0 -1
  164. package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts +0 -14
  165. package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts.map +0 -1
  166. package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts +0 -14
  167. package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,64 +1,206 @@
1
- // packages/text2sql/src/lib/agents/suggestions.agents.ts
1
+ // packages/text2sql/src/lib/adapters/groundings/context.ts
2
+ function createGroundingContext() {
3
+ return {
4
+ tables: [],
5
+ views: [],
6
+ relationships: [],
7
+ info: void 0
8
+ };
9
+ }
10
+
11
+ // packages/text2sql/src/lib/adapters/adapter.ts
12
+ var Adapter = class {
13
+ async introspect(ctx = createGroundingContext()) {
14
+ const lines = [];
15
+ for (const fn of this.grounding) {
16
+ const grounding = fn(this);
17
+ lines.push({
18
+ tag: grounding.tag,
19
+ fn: await grounding.execute(ctx)
20
+ });
21
+ }
22
+ return lines.map(({ fn, tag }) => {
23
+ const description = fn();
24
+ if (description === null) {
25
+ return "";
26
+ }
27
+ return `<${tag}>
28
+ ${description}
29
+ </${tag}>`;
30
+ }).join("\n");
31
+ }
32
+ /**
33
+ * Convert unknown database value to number.
34
+ * Handles number, bigint, and string types.
35
+ */
36
+ toNumber(value) {
37
+ if (typeof value === "number" && Number.isFinite(value)) {
38
+ return value;
39
+ }
40
+ if (typeof value === "bigint") {
41
+ return Number(value);
42
+ }
43
+ if (typeof value === "string" && value.trim() !== "") {
44
+ const parsed = Number(value);
45
+ return Number.isFinite(parsed) ? parsed : void 0;
46
+ }
47
+ return void 0;
48
+ }
49
+ /**
50
+ * Parse a potentially qualified table name into schema and table parts.
51
+ */
52
+ parseTableName(name) {
53
+ if (name.includes(".")) {
54
+ const [schema, ...rest] = name.split(".");
55
+ return { schema, table: rest.join(".") };
56
+ }
57
+ return { schema: this.defaultSchema ?? "", table: name };
58
+ }
59
+ /**
60
+ * Escape a string value for use in SQL string literals (single quotes).
61
+ * Used in WHERE clauses like: WHERE name = '${escapeString(value)}'
62
+ */
63
+ escapeString(value) {
64
+ return value.replace(/'/g, "''");
65
+ }
66
+ /**
67
+ * Build a SQL filter clause to include/exclude schemas.
68
+ * @param columnName - The schema column name (e.g., 'TABLE_SCHEMA')
69
+ * @param allowedSchemas - If provided, filter to these schemas only
70
+ */
71
+ buildSchemaFilter(columnName, allowedSchemas) {
72
+ if (allowedSchemas && allowedSchemas.length > 0) {
73
+ const values = allowedSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
74
+ return `AND ${columnName} IN (${values})`;
75
+ }
76
+ if (this.systemSchemas.length > 0) {
77
+ const values = this.systemSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
78
+ return `AND ${columnName} NOT IN (${values})`;
79
+ }
80
+ return "";
81
+ }
82
+ };
83
+ function filterTablesByName(tables, filter) {
84
+ if (!filter) return tables;
85
+ return tables.filter((table) => matchesFilter(table.name, filter));
86
+ }
87
+ function filterRelationshipsByTables(relationships, tableNames) {
88
+ if (tableNames === void 0) {
89
+ return relationships;
90
+ }
91
+ if (tableNames.size === 0) {
92
+ return [];
93
+ }
94
+ return relationships.filter(
95
+ (it) => tableNames.has(it.table) || tableNames.has(it.referenced_table)
96
+ );
97
+ }
98
+ function applyTablesFilter(tables, relationships, filter) {
99
+ if (!filter) {
100
+ return { tables, relationships };
101
+ }
102
+ const allowedNames = new Set(
103
+ getTablesWithRelated(tables, relationships, filter)
104
+ );
105
+ return {
106
+ tables: tables.filter((table) => allowedNames.has(table.name)),
107
+ relationships: filterRelationshipsByTables(relationships, allowedNames)
108
+ };
109
+ }
110
+ function matchesFilter(tableName, filter) {
111
+ if (Array.isArray(filter)) {
112
+ return filter.includes(tableName);
113
+ }
114
+ return filter.test(tableName);
115
+ }
116
+ function getTablesWithRelated(allTables, relationships, filter) {
117
+ const matchedTables = filterTablesByName(allTables, filter).map(
118
+ (it) => it.name
119
+ );
120
+ if (matchedTables.length === 0) {
121
+ return [];
122
+ }
123
+ const adjacency = /* @__PURE__ */ new Map();
124
+ for (const rel of relationships) {
125
+ if (!adjacency.has(rel.table)) {
126
+ adjacency.set(rel.table, /* @__PURE__ */ new Set());
127
+ }
128
+ if (!adjacency.has(rel.referenced_table)) {
129
+ adjacency.set(rel.referenced_table, /* @__PURE__ */ new Set());
130
+ }
131
+ adjacency.get(rel.table).add(rel.referenced_table);
132
+ adjacency.get(rel.referenced_table).add(rel.table);
133
+ }
134
+ const result = new Set(matchedTables);
135
+ const queue = [...matchedTables];
136
+ while (queue.length > 0) {
137
+ const current = queue.shift();
138
+ const neighbors = adjacency.get(current);
139
+ if (!neighbors) {
140
+ continue;
141
+ }
142
+ for (const neighbor of neighbors) {
143
+ if (!result.has(neighbor)) {
144
+ result.add(neighbor);
145
+ queue.push(neighbor);
146
+ }
147
+ }
148
+ }
149
+ return Array.from(result);
150
+ }
151
+
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
2
161
  import { groq } from "@ai-sdk/groq";
3
162
  import dedent from "dedent";
4
163
  import z from "zod";
5
- import { agent, thirdPersonPrompt } from "@deepagents/agent";
6
- var suggestionsAgent = agent({
7
- name: "text2sql-suggestions",
164
+ import { agent } from "@deepagents/agent";
165
+ var explainerAgent = agent({
166
+ name: "explainer",
8
167
  model: groq("openai/gpt-oss-20b"),
9
- output: z.object({
10
- suggestions: z.array(
11
- z.object({
12
- question: z.string().describe("A complex, high-impact business question."),
13
- sql: z.string().describe("The SQL statement needed to answer the question."),
14
- businessValue: z.string().describe("Why the question matters to stakeholders.")
15
- })
16
- ).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
17
- }),
18
- prompt: (state) => {
19
- return dedent`
20
- ${thirdPersonPrompt()}
21
-
22
- <identity>
23
- You are a senior analytics strategist who proposes ambitious business questions
24
- and drafts the SQL needed to answer them. You specialize in identifying ideas
25
- that combine multiple tables, apply segmentation or time analysis, and surface
26
- metrics that drive executive decisions.
27
- </identity>
28
-
29
-
30
- <instructions>
31
- - Recommend one or two UNIQUE questions that go beyond simple counts or listings.
32
- - Favor questions that require joins, aggregates, time comparisons, cohort analysis,
33
- or window functions.
34
- - For each question, explain the business reason stakeholders care about it.
35
- - Provide the complete SQL query that could answer the question in the given schema.
36
- - Keep result sets scoped with LIMIT clauses (max 50 rows) when returning raw rows.
37
- - Ensure table/column names match the provided schema exactly.
38
- - Use columns marked [LowCardinality: ...] to identify meaningful categorical filters or segmentations.
39
- - Leverage table [rows / size] hints to determine whether to aggregate (large tables) or inspect detailed data (tiny tables).
40
- - Reference PK/Indexed annotations and the Indexes list to recommend queries that use efficient join/filter paths.
41
- - Column annotations may expose ranges/null percentages—use them to suggest realistic thresholds or quality checks.
42
- - Consult <relationship_examples> to anchor your recommendations in the actual join paths between tables.
43
- - Output only information grounded in the schema/context provided.
44
- </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.
45
172
 
46
- <response-format>
47
- Return valid JSON that satisfies the defined output schema.
48
- </response-format>
49
- `;
50
- }
173
+ <sql>
174
+ ${state?.sql}
175
+ </sql>
176
+ `,
177
+ output: z.object({
178
+ explanation: z.string().describe("The explanation of the SQL query.")
179
+ })
51
180
  });
52
181
 
53
- // packages/text2sql/src/lib/agents/text2sql.agent.ts
182
+ // packages/text2sql/src/lib/agents/sql.agent.ts
54
183
  import { groq as groq2 } from "@ai-sdk/groq";
55
- import { tool } from "ai";
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";
56
197
  import z2 from "zod";
57
198
  import {
58
199
  agent as agent2,
59
- toState
200
+ generate,
201
+ toOutput,
202
+ user
60
203
  } from "@deepagents/agent";
61
- import { scratchpad_tool } from "@deepagents/toolbox";
62
204
 
63
205
  // packages/text2sql/src/lib/teach/xml.ts
64
206
  function wrapBlock(tag, children) {
@@ -106,20 +248,23 @@ function escapeXml(value) {
106
248
  function term(name, definition) {
107
249
  return {
108
250
  type: "term",
109
- format: () => wrapBlock("term", [leaf("name", name), leaf("definition", definition)])
251
+ encode: () => ({ type: "term", name, definition }),
252
+ decode: () => wrapBlock("term", [leaf("name", name), leaf("definition", definition)])
110
253
  };
111
254
  }
112
255
  function hint(text) {
113
256
  return {
114
257
  type: "hint",
115
- format: () => leaf("hint", text)
258
+ encode: () => ({ type: "hint", text }),
259
+ decode: () => leaf("hint", text)
116
260
  };
117
261
  }
118
262
  function guardrail(input) {
119
263
  const { rule, reason, action } = input;
120
264
  return {
121
265
  type: "guardrail",
122
- format: () => wrapBlock("guardrail", [
266
+ encode: () => ({ type: "guardrail", rule, reason, action }),
267
+ decode: () => wrapBlock("guardrail", [
123
268
  leaf("rule", rule),
124
269
  reason ? leaf("reason", reason) : "",
125
270
  action ? leaf("action", action) : ""
@@ -130,7 +275,8 @@ function explain(input) {
130
275
  const { concept, explanation, therefore } = input;
131
276
  return {
132
277
  type: "explain",
133
- format: () => wrapBlock("explanation", [
278
+ encode: () => ({ type: "explain", concept, explanation, therefore }),
279
+ decode: () => wrapBlock("explanation", [
134
280
  leaf("concept", concept),
135
281
  leaf("details", explanation),
136
282
  therefore ? leaf("therefore", therefore) : ""
@@ -141,7 +287,8 @@ function example(input) {
141
287
  const { question, answer, note } = input;
142
288
  return {
143
289
  type: "example",
144
- format: () => wrapBlock("example", [
290
+ encode: () => ({ type: "example", question, answer, note }),
291
+ decode: () => wrapBlock("example", [
145
292
  leaf("question", question),
146
293
  leaf("answer", answer),
147
294
  note ? leaf("note", note) : ""
@@ -152,7 +299,8 @@ function clarification(input) {
152
299
  const { when, ask, reason } = input;
153
300
  return {
154
301
  type: "clarification",
155
- format: () => wrapBlock("clarification", [
302
+ encode: () => ({ type: "clarification", when, ask, reason }),
303
+ decode: () => wrapBlock("clarification", [
156
304
  leaf("when", when),
157
305
  leaf("ask", ask),
158
306
  leaf("reason", reason)
@@ -163,7 +311,8 @@ function workflow(input) {
163
311
  const { task, steps, triggers, notes } = input;
164
312
  return {
165
313
  type: "workflow",
166
- format: () => wrapBlock("workflow", [
314
+ encode: () => ({ type: "workflow", task, steps, triggers, notes }),
315
+ decode: () => wrapBlock("workflow", [
167
316
  leaf("task", task),
168
317
  triggers?.length ? list("triggers", triggers, "trigger") : "",
169
318
  list("steps", steps, "step"),
@@ -175,7 +324,8 @@ function quirk(input) {
175
324
  const { issue, workaround } = input;
176
325
  return {
177
326
  type: "quirk",
178
- format: () => wrapBlock("quirk", [
327
+ encode: () => ({ type: "quirk", issue, workaround }),
328
+ decode: () => wrapBlock("quirk", [
179
329
  leaf("issue", issue),
180
330
  leaf("workaround", workaround)
181
331
  ])
@@ -185,7 +335,8 @@ function styleGuide(input) {
185
335
  const { prefer, never, always } = input;
186
336
  return {
187
337
  type: "styleGuide",
188
- format: () => wrapBlock("style_guide", [
338
+ encode: () => ({ type: "styleGuide", prefer, never, always }),
339
+ decode: () => wrapBlock("style_guide", [
189
340
  leaf("prefer", prefer),
190
341
  always ? leaf("always", always) : "",
191
342
  never ? leaf("never", never) : ""
@@ -196,7 +347,15 @@ function analogy(input) {
196
347
  const { concept, relationship, insight, therefore, pitfall } = input;
197
348
  return {
198
349
  type: "analogy",
199
- format: () => wrapBlock("analogy", [
350
+ encode: () => ({
351
+ type: "analogy",
352
+ concept,
353
+ relationship,
354
+ insight,
355
+ therefore,
356
+ pitfall
357
+ }),
358
+ decode: () => wrapBlock("analogy", [
200
359
  list("concepts", concept, "concept"),
201
360
  leaf("relationship", relationship),
202
361
  insight ? leaf("insight", insight) : "",
@@ -208,7 +367,8 @@ function analogy(input) {
208
367
  function glossary(entries) {
209
368
  return {
210
369
  type: "glossary",
211
- format: () => wrapBlock(
370
+ encode: () => ({ type: "glossary", entries }),
371
+ decode: () => wrapBlock(
212
372
  "glossary",
213
373
  Object.entries(entries).map(
214
374
  ([term2, sql]) => wrapBlock("entry", [leaf("term", term2), leaf("sql", sql)])
@@ -220,7 +380,8 @@ function identity(input) {
220
380
  const { name, role } = input;
221
381
  return {
222
382
  type: "identity",
223
- format: () => wrapBlock("identity", [
383
+ encode: () => ({ type: "identity", name, role }),
384
+ decode: () => wrapBlock("identity", [
224
385
  name ? leaf("name", name) : "",
225
386
  role ? leaf("role", role) : ""
226
387
  ])
@@ -230,35 +391,40 @@ function persona(input) {
230
391
  const { name, role, tone } = input;
231
392
  return {
232
393
  type: "persona",
233
- format: () => wrapBlock("persona", [
394
+ encode: () => ({ type: "persona", name, role, tone: tone ?? "" }),
395
+ decode: () => wrapBlock("persona", [
234
396
  leaf("name", name),
235
397
  leaf("role", role),
236
- leaf("tone", tone)
398
+ tone ? leaf("tone", tone) : ""
237
399
  ])
238
400
  };
239
401
  }
240
402
  function alias(termName, meaning) {
241
403
  return {
242
404
  type: "alias",
243
- format: () => wrapBlock("alias", [leaf("term", termName), leaf("meaning", meaning)])
405
+ encode: () => ({ type: "alias", term: termName, meaning }),
406
+ decode: () => wrapBlock("alias", [leaf("term", termName), leaf("meaning", meaning)])
244
407
  };
245
408
  }
246
409
  function preference(aspect, value) {
247
410
  return {
248
411
  type: "preference",
249
- format: () => wrapBlock("preference", [leaf("aspect", aspect), leaf("value", value)])
412
+ encode: () => ({ type: "preference", aspect, value }),
413
+ decode: () => wrapBlock("preference", [leaf("aspect", aspect), leaf("value", value)])
250
414
  };
251
415
  }
252
416
  function context(description) {
253
417
  return {
254
418
  type: "context",
255
- format: () => leaf("context", description)
419
+ encode: () => ({ type: "context", description }),
420
+ decode: () => leaf("context", description)
256
421
  };
257
422
  }
258
423
  function correction(subject, clarification2) {
259
424
  return {
260
425
  type: "correction",
261
- format: () => wrapBlock("correction", [
426
+ encode: () => ({ type: "correction", subject, clarification: clarification2 }),
427
+ decode: () => wrapBlock("correction", [
262
428
  leaf("subject", subject),
263
429
  leaf("clarification", clarification2)
264
430
  ])
@@ -267,7 +433,8 @@ function correction(subject, clarification2) {
267
433
  function teachable(tag, ...teachables) {
268
434
  return {
269
435
  type: "user_profile",
270
- format: () => toInstructions(tag, ...teachables)
436
+ encode: () => teachables[0]?.encode() ?? { type: "context", description: "" },
437
+ decode: () => toInstructions(tag, ...teachables)
271
438
  };
272
439
  }
273
440
  function toInstructions(tag, ...teachables) {
@@ -286,7 +453,7 @@ function toInstructions(tag, ...teachables) {
286
453
  if (!items?.length) {
287
454
  return "";
288
455
  }
289
- const renderedItems = items.map((item) => item.format().trim()).filter(Boolean).map((item) => indentBlock(item, 2)).join("\n");
456
+ const renderedItems = items.map((item) => item.decode().trim()).filter(Boolean).map((item) => indentBlock(item, 2)).join("\n");
290
457
  if (!renderedItems.length) {
291
458
  return "";
292
459
  }
@@ -298,7 +465,7 @@ ${renderedItems}
298
465
  if (definedTypes.has(type)) {
299
466
  continue;
300
467
  }
301
- const renderedItems = items.map((item) => item.format().trim()).filter(Boolean).map((item) => indentBlock(item, 2)).join("\n");
468
+ const renderedItems = items.map((item) => item.decode().trim()).filter(Boolean).map((item) => indentBlock(item, 2)).join("\n");
302
469
  if (renderedItems.length) {
303
470
  sections.push(renderedItems);
304
471
  }
@@ -408,6 +575,384 @@ function toTeachables(generated) {
408
575
  });
409
576
  }
410
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
+
411
956
  // packages/text2sql/src/lib/memory/memory.prompt.ts
412
957
  var memory_prompt_default = toInstructions(
413
958
  "memory_guidelines",
@@ -537,14 +1082,14 @@ var memory_prompt_default = toInstructions(
537
1082
  );
538
1083
 
539
1084
  // packages/text2sql/src/lib/agents/text2sql.agent.ts
540
- var tools = {
541
- validate_query: tool({
1085
+ var tools2 = {
1086
+ validate_query: tool2({
542
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.`,
543
- inputSchema: z2.object({
544
- sql: z2.string().describe("The SQL query to validate.")
1088
+ inputSchema: z5.object({
1089
+ sql: z5.string().describe("The SQL query to validate.")
545
1090
  }),
546
1091
  execute: async ({ sql }, options) => {
547
- const state = toState(options);
1092
+ const state = toState2(options);
548
1093
  const result = await state.adapter.validate(sql);
549
1094
  if (typeof result === "string") {
550
1095
  return `Validation Error: ${result}`;
@@ -552,22 +1097,22 @@ var tools = {
552
1097
  return "Query is valid.";
553
1098
  }
554
1099
  }),
555
- get_sample_rows: tool({
1100
+ get_sample_rows: tool2({
556
1101
  description: `Sample rows from a table to understand data formatting, codes, and value patterns. Use BEFORE writing queries when:
557
1102
  - Column types in schema don't reveal format (e.g., "status" could be 'active'/'inactive' or 1/0)
558
1103
  - Date/time formats are unclear (ISO, Unix timestamp, locale-specific)
559
1104
  - You need to understand lookup table codes or enum values
560
1105
  - Column names are ambiguous (e.g., "type", "category", "code")`,
561
- inputSchema: z2.object({
562
- tableName: z2.string().describe("The name of the table to sample."),
563
- columns: z2.array(z2.string()).optional().describe(
1106
+ inputSchema: z5.object({
1107
+ tableName: z5.string().describe("The name of the table to sample."),
1108
+ columns: z5.array(z5.string()).optional().describe(
564
1109
  "Specific columns to sample. If omitted, samples all columns."
565
1110
  ),
566
- limit: z2.number().min(1).max(10).default(3).optional().describe("Number of rows to sample (1-10, default 3).")
1111
+ limit: z5.number().min(1).max(10).default(3).optional().describe("Number of rows to sample (1-10, default 3).")
567
1112
  }),
568
1113
  execute: ({ tableName, columns, limit = 3 }, options) => {
569
1114
  const safeLimit = Math.min(Math.max(1, limit), 10);
570
- const state = toState(options);
1115
+ const state = toState2(options);
571
1116
  const sql = state.adapter.buildSampleRowsQuery(
572
1117
  tableName,
573
1118
  columns,
@@ -576,13 +1121,13 @@ var tools = {
576
1121
  return state.adapter.execute(sql);
577
1122
  }
578
1123
  }),
579
- db_query: tool({
1124
+ db_query: tool2({
580
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.`,
581
- inputSchema: z2.object({
582
- reasoning: z2.string().describe(
1126
+ inputSchema: z5.object({
1127
+ reasoning: z5.string().describe(
583
1128
  "Your reasoning for why this SQL query is relevant to the user request."
584
1129
  ),
585
- sql: z2.string().min(1, { message: "SQL query cannot be empty." }).refine(
1130
+ sql: z5.string().min(1, { message: "SQL query cannot be empty." }).refine(
586
1131
  (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
587
1132
  {
588
1133
  message: "Only read-only SELECT or WITH queries are allowed."
@@ -590,11 +1135,11 @@ var tools = {
590
1135
  ).describe("The SQL query to execute against the database.")
591
1136
  }),
592
1137
  execute: ({ sql }, options) => {
593
- const state = toState(options);
1138
+ const state = toState2(options);
594
1139
  return state.adapter.execute(sql);
595
1140
  }
596
1141
  }),
597
- scratchpad: scratchpad_tool
1142
+ scratchpad: scratchpad_tool2
598
1143
  };
599
1144
  var userMemoryTypes = [
600
1145
  "identity",
@@ -603,61 +1148,61 @@ var userMemoryTypes = [
603
1148
  "context",
604
1149
  "correction"
605
1150
  ];
606
- var userMemorySchema = z2.discriminatedUnion("type", [
607
- z2.object({
608
- type: z2.literal("identity"),
609
- description: z2.string().describe("The user's identity: role or/and name")
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")
610
1155
  }),
611
- z2.object({
612
- type: z2.literal("alias"),
613
- term: z2.string().describe("The term the user uses"),
614
- meaning: z2.string().describe("What the user means by this term")
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")
615
1160
  }),
616
- z2.object({
617
- type: z2.literal("preference"),
618
- aspect: z2.string().describe("What aspect of output this preference applies to"),
619
- value: z2.string().describe("The user's preference")
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")
620
1165
  }),
621
- z2.object({
622
- type: z2.literal("context"),
623
- description: z2.string().describe("What the user is currently working on")
1166
+ z5.object({
1167
+ type: z5.literal("context"),
1168
+ description: z5.string().describe("What the user is currently working on")
624
1169
  }),
625
- z2.object({
626
- type: z2.literal("correction"),
627
- subject: z2.string().describe("What was misunderstood"),
628
- clarification: z2.string().describe("The correct understanding")
1170
+ z5.object({
1171
+ type: z5.literal("correction"),
1172
+ subject: z5.string().describe("What was misunderstood"),
1173
+ clarification: z5.string().describe("The correct understanding")
629
1174
  })
630
1175
  ]);
631
1176
  var memoryTools = {
632
- remember_memory: tool({
1177
+ remember_memory: tool2({
633
1178
  description: "Store something about the user for future conversations. Use silently when user shares facts, preferences, vocabulary, corrections, or context.",
634
- inputSchema: z2.object({ memory: userMemorySchema }),
1179
+ inputSchema: z5.object({ memory: userMemorySchema }),
635
1180
  execute: async ({ memory }, options) => {
636
- const state = toState(
1181
+ const state = toState2(
637
1182
  options
638
1183
  );
639
1184
  await state.memory.remember(state.userId, memory);
640
1185
  return "Remembered.";
641
1186
  }
642
1187
  }),
643
- forget_memory: tool({
1188
+ forget_memory: tool2({
644
1189
  description: "Forget a specific memory. Use when user asks to remove something.",
645
- inputSchema: z2.object({
646
- id: z2.string().describe("The ID of the teachable to forget")
1190
+ inputSchema: z5.object({
1191
+ id: z5.string().describe("The ID of the teachable to forget")
647
1192
  }),
648
1193
  execute: async ({ id }, options) => {
649
- const state = toState(options);
1194
+ const state = toState2(options);
650
1195
  await state.memory.forget(id);
651
1196
  return "Forgotten.";
652
1197
  }
653
1198
  }),
654
- recall_memory: tool({
1199
+ recall_memory: tool2({
655
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.",
656
- inputSchema: z2.object({
657
- type: z2.enum(userMemoryTypes).optional().catch(void 0).describe("Optional: filter by memory type")
1201
+ inputSchema: z5.object({
1202
+ type: z5.enum(userMemoryTypes).optional().catch(void 0).describe("Optional: filter by memory type")
658
1203
  }),
659
1204
  execute: async ({ type }, options) => {
660
- const state = toState(
1205
+ const state = toState2(
661
1206
  options
662
1207
  );
663
1208
  const memories = await state.memory.recall(state.userId, type);
@@ -672,71 +1217,301 @@ var memoryTools = {
672
1217
  }));
673
1218
  }
674
1219
  }),
675
- update_memory: tool({
1220
+ update_memory: tool2({
676
1221
  description: "Update an existing memory. Use when user wants to modify something you previously stored.",
677
- inputSchema: z2.object({
1222
+ inputSchema: z5.object({
678
1223
  memory: userMemorySchema,
679
- id: z2.string().describe("The ID of the memory to update")
1224
+ id: z5.string().describe("The ID of the memory to update")
680
1225
  }),
681
1226
  execute: async ({ id, memory }, options) => {
682
- const state = toState(options);
1227
+ const state = toState2(options);
683
1228
  await state.memory.update(id, memory);
684
1229
  return "Updated.";
685
1230
  }
686
1231
  })
687
1232
  };
688
- var sqlQueryAgent = agent2({
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,
689
1320
  name: "text2sql",
690
- model: groq2("openai/gpt-oss-20b"),
691
- tools,
692
- // output: z.object({
693
- // sql: z
694
- // .string()
695
- // .describe('The SQL query generated to answer the user question.'),
696
- // }),
697
1321
  prompt: (state) => {
1322
+ const hasMemory = !!state?.memory;
698
1323
  return `
699
- <agent>
700
- <name>Freya</name>
701
- <role>You are an expert SQL query generator, answering business questions with accurate queries.</role>
702
- <tone>Your tone should be concise and business-friendly.</tone>
703
- </agent>
704
1324
  ${state?.teachings || ""}
705
1325
  ${state?.introspection || ""}
706
- <output>SQL query that can run directly without prose whatsoever</output>
1326
+
1327
+ ${chainOfThoughtPrompt}
1328
+
1329
+ ${fewShotExamples}
1330
+
1331
+ ${hasMemory ? memory_prompt_default : ""}
707
1332
  `;
708
1333
  }
709
1334
  });
710
- var t_a_g = agent2({
711
- model: groq2("openai/gpt-oss-20b"),
712
- tools,
713
- name: "text2sql",
714
- prompt: (state) => {
715
- const hasMemory = !!state?.memory;
716
- return `
717
1335
 
718
- ${state?.teachings || ""}
719
- ${state?.introspection || ""}
720
-
721
- ${hasMemory ? memory_prompt_default : ""}
722
- `;
1336
+ // packages/text2sql/src/lib/checkpoint.ts
1337
+ import { createHash } from "node:crypto";
1338
+ import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
1339
+ import pLimit from "p-limit";
1340
+ var Checkpoint = class _Checkpoint {
1341
+ constructor(path2, configHash, points) {
1342
+ this.path = path2;
1343
+ this.configHash = configHash;
1344
+ this.points = points;
723
1345
  }
724
- });
1346
+ points;
1347
+ /**
1348
+ * Load checkpoint from file, or return empty checkpoint if none exists.
1349
+ * Handles corrupted files and config changes gracefully.
1350
+ */
1351
+ static async load(options) {
1352
+ const { path: path2, configHash } = options;
1353
+ if (existsSync(path2)) {
1354
+ try {
1355
+ const content = readFileSync(path2, "utf-8");
1356
+ const file = JSON.parse(content);
1357
+ if (configHash && file.configHash && file.configHash !== configHash) {
1358
+ console.log("\u26A0 Config changed, starting fresh");
1359
+ return new _Checkpoint(path2, configHash, {});
1360
+ }
1361
+ const points = file.points ?? {};
1362
+ const totalEntries = Object.values(points).reduce(
1363
+ (sum, p) => sum + p.entries.length,
1364
+ 0
1365
+ );
1366
+ console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
1367
+ return new _Checkpoint(path2, configHash, points);
1368
+ } catch {
1369
+ console.log("\u26A0 Checkpoint corrupted, starting fresh");
1370
+ return new _Checkpoint(path2, configHash, {});
1371
+ }
1372
+ }
1373
+ console.log("Starting new checkpoint");
1374
+ return new _Checkpoint(path2, configHash, {});
1375
+ }
1376
+ /**
1377
+ * Run a single computation with checkpointing.
1378
+ * If already completed, returns cached value.
1379
+ *
1380
+ * @param key - Unique identifier for this computation
1381
+ * @param computation - Async function that produces the value
1382
+ * @param codec - Optional codec for encoding/decoding non-primitive values
1383
+ */
1384
+ async run(key, computation, codec) {
1385
+ const point = this.point(key);
1386
+ return point.through(
1387
+ "single",
1388
+ async () => {
1389
+ const result = await computation();
1390
+ return codec ? codec.encode(result) : result;
1391
+ },
1392
+ codec
1393
+ );
1394
+ }
1395
+ /**
1396
+ * Create a resumable checkpoint point for iterative operations.
1397
+ *
1398
+ * @param step - Unique identifier for this checkpoint point
1399
+ */
1400
+ point(step) {
1401
+ this.points[step] ??= { committed: false, entries: [] };
1402
+ return new Point(this.points[step], () => this.save());
1403
+ }
1404
+ /**
1405
+ * Process each input with automatic checkpointing and concurrency.
1406
+ *
1407
+ * @param step - Unique identifier for this checkpoint
1408
+ * @param inputs - Items to process
1409
+ * @param process - Function to process each input
1410
+ * @param options - Optional settings like concurrency
1411
+ * @returns All outputs (use `.flat()` if outputs are arrays)
1412
+ */
1413
+ async each(step, inputs, process2, options) {
1414
+ const point = this.point(step);
1415
+ const limit = pLimit(options?.concurrency ?? 1);
1416
+ const inputArray = Array.from(inputs);
1417
+ await Promise.all(
1418
+ inputArray.map(
1419
+ (input) => limit(() => point.through(input, () => process2(input)))
1420
+ )
1421
+ );
1422
+ await point.commit();
1423
+ return point.values();
1424
+ }
1425
+ /**
1426
+ * Get clean output from all completed points.
1427
+ * Single-entry points return the value directly, multi-entry return arrays.
1428
+ */
1429
+ getOutput() {
1430
+ const output = {};
1431
+ for (const [key, pointData] of Object.entries(this.points)) {
1432
+ if (pointData.entries.length === 1) {
1433
+ output[key] = pointData.entries[0].output;
1434
+ } else {
1435
+ output[key] = pointData.entries.map((e) => e.output);
1436
+ }
1437
+ }
1438
+ return output;
1439
+ }
1440
+ /** Get the file path where checkpoint is stored */
1441
+ getPath() {
1442
+ return this.path;
1443
+ }
1444
+ async save() {
1445
+ const file = {
1446
+ configHash: this.configHash,
1447
+ points: this.points
1448
+ };
1449
+ const content = JSON.stringify(file, null, 2);
1450
+ const tempPath = `${this.path}.tmp`;
1451
+ writeFileSync(tempPath, content);
1452
+ renameSync(tempPath, this.path);
1453
+ }
1454
+ };
1455
+ function hash(value) {
1456
+ return createHash("md5").update(JSON.stringify(value)).digest("hex");
1457
+ }
1458
+ var Point = class {
1459
+ constructor(data, persist) {
1460
+ this.data = data;
1461
+ this.persist = persist;
1462
+ this.#cache = new Map(
1463
+ data.entries.map((e) => [e.inputHash, e.output])
1464
+ );
1465
+ }
1466
+ #cache;
1467
+ /**
1468
+ * Execute computation if input wasn't processed before.
1469
+ * Returns cached output if input hash exists, otherwise executes, saves, and returns.
1470
+ */
1471
+ async through(input, compute, codec) {
1472
+ const inputHash = hash(input);
1473
+ if (this.#cache.has(inputHash)) {
1474
+ const cached = this.#cache.get(inputHash);
1475
+ return codec ? codec.decode(cached) : cached;
1476
+ }
1477
+ const output = await compute();
1478
+ this.data.entries.push({ inputHash, output });
1479
+ this.#cache.set(inputHash, output);
1480
+ await this.persist();
1481
+ return codec ? codec.decode(output) : output;
1482
+ }
1483
+ /** Mark this point as complete. */
1484
+ async commit() {
1485
+ this.data.committed = true;
1486
+ await this.persist();
1487
+ }
1488
+ /** Check if this point has been committed. */
1489
+ isCommitted() {
1490
+ return this.data.committed;
1491
+ }
1492
+ /** Get all outputs from this point. */
1493
+ values() {
1494
+ return this.data.entries.map((e) => e.output);
1495
+ }
1496
+ };
1497
+ function hashConfig(config) {
1498
+ return createHash("md5").update(JSON.stringify(config)).digest("hex");
1499
+ }
725
1500
 
726
1501
  // packages/text2sql/src/lib/file-cache.ts
727
- import { createHash } from "node:crypto";
728
- import { existsSync } from "node:fs";
1502
+ import { createHash as createHash2 } from "node:crypto";
1503
+ import { existsSync as existsSync2 } from "node:fs";
729
1504
  import { readFile, writeFile } from "node:fs/promises";
730
1505
  import { tmpdir } from "node:os";
731
1506
  import path from "node:path";
732
1507
  var FileCache = class {
733
1508
  path;
734
1509
  constructor(watermark, extension = ".txt") {
735
- const hash = createHash("md5").update(watermark).digest("hex");
736
- this.path = path.join(tmpdir(), `text2sql-${hash}${extension}`);
1510
+ const hash2 = createHash2("md5").update(watermark).digest("hex");
1511
+ this.path = path.join(tmpdir(), `text2sql-${hash2}${extension}`);
737
1512
  }
738
1513
  async get() {
739
- if (existsSync(this.path)) {
1514
+ if (existsSync2(this.path)) {
740
1515
  return readFile(this.path, "utf-8");
741
1516
  }
742
1517
  return null;
@@ -961,166 +1736,1156 @@ import {
961
1736
  } from "ai";
962
1737
  import { v7 as v72 } from "uuid";
963
1738
  import {
964
- generate,
1739
+ generate as generate5,
965
1740
  stream,
966
- user
1741
+ user as user4
967
1742
  } from "@deepagents/agent";
968
1743
 
969
- // packages/text2sql/src/lib/agents/explainer.agent.ts
970
- import { groq as groq3 } from "@ai-sdk/groq";
971
- import dedent2 from "dedent";
972
- import z3 from "zod";
973
- import { agent as agent3 } from "@deepagents/agent";
974
- var explainerAgent = agent3({
975
- name: "explainer",
976
- model: groq3("openai/gpt-oss-20b"),
977
- prompt: (state) => dedent2`
978
- You are an expert SQL tutor.
979
- Explain the following SQL query in plain English to a non-technical user.
980
- Focus on the intent and logic, not the syntax.
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.
981
1760
 
982
- <sql>
983
- ${state?.sql}
984
- </sql>
985
- `,
986
- output: z3.object({
987
- explanation: z3.string().describe("The explanation of the SQL query.")
988
- })
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.")
1774
+ }),
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 };
1780
+ }
1781
+ return { valid: true };
1782
+ }
1783
+ }),
1784
+ /**
1785
+ * Record insights and reasoning during schema analysis and dashboard design.
1786
+ */
1787
+ scratchpad: scratchpad_tool3
1788
+ };
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
+ `;
2060
+ }
989
2061
  });
990
2062
 
991
- // packages/text2sql/src/lib/teach/teachings.ts
992
- var teachings_default = [
993
- hint(
994
- "If the user asks to show a table or entity without specifying columns, use SELECT *."
995
- ),
996
- hint(
997
- "When showing items associated with another entity, include the item ID and the related details requested."
998
- ),
999
- hint(
1000
- 'When asked to "show" items, list them unless the user explicitly asks to count or total.'
1001
- ),
1002
- hint(
1003
- "Use canonical/LowCardinality values verbatim for filtering; [rows/size] hints suggest when to aggregate instead of listing."
1004
- ),
1005
- hint(
1006
- "Favor PK/indexed columns for joins and filters; follow relationship metadata for join direction and cardinality."
1007
- ),
1008
- guardrail({
1009
- rule: "Avoid unbounded scans on large/time-based tables.",
1010
- action: "Ask for or apply a reasonable recent date range before querying broad fact tables."
2063
+ // packages/text2sql/src/lib/agents/chat1.agent.ts
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({
2071
+ description: `Query the database to answer a question. Provide your question in natural language and this tool will:
2072
+ 1. Generate the appropriate SQL query
2073
+ 2. Validate the SQL syntax
2074
+ 3. Execute the query
2075
+ 4. Return the results
2076
+
2077
+ Use this tool when you need to retrieve data to answer the user's question.`,
2078
+ inputSchema: z7.object({
2079
+ question: z7.string().min(1).describe(
2080
+ "The question to answer, expressed in natural language. Be specific about what data you need."
2081
+ ),
2082
+ reasoning: z7.string().optional().describe(
2083
+ "Your reasoning for why this query is needed to answer the user."
2084
+ )
2085
+ }),
2086
+ execute: async ({ question }, options) => {
2087
+ const state = toState4(options);
2088
+ try {
2089
+ const sqlResult = await toSql({
2090
+ input: question,
2091
+ adapter: state.adapter,
2092
+ introspection: state.introspection,
2093
+ instructions: state.instructions
2094
+ });
2095
+ if (!sqlResult.sql) {
2096
+ return {
2097
+ success: false,
2098
+ error: sqlResult.errors?.join("; ") || "Failed to generate SQL"
2099
+ };
2100
+ }
2101
+ const data = await state.adapter.execute(sqlResult.sql);
2102
+ return {
2103
+ success: true,
2104
+ sql: sqlResult.sql,
2105
+ data
2106
+ };
2107
+ } catch (error) {
2108
+ return {
2109
+ success: false,
2110
+ error: error instanceof Error ? error.message : "Unknown error occurred"
2111
+ };
2112
+ }
2113
+ }
1011
2114
  }),
1012
- guardrail({
1013
- rule: "Do not return oversized raw result sets.",
1014
- action: "Keep raw limit strictly to ~100 rows even if users request more or coearced by hints.",
1015
- reason: "Browser will time out or crash on huge datasets. Data overload harms usability."
2115
+ scratchpad: scratchpad_tool4
2116
+ };
2117
+ var chat1Agent = agent7({
2118
+ name: "chat1-combined",
2119
+ model: groq7("openai/gpt-oss-20b"),
2120
+ tools: tools4,
2121
+ prompt: (state) => {
2122
+ return `
2123
+ ${state?.teachings || ""}
2124
+ ${state?.introspection || ""}
2125
+ `;
2126
+ }
2127
+ });
2128
+
2129
+ // packages/text2sql/src/lib/agents/chat2.agent.ts
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({
2137
+ description: `Generate a SQL query from a natural language question. This tool will:
2138
+ 1. Translate your question into SQL
2139
+ 2. Validate the SQL syntax
2140
+ 3. Retry with corrections if validation fails
2141
+ 4. Return the validated SQL for your review
2142
+
2143
+ Use this BEFORE execute_sql to see what query will be run. You can then:
2144
+ - Explain the approach to the user
2145
+ - Decide if the SQL looks correct
2146
+ - Refine your question and regenerate if needed`,
2147
+ inputSchema: z8.object({
2148
+ question: z8.string().min(1).describe(
2149
+ "The question to translate into SQL. Be specific about what data you need."
2150
+ ),
2151
+ reasoning: z8.string().optional().describe("Your reasoning for why this data is needed.")
2152
+ }),
2153
+ execute: async ({ question }, options) => {
2154
+ const state = toState5(options);
2155
+ try {
2156
+ const sqlResult = await toSql({
2157
+ input: question,
2158
+ adapter: state.adapter,
2159
+ introspection: state.introspection,
2160
+ instructions: state.instructions
2161
+ });
2162
+ if (!sqlResult.sql) {
2163
+ return {
2164
+ success: false,
2165
+ error: sqlResult.errors?.join("; ") || "Failed to generate SQL",
2166
+ validationErrors: sqlResult.errors
2167
+ };
2168
+ }
2169
+ return {
2170
+ success: true,
2171
+ sql: sqlResult.sql,
2172
+ validationErrors: sqlResult.errors
2173
+ };
2174
+ } catch (error) {
2175
+ return {
2176
+ success: false,
2177
+ error: error instanceof Error ? error.message : "Unknown error occurred"
2178
+ };
2179
+ }
2180
+ }
1016
2181
  }),
1017
- guardrail({
1018
- rule: "Prevent cartesian or guesswork joins.",
1019
- reason: "Protect correctness and performance.",
1020
- action: "If join keys are missing or unclear, inspect relationships and ask for the intended join path before executing."
2182
+ execute_sql: tool5({
2183
+ description: `Execute a SQL query and return the results. Use this AFTER generate_sql to run the query.
2184
+
2185
+ Only SELECT and WITH (CTE) queries are allowed - no data modification.`,
2186
+ inputSchema: z8.object({
2187
+ sql: z8.string().min(1).refine(
2188
+ (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
2189
+ {
2190
+ message: "Only read-only SELECT or WITH queries are allowed."
2191
+ }
2192
+ ).describe("The SQL query to execute (must be SELECT or WITH)."),
2193
+ reasoning: z8.string().optional().describe("Brief explanation of what this query retrieves.")
2194
+ }),
2195
+ execute: async ({ sql }, options) => {
2196
+ const state = toState5(options);
2197
+ try {
2198
+ const data = await state.adapter.execute(sql);
2199
+ return {
2200
+ success: true,
2201
+ data,
2202
+ rowCount: Array.isArray(data) ? data.length : void 0
2203
+ };
2204
+ } catch (error) {
2205
+ return {
2206
+ success: false,
2207
+ error: error instanceof Error ? error.message : "Query execution failed"
2208
+ };
2209
+ }
2210
+ }
1021
2211
  }),
1022
- clarification({
1023
- when: "The request targets time-based data without a date range.",
1024
- ask: "Confirm the intended timeframe (e.g., last 30/90 days, YTD, specific year).",
1025
- reason: "Prevents large scans and irrelevant results."
2212
+ scratchpad: scratchpad_tool5
2213
+ };
2214
+ var chat2Agent = agent8({
2215
+ name: "chat2-with-peek",
2216
+ model: groq8("openai/gpt-oss-20b"),
2217
+ tools: tools5,
2218
+ prompt: (state) => {
2219
+ return `
2220
+ ${state?.teachings || ""}
2221
+ ${state?.introspection || ""}
2222
+
2223
+ When answering questions that require database queries:
2224
+ 1. First use generate_sql to create the SQL query
2225
+ 2. Review the generated SQL to ensure it matches the user's intent
2226
+ 3. Use execute_sql to run the query
2227
+ 4. Present the results to the user
2228
+
2229
+ If the generated SQL doesn't look right, you can refine your question and regenerate.
2230
+ `;
2231
+ }
2232
+ });
2233
+
2234
+ // packages/text2sql/src/lib/agents/chat3.agent.ts
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")
1026
2247
  }),
1027
- clarification({
1028
- when: 'The request uses ambiguous scoring or ranking language (e.g., "top", "best", "active") without a metric.',
1029
- ask: "Clarify the ranking metric or definition before writing the query.",
1030
- reason: "Ensures the correct aggregation/ordering is used."
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")
1031
2253
  }),
1032
- workflow({
1033
- task: "SQL generation plan",
1034
- steps: [
1035
- "Translate the question into SQL patterns (aggregation, segmentation, time range, ranking).",
1036
- "Choose tables/relations that satisfy those patterns; note lookup tables and filter values implied by schema hints.",
1037
- "Inspect samples with 'get_sample_rows' for any column you'll use in WHERE/JOIN conditions - target just those columns (e.g., get_sample_rows('orders', ['status', 'order_type'])).",
1038
- "Sketch join/filter/aggregation order considering table sizes, indexes, and stats.",
1039
- "Draft SQL, validate via 'validate_query', then execute via 'db_query' with a short reasoning note."
1040
- ]
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")
2258
+ })
2259
+ ]);
2260
+ var collaborativeSqlAgent = agent9({
2261
+ name: "collaborative-sql",
2262
+ model: groq9("openai/gpt-oss-20b"),
2263
+ output: collaborativeSqlOutputSchema,
2264
+ prompt: (state) => {
2265
+ return `
2266
+ ${toInstructions(
2267
+ "instructions",
2268
+ persona({
2269
+ name: "SQLCollab",
2270
+ role: "You are an expert SQL query generator that collaborates with the user to ensure accuracy."
2271
+ }),
2272
+ ...state?.instructions || []
2273
+ )}
2274
+ ${state?.introspection || ""}
2275
+
2276
+ IMPORTANT: You have three response options:
2277
+
2278
+ 1. SUCCESS - When you can confidently generate SQL:
2279
+ - Provide the SQL query
2280
+ - Rate your confidence (high/medium/low)
2281
+ - List any assumptions you made
2282
+
2283
+ 2. CLARIFICATION_NEEDED - When the question is ambiguous:
2284
+ - Ask a specific clarifying question
2285
+ - Explain why clarification is needed
2286
+ - Provide options if applicable
2287
+
2288
+ 3. UNANSWERABLE - When the question cannot be answered with available data:
2289
+ - Explain why
2290
+ - Suggest alternative questions that could be answered
2291
+
2292
+ Prefer asking for clarification over making low-confidence guesses.
2293
+ `;
2294
+ }
2295
+ });
2296
+ var tools6 = {
2297
+ consult_sql_agent: tool6({
2298
+ description: `Consult the SQL specialist agent to generate a query. The SQL agent may:
2299
+ - Return a SQL query with confidence level and assumptions
2300
+ - Ask for clarification if the question is ambiguous
2301
+ - Indicate if the question cannot be answered with available data
2302
+
2303
+ Based on the response:
2304
+ - If clarification is needed, you can provide context or ask the user
2305
+ - If assumptions were made, verify them with the user for important queries
2306
+ - If unanswerable, relay the suggestions to the user`,
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(
2311
+ "Answer to a previous clarification question from the SQL agent."
2312
+ )
2313
+ }),
2314
+ execute: async ({ question, context: context2, previousClarification }, options) => {
2315
+ const state = toState6(options);
2316
+ try {
2317
+ let fullQuestion = question;
2318
+ if (context2) {
2319
+ fullQuestion = `${question}
2320
+
2321
+ Additional context: ${context2}`;
2322
+ }
2323
+ if (previousClarification) {
2324
+ fullQuestion = `${fullQuestion}
2325
+
2326
+ Clarification provided: ${previousClarification}`;
2327
+ }
2328
+ const agentInstance = collaborativeSqlAgent.clone({
2329
+ model: wrapLanguageModel2({
2330
+ model: collaborativeSqlAgent.model,
2331
+ middleware: defaultSettingsMiddleware2({
2332
+ settings: { temperature: 0.1 }
2333
+ })
2334
+ })
2335
+ });
2336
+ const { experimental_output: output } = await generate3(
2337
+ agentInstance,
2338
+ [user2(fullQuestion)],
2339
+ state
2340
+ );
2341
+ if (output.status === "success") {
2342
+ const validationError = await state.adapter.validate(output.sql);
2343
+ if (validationError) {
2344
+ return {
2345
+ success: false,
2346
+ error: `SQL validation failed: ${validationError}`
2347
+ };
2348
+ }
2349
+ const data = await state.adapter.execute(output.sql);
2350
+ return {
2351
+ success: true,
2352
+ sql: output.sql,
2353
+ data,
2354
+ confidence: output.confidence,
2355
+ assumptions: output.assumptions
2356
+ };
2357
+ }
2358
+ if (output.status === "clarification_needed") {
2359
+ return {
2360
+ success: false,
2361
+ clarificationNeeded: output.question,
2362
+ clarificationContext: output.context,
2363
+ clarificationOptions: output.options
2364
+ };
2365
+ }
2366
+ if (output.status === "unanswerable") {
2367
+ return {
2368
+ success: false,
2369
+ unanswerableReason: output.reason,
2370
+ suggestions: output.suggestions
2371
+ };
2372
+ }
2373
+ return {
2374
+ success: false,
2375
+ error: "Unexpected response from SQL agent"
2376
+ };
2377
+ } catch (error) {
2378
+ return {
2379
+ success: false,
2380
+ error: error instanceof Error ? error.message : "Unknown error occurred"
2381
+ };
2382
+ }
2383
+ }
1041
2384
  }),
1042
- styleGuide({
1043
- prefer: "Summaries should be concise, business-friendly, highlight key comparisons, and add a short helpful follow-up when useful."
2385
+ execute_sql: tool6({
2386
+ description: `Execute a SQL query directly. Use this when you have SQL that you want to run
2387
+ (e.g., after receiving SQL from consult_sql_agent or for follow-up queries).`,
2388
+ inputSchema: z9.object({
2389
+ sql: z9.string().min(1).refine(
2390
+ (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
2391
+ {
2392
+ message: "Only read-only SELECT or WITH queries are allowed."
2393
+ }
2394
+ ).describe("The SQL query to execute.")
2395
+ }),
2396
+ execute: async ({ sql }, options) => {
2397
+ const state = toState6(options);
2398
+ try {
2399
+ const validationError = await state.adapter.validate(sql);
2400
+ if (validationError) {
2401
+ return {
2402
+ success: false,
2403
+ error: `Validation failed: ${validationError}`
2404
+ };
2405
+ }
2406
+ const data = await state.adapter.execute(sql);
2407
+ return {
2408
+ success: true,
2409
+ data,
2410
+ rowCount: Array.isArray(data) ? data.length : void 0
2411
+ };
2412
+ } catch (error) {
2413
+ return {
2414
+ success: false,
2415
+ error: error instanceof Error ? error.message : "Execution failed"
2416
+ };
2417
+ }
2418
+ }
1044
2419
  }),
1045
- // Tool usage constraints
1046
- guardrail({
1047
- rule: "You must validate your query before final execution.",
1048
- action: "Follow the pattern: Draft Query \u2192 `validate_query` \u2192 Fix (if needed) \u2192 `db_query`."
2420
+ scratchpad: scratchpad_tool6
2421
+ };
2422
+ var chat3Agent = agent9({
2423
+ name: "chat3-collaborative",
2424
+ model: groq9("openai/gpt-oss-20b"),
2425
+ tools: tools6,
2426
+ prompt: (state) => {
2427
+ return `
2428
+ ${state?.teachings || ""}
2429
+ ${state?.introspection || ""}
2430
+
2431
+ When answering questions that require database queries, use the consult_sql_agent tool.
2432
+
2433
+ The SQL agent may respond in three ways:
2434
+ 1. SUCCESS with SQL, confidence, and assumptions - review the confidence and assumptions
2435
+ 2. CLARIFICATION_NEEDED with a question - either answer from context or ask the user
2436
+ 3. UNANSWERABLE with reason and suggestions - relay this to the user helpfully
2437
+
2438
+ For medium/low confidence results, consider mentioning the assumptions to the user.
2439
+ For clarification requests, try to answer from conversation context first before asking the user.
2440
+ `;
2441
+ }
2442
+ });
2443
+
2444
+ // packages/text2sql/src/lib/agents/chat4.agent.ts
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(
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."
2454
+ ),
2455
+ entities: z10.array(z10.string()).optional().describe(
2456
+ "Key entities/concepts mentioned (e.g., customers, orders, products)"
2457
+ ),
2458
+ filters: z10.array(z10.string()).optional().describe(
2459
+ 'Filtering criteria mentioned (e.g., "last quarter", "above $100")'
2460
+ ),
2461
+ aggregation: z10.string().optional().describe(
2462
+ 'Type of aggregation if any (e.g., "count", "sum", "average", "top N")'
2463
+ ),
2464
+ ambiguities: z10.array(z10.string()).optional().describe("Any ambiguous parts that might need clarification")
2465
+ });
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")
1049
2470
  }),
1050
- guardrail({
1051
- rule: "ALWAYS use `get_sample_rows` before writing queries that filter or compare against string columns.",
1052
- reason: "Prevents SQL errors from wrong value formats.",
1053
- action: "Target specific columns (e.g., get_sample_rows('table', ['status', 'type']))."
2471
+ z10.object({
2472
+ error: z10.string().describe("Error message if the question cannot be answered")
2473
+ })
2474
+ ]);
2475
+ var decompositionSqlAgent = agent10({
2476
+ name: "decomposition-sql",
2477
+ model: groq10("openai/gpt-oss-20b"),
2478
+ output: decompositionSqlOutputSchema,
2479
+ prompt: (state) => {
2480
+ return `
2481
+ ${toInstructions(
2482
+ "instructions",
2483
+ persona({
2484
+ name: "SQLDecomp",
2485
+ role: "You are an expert SQL query generator. You receive questions broken down into semantic components and generate precise SQL."
2486
+ }),
2487
+ ...state?.instructions || []
2488
+ )}
2489
+ ${state?.introspection || ""}
2490
+
2491
+ You will receive questions in a decomposed format with:
2492
+ - breakdown: Semantic parts of the question
2493
+ - entities: Key concepts mentioned
2494
+ - filters: Filtering criteria
2495
+ - aggregation: Type of aggregation needed
2496
+ - ambiguities: Potentially unclear parts
2497
+
2498
+ Address each component of the breakdown in your SQL.
2499
+ If there are ambiguities, make reasonable assumptions and note them in your reasoning.
2500
+ `;
2501
+ }
2502
+ });
2503
+ var RETRY_TEMPERATURES2 = [0, 0.2, 0.3];
2504
+ var tools7 = {
2505
+ query_with_decomposition: tool7({
2506
+ description: `Query the database using question decomposition. This tool:
2507
+ 1. Breaks down your question into semantic components (entities, filters, aggregations)
2508
+ 2. Passes the decomposition to the SQL specialist
2509
+ 3. Generates and validates SQL
2510
+ 4. Executes and returns results
2511
+
2512
+ This approach helps ensure all aspects of the question are addressed in the query.`,
2513
+ inputSchema: z10.object({
2514
+ question: z10.string().min(1).describe("The question to answer."),
2515
+ breakdown: z10.array(z10.string()).min(1).describe(
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"]'
2517
+ ),
2518
+ entities: z10.array(z10.string()).optional().describe(
2519
+ 'Key entities mentioned (e.g., ["customers", "orders", "products"])'
2520
+ ),
2521
+ filters: z10.array(z10.string()).optional().describe('Filter criteria (e.g., ["last month", "status = active"])'),
2522
+ aggregation: z10.string().optional().describe(
2523
+ 'Aggregation type if any (e.g., "sum revenue", "count orders", "top 10")'
2524
+ ),
2525
+ ambiguities: z10.array(z10.string()).optional().describe("Note any ambiguous parts you identified")
2526
+ }),
2527
+ execute: async ({ question, breakdown, entities, filters, aggregation, ambiguities }, options) => {
2528
+ const state = toState7(options);
2529
+ const decomposition = {
2530
+ originalQuestion: question,
2531
+ breakdown,
2532
+ entities,
2533
+ filters,
2534
+ aggregation,
2535
+ ambiguities
2536
+ };
2537
+ const decomposedPrompt = formatDecomposition(decomposition);
2538
+ try {
2539
+ let lastError;
2540
+ for (let attempt = 0; attempt < RETRY_TEMPERATURES2.length; attempt++) {
2541
+ const temperature = RETRY_TEMPERATURES2[attempt];
2542
+ const agentInstance = decompositionSqlAgent.clone({
2543
+ model: wrapLanguageModel3({
2544
+ model: decompositionSqlAgent.model,
2545
+ middleware: defaultSettingsMiddleware3({
2546
+ settings: { temperature }
2547
+ })
2548
+ })
2549
+ });
2550
+ const prompt = lastError ? `${decomposedPrompt}
2551
+
2552
+ Previous attempt failed with: ${lastError}. Please fix the query.` : decomposedPrompt;
2553
+ const { experimental_output: output } = await generate4(
2554
+ agentInstance,
2555
+ [user3(prompt)],
2556
+ state
2557
+ );
2558
+ if ("error" in output) {
2559
+ return {
2560
+ success: false,
2561
+ question,
2562
+ decomposition,
2563
+ error: output.error,
2564
+ attempts: attempt + 1
2565
+ };
2566
+ }
2567
+ const validationError = await state.adapter.validate(output.sql);
2568
+ if (validationError) {
2569
+ lastError = validationError;
2570
+ continue;
2571
+ }
2572
+ const data = await state.adapter.execute(output.sql);
2573
+ return {
2574
+ success: true,
2575
+ question,
2576
+ decomposition,
2577
+ sql: output.sql,
2578
+ data,
2579
+ reasoning: output.reasoning,
2580
+ attempts: attempt + 1
2581
+ };
2582
+ }
2583
+ return {
2584
+ success: false,
2585
+ question,
2586
+ decomposition,
2587
+ error: `Failed after ${RETRY_TEMPERATURES2.length} attempts. Last error: ${lastError}`,
2588
+ attempts: RETRY_TEMPERATURES2.length
2589
+ };
2590
+ } catch (error) {
2591
+ return {
2592
+ success: false,
2593
+ question,
2594
+ decomposition,
2595
+ error: error instanceof Error ? error.message : "Unknown error occurred"
2596
+ };
2597
+ }
2598
+ }
1054
2599
  }),
1055
- guardrail({
1056
- rule: "Do not call `db_query` without first producing and validating a SQL snippet.",
1057
- action: "First produce the query string, then validate."
2600
+ execute_sql: tool7({
2601
+ description: `Execute a SQL query directly. Use for follow-up queries or when you already have SQL.`,
2602
+ inputSchema: z10.object({
2603
+ sql: z10.string().min(1).refine(
2604
+ (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
2605
+ {
2606
+ message: "Only read-only SELECT or WITH queries are allowed."
2607
+ }
2608
+ ).describe("The SQL query to execute.")
2609
+ }),
2610
+ execute: async ({ sql }, options) => {
2611
+ const state = toState7(options);
2612
+ try {
2613
+ const validationError = await state.adapter.validate(sql);
2614
+ if (validationError) {
2615
+ return {
2616
+ success: false,
2617
+ error: `Validation failed: ${validationError}`
2618
+ };
2619
+ }
2620
+ const data = await state.adapter.execute(sql);
2621
+ return {
2622
+ success: true,
2623
+ data,
2624
+ rowCount: Array.isArray(data) ? data.length : void 0
2625
+ };
2626
+ } catch (error) {
2627
+ return {
2628
+ success: false,
2629
+ error: error instanceof Error ? error.message : "Execution failed"
2630
+ };
2631
+ }
2632
+ }
1058
2633
  }),
1059
- hint(
1060
- "Use the `scratchpad` tool for strategic reflection during SQL query generation."
1061
- )
1062
- ];
1063
-
1064
- // packages/text2sql/src/lib/sql.ts
1065
- var Text2Sql = class {
1066
- #config;
1067
- constructor(config) {
1068
- this.#config = {
1069
- adapter: config.adapter,
1070
- briefCache: new FileCache("brief-" + config.version),
1071
- history: config.history,
1072
- instructions: [...teachings_default, ...config.instructions ?? []],
1073
- tools: config.tools ?? {},
1074
- model: config.model,
1075
- memory: config.memory,
1076
- introspection: new FileCache("introspection-" + config.version)
1077
- };
2634
+ scratchpad: scratchpad_tool7
2635
+ };
2636
+ function formatDecomposition(decomposition) {
2637
+ const parts = [
2638
+ `Original Question: ${decomposition.originalQuestion}`,
2639
+ "",
2640
+ "Question Breakdown:",
2641
+ ...decomposition.breakdown.map((part, i) => ` ${i + 1}. ${part}`)
2642
+ ];
2643
+ if (decomposition.entities?.length) {
2644
+ parts.push("", `Entities: ${decomposition.entities.join(", ")}`);
1078
2645
  }
1079
- async explain(sql) {
1080
- const { experimental_output } = await generate(
1081
- explainerAgent,
1082
- [user("Explain this SQL.")],
1083
- { sql }
1084
- );
1085
- return experimental_output.explanation;
2646
+ if (decomposition.filters?.length) {
2647
+ parts.push("", `Filters: ${decomposition.filters.join(", ")}`);
1086
2648
  }
1087
- async toSql(query, options) {
1088
- const introspection = await this.index();
1089
- const { text } = await generate(
1090
- sqlQueryAgent.clone({
1091
- model: this.#config.model,
1092
- tools: {
1093
- ...t_a_g.handoff.tools,
1094
- ...options?.tools ?? this.#config.tools
1095
- }
1096
- }),
1097
- [user(query)],
1098
- {
1099
- teachings: toInstructions(
1100
- "instructions",
1101
- persona({
1102
- name: "Freya",
1103
- role: "You are an expert SQL query generator, answering business questions with accurate queries.",
1104
- tone: "Your tone should be concise and business-friendly."
1105
- }),
1106
- ...this.#config.instructions
1107
- ),
1108
- adapter: this.#config.adapter,
1109
- introspection
1110
- }
2649
+ if (decomposition.aggregation) {
2650
+ parts.push("", `Aggregation: ${decomposition.aggregation}`);
2651
+ }
2652
+ if (decomposition.ambiguities?.length) {
2653
+ parts.push(
2654
+ "",
2655
+ "Potential Ambiguities:",
2656
+ ...decomposition.ambiguities.map((a) => ` - ${a}`)
1111
2657
  );
1112
- return text;
2658
+ }
2659
+ parts.push(
2660
+ "",
2661
+ "Generate SQL that addresses each component of the breakdown."
2662
+ );
2663
+ return parts.join("\n");
2664
+ }
2665
+ var chat4Agent = agent10({
2666
+ name: "chat4-decomposition",
2667
+ model: groq10("openai/gpt-oss-20b"),
2668
+ tools: tools7,
2669
+ prompt: (state) => {
2670
+ return `
2671
+ ${state?.teachings || ""}
2672
+ ${state?.introspection || ""}
2673
+
2674
+ When answering questions that require database queries, use the query_with_decomposition tool.
2675
+
2676
+ IMPORTANT: You must break down the question into semantic parts - describe WHAT is being asked, not HOW to implement it.
2677
+
2678
+ Good breakdown example for "Which customers bought the most expensive products last quarter?":
2679
+ - "customers who made purchases" (entity relationship)
2680
+ - "products they purchased" (what products)
2681
+ - "expensive products - need definition" (filter criteria - note ambiguity)
2682
+ - "last quarter" (time filter)
2683
+ - "most - ranking by count or value?" (aggregation - note ambiguity)
2684
+
2685
+ Bad breakdown (too instructional):
2686
+ - "JOIN customers with orders" (this is HOW, not WHAT)
2687
+ - "Use ORDER BY and LIMIT" (this is implementation)
2688
+
2689
+ Break the question into its semantic aspects, and let the SQL specialist figure out the implementation.
2690
+ `;
2691
+ }
2692
+ });
2693
+
2694
+ // packages/text2sql/src/lib/synthesis/types.ts
2695
+ async function toPairs(producer) {
2696
+ const pairs = [];
2697
+ for await (const chunk of producer.produce()) {
2698
+ pairs.push(...chunk);
2699
+ }
2700
+ return pairs;
2701
+ }
2702
+
2703
+ // packages/text2sql/src/lib/teach/teachings.ts
2704
+ function guidelines(options = {}) {
2705
+ const { date = "strict" } = options;
2706
+ const baseTeachings = [
2707
+ // Schema adherence
2708
+ hint(
2709
+ "Use only tables and columns that exist in the schema. Never reference non-existent entities."
2710
+ ),
2711
+ hint(
2712
+ "If the user asks to show a table or entity without specifying columns, use SELECT *."
2713
+ ),
2714
+ hint(
2715
+ "When showing items associated with another entity, include the item ID and the related details requested."
2716
+ ),
2717
+ hint(
2718
+ 'When asked to "show" items, list them unless the user explicitly asks to count or total.'
2719
+ ),
2720
+ hint(
2721
+ "Use canonical/LowCardinality values verbatim for filtering; [rows/size] hints suggest when to aggregate instead of listing."
2722
+ ),
2723
+ // Joins and relationships
2724
+ hint(
2725
+ "Use appropriate JOINs based on the relationships defined in the schema."
2726
+ ),
2727
+ hint(
2728
+ "Favor PK/indexed columns for joins and filters; follow relationship metadata for join direction and cardinality."
2729
+ ),
2730
+ // Aggregations and calculations
2731
+ hint(
2732
+ "Apply proper aggregations (COUNT, SUM, AVG, etc.) when the question implies summarization."
2733
+ ),
2734
+ hint(
2735
+ 'When asked "how many X are there" about types/categories/statuses (e.g., "how many statuses are there?"), use COUNT(DISTINCT column). This asks about variety, not row count.'
2736
+ ),
2737
+ hint(
2738
+ "Use window functions when the question requires ranking, running totals, or comparisons across rows."
2739
+ ),
2740
+ // Query semantics
2741
+ hint(
2742
+ 'Words like "reach", "reached", "hit" with a value (e.g., "temperature reach 80") mean >= (greater than or equal), not = (exact match).'
2743
+ ),
2744
+ hint(
2745
+ 'For "shared by" two groups or mutually exclusive conditions (e.g., population > 1500 AND < 500), use INTERSECT between separate queries. A single WHERE with contradictory AND returns nothing.'
2746
+ ),
2747
+ hint(
2748
+ 'When filtering by a specific value from a joined table (e.g., "students who registered course statistics"), always include that WHERE condition. Do not omit mentioned filters.'
2749
+ ),
2750
+ hint(
2751
+ "Handle NULL values appropriately using IS NULL, IS NOT NULL, or COALESCE."
2752
+ ),
2753
+ // Style and readability
2754
+ styleGuide({
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)."
2757
+ }),
2758
+ styleGuide({
2759
+ prefer: "Summaries should be concise, business-friendly, highlight key comparisons, and add a short helpful follow-up when useful."
2760
+ }),
2761
+ // Guardrails - Query safety
2762
+ guardrail({
2763
+ rule: "Generate ONLY valid, executable SQL.",
2764
+ reason: "Invalid SQL wastes resources and confuses users.",
2765
+ action: "Validate syntax and schema references before returning."
2766
+ }),
2767
+ guardrail({
2768
+ rule: "Only generate SELECT/WITH statements (read-only queries).",
2769
+ reason: "Prevents accidental data modification.",
2770
+ action: "Never generate INSERT, UPDATE, DELETE, DROP, or other DDL/DML statements."
2771
+ }),
2772
+ guardrail({
2773
+ rule: "Avoid unbounded scans on large tables.",
2774
+ reason: "Protects performance and prevents runaway queries.",
2775
+ action: "Ensure filters are applied on indexed columns before querying broad fact tables."
2776
+ }),
2777
+ guardrail({
2778
+ rule: "Do not add LIMIT unless explicitly requested.",
2779
+ action: 'Only add LIMIT when user explicitly asks for "top N", "first N", or similar. Do NOT add LIMIT for "list all", "show all", or simple "list" queries.',
2780
+ reason: "Adding arbitrary limits changes query semantics."
2781
+ }),
2782
+ guardrail({
2783
+ rule: "Add ORDER BY where appropriate for deterministic results.",
2784
+ reason: "Ensures consistent query output.",
2785
+ action: "Include ORDER BY when results have a natural ordering or when combined with LIMIT."
2786
+ }),
2787
+ guardrail({
2788
+ rule: "Prevent cartesian or guesswork joins.",
2789
+ reason: "Protect correctness and performance.",
2790
+ action: "If join keys are missing or unclear, inspect relationships and ask for the intended join path before executing."
2791
+ }),
2792
+ guardrail({
2793
+ rule: "Ensure the query is optimized for the schema.",
2794
+ reason: "Better performance and resource usage.",
2795
+ action: "Use indexed columns for filtering, avoid SELECT * on large joins, prefer specific column selection when appropriate."
2796
+ }),
2797
+ guardrail({
2798
+ rule: "When facing genuine ambiguity with multiple valid interpretations, seek clarification.",
2799
+ reason: "Prevents incorrect assumptions in edge cases.",
2800
+ action: "Ask a focused clarifying question before proceeding with a guess."
2801
+ }),
2802
+ // Clarifications
2803
+ clarification({
2804
+ when: 'The request uses ambiguous scoring or ranking language (e.g., "top", "best", "active") without a metric.',
2805
+ ask: "Clarify the ranking metric or definition before writing the query.",
2806
+ reason: "Ensures the correct aggregation/ordering is used."
2807
+ }),
2808
+ // Workflow
2809
+ workflow({
2810
+ task: "SQL generation plan",
2811
+ steps: [
2812
+ 'Scan column names for terms matching the question. If a phrase like "total X" or "number of Y" matches a column name (e.g., Total_X, Num_Y), select that column directly instead of aggregating.',
2813
+ "Translate the question into SQL patterns (aggregation, segmentation, time range, ranking) only if no column name match.",
2814
+ "Choose tables/relations that satisfy those patterns; note lookup tables and filter values implied by schema hints.",
2815
+ "Sketch join/filter/aggregation order considering table sizes, indexes, and stats.",
2816
+ "Generate precise, validated SQL that answers the question."
2817
+ ]
2818
+ })
2819
+ ];
2820
+ if (date === "strict") {
2821
+ baseTeachings.push(
2822
+ clarification({
2823
+ when: "The request targets time-based data without a date range.",
2824
+ ask: "Confirm the intended timeframe (e.g., last 30/90 days, YTD, specific year).",
2825
+ reason: "Prevents large scans and irrelevant results."
2826
+ })
2827
+ );
2828
+ } else {
2829
+ baseTeachings.push(
2830
+ hint(
2831
+ 'When a month, day, or time period is mentioned without a year (e.g., "in August", "on Monday"), assume ALL occurrences of that period in the data. Do not ask for year clarification.'
2832
+ )
2833
+ );
2834
+ }
2835
+ return baseTeachings;
2836
+ }
2837
+
2838
+ // packages/text2sql/src/lib/sql.ts
2839
+ var Text2Sql = class {
2840
+ #config;
2841
+ constructor(config) {
2842
+ this.#config = {
2843
+ adapter: config.adapter,
2844
+ history: config.history,
2845
+ instructions: [
2846
+ ...guidelines(config.teachingsOptions),
2847
+ ...config.instructions ?? []
2848
+ ],
2849
+ tools: config.tools ?? {},
2850
+ model: config.model,
2851
+ memory: config.memory,
2852
+ introspection: new FileCache("introspection-" + config.version)
2853
+ };
2854
+ }
2855
+ async explain(sql) {
2856
+ const { experimental_output } = await generate5(
2857
+ explainerAgent,
2858
+ [user4("Explain this SQL.")],
2859
+ { sql }
2860
+ );
2861
+ return experimental_output.explanation;
2862
+ }
2863
+ async toSql(input) {
2864
+ const introspection = await this.index();
2865
+ const result = await toSql({
2866
+ input,
2867
+ adapter: this.#config.adapter,
2868
+ introspection,
2869
+ instructions: this.#config.instructions,
2870
+ model: this.#config.model
2871
+ });
2872
+ return result.sql;
1113
2873
  }
1114
2874
  instruct(...dataset) {
1115
2875
  this.#config.instructions.push(...dataset);
1116
2876
  }
1117
- async inspect(agent4) {
2877
+ async inspect(agent11) {
1118
2878
  const [grounding] = await Promise.all([this.index()]);
1119
2879
  const renderToolNames = Object.keys(this.#config.tools ?? {}).filter(
1120
2880
  (name) => name.startsWith("render_")
1121
2881
  );
1122
2882
  const allInstructions = [
1123
2883
  ...this.#config.instructions,
2884
+ guardrail({
2885
+ rule: "ALWAYS use `get_sample_rows` before writing queries that filter or compare against string columns.",
2886
+ reason: "Prevents SQL errors from wrong value formats.",
2887
+ action: "Target specific columns (e.g., get_sample_rows('table', ['status', 'type']))."
2888
+ }),
1124
2889
  ...renderToolNames.length ? [
1125
2890
  hint(`Rendering tools available: ${renderToolNames.join(", ")}.`),
1126
2891
  styleGuide({
@@ -1129,24 +2894,16 @@ var Text2Sql = class {
1129
2894
  })
1130
2895
  ] : []
1131
2896
  ];
1132
- const tools2 = Object.keys({
1133
- ...agent4.handoff.tools,
2897
+ const tools8 = Object.keys({
2898
+ ...agent11.handoff.tools,
1134
2899
  ...this.#config.memory ? memoryTools : {},
1135
2900
  ...this.#config.tools
1136
2901
  });
1137
2902
  return {
1138
- tools: tools2,
1139
- prompt: agent4.instructions({
2903
+ tools: tools8,
2904
+ prompt: agent11.instructions({
1140
2905
  introspection: grounding,
1141
- teachings: toInstructions(
1142
- "instructions",
1143
- persona({
1144
- name: "Freya",
1145
- role: "You are an expert SQL query generator, answering business questions with accurate queries.",
1146
- tone: "Your tone should be concise and business-friendly."
1147
- }),
1148
- ...allInstructions
1149
- )
2906
+ teachings: toInstructions("instructions", ...allInstructions)
1150
2907
  })
1151
2908
  };
1152
2909
  }
@@ -1159,6 +2916,29 @@ var Text2Sql = class {
1159
2916
  await this.#config.introspection.set(introspection);
1160
2917
  return introspection;
1161
2918
  }
2919
+ /**
2920
+ * Generate training data pairs using a producer factory.
2921
+ * The factory receives the configured adapter, so users don't need to pass it manually.
2922
+ *
2923
+ * @example
2924
+ * // Generate questions for existing SQL
2925
+ * const pairs = await text2sql.toPairs(
2926
+ * (adapter) => new SqlExtractor(sqls, adapter, { validateSql: true })
2927
+ * );
2928
+ *
2929
+ * @example
2930
+ * // Extract from chat history with validation
2931
+ * const pairs = await text2sql.toPairs(
2932
+ * (adapter) => new ValidatedProducer(
2933
+ * new MessageExtractor(messages),
2934
+ * adapter
2935
+ * )
2936
+ * );
2937
+ */
2938
+ async toPairs(factory) {
2939
+ const producer = factory(this.#config.adapter);
2940
+ return toPairs(producer);
2941
+ }
1162
2942
  // public async suggest() {
1163
2943
  // const [introspection, adapterInfo] = await Promise.all([
1164
2944
  // this.index(),
@@ -1191,6 +2971,11 @@ var Text2Sql = class {
1191
2971
  );
1192
2972
  const instructions = [
1193
2973
  ...this.#config.instructions,
2974
+ guardrail({
2975
+ rule: "ALWAYS use `get_sample_rows` before writing queries that filter or compare against string columns.",
2976
+ reason: "Prevents SQL errors from wrong value formats.",
2977
+ action: "Target specific columns (e.g., get_sample_rows('table', ['status', 'type']))."
2978
+ }),
1194
2979
  ...renderToolNames.length ? [
1195
2980
  hint(`Rendering tools available: ${renderToolNames.join(", ")}.`),
1196
2981
  styleGuide({
@@ -1199,6 +2984,10 @@ var Text2Sql = class {
1199
2984
  })
1200
2985
  ] : []
1201
2986
  ];
2987
+ const originalMessage = [
2988
+ ...chat.messages.map((it) => it.content),
2989
+ ...messages
2990
+ ];
1202
2991
  const result = stream(
1203
2992
  t_a_g.clone({
1204
2993
  model: this.#config.model,
@@ -1208,7 +2997,7 @@ var Text2Sql = class {
1208
2997
  ...this.#config.tools
1209
2998
  }
1210
2999
  }),
1211
- [...chat.messages.map((it) => it.content), ...messages],
3000
+ originalMessage,
1212
3001
  {
1213
3002
  teachings: toInstructions(
1214
3003
  "instructions",
@@ -1226,10 +3015,395 @@ var Text2Sql = class {
1226
3015
  userId: params.userId
1227
3016
  }
1228
3017
  );
3018
+ return this.#createUIMessageStream(
3019
+ result,
3020
+ messages,
3021
+ params,
3022
+ originalMessage
3023
+ );
3024
+ }
3025
+ /**
3026
+ * Chat1 - Combined tool, no peek.
3027
+ *
3028
+ * Uses a single `query_database` tool that:
3029
+ * 1. Takes a natural language question
3030
+ * 2. Internally calls toSql() to generate validated SQL
3031
+ * 3. Executes the SQL
3032
+ * 4. Returns both SQL and results
3033
+ *
3034
+ * The agent does NOT see the SQL before execution.
3035
+ */
3036
+ async chat1(messages, params) {
3037
+ const [introspection, userTeachables] = await Promise.all([
3038
+ this.index({ onProgress: console.log }),
3039
+ this.#config.memory ? this.#config.memory.toTeachables(params.userId) : []
3040
+ ]);
3041
+ const chat = await this.#config.history.upsertChat({
3042
+ id: params.chatId,
3043
+ userId: params.userId,
3044
+ title: "Chat " + params.chatId
3045
+ });
3046
+ const renderToolNames = Object.keys(this.#config.tools ?? {}).filter(
3047
+ (name) => name.startsWith("render_")
3048
+ );
3049
+ const instructions = [
3050
+ ...this.#config.instructions,
3051
+ ...renderToolNames.length ? [
3052
+ hint(`Rendering tools available: ${renderToolNames.join(", ")}.`),
3053
+ styleGuide({
3054
+ prefer: "Use render_* tools for trend/over time/monthly requests or chart asks",
3055
+ always: "Include text insight alongside visualizations. Prefer line charts for time-based data."
3056
+ })
3057
+ ] : []
3058
+ ];
3059
+ const originalMessage = [
3060
+ ...chat.messages.map((it) => it.content),
3061
+ ...messages
3062
+ ];
3063
+ const result = stream(
3064
+ chat1Agent.clone({
3065
+ model: this.#config.model,
3066
+ tools: {
3067
+ ...tools4,
3068
+ ...this.#config.memory ? memoryTools : {},
3069
+ ...this.#config.tools
3070
+ }
3071
+ }),
3072
+ originalMessage,
3073
+ {
3074
+ teachings: toInstructions(
3075
+ "instructions",
3076
+ ...instructions,
3077
+ teachable("user_profile", ...userTeachables)
3078
+ ),
3079
+ adapter: this.#config.adapter,
3080
+ introspection,
3081
+ instructions: this.#config.instructions,
3082
+ memory: this.#config.memory,
3083
+ userId: params.userId
3084
+ }
3085
+ );
3086
+ return this.#createUIMessageStream(
3087
+ result,
3088
+ messages,
3089
+ params,
3090
+ originalMessage
3091
+ );
3092
+ }
3093
+ /**
3094
+ * Chat2 - Separate generate + execute tools (with peek).
3095
+ *
3096
+ * Uses two separate tools:
3097
+ * 1. `generate_sql` - Takes a question, returns validated SQL
3098
+ * 2. `execute_sql` - Takes SQL, executes it
3099
+ *
3100
+ * The agent sees the SQL before execution and can review/refine.
3101
+ */
3102
+ async chat2(messages, params) {
3103
+ const [introspection, userTeachables] = await Promise.all([
3104
+ this.index({ onProgress: console.log }),
3105
+ this.#config.memory ? this.#config.memory.toTeachables(params.userId) : []
3106
+ ]);
3107
+ const chat = await this.#config.history.upsertChat({
3108
+ id: params.chatId,
3109
+ userId: params.userId,
3110
+ title: "Chat " + params.chatId
3111
+ });
3112
+ const renderToolNames = Object.keys(this.#config.tools ?? {}).filter(
3113
+ (name) => name.startsWith("render_")
3114
+ );
3115
+ const instructions = [
3116
+ ...this.#config.instructions,
3117
+ ...renderToolNames.length ? [
3118
+ hint(`Rendering tools available: ${renderToolNames.join(", ")}.`),
3119
+ styleGuide({
3120
+ prefer: "Use render_* tools for trend/over time/monthly requests or chart asks",
3121
+ always: "Include text insight alongside visualizations. Prefer line charts for time-based data."
3122
+ })
3123
+ ] : []
3124
+ ];
3125
+ const originalMessage = [
3126
+ ...chat.messages.map((it) => it.content),
3127
+ ...messages
3128
+ ];
3129
+ const result = stream(
3130
+ chat2Agent.clone({
3131
+ model: this.#config.model,
3132
+ tools: {
3133
+ ...tools5,
3134
+ ...this.#config.memory ? memoryTools : {},
3135
+ ...this.#config.tools
3136
+ }
3137
+ }),
3138
+ originalMessage,
3139
+ {
3140
+ teachings: toInstructions(
3141
+ "instructions",
3142
+ ...instructions,
3143
+ teachable("user_profile", ...userTeachables)
3144
+ ),
3145
+ adapter: this.#config.adapter,
3146
+ introspection,
3147
+ instructions: this.#config.instructions,
3148
+ memory: this.#config.memory,
3149
+ userId: params.userId
3150
+ }
3151
+ );
3152
+ return this.#createUIMessageStream(
3153
+ result,
3154
+ messages,
3155
+ params,
3156
+ originalMessage
3157
+ );
3158
+ }
3159
+ /**
3160
+ * Chat3 - Agent conversation/collaboration.
3161
+ *
3162
+ * Enables richer interaction where the SQL agent can:
3163
+ * - Surface confidence levels
3164
+ * - State assumptions
3165
+ * - Request clarification when uncertain
3166
+ */
3167
+ async chat3(messages, params) {
3168
+ const [introspection, userTeachables] = await Promise.all([
3169
+ this.index({ onProgress: console.log }),
3170
+ this.#config.memory ? this.#config.memory.toTeachables(params.userId) : []
3171
+ ]);
3172
+ const chat = await this.#config.history.upsertChat({
3173
+ id: params.chatId,
3174
+ userId: params.userId,
3175
+ title: "Chat " + params.chatId
3176
+ });
3177
+ const renderToolNames = Object.keys(this.#config.tools ?? {}).filter(
3178
+ (name) => name.startsWith("render_")
3179
+ );
3180
+ const instructions = [
3181
+ ...this.#config.instructions,
3182
+ ...renderToolNames.length ? [
3183
+ hint(`Rendering tools available: ${renderToolNames.join(", ")}.`),
3184
+ styleGuide({
3185
+ prefer: "Use render_* tools for trend/over time/monthly requests or chart asks",
3186
+ always: "Include text insight alongside visualizations. Prefer line charts for time-based data."
3187
+ })
3188
+ ] : []
3189
+ ];
3190
+ const originalMessage = [
3191
+ ...chat.messages.map((it) => it.content),
3192
+ ...messages
3193
+ ];
3194
+ const result = stream(
3195
+ chat3Agent.clone({
3196
+ model: this.#config.model,
3197
+ tools: {
3198
+ ...tools6,
3199
+ ...this.#config.memory ? memoryTools : {},
3200
+ ...this.#config.tools
3201
+ }
3202
+ }),
3203
+ originalMessage,
3204
+ {
3205
+ teachings: toInstructions(
3206
+ "instructions",
3207
+ ...instructions,
3208
+ teachable("user_profile", ...userTeachables)
3209
+ ),
3210
+ adapter: this.#config.adapter,
3211
+ introspection,
3212
+ instructions: this.#config.instructions,
3213
+ memory: this.#config.memory,
3214
+ userId: params.userId
3215
+ }
3216
+ );
3217
+ return this.#createUIMessageStream(
3218
+ result,
3219
+ messages,
3220
+ params,
3221
+ originalMessage
3222
+ );
3223
+ }
3224
+ /**
3225
+ * Chat4 - Question decomposition approach.
3226
+ *
3227
+ * Breaks down questions into semantic components before SQL generation:
3228
+ * - entities: Key concepts mentioned
3229
+ * - filters: Filtering criteria
3230
+ * - aggregation: Type of aggregation
3231
+ * - breakdown: Semantic parts of the question
3232
+ *
3233
+ * This helps ensure all aspects of the question are addressed.
3234
+ */
3235
+ async chat4(messages, params) {
3236
+ const [introspection, userTeachables] = await Promise.all([
3237
+ this.index({ onProgress: console.log }),
3238
+ this.#config.memory ? this.#config.memory.toTeachables(params.userId) : []
3239
+ ]);
3240
+ const chat = await this.#config.history.upsertChat({
3241
+ id: params.chatId,
3242
+ userId: params.userId,
3243
+ title: "Chat " + params.chatId
3244
+ });
3245
+ const renderToolNames = Object.keys(this.#config.tools ?? {}).filter(
3246
+ (name) => name.startsWith("render_")
3247
+ );
3248
+ const instructions = [
3249
+ ...this.#config.instructions,
3250
+ ...renderToolNames.length ? [
3251
+ hint(`Rendering tools available: ${renderToolNames.join(", ")}.`),
3252
+ styleGuide({
3253
+ prefer: "Use render_* tools for trend/over time/monthly requests or chart asks",
3254
+ always: "Include text insight alongside visualizations. Prefer line charts for time-based data."
3255
+ })
3256
+ ] : []
3257
+ ];
3258
+ const originalMessage = [
3259
+ ...chat.messages.map((it) => it.content),
3260
+ ...messages
3261
+ ];
3262
+ const result = stream(
3263
+ chat4Agent.clone({
3264
+ model: this.#config.model,
3265
+ tools: {
3266
+ ...tools7,
3267
+ ...this.#config.memory ? memoryTools : {},
3268
+ ...this.#config.tools
3269
+ }
3270
+ }),
3271
+ originalMessage,
3272
+ {
3273
+ teachings: toInstructions(
3274
+ "instructions",
3275
+ ...instructions,
3276
+ teachable("user_profile", ...userTeachables)
3277
+ ),
3278
+ adapter: this.#config.adapter,
3279
+ introspection,
3280
+ instructions: this.#config.instructions,
3281
+ memory: this.#config.memory,
3282
+ userId: params.userId
3283
+ }
3284
+ );
3285
+ return this.#createUIMessageStream(
3286
+ result,
3287
+ messages,
3288
+ params,
3289
+ originalMessage
3290
+ );
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
+ }
3399
+ /**
3400
+ * Helper to create UI message stream with common error handling and persistence.
3401
+ */
3402
+ #createUIMessageStream(result, messages, params, originalMessages) {
1229
3403
  return result.toUIMessageStream({
1230
3404
  onError: (error) => {
1231
3405
  if (NoSuchToolError.isInstance(error)) {
1232
- return "The model tried to call a unknown tool.";
3406
+ return "The model tried to call an unknown tool.";
1233
3407
  } else if (InvalidToolInputError.isInstance(error)) {
1234
3408
  return "The model called a tool with invalid arguments.";
1235
3409
  } else if (ToolCallRepairError.isInstance(error)) {
@@ -1242,46 +3416,54 @@ var Text2Sql = class {
1242
3416
  sendFinish: true,
1243
3417
  sendReasoning: true,
1244
3418
  sendSources: true,
1245
- originalMessages: messages,
3419
+ originalMessages,
1246
3420
  generateMessageId: generateId,
1247
- onFinish: async ({ messages: messages2 }) => {
1248
- const userMessage = messages2.at(-2);
1249
- const botMessage = messages2.at(-1);
1250
- if (!userMessage || !botMessage) {
1251
- throw new Error("Not implemented yet");
3421
+ onFinish: async ({ responseMessage, isContinuation }) => {
3422
+ const userMessage = messages.at(-1);
3423
+ if (!isContinuation && userMessage) {
3424
+ console.log(
3425
+ "Saving user message to history:",
3426
+ JSON.stringify(userMessage)
3427
+ );
3428
+ await this.#config.history.addMessage({
3429
+ id: v72(),
3430
+ chatId: params.chatId,
3431
+ role: userMessage.role,
3432
+ content: userMessage
3433
+ });
1252
3434
  }
1253
3435
  await this.#config.history.addMessage({
1254
3436
  id: v72(),
1255
3437
  chatId: params.chatId,
1256
- role: userMessage.role,
1257
- content: userMessage
1258
- });
1259
- await this.#config.history.addMessage({
1260
- id: v72(),
1261
- chatId: params.chatId,
1262
- role: botMessage.role,
1263
- content: botMessage
3438
+ role: responseMessage.role,
3439
+ content: responseMessage
1264
3440
  });
1265
3441
  }
1266
3442
  });
1267
3443
  }
1268
3444
  };
1269
-
1270
- // packages/text2sql/src/index.ts
1271
- if (import.meta.main) {
1272
- }
1273
3445
  export {
3446
+ Adapter,
3447
+ Checkpoint,
1274
3448
  FileCache,
1275
3449
  History,
1276
3450
  InMemoryHistory,
1277
3451
  InMemoryTeachablesStore,
1278
3452
  JsonCache,
3453
+ Point,
1279
3454
  SqliteHistory,
1280
3455
  SqliteTeachablesStore,
1281
3456
  TeachablesStore,
1282
3457
  Text2Sql,
3458
+ applyTablesFilter,
3459
+ developerAgent,
3460
+ filterRelationshipsByTables,
3461
+ filterTablesByName,
3462
+ getTablesWithRelated,
3463
+ guidelines,
3464
+ hashConfig,
3465
+ matchesFilter,
1283
3466
  memoryTools,
1284
- sqlQueryAgent,
1285
3467
  suggestionsAgent,
1286
3468
  t_a_g
1287
3469
  };