@deepagents/text2sql 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1060 -218
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/groundings/index.js +166 -154
- package/dist/lib/adapters/groundings/index.js.map +4 -4
- package/dist/lib/adapters/mysql/index.js +166 -154
- package/dist/lib/adapters/mysql/index.js.map +4 -4
- package/dist/lib/adapters/postgres/index.js +168 -155
- package/dist/lib/adapters/postgres/index.js.map +4 -4
- package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +22 -2
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +166 -154
- package/dist/lib/adapters/sqlite/index.js.map +4 -4
- package/dist/lib/adapters/sqlserver/index.js +166 -154
- package/dist/lib/adapters/sqlserver/index.js.map +4 -4
- package/dist/lib/agents/result-tools.d.ts +20 -23
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/fs/index.d.ts +4 -0
- package/dist/lib/fs/index.d.ts.map +1 -0
- package/dist/lib/fs/scoped-fs.d.ts +53 -0
- package/dist/lib/fs/scoped-fs.d.ts.map +1 -0
- package/dist/lib/fs/sqlite-fs.d.ts +66 -0
- package/dist/lib/fs/sqlite-fs.d.ts.map +1 -0
- package/dist/lib/fs/tracked-fs.d.ts +40 -0
- package/dist/lib/fs/tracked-fs.d.ts.map +1 -0
- package/dist/lib/sql.d.ts +3 -4
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +181 -181
- package/dist/lib/synthesis/index.js.map +4 -4
- package/package.json +5 -5
|
@@ -171,7 +171,7 @@ import {
|
|
|
171
171
|
isTextUIPart,
|
|
172
172
|
isToolOrDynamicToolUIPart
|
|
173
173
|
} from "ai";
|
|
174
|
-
import
|
|
174
|
+
import dedent2 from "dedent";
|
|
175
175
|
import z from "zod";
|
|
176
176
|
|
|
177
177
|
// packages/context/dist/index.js
|
|
@@ -188,6 +188,7 @@ import spawn2 from "nano-spawn";
|
|
|
188
188
|
import {
|
|
189
189
|
createBashTool
|
|
190
190
|
} from "bash-tool";
|
|
191
|
+
import dedent from "dedent";
|
|
191
192
|
import YAML from "yaml";
|
|
192
193
|
import { DatabaseSync } from "node:sqlite";
|
|
193
194
|
import { groq } from "@ai-sdk/groq";
|
|
@@ -409,7 +410,7 @@ function assistantText(content, options) {
|
|
|
409
410
|
parts: [{ type: "text", text: content }]
|
|
410
411
|
});
|
|
411
412
|
}
|
|
412
|
-
var LAZY_ID = Symbol("lazy-id");
|
|
413
|
+
var LAZY_ID = Symbol.for("@deepagents/context:lazy-id");
|
|
413
414
|
function isLazyFragment(fragment2) {
|
|
414
415
|
return LAZY_ID in fragment2;
|
|
415
416
|
}
|
|
@@ -784,7 +785,7 @@ var ContextEngine = class {
|
|
|
784
785
|
async #createBranchFrom(messageId, switchTo) {
|
|
785
786
|
const branches = await this.#store.listBranches(this.#chatId);
|
|
786
787
|
const samePrefix = branches.filter(
|
|
787
|
-
(
|
|
788
|
+
(it) => it.name === this.#branchName || it.name.startsWith(`${this.#branchName}-v`)
|
|
788
789
|
);
|
|
789
790
|
const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;
|
|
790
791
|
const newBranch = {
|
|
@@ -812,6 +813,15 @@ var ContextEngine = class {
|
|
|
812
813
|
createdAt: newBranch.createdAt
|
|
813
814
|
};
|
|
814
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Rewind to a message without clearing pending messages.
|
|
818
|
+
* Used internally when saving an update to an existing message.
|
|
819
|
+
*/
|
|
820
|
+
async #rewindForUpdate(messageId) {
|
|
821
|
+
const pendingBackup = [...this.#pendingMessages];
|
|
822
|
+
await this.rewind(messageId);
|
|
823
|
+
this.#pendingMessages = pendingBackup;
|
|
824
|
+
}
|
|
815
825
|
/**
|
|
816
826
|
* Get the current chat ID.
|
|
817
827
|
*/
|
|
@@ -896,7 +906,18 @@ var ContextEngine = class {
|
|
|
896
906
|
messages.push(message(msg.data).codec?.decode());
|
|
897
907
|
}
|
|
898
908
|
}
|
|
909
|
+
for (let i = 0; i < this.#pendingMessages.length; i++) {
|
|
910
|
+
const fragment2 = this.#pendingMessages[i];
|
|
911
|
+
if (isLazyFragment(fragment2)) {
|
|
912
|
+
this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
899
915
|
for (const fragment2 of this.#pendingMessages) {
|
|
916
|
+
if (!fragment2.codec) {
|
|
917
|
+
throw new Error(
|
|
918
|
+
`Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before decode.`
|
|
919
|
+
);
|
|
920
|
+
}
|
|
900
921
|
const decoded = fragment2.codec.decode();
|
|
901
922
|
messages.push(decoded);
|
|
902
923
|
}
|
|
@@ -927,9 +948,24 @@ var ContextEngine = class {
|
|
|
927
948
|
this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
|
|
928
949
|
}
|
|
929
950
|
}
|
|
951
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
952
|
+
if (fragment2.id) {
|
|
953
|
+
const existing = await this.#store.getMessage(fragment2.id);
|
|
954
|
+
if (existing && existing.parentId) {
|
|
955
|
+
await this.#rewindForUpdate(existing.parentId);
|
|
956
|
+
fragment2.id = crypto.randomUUID();
|
|
957
|
+
break;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
930
961
|
let parentId = this.#branch.headMessageId;
|
|
931
962
|
const now = Date.now();
|
|
932
963
|
for (const fragment2 of this.#pendingMessages) {
|
|
964
|
+
if (!fragment2.codec) {
|
|
965
|
+
throw new Error(
|
|
966
|
+
`Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before encode.`
|
|
967
|
+
);
|
|
968
|
+
}
|
|
933
969
|
const messageData = {
|
|
934
970
|
id: fragment2.id ?? crypto.randomUUID(),
|
|
935
971
|
chatId: this.#chatId,
|
|
@@ -1289,33 +1325,28 @@ var ContextEngine = class {
|
|
|
1289
1325
|
return void 0;
|
|
1290
1326
|
}
|
|
1291
1327
|
/**
|
|
1292
|
-
* Extract skill
|
|
1293
|
-
* Returns
|
|
1294
|
-
*
|
|
1295
|
-
* Reads the original `paths` configuration stored in fragment metadata
|
|
1296
|
-
* by the skills() fragment helper.
|
|
1328
|
+
* Extract skill mounts from available_skills fragments.
|
|
1329
|
+
* Returns unified mount array where entries with `name` are individual skills.
|
|
1297
1330
|
*
|
|
1298
1331
|
* @example
|
|
1299
1332
|
* ```ts
|
|
1300
1333
|
* const context = new ContextEngine({ store, chatId, userId })
|
|
1301
1334
|
* .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
|
|
1302
1335
|
*
|
|
1303
|
-
* const mounts = context.getSkillMounts();
|
|
1304
|
-
* // [{ host: './skills', sandbox: '/skills' }]
|
|
1336
|
+
* const { mounts } = context.getSkillMounts();
|
|
1337
|
+
* // mounts: [{ name: 'bi-dashboards', host: './skills/bi-dashboards/SKILL.md', sandbox: '/skills/bi-dashboards/SKILL.md' }]
|
|
1338
|
+
*
|
|
1339
|
+
* // Extract skills only (entries with name)
|
|
1340
|
+
* const skills = mounts.filter(m => m.name);
|
|
1305
1341
|
* ```
|
|
1306
1342
|
*/
|
|
1307
1343
|
getSkillMounts() {
|
|
1308
|
-
const mounts = [];
|
|
1309
1344
|
for (const fragment2 of this.#fragments) {
|
|
1310
|
-
if (fragment2.name === "available_skills" && fragment2.metadata
|
|
1311
|
-
|
|
1312
|
-
if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
|
|
1313
|
-
mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1345
|
+
if (fragment2.name === "available_skills" && fragment2.metadata?.mounts) {
|
|
1346
|
+
return { mounts: fragment2.metadata.mounts };
|
|
1316
1347
|
}
|
|
1317
1348
|
}
|
|
1318
|
-
return mounts;
|
|
1349
|
+
return { mounts: [] };
|
|
1319
1350
|
}
|
|
1320
1351
|
/**
|
|
1321
1352
|
* Inspect the full context state for debugging.
|
|
@@ -1471,84 +1502,46 @@ function persona(input) {
|
|
|
1471
1502
|
}
|
|
1472
1503
|
};
|
|
1473
1504
|
}
|
|
1474
|
-
var
|
|
1475
|
-
-- Chats table
|
|
1476
|
-
-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
|
|
1477
|
-
CREATE TABLE IF NOT EXISTS chats (
|
|
1478
|
-
id TEXT PRIMARY KEY,
|
|
1479
|
-
userId TEXT NOT NULL,
|
|
1480
|
-
title TEXT,
|
|
1481
|
-
metadata TEXT,
|
|
1482
|
-
createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
1483
|
-
updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
|
|
1484
|
-
);
|
|
1485
|
-
|
|
1486
|
-
CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
|
|
1487
|
-
CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
|
|
1505
|
+
var SKILLS_INSTRUCTIONS = dedent`A skill is a set of local instructions to follow that is stored in a \`SKILL.md\` file. Below is the list of skills that can be used. Each entry includes a name, description, and file path so you can open the source for full instructions when using a specific skill.
|
|
1488
1506
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
)
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
id TEXT PRIMARY KEY,
|
|
1508
|
-
chatId TEXT NOT NULL,
|
|
1509
|
-
name TEXT NOT NULL,
|
|
1510
|
-
headMessageId TEXT,
|
|
1511
|
-
isActive INTEGER NOT NULL DEFAULT 0,
|
|
1512
|
-
createdAt INTEGER NOT NULL,
|
|
1513
|
-
FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
|
|
1514
|
-
FOREIGN KEY (headMessageId) REFERENCES messages(id),
|
|
1515
|
-
UNIQUE(chatId, name)
|
|
1516
|
-
);
|
|
1517
|
-
|
|
1518
|
-
CREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);
|
|
1519
|
-
|
|
1520
|
-
-- Checkpoints table (pointers to message nodes)
|
|
1521
|
-
CREATE TABLE IF NOT EXISTS checkpoints (
|
|
1522
|
-
id TEXT PRIMARY KEY,
|
|
1523
|
-
chatId TEXT NOT NULL,
|
|
1524
|
-
name TEXT NOT NULL,
|
|
1525
|
-
messageId TEXT NOT NULL,
|
|
1526
|
-
createdAt INTEGER NOT NULL,
|
|
1527
|
-
FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
|
|
1528
|
-
FOREIGN KEY (messageId) REFERENCES messages(id),
|
|
1529
|
-
UNIQUE(chatId, name)
|
|
1530
|
-
);
|
|
1531
|
-
|
|
1532
|
-
CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);
|
|
1533
|
-
|
|
1534
|
-
-- FTS5 virtual table for full-text search
|
|
1535
|
-
-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
|
|
1536
|
-
-- Only 'content' is indexed for full-text search
|
|
1537
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
|
1538
|
-
messageId UNINDEXED,
|
|
1539
|
-
chatId UNINDEXED,
|
|
1540
|
-
name UNINDEXED,
|
|
1541
|
-
content,
|
|
1542
|
-
tokenize='porter unicode61'
|
|
1543
|
-
);
|
|
1544
|
-
`;
|
|
1507
|
+
### How to use skills
|
|
1508
|
+
- Discovery: The list below shows the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
|
|
1509
|
+
- Trigger rules: If the user names a skill (with \`$SkillName\` or plain text) OR the task clearly matches a skill's description shown below, you must use that skill for that turn before doing anything else. Multiple mentions mean use them all. Do not carry skills across turns unless re-mentioned.
|
|
1510
|
+
- Missing/blocked: If a named skill isn't in the list or the path can't be read, say so briefly and continue with the best fallback.
|
|
1511
|
+
- How to use a skill (progressive disclosure):
|
|
1512
|
+
1) After deciding to use a skill, open its \`SKILL.md\`. Read only enough to follow the workflow.
|
|
1513
|
+
2) If \`SKILL.md\` points to extra folders such as \`references/\`, load only the specific files needed for the request; don't bulk-load everything.
|
|
1514
|
+
3) If \`scripts/\` exist, prefer running or patching them instead of retyping large code blocks.
|
|
1515
|
+
4) If \`assets/\` or templates exist, reuse them instead of recreating from scratch.
|
|
1516
|
+
- Coordination and sequencing:
|
|
1517
|
+
- If multiple skills apply, choose the minimal set that covers the request and state the order you'll use them.
|
|
1518
|
+
- Announce which skill(s) you're using and why (one short line). If you skip an obvious skill, say why.
|
|
1519
|
+
- Context hygiene:
|
|
1520
|
+
- Keep context small: summarize long sections instead of pasting them; only load extra files when needed.
|
|
1521
|
+
- Avoid deep reference-chasing: prefer opening only files directly linked from \`SKILL.md\` unless you're blocked.
|
|
1522
|
+
- When variants exist (frameworks, providers, domains), pick only the relevant reference file(s) and note that choice.
|
|
1523
|
+
- Safety and fallback: If a skill can't be applied cleanly (missing files, unclear instructions), state the issue, pick the next-best approach, and continue.`;
|
|
1524
|
+
var ddl_sqlite_default = "-- Context Store DDL for SQLite\n-- This schema implements a DAG-based message history with branching and checkpoints.\n\n-- Performance PRAGMAs (session-level, run on each connection)\nPRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA cache_size = -64000;\nPRAGMA temp_store = MEMORY;\nPRAGMA mmap_size = 268435456;\n\n-- Integrity\nPRAGMA foreign_keys = ON;\n\n-- Chats table\n-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates\nCREATE TABLE IF NOT EXISTS chats (\n id TEXT PRIMARY KEY,\n userId TEXT NOT NULL,\n title TEXT,\n metadata TEXT,\n createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\n updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);\nCREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);\n-- Composite index for listChats(): WHERE userId = ? ORDER BY updatedAt DESC\nCREATE INDEX IF NOT EXISTS idx_chats_userId_updatedAt ON chats(userId, updatedAt DESC);\n\n-- Messages table (nodes in the DAG)\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n parentId TEXT,\n name TEXT NOT NULL,\n type TEXT,\n data TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (parentId) REFERENCES messages(id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);\nCREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);\n-- Composite index for recursive CTE parent traversal in getMessageChain()\nCREATE INDEX IF NOT EXISTS idx_messages_chatId_parentId ON messages(chatId, parentId);\n\n-- Branches table (pointers to head messages)\nCREATE TABLE IF NOT EXISTS branches (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n headMessageId TEXT,\n isActive INTEGER NOT NULL DEFAULT 0,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (headMessageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);\n-- Composite index for getActiveBranch(): WHERE chatId = ? AND isActive = 1\nCREATE INDEX IF NOT EXISTS idx_branches_chatId_isActive ON branches(chatId, isActive);\n\n-- Checkpoints table (pointers to message nodes)\nCREATE TABLE IF NOT EXISTS checkpoints (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n messageId TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (messageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);\n\n-- FTS5 virtual table for full-text search\n-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)\n-- Only 'content' is indexed for full-text search\nCREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(\n messageId UNINDEXED,\n chatId UNINDEXED,\n name UNINDEXED,\n content,\n tokenize='porter unicode61'\n);\n";
|
|
1545
1525
|
var SqliteContextStore = class extends ContextStore {
|
|
1546
1526
|
#db;
|
|
1527
|
+
#statements = /* @__PURE__ */ new Map();
|
|
1528
|
+
/**
|
|
1529
|
+
* Get or create a prepared statement.
|
|
1530
|
+
* Statements are cached for the lifetime of the store to avoid
|
|
1531
|
+
* repeated SQL parsing and compilation overhead.
|
|
1532
|
+
*/
|
|
1533
|
+
#stmt(sql) {
|
|
1534
|
+
let stmt = this.#statements.get(sql);
|
|
1535
|
+
if (!stmt) {
|
|
1536
|
+
stmt = this.#db.prepare(sql);
|
|
1537
|
+
this.#statements.set(sql, stmt);
|
|
1538
|
+
}
|
|
1539
|
+
return stmt;
|
|
1540
|
+
}
|
|
1547
1541
|
constructor(path3) {
|
|
1548
1542
|
super();
|
|
1549
1543
|
this.#db = new DatabaseSync(path3);
|
|
1550
|
-
this.#db.exec(
|
|
1551
|
-
this.#db.exec(STORE_DDL);
|
|
1544
|
+
this.#db.exec(ddl_sqlite_default);
|
|
1552
1545
|
}
|
|
1553
1546
|
/**
|
|
1554
1547
|
* Execute a function within a transaction.
|
|
@@ -1569,11 +1562,12 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1569
1562
|
// Chat Operations
|
|
1570
1563
|
// ==========================================================================
|
|
1571
1564
|
async createChat(chat) {
|
|
1572
|
-
this.#useTransaction(() => {
|
|
1573
|
-
this.#db.prepare(
|
|
1565
|
+
return this.#useTransaction(() => {
|
|
1566
|
+
const row = this.#db.prepare(
|
|
1574
1567
|
`INSERT INTO chats (id, userId, title, metadata)
|
|
1575
|
-
VALUES (?, ?, ?, ?)
|
|
1576
|
-
|
|
1568
|
+
VALUES (?, ?, ?, ?)
|
|
1569
|
+
RETURNING *`
|
|
1570
|
+
).get(
|
|
1577
1571
|
chat.id,
|
|
1578
1572
|
chat.userId,
|
|
1579
1573
|
chat.title ?? null,
|
|
@@ -1583,6 +1577,14 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1583
1577
|
`INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
|
|
1584
1578
|
VALUES (?, ?, 'main', NULL, 1, ?)`
|
|
1585
1579
|
).run(crypto.randomUUID(), chat.id, Date.now());
|
|
1580
|
+
return {
|
|
1581
|
+
id: row.id,
|
|
1582
|
+
userId: row.userId,
|
|
1583
|
+
title: row.title ?? void 0,
|
|
1584
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
1585
|
+
createdAt: row.createdAt,
|
|
1586
|
+
updatedAt: row.updatedAt
|
|
1587
|
+
};
|
|
1586
1588
|
});
|
|
1587
1589
|
}
|
|
1588
1590
|
async upsertChat(chat) {
|
|
@@ -1725,21 +1727,16 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1725
1727
|
// Message Operations (Graph Nodes)
|
|
1726
1728
|
// ==========================================================================
|
|
1727
1729
|
async addMessage(message2) {
|
|
1728
|
-
|
|
1730
|
+
if (message2.parentId === message2.id) {
|
|
1731
|
+
throw new Error(`Message ${message2.id} cannot be its own parent`);
|
|
1732
|
+
}
|
|
1733
|
+
this.#stmt(
|
|
1729
1734
|
`INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
?5,
|
|
1736
|
-
?6,
|
|
1737
|
-
?7
|
|
1738
|
-
)
|
|
1739
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
1740
|
-
name = excluded.name,
|
|
1741
|
-
type = excluded.type,
|
|
1742
|
-
data = excluded.data`
|
|
1735
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1736
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1737
|
+
name = excluded.name,
|
|
1738
|
+
type = excluded.type,
|
|
1739
|
+
data = excluded.data`
|
|
1743
1740
|
).run(
|
|
1744
1741
|
message2.id,
|
|
1745
1742
|
message2.chatId,
|
|
@@ -1750,14 +1747,16 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1750
1747
|
message2.createdAt
|
|
1751
1748
|
);
|
|
1752
1749
|
const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
|
|
1753
|
-
this.#
|
|
1754
|
-
this.#
|
|
1750
|
+
this.#stmt(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
|
|
1751
|
+
this.#stmt(
|
|
1755
1752
|
`INSERT INTO messages_fts(messageId, chatId, name, content)
|
|
1756
|
-
|
|
1753
|
+
VALUES (?, ?, ?, ?)`
|
|
1757
1754
|
).run(message2.id, message2.chatId, message2.name, content);
|
|
1758
1755
|
}
|
|
1759
1756
|
async getMessage(messageId) {
|
|
1760
|
-
const row = this.#
|
|
1757
|
+
const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
|
|
1758
|
+
messageId
|
|
1759
|
+
);
|
|
1761
1760
|
if (!row) {
|
|
1762
1761
|
return void 0;
|
|
1763
1762
|
}
|
|
@@ -1772,15 +1771,16 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1772
1771
|
};
|
|
1773
1772
|
}
|
|
1774
1773
|
async getMessageChain(headId) {
|
|
1775
|
-
const rows = this.#
|
|
1774
|
+
const rows = this.#stmt(
|
|
1776
1775
|
`WITH RECURSIVE chain AS (
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1776
|
+
SELECT *, 0 as depth FROM messages WHERE id = ?
|
|
1777
|
+
UNION ALL
|
|
1778
|
+
SELECT m.*, c.depth + 1 FROM messages m
|
|
1779
|
+
INNER JOIN chain c ON m.id = c.parentId
|
|
1780
|
+
WHERE c.depth < 100000
|
|
1781
|
+
)
|
|
1782
|
+
SELECT * FROM chain
|
|
1783
|
+
ORDER BY depth DESC`
|
|
1784
1784
|
).all(headId);
|
|
1785
1785
|
return rows.map((row) => ({
|
|
1786
1786
|
id: row.id,
|
|
@@ -1793,7 +1793,7 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1793
1793
|
}));
|
|
1794
1794
|
}
|
|
1795
1795
|
async hasChildren(messageId) {
|
|
1796
|
-
const row = this.#
|
|
1796
|
+
const row = this.#stmt(
|
|
1797
1797
|
"SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren"
|
|
1798
1798
|
).get(messageId);
|
|
1799
1799
|
return row.hasChildren === 1;
|
|
@@ -1840,7 +1840,9 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1840
1840
|
};
|
|
1841
1841
|
}
|
|
1842
1842
|
async getActiveBranch(chatId) {
|
|
1843
|
-
const row = this.#
|
|
1843
|
+
const row = this.#stmt(
|
|
1844
|
+
"SELECT * FROM branches WHERE chatId = ? AND isActive = 1"
|
|
1845
|
+
).get(chatId);
|
|
1844
1846
|
if (!row) {
|
|
1845
1847
|
return void 0;
|
|
1846
1848
|
}
|
|
@@ -1858,45 +1860,43 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1858
1860
|
this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
|
|
1859
1861
|
}
|
|
1860
1862
|
async updateBranchHead(branchId, messageId) {
|
|
1861
|
-
this.#
|
|
1863
|
+
this.#stmt("UPDATE branches SET headMessageId = ? WHERE id = ?").run(
|
|
1864
|
+
messageId,
|
|
1865
|
+
branchId
|
|
1866
|
+
);
|
|
1862
1867
|
}
|
|
1863
1868
|
async listBranches(chatId) {
|
|
1864
|
-
const
|
|
1869
|
+
const rows = this.#db.prepare(
|
|
1865
1870
|
`SELECT
|
|
1866
1871
|
b.id,
|
|
1867
1872
|
b.name,
|
|
1868
1873
|
b.headMessageId,
|
|
1869
1874
|
b.isActive,
|
|
1870
|
-
b.createdAt
|
|
1875
|
+
b.createdAt,
|
|
1876
|
+
COALESCE(
|
|
1877
|
+
(
|
|
1878
|
+
WITH RECURSIVE chain AS (
|
|
1879
|
+
SELECT id, parentId FROM messages WHERE id = b.headMessageId
|
|
1880
|
+
UNION ALL
|
|
1881
|
+
SELECT m.id, m.parentId FROM messages m
|
|
1882
|
+
INNER JOIN chain c ON m.id = c.parentId
|
|
1883
|
+
)
|
|
1884
|
+
SELECT COUNT(*) FROM chain
|
|
1885
|
+
),
|
|
1886
|
+
0
|
|
1887
|
+
) as messageCount
|
|
1871
1888
|
FROM branches b
|
|
1872
1889
|
WHERE b.chatId = ?
|
|
1873
1890
|
ORDER BY b.createdAt ASC`
|
|
1874
1891
|
).all(chatId);
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
SELECT m.id, m.parentId FROM messages m
|
|
1884
|
-
INNER JOIN chain c ON m.id = c.parentId
|
|
1885
|
-
)
|
|
1886
|
-
SELECT COUNT(*) as count FROM chain`
|
|
1887
|
-
).get(branch.headMessageId);
|
|
1888
|
-
messageCount = countRow.count;
|
|
1889
|
-
}
|
|
1890
|
-
result.push({
|
|
1891
|
-
id: branch.id,
|
|
1892
|
-
name: branch.name,
|
|
1893
|
-
headMessageId: branch.headMessageId,
|
|
1894
|
-
isActive: branch.isActive === 1,
|
|
1895
|
-
messageCount,
|
|
1896
|
-
createdAt: branch.createdAt
|
|
1897
|
-
});
|
|
1898
|
-
}
|
|
1899
|
-
return result;
|
|
1892
|
+
return rows.map((row) => ({
|
|
1893
|
+
id: row.id,
|
|
1894
|
+
name: row.name,
|
|
1895
|
+
headMessageId: row.headMessageId,
|
|
1896
|
+
isActive: row.isActive === 1,
|
|
1897
|
+
messageCount: row.messageCount,
|
|
1898
|
+
createdAt: row.createdAt
|
|
1899
|
+
}));
|
|
1900
1900
|
}
|
|
1901
1901
|
// ==========================================================================
|
|
1902
1902
|
// Checkpoint Operations
|
|
@@ -2149,7 +2149,7 @@ async function resolveContext(params) {
|
|
|
2149
2149
|
fragment("sql", params.sql),
|
|
2150
2150
|
fragment(
|
|
2151
2151
|
"task",
|
|
2152
|
-
|
|
2152
|
+
dedent2`
|
|
2153
2153
|
Given the conversation above and the SQL query that was executed,
|
|
2154
2154
|
generate a single, standalone natural language question that:
|
|
2155
2155
|
1. Fully captures the user's intent without needing prior context
|
|
@@ -2160,7 +2160,7 @@ async function resolveContext(params) {
|
|
|
2160
2160
|
),
|
|
2161
2161
|
fragment(
|
|
2162
2162
|
"examples",
|
|
2163
|
-
|
|
2163
|
+
dedent2`
|
|
2164
2164
|
Conversation: "Show me customers" → "Filter to NY" → "Sort by revenue"
|
|
2165
2165
|
SQL: SELECT * FROM customers WHERE region = 'NY' ORDER BY revenue DESC
|
|
2166
2166
|
Question: "Show me customers in the NY region sorted by revenue"
|
|
@@ -2354,7 +2354,7 @@ var MessageExtractor = class extends PairProducer {
|
|
|
2354
2354
|
|
|
2355
2355
|
// packages/text2sql/src/lib/synthesis/extractors/sql-extractor.ts
|
|
2356
2356
|
import { groq as groq3 } from "@ai-sdk/groq";
|
|
2357
|
-
import
|
|
2357
|
+
import dedent3 from "dedent";
|
|
2358
2358
|
import z2 from "zod";
|
|
2359
2359
|
var outputSchema = z2.object({
|
|
2360
2360
|
question: z2.string().describe("A natural language question that the SQL query answers")
|
|
@@ -2405,7 +2405,7 @@ var SqlExtractor = class extends PairProducer {
|
|
|
2405
2405
|
fragment("sql", sql),
|
|
2406
2406
|
fragment(
|
|
2407
2407
|
"task",
|
|
2408
|
-
|
|
2408
|
+
dedent3`
|
|
2409
2409
|
Given the database schema and the SQL query above, generate a single
|
|
2410
2410
|
natural language question that:
|
|
2411
2411
|
1. Accurately describes what information the query retrieves
|
|
@@ -2416,7 +2416,7 @@ var SqlExtractor = class extends PairProducer {
|
|
|
2416
2416
|
),
|
|
2417
2417
|
fragment(
|
|
2418
2418
|
"examples",
|
|
2419
|
-
|
|
2419
|
+
dedent3`
|
|
2420
2420
|
SQL: SELECT COUNT(*) FROM customers WHERE region = 'NY'
|
|
2421
2421
|
Question: "How many customers do we have in New York?"
|
|
2422
2422
|
|
|
@@ -2491,7 +2491,7 @@ var WindowedContextExtractor = class extends BaseContextualExtractor {
|
|
|
2491
2491
|
|
|
2492
2492
|
// packages/text2sql/src/lib/synthesis/extractors/segmented-context-extractor.ts
|
|
2493
2493
|
import { groq as groq4 } from "@ai-sdk/groq";
|
|
2494
|
-
import
|
|
2494
|
+
import dedent4 from "dedent";
|
|
2495
2495
|
import z3 from "zod";
|
|
2496
2496
|
var topicChangeSchema = z3.object({
|
|
2497
2497
|
isTopicChange: z3.boolean().describe("Whether the new message represents a topic change"),
|
|
@@ -2513,7 +2513,7 @@ async function detectTopicChange(params) {
|
|
|
2513
2513
|
fragment("new_message", params.newMessage),
|
|
2514
2514
|
fragment(
|
|
2515
2515
|
"task",
|
|
2516
|
-
|
|
2516
|
+
dedent4`
|
|
2517
2517
|
Determine if the new message represents a significant topic change from the
|
|
2518
2518
|
prior conversation context. A topic change occurs when:
|
|
2519
2519
|
1. The user asks about a completely different entity/table/domain
|
|
@@ -2528,7 +2528,7 @@ async function detectTopicChange(params) {
|
|
|
2528
2528
|
),
|
|
2529
2529
|
fragment(
|
|
2530
2530
|
"examples",
|
|
2531
|
-
|
|
2531
|
+
dedent4`
|
|
2532
2532
|
Context: "Show me customers in NY" → "Sort by revenue"
|
|
2533
2533
|
New: "Filter to those with orders over $1000"
|
|
2534
2534
|
Decision: NOT a topic change (still refining customer query)
|
|
@@ -2646,11 +2646,11 @@ import pLimit from "p-limit";
|
|
|
2646
2646
|
|
|
2647
2647
|
// packages/text2sql/src/lib/agents/question.agent.ts
|
|
2648
2648
|
import { groq as groq5 } from "@ai-sdk/groq";
|
|
2649
|
-
import
|
|
2649
|
+
import dedent5 from "dedent";
|
|
2650
2650
|
import z4 from "zod";
|
|
2651
2651
|
import "@deepagents/agent";
|
|
2652
2652
|
var complexityInstructions = {
|
|
2653
|
-
simple:
|
|
2653
|
+
simple: dedent5`
|
|
2654
2654
|
Generate simple questions that require:
|
|
2655
2655
|
- Basic SELECT with single table
|
|
2656
2656
|
- Simple WHERE clauses with one condition
|
|
@@ -2658,7 +2658,7 @@ var complexityInstructions = {
|
|
|
2658
2658
|
- No joins required
|
|
2659
2659
|
Examples: "How many customers do we have?", "List all products", "What is the total revenue?"
|
|
2660
2660
|
`,
|
|
2661
|
-
moderate:
|
|
2661
|
+
moderate: dedent5`
|
|
2662
2662
|
Generate moderate questions that require:
|
|
2663
2663
|
- JOINs between 2-3 tables
|
|
2664
2664
|
- Multiple WHERE conditions (AND/OR)
|
|
@@ -2667,7 +2667,7 @@ var complexityInstructions = {
|
|
|
2667
2667
|
- Basic subqueries
|
|
2668
2668
|
Examples: "What are the top 5 customers by total orders?", "Which products have never been ordered?"
|
|
2669
2669
|
`,
|
|
2670
|
-
complex:
|
|
2670
|
+
complex: dedent5`
|
|
2671
2671
|
Generate complex questions that require:
|
|
2672
2672
|
- Multiple JOINs (3+ tables)
|
|
2673
2673
|
- Nested subqueries or CTEs
|
|
@@ -2676,7 +2676,7 @@ var complexityInstructions = {
|
|
|
2676
2676
|
- Date/time calculations
|
|
2677
2677
|
Examples: "What is the month-over-month growth rate?", "Which customers have increased spending compared to last year?"
|
|
2678
2678
|
`,
|
|
2679
|
-
"high complex":
|
|
2679
|
+
"high complex": dedent5`
|
|
2680
2680
|
Generate highly complex questions that require advanced SQL features:
|
|
2681
2681
|
- Window functions (ROW_NUMBER, RANK, DENSE_RANK)
|
|
2682
2682
|
- LAG, LEAD for comparisons
|
|
@@ -2711,7 +2711,7 @@ async function generateQuestions(params) {
|
|
|
2711
2711
|
),
|
|
2712
2712
|
fragment(
|
|
2713
2713
|
"task",
|
|
2714
|
-
|
|
2714
|
+
dedent5`
|
|
2715
2715
|
Generate exactly ${count} natural language questions at the "${complexity}" complexity level.
|
|
2716
2716
|
The questions should:
|
|
2717
2717
|
1. Match the complexity requirements above
|
|
@@ -3014,7 +3014,7 @@ Generate ${this.options.count} questions at ${complexity} complexity.` : void 0;
|
|
|
3014
3014
|
|
|
3015
3015
|
// packages/text2sql/src/lib/synthesis/synthesizers/breadth-evolver.ts
|
|
3016
3016
|
import { groq as groq7 } from "@ai-sdk/groq";
|
|
3017
|
-
import
|
|
3017
|
+
import dedent6 from "dedent";
|
|
3018
3018
|
import pLimit2 from "p-limit";
|
|
3019
3019
|
import z6 from "zod";
|
|
3020
3020
|
import "@deepagents/agent";
|
|
@@ -3064,7 +3064,7 @@ async function paraphraseQuestion(params) {
|
|
|
3064
3064
|
chatId: `paraphraser-${crypto.randomUUID()}`,
|
|
3065
3065
|
userId: "system"
|
|
3066
3066
|
});
|
|
3067
|
-
const personaInstruction = params.persona ?
|
|
3067
|
+
const personaInstruction = params.persona ? dedent6`
|
|
3068
3068
|
<persona role="${params.persona.role}">
|
|
3069
3069
|
${params.persona.perspective}
|
|
3070
3070
|
|
|
@@ -3072,7 +3072,7 @@ async function paraphraseQuestion(params) {
|
|
|
3072
3072
|
Use their vocabulary, priorities, and framing style.
|
|
3073
3073
|
</persona>
|
|
3074
3074
|
` : "";
|
|
3075
|
-
const styleInstruction = params.persona?.styles && params.persona.styles.length > 0 ?
|
|
3075
|
+
const styleInstruction = params.persona?.styles && params.persona.styles.length > 0 ? dedent6`
|
|
3076
3076
|
<communication_styles>
|
|
3077
3077
|
Generate paraphrases using these communication styles: ${params.persona.styles.join(", ")}
|
|
3078
3078
|
|
|
@@ -3098,7 +3098,7 @@ async function paraphraseQuestion(params) {
|
|
|
3098
3098
|
...styleInstruction ? [fragment("communication_styles", styleInstruction)] : [],
|
|
3099
3099
|
fragment(
|
|
3100
3100
|
"task",
|
|
3101
|
-
|
|
3101
|
+
dedent6`
|
|
3102
3102
|
Generate exactly ${params.count} paraphrased versions of the original question.
|
|
3103
3103
|
|
|
3104
3104
|
Requirements:
|
|
@@ -3175,13 +3175,13 @@ var BreadthEvolver = class extends PairProducer {
|
|
|
3175
3175
|
// packages/text2sql/src/lib/synthesis/synthesizers/depth-evolver.ts
|
|
3176
3176
|
import { groq as groq8 } from "@ai-sdk/groq";
|
|
3177
3177
|
import { NoObjectGeneratedError as NoObjectGeneratedError2, NoOutputGeneratedError as NoOutputGeneratedError2 } from "ai";
|
|
3178
|
-
import
|
|
3178
|
+
import dedent7 from "dedent";
|
|
3179
3179
|
import pLimit3 from "p-limit";
|
|
3180
3180
|
import pRetry2 from "p-retry";
|
|
3181
3181
|
import z7 from "zod";
|
|
3182
3182
|
import "@deepagents/agent";
|
|
3183
3183
|
var techniqueInstructions = {
|
|
3184
|
-
"add-aggregation":
|
|
3184
|
+
"add-aggregation": dedent7`
|
|
3185
3185
|
Add aggregation requirements to the question.
|
|
3186
3186
|
Transform it to require GROUP BY, COUNT, SUM, AVG, MIN, MAX, or similar operations.
|
|
3187
3187
|
Examples:
|
|
@@ -3189,7 +3189,7 @@ var techniqueInstructions = {
|
|
|
3189
3189
|
- "List products" → "What is the average price per category?"
|
|
3190
3190
|
- "Get employees" → "How many employees are in each department?"
|
|
3191
3191
|
`,
|
|
3192
|
-
"add-filter":
|
|
3192
|
+
"add-filter": dedent7`
|
|
3193
3193
|
Add filtering conditions to the question.
|
|
3194
3194
|
Transform it to require WHERE clauses with specific conditions.
|
|
3195
3195
|
Examples:
|
|
@@ -3197,7 +3197,7 @@ var techniqueInstructions = {
|
|
|
3197
3197
|
- "List customers" → "List customers who have made more than 5 purchases"
|
|
3198
3198
|
- "Get products" → "Get products with price above $100"
|
|
3199
3199
|
`,
|
|
3200
|
-
"add-join":
|
|
3200
|
+
"add-join": dedent7`
|
|
3201
3201
|
Add requirements that need data from related tables.
|
|
3202
3202
|
Transform it to require JOIN operations between multiple tables.
|
|
3203
3203
|
Examples:
|
|
@@ -3205,7 +3205,7 @@ var techniqueInstructions = {
|
|
|
3205
3205
|
- "List products" → "List products with their supplier information"
|
|
3206
3206
|
- "Get employees" → "Get employees with their department and manager names"
|
|
3207
3207
|
`,
|
|
3208
|
-
"add-reasoning":
|
|
3208
|
+
"add-reasoning": dedent7`
|
|
3209
3209
|
Add multi-step reasoning requirements.
|
|
3210
3210
|
Transform it to require logical deduction, comparisons, or derived calculations.
|
|
3211
3211
|
Examples:
|
|
@@ -3213,7 +3213,7 @@ var techniqueInstructions = {
|
|
|
3213
3213
|
- "List products" → "Which products are underperforming compared to their category average?"
|
|
3214
3214
|
- "Get revenue" → "Which month had the highest growth compared to the previous month?"
|
|
3215
3215
|
`,
|
|
3216
|
-
hypothetical:
|
|
3216
|
+
hypothetical: dedent7`
|
|
3217
3217
|
Add a hypothetical or speculative scenario.
|
|
3218
3218
|
Transform it to require applying calculations or projections.
|
|
3219
3219
|
Examples:
|
|
@@ -3251,7 +3251,7 @@ async function evolveQuestion(params) {
|
|
|
3251
3251
|
),
|
|
3252
3252
|
fragment(
|
|
3253
3253
|
"task",
|
|
3254
|
-
|
|
3254
|
+
dedent7`
|
|
3255
3255
|
Evolve the original question using the "${params.technique}" technique.
|
|
3256
3256
|
|
|
3257
3257
|
Requirements:
|
|
@@ -3396,7 +3396,7 @@ async function withRetry2(computation) {
|
|
|
3396
3396
|
|
|
3397
3397
|
// packages/text2sql/src/lib/synthesis/synthesizers/persona-generator.ts
|
|
3398
3398
|
import { groq as groq9 } from "@ai-sdk/groq";
|
|
3399
|
-
import
|
|
3399
|
+
import dedent8 from "dedent";
|
|
3400
3400
|
import z8 from "zod";
|
|
3401
3401
|
import "@deepagents/agent";
|
|
3402
3402
|
var outputSchema3 = z8.object({
|
|
@@ -3429,7 +3429,7 @@ async function generatePersonas(schemaFragments, options) {
|
|
|
3429
3429
|
fragment("database_schema", schema),
|
|
3430
3430
|
fragment(
|
|
3431
3431
|
"task",
|
|
3432
|
-
|
|
3432
|
+
dedent8`
|
|
3433
3433
|
Analyze the database schema and generate realistic personas representing
|
|
3434
3434
|
the different types of users who would query this database.
|
|
3435
3435
|
|
|
@@ -3462,7 +3462,7 @@ async function generatePersonas(schemaFragments, options) {
|
|
|
3462
3462
|
),
|
|
3463
3463
|
fragment(
|
|
3464
3464
|
"example",
|
|
3465
|
-
|
|
3465
|
+
dedent8`
|
|
3466
3466
|
For an e-commerce schema with orders, customers, products tables:
|
|
3467
3467
|
|
|
3468
3468
|
{
|
|
@@ -3502,7 +3502,7 @@ async function generatePersonas(schemaFragments, options) {
|
|
|
3502
3502
|
|
|
3503
3503
|
// packages/text2sql/src/lib/agents/teachables.agent.ts
|
|
3504
3504
|
import { groq as groq10 } from "@ai-sdk/groq";
|
|
3505
|
-
import
|
|
3505
|
+
import dedent9 from "dedent";
|
|
3506
3506
|
import z9 from "zod";
|
|
3507
3507
|
import "@deepagents/agent";
|
|
3508
3508
|
var outputSchema4 = z9.object({
|
|
@@ -3572,7 +3572,7 @@ async function toTeachings(input, options) {
|
|
|
3572
3572
|
...input.context ? [fragment("additional_context", input.context)] : [],
|
|
3573
3573
|
fragment(
|
|
3574
3574
|
"output_structure",
|
|
3575
|
-
|
|
3575
|
+
dedent9`
|
|
3576
3576
|
Output a JSON object with these optional arrays (include only relevant ones):
|
|
3577
3577
|
- terms: [{ name: string, definition: string }] - Domain terminology
|
|
3578
3578
|
- hints: [{ text: string }] - Helpful SQL generation hints
|
|
@@ -3588,7 +3588,7 @@ async function toTeachings(input, options) {
|
|
|
3588
3588
|
),
|
|
3589
3589
|
fragment(
|
|
3590
3590
|
"task",
|
|
3591
|
-
|
|
3591
|
+
dedent9`
|
|
3592
3592
|
1. Analyze the schema to infer domain, relationships, and sensitive columns.
|
|
3593
3593
|
2. Generate 3-10 fragments total across all categories, prioritizing:
|
|
3594
3594
|
- guardrails for PII columns (email, ssn, phone, etc)
|