@deepagents/text2sql 0.25.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.
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +147 -239
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +6 -0
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +16 -0
- package/dist/lib/adapters/bigquery/index.js.map +2 -2
- package/dist/lib/adapters/mysql/index.js +16 -0
- package/dist/lib/adapters/mysql/index.js.map +2 -2
- package/dist/lib/adapters/postgres/index.js +16 -0
- package/dist/lib/adapters/postgres/index.js.map +2 -2
- package/dist/lib/adapters/spreadsheet/index.js +16 -0
- package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
- package/dist/lib/adapters/sqlite/index.js +16 -0
- package/dist/lib/adapters/sqlite/index.js.map +2 -2
- package/dist/lib/adapters/sqlserver/index.js +16 -0
- package/dist/lib/adapters/sqlserver/index.js.map +2 -2
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/lib/agents/developer.agent.d.ts +0 -41
- 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
|
|
145
|
+
const fragments = [];
|
|
130
146
|
if (ctx.info) {
|
|
131
|
-
|
|
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
|
-
|
|
156
|
+
fragments.push(this.#tableToFragment(t));
|
|
141
157
|
}
|
|
142
158
|
for (const v of ctx.views) {
|
|
143
|
-
|
|
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
|
-
|
|
165
|
+
fragments.push(
|
|
150
166
|
this.#relationshipToFragment(rel, sourceTable, targetTable)
|
|
151
167
|
);
|
|
152
168
|
}
|
|
153
169
|
if (ctx.report) {
|
|
154
|
-
|
|
170
|
+
fragments.push({ name: "businessContext", data: ctx.report });
|
|
155
171
|
}
|
|
156
|
-
return
|
|
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
|
|
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
|
|
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,12 +478,17 @@ 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) => {
|
|
488
|
+
const store = metaStore.getStore();
|
|
489
|
+
if (store) {
|
|
490
|
+
store.value = { ...store.value, reminder: SQL_VALIDATE_REMINDER };
|
|
491
|
+
}
|
|
587
492
|
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
588
493
|
if (!rawQuery) {
|
|
589
494
|
return {
|
|
@@ -601,8 +506,9 @@ function createSqlCommand(adapter, metaStore) {
|
|
|
601
506
|
};
|
|
602
507
|
}
|
|
603
508
|
const query = adapter.format(rawQuery);
|
|
604
|
-
|
|
605
|
-
|
|
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 {
|
|
@@ -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
|
|
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 =
|
|
1387
|
-
...
|
|
1388
|
-
inputSchema:
|
|
1389
|
-
command:
|
|
1390
|
-
reasoning:
|
|
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 =
|
|
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
|
|
1404
|
-
|
|
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
|
-
...
|
|
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
|
|
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
|
|
1344
|
+
import dedent from "dedent";
|
|
1437
1345
|
import pRetry from "p-retry";
|
|
1438
|
-
import
|
|
1346
|
+
import z2 from "zod";
|
|
1439
1347
|
import "@deepagents/agent";
|
|
1440
1348
|
import {
|
|
1441
|
-
ContextEngine
|
|
1442
|
-
InMemoryContextStore
|
|
1349
|
+
ContextEngine,
|
|
1350
|
+
InMemoryContextStore,
|
|
1443
1351
|
example,
|
|
1444
|
-
fragment
|
|
1352
|
+
fragment,
|
|
1445
1353
|
guardrail,
|
|
1446
|
-
hint
|
|
1447
|
-
persona
|
|
1354
|
+
hint,
|
|
1355
|
+
persona,
|
|
1448
1356
|
policy,
|
|
1449
|
-
structuredOutput
|
|
1450
|
-
user
|
|
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
|
-
|
|
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
|
-
|
|
1370
|
+
hint("Preserve schema spelling exactly, including typos in column names.")
|
|
1463
1371
|
),
|
|
1464
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1666
|
-
store: new
|
|
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
|
-
|
|
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
|
-
|
|
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) ?
|
|
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
|
-
) :
|
|
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
|
-
|
|
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 ??
|
|
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 =
|
|
1620
|
+
const sqlOutput = structuredOutput({
|
|
1713
1621
|
model,
|
|
1714
1622
|
context,
|
|
1715
|
-
schema:
|
|
1716
|
-
result:
|
|
1717
|
-
|
|
1718
|
-
sql:
|
|
1719
|
-
reasoning:
|
|
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
|
-
|
|
1722
|
-
error:
|
|
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
|
-
|
|
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 =
|
|
1656
|
+
const forcedSqlOutput = structuredOutput({
|
|
1749
1657
|
model,
|
|
1750
1658
|
context,
|
|
1751
|
-
schema:
|
|
1752
|
-
sql:
|
|
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:
|
|
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
|
|
1839
|
-
import
|
|
1840
|
-
import
|
|
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:
|
|
1845
|
-
output:
|
|
1846
|
-
suggestions:
|
|
1847
|
-
|
|
1848
|
-
question:
|
|
1849
|
-
sql:
|
|
1850
|
-
businessValue:
|
|
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
|
|
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
|
|
4729
|
+
fragment as fragment2,
|
|
4822
4730
|
guardrail as guardrail2,
|
|
4823
|
-
hint as
|
|
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
|
-
|
|
4744
|
+
fragment2(
|
|
4837
4745
|
"meta-cognitive-reasoning-framework",
|
|
4838
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4925
|
+
fragment2(
|
|
5018
4926
|
"schema_adherence",
|
|
5019
|
-
|
|
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
|
-
|
|
4930
|
+
hint2(
|
|
5023
4931
|
'"Show" means list items; "count" or "total" means aggregate. Use canonical values verbatim for filtering.'
|
|
5024
4932
|
)
|
|
5025
4933
|
),
|
|
5026
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4955
|
+
fragment2(
|
|
5048
4956
|
"Aggregations",
|
|
5049
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5270
|
-
await this.#config.introspection.write(
|
|
5271
|
-
return
|
|
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
|
|
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
|
-
...
|
|
5238
|
+
...tools,
|
|
5331
5239
|
...this.#config.tools
|
|
5332
5240
|
},
|
|
5333
5241
|
guardrails: [errorRecoveryGuardrail],
|