@deepagents/text2sql 0.24.0 → 0.26.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 (35) hide show
  1. package/dist/index.d.ts +0 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +149 -241
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/adapters/adapter.d.ts +6 -0
  6. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  7. package/dist/lib/adapters/bigquery/index.js +30 -8
  8. package/dist/lib/adapters/bigquery/index.js.map +2 -2
  9. package/dist/lib/adapters/bigquery/view.bigquery.grounding.d.ts.map +1 -1
  10. package/dist/lib/adapters/groundings/index.js +2 -0
  11. package/dist/lib/adapters/groundings/index.js.map +2 -2
  12. package/dist/lib/adapters/groundings/view.grounding.d.ts +3 -0
  13. package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
  14. package/dist/lib/adapters/mysql/index.js +29 -7
  15. package/dist/lib/adapters/mysql/index.js.map +2 -2
  16. package/dist/lib/adapters/mysql/view.mysql.grounding.d.ts.map +1 -1
  17. package/dist/lib/adapters/postgres/index.js +29 -7
  18. package/dist/lib/adapters/postgres/index.js.map +2 -2
  19. package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts.map +1 -1
  20. package/dist/lib/adapters/spreadsheet/index.js +16 -0
  21. package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
  22. package/dist/lib/adapters/sqlite/index.js +26 -4
  23. package/dist/lib/adapters/sqlite/index.js.map +2 -2
  24. package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts.map +1 -1
  25. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts +2 -4
  26. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts.map +1 -1
  27. package/dist/lib/adapters/sqlserver/column-values.sqlserver.grounding.d.ts +0 -9
  28. package/dist/lib/adapters/sqlserver/column-values.sqlserver.grounding.d.ts.map +1 -1
  29. package/dist/lib/adapters/sqlserver/index.js +107 -11
  30. package/dist/lib/adapters/sqlserver/index.js.map +3 -3
  31. package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts.map +1 -1
  32. package/dist/lib/agents/result-tools.d.ts.map +1 -1
  33. package/package.json +4 -4
  34. package/dist/lib/agents/developer.agent.d.ts +0 -41
  35. package/dist/lib/agents/developer.agent.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -121,14 +121,30 @@ var Adapter = class {
121
121
  }
122
122
  return this.#toSchemaFragments(ctx);
123
123
  }
124
+ /**
125
+ * Resolve the allowed entity names (tables + views) from grounding config.
126
+ * Runs all configured groundings and returns the resolved set of names.
127
+ * Results are NOT cached — call once and store the result.
128
+ */
129
+ async resolveAllowedEntities() {
130
+ const ctx = createGroundingContext();
131
+ for (const fn of this.grounding) {
132
+ const grounding = fn(this);
133
+ await grounding.execute(ctx);
134
+ }
135
+ return [
136
+ ...ctx.tables.map((t) => t.name),
137
+ ...ctx.views.map((v) => v.name)
138
+ ];
139
+ }
124
140
  /**
125
141
  * Convert complete grounding context to schema fragments.
126
142
  * Called after all groundings have populated ctx with data.
127
143
  */
128
144
  #toSchemaFragments(ctx) {
129
- const fragments2 = [];
145
+ const fragments = [];
130
146
  if (ctx.info) {
131
- fragments2.push(
147
+ fragments.push(
132
148
  dialectInfo({
133
149
  dialect: ctx.info.dialect,
134
150
  version: ctx.info.version,
@@ -137,23 +153,23 @@ var Adapter = class {
137
153
  );
138
154
  }
139
155
  for (const t of ctx.tables) {
140
- fragments2.push(this.#tableToFragment(t));
156
+ fragments.push(this.#tableToFragment(t));
141
157
  }
142
158
  for (const v of ctx.views) {
143
- fragments2.push(this.#viewToFragment(v));
159
+ fragments.push(this.#viewToFragment(v));
144
160
  }
145
161
  const tableMap = new Map(ctx.tables.map((t) => [t.name, t]));
146
162
  for (const rel of ctx.relationships) {
147
163
  const sourceTable = tableMap.get(rel.table);
148
164
  const targetTable = tableMap.get(rel.referenced_table);
149
- fragments2.push(
165
+ fragments.push(
150
166
  this.#relationshipToFragment(rel, sourceTable, targetTable)
151
167
  );
152
168
  }
153
169
  if (ctx.report) {
154
- fragments2.push({ name: "businessContext", data: ctx.report });
170
+ fragments.push({ name: "businessContext", data: ctx.report });
155
171
  }
156
- return fragments2;
172
+ return fragments;
157
173
  }
158
174
  /**
159
175
  * Convert a Table to a table fragment with nested column, index, and constraint fragments.
@@ -396,122 +412,6 @@ function getTablesWithRelated(allTables, relationships, filter) {
396
412
  return Array.from(result);
397
413
  }
398
414
 
399
- // packages/text2sql/src/lib/agents/developer.agent.ts
400
- import { tool } from "ai";
401
- import dedent from "dedent";
402
- import z2 from "zod";
403
- import { toState } from "@deepagents/agent";
404
- import { hint, persona as persona2 } from "@deepagents/context";
405
-
406
- // packages/text2sql/src/lib/agents/explainer.agent.ts
407
- import { groq } from "@ai-sdk/groq";
408
- import z from "zod";
409
- import {
410
- ContextEngine,
411
- InMemoryContextStore,
412
- fragment,
413
- persona,
414
- structuredOutput,
415
- user
416
- } from "@deepagents/context";
417
- var outputSchema = z.object({
418
- explanation: z.string().describe("The explanation of the SQL query.")
419
- });
420
- async function explainSql(sql) {
421
- const context = new ContextEngine({
422
- store: new InMemoryContextStore(),
423
- chatId: `explainer-${crypto.randomUUID()}`,
424
- userId: "system"
425
- });
426
- context.set(
427
- persona({
428
- name: "explainer",
429
- role: "You are an expert SQL tutor.",
430
- objective: "Explain SQL queries in plain English that non-technical users understand"
431
- }),
432
- fragment("sql", sql),
433
- fragment("task", "Focus on the intent and logic, not the syntax."),
434
- user("Explain this SQL query in plain English to a non-technical user.")
435
- );
436
- const explainerOutput = structuredOutput({
437
- model: groq("openai/gpt-oss-20b"),
438
- context,
439
- schema: outputSchema
440
- });
441
- return explainerOutput.generate();
442
- }
443
-
444
- // packages/text2sql/src/lib/agents/developer.agent.ts
445
- var tools = {
446
- /**
447
- * Validate SQL query syntax before execution.
448
- */
449
- validate_query: tool({
450
- 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.`,
451
- inputSchema: z2.object({
452
- sql: z2.string().describe("The SQL query to validate.")
453
- }),
454
- execute: async ({ sql }, options) => {
455
- const state = toState(options);
456
- const result = await state.adapter.validate(sql);
457
- if (typeof result === "string") {
458
- return `Validation Error: ${result}`;
459
- }
460
- return "Query is valid.";
461
- }
462
- }),
463
- /**
464
- * Execute SQL query against the database.
465
- */
466
- db_query: tool({
467
- 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.`,
468
- inputSchema: z2.object({
469
- reasoning: z2.string().describe(
470
- "Your reasoning for why this SQL query is relevant to the user request."
471
- ),
472
- sql: z2.string().min(1, { message: "SQL query cannot be empty." }).refine(
473
- (sql) => sql.trim().toUpperCase().startsWith("SELECT") || sql.trim().toUpperCase().startsWith("WITH"),
474
- {
475
- message: "Only read-only SELECT or WITH queries are allowed."
476
- }
477
- ).describe("The SQL query to execute against the database.")
478
- }),
479
- execute: ({ sql }, options) => {
480
- const state = toState(options);
481
- return state.adapter.execute(sql);
482
- }
483
- }),
484
- /**
485
- * Get plain-English explanation of a SQL query.
486
- */
487
- explain_sql: tool({
488
- description: dedent`
489
- Get a plain-English explanation of a SQL query.
490
- Use this to help the user understand what a query does.
491
-
492
- The explanation focuses on intent and logic, not syntax.
493
- `,
494
- inputSchema: z2.object({
495
- sql: z2.string().min(1).describe("The SQL query to explain")
496
- }),
497
- execute: async ({ sql }) => {
498
- return explainSql(sql);
499
- }
500
- })
501
- };
502
- var fragments = [
503
- persona2({
504
- name: "developer_assistant",
505
- role: "You are an expert SQL developer assistant helping power users build and refine queries.",
506
- objective: "Help power users build and refine SQL queries with precision and clarity"
507
- }),
508
- hint("Be transparent: show the SQL you generate before explaining it"),
509
- hint("Be precise: provide exact column names and table references"),
510
- hint("Suggest refinements and alternatives when appropriate"),
511
- hint("Support both natural language questions AND raw SQL input"),
512
- hint("When validating user SQL, explain any errors clearly")
513
- ];
514
-
515
415
  // packages/text2sql/src/lib/agents/exceptions.ts
516
416
  var sqlValidationMarker = Symbol("SQLValidationError");
517
417
  var unanswerableSqlMarker = Symbol("UnanswerableSQLError");
@@ -539,7 +439,7 @@ var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
539
439
  };
540
440
 
541
441
  // packages/text2sql/src/lib/agents/result-tools.ts
542
- import { tool as tool2 } from "ai";
442
+ import { tool } from "ai";
543
443
  import { createBashTool } from "bash-tool";
544
444
  import chalk from "chalk";
545
445
  import {
@@ -552,7 +452,7 @@ import {
552
452
  import { AsyncLocalStorage } from "node:async_hooks";
553
453
  import * as path from "node:path";
554
454
  import { v7 } from "uuid";
555
- import z3 from "zod";
455
+ import z from "zod";
556
456
  function createCommand(name, subcommands) {
557
457
  const usageLines = Object.entries(subcommands).map(([, def]) => ` ${name} ${def.usage.padEnd(30)} ${def.description}`).join("\n");
558
458
  return defineCommand(name, async (args, ctx) => {
@@ -578,13 +478,18 @@ function validateReadOnly(query) {
578
478
  }
579
479
  return { valid: true };
580
480
  }
481
+ var SQL_VALIDATE_REMINDER = "Always run `sql validate` before `sql run` to catch syntax errors early.";
581
482
  function createSqlCommand(adapter, metaStore) {
582
483
  return createCommand("sql", {
583
484
  run: {
584
485
  usage: 'run "SELECT ..."',
585
486
  description: "Execute query and store results",
586
487
  handler: async (args, ctx) => {
587
- const rawQuery = args.join(" ").trim();
488
+ const store = metaStore.getStore();
489
+ if (store) {
490
+ store.value = { ...store.value, reminder: SQL_VALIDATE_REMINDER };
491
+ }
492
+ const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
588
493
  if (!rawQuery) {
589
494
  return {
590
495
  stdout: "",
@@ -601,8 +506,9 @@ function createSqlCommand(adapter, metaStore) {
601
506
  };
602
507
  }
603
508
  const query = adapter.format(rawQuery);
604
- const store = metaStore.getStore();
605
- if (store) store.value = { formattedSql: query };
509
+ if (store) {
510
+ store.value = { ...store.value, formattedSql: query };
511
+ }
606
512
  const syntaxError = await adapter.validate(query);
607
513
  if (syntaxError) {
608
514
  return {
@@ -642,7 +548,7 @@ function createSqlCommand(adapter, metaStore) {
642
548
  usage: 'validate "SELECT ..."',
643
549
  description: "Validate query syntax",
644
550
  handler: async (args) => {
645
- const rawQuery = args.join(" ").trim();
551
+ const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
646
552
  if (!rawQuery) {
647
553
  return {
648
554
  stdout: "",
@@ -660,7 +566,7 @@ function createSqlCommand(adapter, metaStore) {
660
566
  }
661
567
  const query = adapter.format(rawQuery);
662
568
  const store = metaStore.getStore();
663
- if (store) store.value = { formattedSql: query };
569
+ if (store) store.value = { ...store.value, formattedSql: query };
664
570
  const syntaxError = await adapter.validate(query);
665
571
  if (syntaxError) {
666
572
  return {
@@ -1358,7 +1264,7 @@ async function createResultTools(options) {
1358
1264
  customCommands: [sqlCommand],
1359
1265
  fs: filesystem
1360
1266
  });
1361
- const { sandbox, tools: tools2 } = await createBashTool({
1267
+ const { sandbox, tools } = await createBashTool({
1362
1268
  sandbox: bashInstance,
1363
1269
  destination: "/",
1364
1270
  extraInstructions: 'Every bash tool call must include a brief non-empty "reasoning" input explaining why the command is needed.',
@@ -1383,14 +1289,14 @@ async function createResultTools(options) {
1383
1289
  return sandbox.executeCommand(command);
1384
1290
  }
1385
1291
  };
1386
- const bash = tool2({
1387
- ...tools2.bash,
1388
- inputSchema: z3.object({
1389
- command: z3.string().describe("The bash command to execute"),
1390
- reasoning: z3.string().trim().describe("Brief reason for executing this command")
1292
+ const bash = tool({
1293
+ ...tools.bash,
1294
+ inputSchema: z.object({
1295
+ command: z.string().describe("The bash command to execute"),
1296
+ reasoning: z.string().trim().describe("Brief reason for executing this command")
1391
1297
  }),
1392
1298
  execute: async ({ command }, execOptions) => {
1393
- const execute = tools2.bash.execute;
1299
+ const execute = tools.bash.execute;
1394
1300
  if (!execute) {
1395
1301
  throw new Error("bash tool execution is not available");
1396
1302
  }
@@ -1400,8 +1306,10 @@ async function createResultTools(options) {
1400
1306
  }
1401
1307
  return metaStore.run({}, async () => {
1402
1308
  const result = await execute({ command }, execOptions);
1403
- const meta = metaStore.getStore()?.value;
1404
- return meta ? { ...result, meta } : result;
1309
+ const storeValue = metaStore.getStore()?.value;
1310
+ if (!storeValue) return result;
1311
+ const { reminder, ...meta } = storeValue;
1312
+ return { ...result, meta, reminder };
1405
1313
  });
1406
1314
  },
1407
1315
  toModelOutput: ({ output }) => {
@@ -1415,14 +1323,14 @@ async function createResultTools(options) {
1415
1323
  return {
1416
1324
  sandbox: guardedSandbox,
1417
1325
  tools: {
1418
- ...tools2,
1326
+ ...tools,
1419
1327
  bash
1420
1328
  }
1421
1329
  };
1422
1330
  }
1423
1331
 
1424
1332
  // packages/text2sql/src/lib/agents/sql.agent.ts
1425
- import { groq as groq2 } from "@ai-sdk/groq";
1333
+ import { groq } from "@ai-sdk/groq";
1426
1334
  import {
1427
1335
  APICallError,
1428
1336
  JSONParseError,
@@ -1433,35 +1341,35 @@ import {
1433
1341
  defaultSettingsMiddleware,
1434
1342
  wrapLanguageModel
1435
1343
  } from "ai";
1436
- import dedent2 from "dedent";
1344
+ import dedent from "dedent";
1437
1345
  import pRetry from "p-retry";
1438
- import z4 from "zod";
1346
+ import z2 from "zod";
1439
1347
  import "@deepagents/agent";
1440
1348
  import {
1441
- ContextEngine as ContextEngine2,
1442
- InMemoryContextStore as InMemoryContextStore2,
1349
+ ContextEngine,
1350
+ InMemoryContextStore,
1443
1351
  example,
1444
- fragment as fragment2,
1352
+ fragment,
1445
1353
  guardrail,
1446
- hint as hint2,
1447
- persona as persona3,
1354
+ hint,
1355
+ persona,
1448
1356
  policy,
1449
- structuredOutput as structuredOutput2,
1450
- user as user2,
1357
+ structuredOutput,
1358
+ user,
1451
1359
  workflow
1452
1360
  } from "@deepagents/context";
1453
1361
  var RETRY_TEMPERATURES = [0, 0.2, 0.3];
1454
1362
  var SQL_AGENT_ROLE = "Expert SQL query generator.";
1455
1363
  var SQL_AGENT_OBJECTIVE = "Generate precise SQL grounded in provided schema.";
1456
1364
  var SQL_AGENT_POLICIES = [
1457
- fragment2(
1365
+ fragment(
1458
1366
  "schema_mapping",
1459
1367
  policy({
1460
1368
  rule: "Translate natural language into precise SQL grounded in available schema entities."
1461
1369
  }),
1462
- hint2("Preserve schema spelling exactly, including typos in column names.")
1370
+ hint("Preserve schema spelling exactly, including typos in column names.")
1463
1371
  ),
1464
- fragment2(
1372
+ fragment(
1465
1373
  "projection_minimality",
1466
1374
  policy({
1467
1375
  rule: "Return only columns requested by the question; do not add helper columns unless explicitly requested."
@@ -1472,17 +1380,17 @@ var SQL_AGENT_POLICIES = [
1472
1380
  policy({
1473
1381
  rule: "Prefer selecting schema columns directly without derived expressions when direct selection answers the request."
1474
1382
  }),
1475
- hint2(
1383
+ hint(
1476
1384
  "Do not include ORDER BY, GROUP BY, or JOIN helper columns in SELECT output unless the question explicitly asks for them."
1477
1385
  ),
1478
1386
  policy({
1479
1387
  rule: "Use DISTINCT only when uniqueness is explicitly requested (for example distinct/unique/different/no duplicates)."
1480
1388
  }),
1481
- hint2(
1389
+ hint(
1482
1390
  'Do not infer DISTINCT from generic wording such as "some", plural nouns, or entity-set phrasing; for transactional/attendance-style tables, default to raw rows unless uniqueness is explicitly requested.'
1483
1391
  )
1484
1392
  ),
1485
- fragment2(
1393
+ fragment(
1486
1394
  "date_transform_safety",
1487
1395
  policy({
1488
1396
  rule: "Do not assume VARCHAR/TEXT values are parseable dates. Avoid date extraction functions on text columns by default."
@@ -1490,14 +1398,14 @@ var SQL_AGENT_POLICIES = [
1490
1398
  policy({
1491
1399
  rule: "Use date-part extraction only when both conditions hold: the question explicitly asks for transformation and schema values require transformation to produce that unit."
1492
1400
  }),
1493
- hint2(
1401
+ hint(
1494
1402
  "Do not apply SUBSTR, STRFTIME, DATE_PART, YEAR, or similar extraction functions unless the question explicitly asks for transformation and schema values require it."
1495
1403
  ),
1496
- hint2(
1404
+ hint(
1497
1405
  "If a column already represents the requested concept (for example a stored year-like value), use the column as-is."
1498
1406
  )
1499
1407
  ),
1500
- fragment2(
1408
+ fragment(
1501
1409
  "sql_minimality",
1502
1410
  guardrail({
1503
1411
  rule: "Never hallucinate tables or columns.",
@@ -1510,7 +1418,7 @@ var SQL_AGENT_POLICIES = [
1510
1418
  action: "Do not add date parsing, substring extraction, or derived columns unless explicitly required by the question or schema."
1511
1419
  })
1512
1420
  ),
1513
- fragment2(
1421
+ fragment(
1514
1422
  "preflight_checklist",
1515
1423
  workflow({
1516
1424
  task: "Final SQL preflight before returning output",
@@ -1523,7 +1431,7 @@ var SQL_AGENT_POLICIES = [
1523
1431
  ]
1524
1432
  })
1525
1433
  ),
1526
- fragment2(
1434
+ fragment(
1527
1435
  "set_semantics",
1528
1436
  policy({
1529
1437
  rule: "For questions asking where both condition A and condition B hold over an attribute, compute the intersection of qualifying sets for that attribute."
@@ -1531,28 +1439,28 @@ var SQL_AGENT_POLICIES = [
1531
1439
  policy({
1532
1440
  rule: "Do not force the same entity instance to satisfy both conditions unless the question explicitly requests the same person/row/entity."
1533
1441
  }),
1534
- hint2(
1442
+ hint(
1535
1443
  "Prefer INTERSECT (or logically equivalent set-based shape) over requiring the same physical row/entity to satisfy both conditions unless explicitly requested."
1536
1444
  ),
1537
- hint2(
1445
+ hint(
1538
1446
  "When two conditions describe different row groups whose shared attribute is requested, build each group separately and intersect the attribute values."
1539
1447
  ),
1540
- hint2(
1448
+ hint(
1541
1449
  "Do not collapse cross-group conditions into a single-row AND predicate when the intent is shared values across groups."
1542
1450
  ),
1543
1451
  policy({
1544
1452
  rule: "If two predicates on the same field cannot both be true for one row, do not combine them with AND; use set operations across separate filtered subsets when shared values are requested."
1545
1453
  })
1546
1454
  ),
1547
- fragment2(
1455
+ fragment(
1548
1456
  "predicate_column_alignment",
1549
1457
  policy({
1550
1458
  rule: "Match literal values to semantically compatible columns. Do not compare descriptive names to identifier columns."
1551
1459
  }),
1552
- hint2(
1460
+ hint(
1553
1461
  "When a filter value is a descriptive label (for example a department name), join through the lookup table and filter on its name/title column, not on *_id columns."
1554
1462
  ),
1555
- hint2(
1463
+ hint(
1556
1464
  "When relation roles are explicit in wording (for example host/home/source/destination), prefer foreign keys with matching role qualifiers over generic similarly named columns."
1557
1465
  ),
1558
1466
  policy({
@@ -1561,7 +1469,7 @@ var SQL_AGENT_POLICIES = [
1561
1469
  policy({
1562
1470
  rule: "For hosting/held semantics, prefer host_* relationship columns when available over generic *_id alternatives."
1563
1471
  }),
1564
- hint2(
1472
+ hint(
1565
1473
  'Interpret wording like "held/hosted a competition or event" as a hosting relationship and map to host_* foreign keys when present.'
1566
1474
  ),
1567
1475
  policy({
@@ -1574,7 +1482,7 @@ var SQL_AGENT_POLICIES = [
1574
1482
  rule: "When filtering by a descriptive label value and a related table exposes a corresponding *_name or title column, join to that table and filter on the descriptive column."
1575
1483
  })
1576
1484
  ),
1577
- fragment2(
1485
+ fragment(
1578
1486
  "ordering_semantics",
1579
1487
  policy({
1580
1488
  rule: "Respect explicit sort direction terms. If direction is not specified, use ascending order unless a superlative intent (most/least/highest/lowest) implies direction."
@@ -1588,23 +1496,23 @@ var SQL_AGENT_POLICIES = [
1588
1496
  policy({
1589
1497
  rule: 'For "most common/frequent <attribute>" requests, return the attribute value(s) only; use counts only for ordering/filtering unless the question explicitly asks to return counts.'
1590
1498
  }),
1591
- hint2(
1499
+ hint(
1592
1500
  'Use DESC with LIMIT 1 for "most/highest/largest"; use ASC with LIMIT 1 for "least/lowest/smallest".'
1593
1501
  )
1594
1502
  ),
1595
- fragment2(
1503
+ fragment(
1596
1504
  "negative_membership_queries",
1597
1505
  policy({
1598
1506
  rule: "For requests asking entities that did not participate/host/appear in related records, prefer NOT IN or NOT EXISTS against the related foreign-key set."
1599
1507
  }),
1600
- hint2(
1508
+ hint(
1601
1509
  "Map role-bearing relationship columns carefully (for example host_* foreign keys for hosting relationships) instead of generic IDs when role wording is explicit."
1602
1510
  ),
1603
- hint2(
1511
+ hint(
1604
1512
  'For "never had/never exceeded" conditions over history tables, exclude entities via NOT IN/NOT EXISTS against the disqualifying entity-id set (often built with GROUP BY/HAVING MAX(...)).'
1605
1513
  )
1606
1514
  ),
1607
- fragment2(
1515
+ fragment(
1608
1516
  "join_completeness",
1609
1517
  policy({
1610
1518
  rule: "Preserve entity-restricting joins implied by the question. Do not widen results by querying only a broader attribute table when a subset entity table is available."
@@ -1612,17 +1520,17 @@ var SQL_AGENT_POLICIES = [
1612
1520
  policy({
1613
1521
  rule: "If an entity term in the question maps to a table, keep that table in query scope and join to attribute tables rather than dropping the entity table."
1614
1522
  }),
1615
- hint2(
1523
+ hint(
1616
1524
  "If the question targets a specific entity group, include that entity table and its join conditions even when selected columns come from a related table."
1617
1525
  ),
1618
- hint2(
1526
+ hint(
1619
1527
  "When the question names an entity type and a relation table links to that entity via *_id, include the entity table in scope instead of counting only relation rows."
1620
1528
  ),
1621
- hint2(
1529
+ hint(
1622
1530
  "Prefer INNER JOIN by default; use LEFT JOIN only when the question explicitly requests including unmatched rows or zero-related entities."
1623
1531
  )
1624
1532
  ),
1625
- fragment2(
1533
+ fragment(
1626
1534
  "aggregation_exactness",
1627
1535
  policy({
1628
1536
  rule: "Preserve requested aggregation semantics exactly: use COUNT(*) by default for total rows, use COUNT(DISTINCT ...) only when uniqueness is explicitly requested, and group by stable entity keys when computing per-entity aggregates."
@@ -1630,11 +1538,11 @@ var SQL_AGENT_POLICIES = [
1630
1538
  policy({
1631
1539
  rule: "For questions asking which entity has lowest/highest average of a metric, compute AVG(metric) per entity (GROUP BY entity) and rank those aggregates."
1632
1540
  }),
1633
- hint2(
1541
+ hint(
1634
1542
  'For "how many <entities>" questions over relation records, default to COUNT(*) on qualifying rows unless explicit uniqueness language is present.'
1635
1543
  )
1636
1544
  ),
1637
- fragment2(
1545
+ fragment(
1638
1546
  "query_shape_examples",
1639
1547
  example({
1640
1548
  question: "List categories ordered by how many records belong to each category.",
@@ -1662,13 +1570,13 @@ async function toSql(options) {
1662
1570
  const { maxRetries = 3 } = options;
1663
1571
  return withRetry(
1664
1572
  async (attemptNumber, errors, attempts) => {
1665
- const context = new ContextEngine2({
1666
- store: new InMemoryContextStore2(),
1573
+ const context = new ContextEngine({
1574
+ store: new InMemoryContextStore(),
1667
1575
  chatId: `sql-gen-${crypto.randomUUID()}`,
1668
1576
  userId: "system"
1669
1577
  });
1670
1578
  context.set(
1671
- persona3({
1579
+ persona({
1672
1580
  name: "Freya",
1673
1581
  role: SQL_AGENT_ROLE,
1674
1582
  objective: SQL_AGENT_OBJECTIVE
@@ -1681,21 +1589,21 @@ async function toSql(options) {
1681
1589
  if (errors.length) {
1682
1590
  const lastError = errors.at(-1);
1683
1591
  context.set(
1684
- user2(dedent2`
1592
+ user(dedent`
1685
1593
  Answer the following question with the SQL code. Use the piece of evidence and base your answer on the database schema.
1686
1594
  Given the question, the evidence and the database schema, return the SQL script that addresses the question.
1687
1595
 
1688
1596
  Question: ${options.input}
1689
1597
  `),
1690
- UnanswerableSQLError.isInstance(lastError) ? user2(
1598
+ UnanswerableSQLError.isInstance(lastError) ? user(
1691
1599
  `<retry_instruction>Your previous response marked the task as unanswerable. Re-evaluate using best-effort schema mapping. If the core intent is answerable with existing tables/columns, return SQL. Return error only when required core intent cannot be mapped without inventing schema elements.</retry_instruction>`
1692
- ) : user2(
1600
+ ) : user(
1693
1601
  `<validation_error>Your previous SQL query had the following error: ${lastError?.message}. Please fix the query.</validation_error>`
1694
1602
  )
1695
1603
  );
1696
1604
  } else {
1697
1605
  context.set(
1698
- user2(dedent2`
1606
+ user(dedent`
1699
1607
  Answer the following question with the SQL code. Use the piece of evidence and base your answer on the database schema.
1700
1608
  Given the question, the evidence and the database schema, return the SQL script that addresses the question.
1701
1609
 
@@ -1704,22 +1612,22 @@ Question: ${options.input}
1704
1612
  );
1705
1613
  }
1706
1614
  const temperature = RETRY_TEMPERATURES[attemptNumber - 1] ?? RETRY_TEMPERATURES[RETRY_TEMPERATURES.length - 1];
1707
- const baseModel = options.model ?? groq2("openai/gpt-oss-20b");
1615
+ const baseModel = options.model ?? groq("openai/gpt-oss-20b");
1708
1616
  const model = wrapLanguageModel({
1709
1617
  model: baseModel,
1710
1618
  middleware: defaultSettingsMiddleware({ settings: { temperature } })
1711
1619
  });
1712
- const sqlOutput = structuredOutput2({
1620
+ const sqlOutput = structuredOutput({
1713
1621
  model,
1714
1622
  context,
1715
- schema: z4.object({
1716
- result: z4.union([
1717
- z4.object({
1718
- sql: z4.string().describe("The SQL query that answers the question"),
1719
- reasoning: z4.string().describe("The reasoning steps taken to generate the SQL")
1623
+ schema: z2.object({
1624
+ result: z2.union([
1625
+ z2.object({
1626
+ sql: z2.string().describe("The SQL query that answers the question"),
1627
+ reasoning: z2.string().describe("The reasoning steps taken to generate the SQL")
1720
1628
  }),
1721
- z4.object({
1722
- error: z4.string().describe(
1629
+ z2.object({
1630
+ error: z2.string().describe(
1723
1631
  "Error message explaining why the question cannot be answered with the given schema"
1724
1632
  )
1725
1633
  })
@@ -1741,18 +1649,18 @@ Question: ${options.input}
1741
1649
  };
1742
1650
  if ("error" in output) {
1743
1651
  context.set(
1744
- user2(
1652
+ user(
1745
1653
  "<best_effort_fallback>Do not return unanswerable. Produce the best valid SQL query that answers the core intent using only available schema entities.</best_effort_fallback>"
1746
1654
  )
1747
1655
  );
1748
- const forcedSqlOutput = structuredOutput2({
1656
+ const forcedSqlOutput = structuredOutput({
1749
1657
  model,
1750
1658
  context,
1751
- schema: z4.object({
1752
- sql: z4.string().describe(
1659
+ schema: z2.object({
1660
+ sql: z2.string().describe(
1753
1661
  "Best-effort SQL query that answers the core intent using only available schema entities."
1754
1662
  ),
1755
- reasoning: z4.string().describe("Reasoning steps for best-effort schema mapping.")
1663
+ reasoning: z2.string().describe("Reasoning steps for best-effort schema mapping.")
1756
1664
  })
1757
1665
  });
1758
1666
  try {
@@ -1835,24 +1743,24 @@ async function withRetry(computation, options = { retries: 3 }) {
1835
1743
  }
1836
1744
 
1837
1745
  // packages/text2sql/src/lib/agents/suggestions.agents.ts
1838
- import { groq as groq3 } from "@ai-sdk/groq";
1839
- import dedent3 from "dedent";
1840
- import z5 from "zod";
1746
+ import { groq as groq2 } from "@ai-sdk/groq";
1747
+ import dedent2 from "dedent";
1748
+ import z3 from "zod";
1841
1749
  import { agent, thirdPersonPrompt } from "@deepagents/agent";
1842
1750
  var suggestionsAgent = agent({
1843
1751
  name: "text2sql-suggestions",
1844
- model: groq3("openai/gpt-oss-20b"),
1845
- output: z5.object({
1846
- suggestions: z5.array(
1847
- z5.object({
1848
- question: z5.string().describe("A complex, high-impact business question."),
1849
- sql: z5.string().describe("The SQL statement needed to answer the question."),
1850
- businessValue: z5.string().describe("Why the question matters to stakeholders.")
1752
+ model: groq2("openai/gpt-oss-20b"),
1753
+ output: z3.object({
1754
+ suggestions: z3.array(
1755
+ z3.object({
1756
+ question: z3.string().describe("A complex, high-impact business question."),
1757
+ sql: z3.string().describe("The SQL statement needed to answer the question."),
1758
+ businessValue: z3.string().describe("Why the question matters to stakeholders.")
1851
1759
  })
1852
1760
  ).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
1853
1761
  }),
1854
1762
  prompt: (state) => {
1855
- return dedent3`
1763
+ return dedent2`
1856
1764
  ${thirdPersonPrompt()}
1857
1765
 
1858
1766
  <identity>
@@ -4818,9 +4726,9 @@ import {
4818
4726
  clarification,
4819
4727
  example as example2,
4820
4728
  explain,
4821
- fragment as fragment3,
4729
+ fragment as fragment2,
4822
4730
  guardrail as guardrail2,
4823
- hint as hint3,
4731
+ hint as hint2,
4824
4732
  policy as policy2,
4825
4733
  principle,
4826
4734
  quirk,
@@ -4833,9 +4741,9 @@ function reasoningFramework() {
4833
4741
  role(
4834
4742
  "You are a very strong reasoner and planner. Use these critical instructions to structure your plans, thoughts, and responses."
4835
4743
  ),
4836
- fragment3(
4744
+ fragment2(
4837
4745
  "meta-cognitive-reasoning-framework",
4838
- hint3(
4746
+ hint2(
4839
4747
  "Before taking any action (either tool calls *or* responses to the user), you must proactively, methodically, and independently plan and reason about:"
4840
4748
  ),
4841
4749
  // 1) Logical dependencies and constraints
@@ -4950,7 +4858,7 @@ function guidelines(options = {}) {
4950
4858
  // Include the meta-cognitive reasoning framework
4951
4859
  ...reasoningFramework(),
4952
4860
  // Prerequisite policies (must do X before Y)
4953
- fragment3(
4861
+ fragment2(
4954
4862
  "prerequisite_policies",
4955
4863
  policy2({
4956
4864
  rule: "YOU MUST inspect schema structure and available tables",
@@ -4974,7 +4882,7 @@ function guidelines(options = {}) {
4974
4882
  })
4975
4883
  ),
4976
4884
  // Few-shot: Applying reasoning principles
4977
- fragment3(
4885
+ fragment2(
4978
4886
  "reasoning-examples",
4979
4887
  example2({
4980
4888
  question: "Show me sales last month",
@@ -5014,16 +4922,16 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5014
4922
  })
5015
4923
  ),
5016
4924
  // Schema adherence - consolidated into clear rules
5017
- fragment3(
4925
+ fragment2(
5018
4926
  "schema_adherence",
5019
- hint3(
4927
+ hint2(
5020
4928
  "Use only tables and columns from the schema. For unspecified columns, use SELECT *. When showing related items, include IDs and requested details."
5021
4929
  ),
5022
- hint3(
4930
+ hint2(
5023
4931
  '"Show" means list items; "count" or "total" means aggregate. Use canonical values verbatim for filtering.'
5024
4932
  )
5025
4933
  ),
5026
- fragment3(
4934
+ fragment2(
5027
4935
  "Column statistics",
5028
4936
  explain({
5029
4937
  concept: "nDistinct in column stats",
@@ -5035,18 +4943,18 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5035
4943
  explanation: "Measures how closely the physical row order matches the logical sort order of the column. Values near 1 or -1 mean the data is well-ordered; near 0 means scattered",
5036
4944
  therefore: "High correlation means range queries (BETWEEN, >, <) on that column benefit from index scans. Low correlation means the index is less effective for ranges"
5037
4945
  }),
5038
- hint3(
4946
+ hint2(
5039
4947
  "When min/max stats are available, use them to validate filter values. If a user asks for values outside the known range, warn them the query may return no results."
5040
4948
  )
5041
4949
  ),
5042
4950
  // Joins - use relationship metadata
5043
- hint3(
4951
+ hint2(
5044
4952
  "Use JOINs based on schema relationships. Favor PK/indexed columns; follow relationship metadata for direction and cardinality."
5045
4953
  ),
5046
4954
  // Aggregations - explain the concepts
5047
- fragment3(
4955
+ fragment2(
5048
4956
  "Aggregations",
5049
- hint3(
4957
+ hint2(
5050
4958
  "Apply COUNT, SUM, AVG when the question implies summarization. Use window functions for ranking, running totals, or row comparisons."
5051
4959
  ),
5052
4960
  explain({
@@ -5056,7 +4964,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5056
4964
  })
5057
4965
  ),
5058
4966
  // Query semantics - explain concepts and document quirks
5059
- fragment3(
4967
+ fragment2(
5060
4968
  "Query interpretation",
5061
4969
  explain({
5062
4970
  concept: "threshold language",
@@ -5071,7 +4979,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5071
4979
  issue: "NULL values behave unexpectedly in comparisons and aggregations",
5072
4980
  workaround: "Use IS NULL, IS NOT NULL, or COALESCE() to handle NULLs explicitly"
5073
4981
  }),
5074
- hint3(
4982
+ hint2(
5075
4983
  "Always include mentioned filters from joined tables in WHERE conditions."
5076
4984
  )
5077
4985
  ),
@@ -5084,7 +4992,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5084
4992
  prefer: "Concise, business-friendly summaries with key comparisons and helpful follow-ups."
5085
4993
  }),
5086
4994
  // Safety guardrails - consolidated
5087
- fragment3(
4995
+ fragment2(
5088
4996
  "Query safety",
5089
4997
  guardrail2({
5090
4998
  rule: "Generate only valid, executable SELECT/WITH statements.",
@@ -5112,7 +5020,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5112
5020
  ask: "Clarify the ranking metric or definition.",
5113
5021
  reason: "Ensures correct aggregation and ordering."
5114
5022
  }),
5115
- hint3(
5023
+ hint2(
5116
5024
  'Use sample cell values from schema hints to match exact casing and format in WHERE conditions (e.g., "Male" vs "male" vs "M").'
5117
5025
  ),
5118
5026
  workflow2({
@@ -5170,7 +5078,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5170
5078
  ],
5171
5079
  notes: "If reference is ambiguous, ask which previous result or entity the user means."
5172
5080
  }),
5173
- fragment3(
5081
+ fragment2(
5174
5082
  "Bash tool usage",
5175
5083
  workflow2({
5176
5084
  task: "Query execution",
@@ -5181,16 +5089,16 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5181
5089
  "For large results, slice first: cat <path> | jq '.[:10]'"
5182
5090
  ]
5183
5091
  }),
5184
- hint3(
5092
+ hint2(
5185
5093
  `You cannot access sql through a tool, it'll fail so the proper way to access it is through the bash tool using "sql run" and "sql validate" commands.`
5186
5094
  ),
5187
- hint3(
5095
+ hint2(
5188
5096
  "The sql command outputs: file path, column names (comma-separated), and row count. Use column names to construct precise jq queries."
5189
5097
  ),
5190
- hint3(
5098
+ hint2(
5191
5099
  'This is virtual bash environment and "sql" commands proxy to the database hence you cannot access sql files directly.'
5192
5100
  ),
5193
- hint3(
5101
+ hint2(
5194
5102
  "If a query fails, the sql command returns an error message in stderr."
5195
5103
  )
5196
5104
  )
@@ -5205,7 +5113,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
5205
5113
  );
5206
5114
  } else {
5207
5115
  baseTeachings.push(
5208
- hint3(
5116
+ hint2(
5209
5117
  '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.'
5210
5118
  )
5211
5119
  );
@@ -5266,9 +5174,9 @@ var Text2Sql = class {
5266
5174
  if (cached) {
5267
5175
  return cached;
5268
5176
  }
5269
- const fragments2 = await this.#config.adapter.introspect();
5270
- await this.#config.introspection.write(fragments2);
5271
- return fragments2;
5177
+ const fragments = await this.#config.adapter.introspect();
5178
+ await this.#config.introspection.write(fragments);
5179
+ return fragments;
5272
5180
  }
5273
5181
  /**
5274
5182
  * Generate training data pairs using a producer factory.
@@ -5317,7 +5225,7 @@ var Text2Sql = class {
5317
5225
  }
5318
5226
  const uiMessages = messages.map(chatMessageToUIMessage);
5319
5227
  const { mounts: skillMounts } = context.getSkillMounts();
5320
- const { tools: tools2 } = await createResultTools({
5228
+ const { tools } = await createResultTools({
5321
5229
  adapter: this.#config.adapter,
5322
5230
  skillMounts,
5323
5231
  filesystem: trackedFs
@@ -5327,7 +5235,7 @@ var Text2Sql = class {
5327
5235
  model: this.#config.model,
5328
5236
  context,
5329
5237
  tools: {
5330
- ...tools2,
5238
+ ...tools,
5331
5239
  ...this.#config.tools
5332
5240
  },
5333
5241
  guardrails: [errorRecoveryGuardrail],