@deepagents/text2sql 0.2.3 → 0.3.1

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 (139) hide show
  1. package/dist/index.d.ts +7 -9
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +879 -2730
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/adapters/adapter.d.ts +72 -10
  6. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  7. package/dist/lib/adapters/grounding.ticket.d.ts +21 -0
  8. package/dist/lib/adapters/grounding.ticket.d.ts.map +1 -0
  9. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts +32 -0
  10. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts.map +1 -0
  11. package/dist/lib/adapters/groundings/constraint.grounding.d.ts +31 -0
  12. package/dist/lib/adapters/groundings/constraint.grounding.d.ts.map +1 -0
  13. package/dist/lib/adapters/groundings/context.d.ts +41 -0
  14. package/dist/lib/adapters/groundings/context.d.ts.map +1 -0
  15. package/dist/lib/adapters/groundings/grounding.d.ts +8 -0
  16. package/dist/lib/adapters/groundings/grounding.d.ts.map +1 -0
  17. package/dist/lib/adapters/groundings/grounding.js +507 -0
  18. package/dist/lib/adapters/groundings/grounding.js.map +7 -0
  19. package/dist/lib/adapters/groundings/indexes.grounding.d.ts +30 -0
  20. package/dist/lib/adapters/groundings/indexes.grounding.d.ts.map +1 -0
  21. package/dist/lib/adapters/groundings/info.grounding.d.ts +29 -0
  22. package/dist/lib/adapters/groundings/info.grounding.d.ts.map +1 -0
  23. package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts +35 -0
  24. package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts.map +1 -0
  25. package/dist/lib/adapters/groundings/report.grounding.d.ts +38 -0
  26. package/dist/lib/adapters/groundings/report.grounding.d.ts.map +1 -0
  27. package/dist/lib/adapters/groundings/row-count.grounding.d.ts +30 -0
  28. package/dist/lib/adapters/groundings/row-count.grounding.d.ts.map +1 -0
  29. package/dist/lib/adapters/groundings/table.grounding.d.ts +61 -0
  30. package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -0
  31. package/dist/lib/adapters/groundings/view.grounding.d.ts +57 -0
  32. package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -0
  33. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts +12 -0
  34. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts.map +1 -0
  35. package/dist/lib/adapters/postgres/constraint.postgres.grounding.d.ts +11 -0
  36. package/dist/lib/adapters/postgres/constraint.postgres.grounding.d.ts.map +1 -0
  37. package/dist/lib/adapters/postgres/index.d.ts +43 -0
  38. package/dist/lib/adapters/postgres/index.d.ts.map +1 -0
  39. package/dist/lib/adapters/postgres/index.js +1646 -0
  40. package/dist/lib/adapters/postgres/index.js.map +7 -0
  41. package/dist/lib/adapters/postgres/indexes.postgres.grounding.d.ts +15 -0
  42. package/dist/lib/adapters/postgres/indexes.postgres.grounding.d.ts.map +1 -0
  43. package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts +11 -0
  44. package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -0
  45. package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts +14 -0
  46. package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts.map +1 -0
  47. package/dist/lib/adapters/postgres/postgres.d.ts +27 -0
  48. package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -0
  49. package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts +11 -0
  50. package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts.map +1 -0
  51. package/dist/lib/adapters/postgres/table.postgres.grounding.d.ts +21 -0
  52. package/dist/lib/adapters/postgres/table.postgres.grounding.d.ts.map +1 -0
  53. package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts +16 -0
  54. package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts.map +1 -0
  55. package/dist/lib/adapters/sqlite/column-stats.sqlite.grounding.d.ts +12 -0
  56. package/dist/lib/adapters/sqlite/column-stats.sqlite.grounding.d.ts.map +1 -0
  57. package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts +15 -0
  58. package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts.map +1 -0
  59. package/dist/lib/adapters/sqlite/index.d.ts +43 -0
  60. package/dist/lib/adapters/sqlite/index.d.ts.map +1 -0
  61. package/dist/lib/adapters/sqlite/index.js +1219 -0
  62. package/dist/lib/adapters/sqlite/index.js.map +7 -0
  63. package/dist/lib/adapters/sqlite/indexes.sqlite.grounding.d.ts +11 -0
  64. package/dist/lib/adapters/sqlite/indexes.sqlite.grounding.d.ts.map +1 -0
  65. package/dist/lib/adapters/sqlite/info.sqlite.grounding.d.ts +11 -0
  66. package/dist/lib/adapters/sqlite/info.sqlite.grounding.d.ts.map +1 -0
  67. package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts +14 -0
  68. package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts.map +1 -0
  69. package/dist/lib/adapters/sqlite/row-count.sqlite.grounding.d.ts +11 -0
  70. package/dist/lib/adapters/sqlite/row-count.sqlite.grounding.d.ts.map +1 -0
  71. package/dist/lib/adapters/sqlite/sqlite.d.ts +26 -0
  72. package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -0
  73. package/dist/lib/adapters/sqlite/table.sqlite.grounding.d.ts +17 -0
  74. package/dist/lib/adapters/sqlite/table.sqlite.grounding.d.ts.map +1 -0
  75. package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts +12 -0
  76. package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts.map +1 -0
  77. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts +12 -0
  78. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts.map +1 -0
  79. package/dist/lib/adapters/sqlserver/constraint.sqlserver.grounding.d.ts +11 -0
  80. package/dist/lib/adapters/sqlserver/constraint.sqlserver.grounding.d.ts.map +1 -0
  81. package/dist/lib/adapters/sqlserver/index.d.ts +43 -0
  82. package/dist/lib/adapters/sqlserver/index.d.ts.map +1 -0
  83. package/dist/lib/adapters/sqlserver/index.js +1699 -0
  84. package/dist/lib/adapters/sqlserver/index.js.map +7 -0
  85. package/dist/lib/adapters/sqlserver/indexes.sqlserver.grounding.d.ts +15 -0
  86. package/dist/lib/adapters/sqlserver/indexes.sqlserver.grounding.d.ts.map +1 -0
  87. package/dist/lib/adapters/sqlserver/info.sqlserver.grounding.d.ts +11 -0
  88. package/dist/lib/adapters/sqlserver/info.sqlserver.grounding.d.ts.map +1 -0
  89. package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts +14 -0
  90. package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts.map +1 -0
  91. package/dist/lib/adapters/sqlserver/row-count.sqlserver.grounding.d.ts +11 -0
  92. package/dist/lib/adapters/sqlserver/row-count.sqlserver.grounding.d.ts.map +1 -0
  93. package/dist/lib/adapters/sqlserver/sqlserver.d.ts +27 -0
  94. package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -0
  95. package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts +21 -0
  96. package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts.map +1 -0
  97. package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts +16 -0
  98. package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts.map +1 -0
  99. package/dist/lib/agents/suggestions.agents.d.ts +0 -2
  100. package/dist/lib/agents/suggestions.agents.d.ts.map +1 -1
  101. package/dist/lib/agents/teachables.agent.d.ts +0 -3
  102. package/dist/lib/agents/teachables.agent.d.ts.map +1 -1
  103. package/dist/lib/agents/text2sql.agent.d.ts +69 -29
  104. package/dist/lib/agents/text2sql.agent.d.ts.map +1 -1
  105. package/dist/lib/file-cache.d.ts +12 -0
  106. package/dist/lib/file-cache.d.ts.map +1 -0
  107. package/dist/lib/instructions.d.ts +3 -0
  108. package/dist/lib/instructions.d.ts.map +1 -0
  109. package/dist/lib/instructions.js +386 -0
  110. package/dist/lib/instructions.js.map +7 -0
  111. package/dist/lib/memory/memory.prompt.d.ts +3 -0
  112. package/dist/lib/memory/memory.prompt.d.ts.map +1 -0
  113. package/dist/lib/memory/memory.store.d.ts +5 -0
  114. package/dist/lib/memory/memory.store.d.ts.map +1 -0
  115. package/dist/lib/memory/sqlite.store.d.ts +14 -0
  116. package/dist/lib/memory/sqlite.store.d.ts.map +1 -0
  117. package/dist/lib/memory/store.d.ts +40 -0
  118. package/dist/lib/memory/store.d.ts.map +1 -0
  119. package/dist/lib/prompt.d.ts +1 -6
  120. package/dist/lib/prompt.d.ts.map +1 -1
  121. package/dist/lib/sql.d.ts +26 -35
  122. package/dist/lib/sql.d.ts.map +1 -1
  123. package/dist/lib/teach/teachables.d.ts +184 -13
  124. package/dist/lib/teach/teachables.d.ts.map +1 -1
  125. package/dist/lib/teach/teachings.d.ts.map +1 -1
  126. package/dist/lib/teach/xml.d.ts.map +1 -1
  127. package/package.json +38 -4
  128. package/dist/lib/adapters/postgres.d.ts +0 -31
  129. package/dist/lib/adapters/postgres.d.ts.map +0 -1
  130. package/dist/lib/adapters/resolveTables.spec.d.ts +0 -2
  131. package/dist/lib/adapters/resolveTables.spec.d.ts.map +0 -1
  132. package/dist/lib/adapters/sqlite.d.ts +0 -30
  133. package/dist/lib/adapters/sqlite.d.ts.map +0 -1
  134. package/dist/lib/adapters/sqlserver.d.ts +0 -31
  135. package/dist/lib/adapters/sqlserver.d.ts.map +0 -1
  136. package/dist/lib/agents/brief.agent.d.ts +0 -21
  137. package/dist/lib/agents/brief.agent.d.ts.map +0 -1
  138. package/dist/lib/memory/user-profile.d.ts +0 -39
  139. package/dist/lib/memory/user-profile.d.ts.map +0 -1
@@ -0,0 +1,1219 @@
1
+ // packages/text2sql/src/lib/adapters/groundings/context.ts
2
+ function createGroundingContext() {
3
+ return {
4
+ tables: [],
5
+ views: [],
6
+ relationships: [],
7
+ info: void 0
8
+ };
9
+ }
10
+
11
+ // packages/text2sql/src/lib/adapters/adapter.ts
12
+ var Adapter = class {
13
+ async introspect() {
14
+ const lines = [];
15
+ const ctx = createGroundingContext();
16
+ for (const fn of this.grounding) {
17
+ const grounding = fn(this);
18
+ lines.push({
19
+ tag: grounding.tag,
20
+ fn: await grounding.execute(ctx)
21
+ });
22
+ }
23
+ return lines.map(({ fn, tag }) => {
24
+ const description = fn();
25
+ if (description === null) {
26
+ return "";
27
+ }
28
+ return `<${tag}>
29
+ ${description}
30
+ </${tag}>`;
31
+ }).join("\n");
32
+ }
33
+ /**
34
+ * Convert unknown database value to number.
35
+ * Handles number, bigint, and string types.
36
+ */
37
+ toNumber(value) {
38
+ if (typeof value === "number" && Number.isFinite(value)) {
39
+ return value;
40
+ }
41
+ if (typeof value === "bigint") {
42
+ return Number(value);
43
+ }
44
+ if (typeof value === "string" && value.trim() !== "") {
45
+ const parsed = Number(value);
46
+ return Number.isFinite(parsed) ? parsed : void 0;
47
+ }
48
+ return void 0;
49
+ }
50
+ /**
51
+ * Parse a potentially qualified table name into schema and table parts.
52
+ */
53
+ parseTableName(name) {
54
+ if (name.includes(".")) {
55
+ const [schema, ...rest] = name.split(".");
56
+ return { schema, table: rest.join(".") };
57
+ }
58
+ return { schema: this.defaultSchema ?? "", table: name };
59
+ }
60
+ /**
61
+ * Escape a string value for use in SQL string literals (single quotes).
62
+ * Used in WHERE clauses like: WHERE name = '${escapeString(value)}'
63
+ */
64
+ escapeString(value) {
65
+ return value.replace(/'/g, "''");
66
+ }
67
+ /**
68
+ * Build a SQL filter clause to include/exclude schemas.
69
+ * @param columnName - The schema column name (e.g., 'TABLE_SCHEMA')
70
+ * @param allowedSchemas - If provided, filter to these schemas only
71
+ */
72
+ buildSchemaFilter(columnName, allowedSchemas) {
73
+ if (allowedSchemas && allowedSchemas.length > 0) {
74
+ const values = allowedSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
75
+ return `AND ${columnName} IN (${values})`;
76
+ }
77
+ if (this.systemSchemas.length > 0) {
78
+ const values = this.systemSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
79
+ return `AND ${columnName} NOT IN (${values})`;
80
+ }
81
+ return "";
82
+ }
83
+ };
84
+
85
+ // packages/text2sql/src/lib/adapters/grounding.ticket.ts
86
+ var AbstractGrounding = class {
87
+ tag;
88
+ constructor(tag) {
89
+ this.tag = tag;
90
+ }
91
+ };
92
+
93
+ // packages/text2sql/src/lib/adapters/groundings/column-stats.grounding.ts
94
+ var ColumnStatsGrounding = class extends AbstractGrounding {
95
+ constructor(config = {}) {
96
+ super("column_stats");
97
+ }
98
+ /**
99
+ * Execute the grounding process.
100
+ * Annotates columns in ctx.tables and ctx.views with statistics.
101
+ */
102
+ async execute(ctx) {
103
+ const allContainers = [...ctx.tables, ...ctx.views];
104
+ for (const container of allContainers) {
105
+ for (const column of container.columns) {
106
+ try {
107
+ const stats = await this.collectStats(container.name, column);
108
+ if (stats) {
109
+ column.stats = stats;
110
+ }
111
+ } catch (error) {
112
+ console.warn(
113
+ "Error collecting stats for",
114
+ container.name,
115
+ column.name,
116
+ error
117
+ );
118
+ }
119
+ }
120
+ }
121
+ return () => this.#describe();
122
+ }
123
+ #describe() {
124
+ return null;
125
+ }
126
+ };
127
+
128
+ // packages/text2sql/src/lib/adapters/groundings/constraint.grounding.ts
129
+ var ConstraintGrounding = class extends AbstractGrounding {
130
+ constructor(config = {}) {
131
+ super("constraints");
132
+ }
133
+ /**
134
+ * Execute the grounding process.
135
+ * Annotates tables in ctx.tables with their constraints.
136
+ */
137
+ async execute(ctx) {
138
+ for (const table of ctx.tables) {
139
+ try {
140
+ table.constraints = await this.getConstraints(table.name);
141
+ } catch (error) {
142
+ console.warn("Error collecting constraints for", table.name, error);
143
+ }
144
+ }
145
+ return () => null;
146
+ }
147
+ };
148
+
149
+ // packages/text2sql/src/lib/adapters/groundings/indexes.grounding.ts
150
+ var IndexesGrounding = class extends AbstractGrounding {
151
+ constructor(config = {}) {
152
+ super("indexes");
153
+ }
154
+ /**
155
+ * Execute the grounding process.
156
+ * Annotates tables in ctx.tables with their indexes and marks indexed columns.
157
+ */
158
+ async execute(ctx) {
159
+ for (const table of ctx.tables) {
160
+ table.indexes = await this.getIndexes(table.name);
161
+ for (const index of table.indexes ?? []) {
162
+ for (const colName of index.columns) {
163
+ const column = table.columns.find((c) => c.name === colName);
164
+ if (column) {
165
+ column.isIndexed = true;
166
+ }
167
+ }
168
+ }
169
+ }
170
+ return () => null;
171
+ }
172
+ };
173
+
174
+ // packages/text2sql/src/lib/adapters/groundings/info.grounding.ts
175
+ var InfoGrounding = class extends AbstractGrounding {
176
+ constructor(config = {}) {
177
+ super("dialect_info");
178
+ }
179
+ /**
180
+ * Execute the grounding process.
181
+ * Writes database info to ctx.info.
182
+ */
183
+ async execute(ctx) {
184
+ ctx.info = await this.collectInfo();
185
+ const lines = [`Dialect: ${ctx.info.dialect ?? "unknown"}`];
186
+ if (ctx.info.version) {
187
+ lines.push(`Version: ${ctx.info.version}`);
188
+ }
189
+ if (ctx.info.database) {
190
+ lines.push(`Database: ${ctx.info.database}`);
191
+ }
192
+ if (ctx.info.details && Object.keys(ctx.info.details).length) {
193
+ lines.push(`Details: ${JSON.stringify(ctx.info.details)}`);
194
+ }
195
+ return () => lines.join("\n");
196
+ }
197
+ };
198
+
199
+ // packages/text2sql/src/lib/adapters/groundings/low-cardinality.grounding.ts
200
+ var LowCardinalityGrounding = class extends AbstractGrounding {
201
+ constructor(config = {}) {
202
+ super("low_cardinality");
203
+ }
204
+ /**
205
+ * Execute the grounding process.
206
+ * Annotates columns in ctx.tables and ctx.views with low cardinality values.
207
+ */
208
+ async execute(ctx) {
209
+ const allContainers = [...ctx.tables, ...ctx.views];
210
+ for (const container of allContainers) {
211
+ for (const column of container.columns) {
212
+ try {
213
+ const lowCard = await this.collectLowCardinality(
214
+ container.name,
215
+ column
216
+ );
217
+ if (lowCard) {
218
+ column.kind = lowCard.kind;
219
+ column.values = lowCard.values;
220
+ }
221
+ } catch (error) {
222
+ console.warn(
223
+ "Error collecting low cardinality values for",
224
+ container.name,
225
+ column.name,
226
+ error
227
+ );
228
+ }
229
+ }
230
+ }
231
+ return () => this.#describe();
232
+ }
233
+ #describe() {
234
+ return null;
235
+ }
236
+ };
237
+
238
+ // packages/text2sql/src/lib/adapters/groundings/report.grounding.ts
239
+ import { groq } from "@ai-sdk/groq";
240
+ import { tool } from "ai";
241
+ import dedent from "dedent";
242
+ import z from "zod";
243
+ import {
244
+ agent,
245
+ generate,
246
+ toState,
247
+ user
248
+ } from "@deepagents/agent";
249
+ var ReportGrounding = class extends AbstractGrounding {
250
+ #adapter;
251
+ #model;
252
+ #cache;
253
+ #forceRefresh;
254
+ constructor(adapter, config = {}) {
255
+ super("business_context");
256
+ this.#adapter = adapter;
257
+ this.#model = config.model ?? groq("openai/gpt-oss-20b");
258
+ this.#cache = config.cache;
259
+ this.#forceRefresh = config.forceRefresh ?? false;
260
+ }
261
+ async execute(ctx) {
262
+ if (!this.#forceRefresh && this.#cache) {
263
+ const cached = await this.#cache.get();
264
+ if (cached) {
265
+ ctx.report = cached;
266
+ return () => cached;
267
+ }
268
+ }
269
+ const report2 = await this.#generateReport();
270
+ ctx.report = report2;
271
+ if (this.#cache) {
272
+ await this.#cache.set(report2);
273
+ }
274
+ return () => report2;
275
+ }
276
+ async #generateReport() {
277
+ const reportAgent = agent({
278
+ name: "db-report-agent",
279
+ model: this.#model,
280
+ prompt: () => dedent`
281
+ <identity>
282
+ You are a database analyst expert. Your job is to understand what
283
+ a database represents and provide business context about it.
284
+ You have READ-ONLY access to the database.
285
+ </identity>
286
+
287
+ <instructions>
288
+ Write a business context that helps another agent answer questions accurately.
289
+
290
+ For EACH table, do queries ONE AT A TIME:
291
+ 1. SELECT COUNT(*) to get row count
292
+ 2. SELECT * LIMIT 3 to see sample data
293
+
294
+ Then write a report with:
295
+ - What business this database is for
296
+ - For each table: purpose, row count, and example of what the data looks like
297
+
298
+ Include concrete examples like "Track prices are $0.99",
299
+ "Customer names like 'Luís Gonçalves'", etc.
300
+
301
+ Keep it 400-600 words, conversational style.
302
+ </instructions>
303
+ `,
304
+ tools: {
305
+ query_database: tool({
306
+ description: "Execute a SELECT query to explore the database and gather insights.",
307
+ inputSchema: z.object({
308
+ sql: z.string().describe("The SELECT query to execute"),
309
+ purpose: z.string().describe(
310
+ "What insight you are trying to gather with this query"
311
+ )
312
+ }),
313
+ execute: ({ sql }, options) => {
314
+ const state = toState(options);
315
+ return state.adapter.execute(sql);
316
+ }
317
+ })
318
+ }
319
+ });
320
+ const { text } = await generate(
321
+ reportAgent,
322
+ [
323
+ user(
324
+ "Please analyze the database and write a contextual report about what this database represents."
325
+ )
326
+ ],
327
+ { adapter: this.#adapter }
328
+ );
329
+ return text;
330
+ }
331
+ };
332
+
333
+ // packages/text2sql/src/lib/adapters/groundings/row-count.grounding.ts
334
+ var RowCountGrounding = class extends AbstractGrounding {
335
+ constructor(config = {}) {
336
+ super("row_counts");
337
+ }
338
+ /**
339
+ * Execute the grounding process.
340
+ * Annotates tables in ctx.tables with row counts and size hints.
341
+ */
342
+ async execute(ctx) {
343
+ for (const table of ctx.tables) {
344
+ const count = await this.getRowCount(table.name);
345
+ if (count != null) {
346
+ table.rowCount = count;
347
+ table.sizeHint = this.#classifyRowCount(count);
348
+ }
349
+ }
350
+ return () => null;
351
+ }
352
+ /**
353
+ * Classify row count into a size hint category.
354
+ */
355
+ #classifyRowCount(count) {
356
+ if (count < 100) return "tiny";
357
+ if (count < 1e3) return "small";
358
+ if (count < 1e4) return "medium";
359
+ if (count < 1e5) return "large";
360
+ return "huge";
361
+ }
362
+ };
363
+
364
+ // packages/text2sql/src/lib/adapters/groundings/table.grounding.ts
365
+ import pluralize from "pluralize";
366
+ var TableGrounding = class extends AbstractGrounding {
367
+ #filter;
368
+ #forward;
369
+ #backward;
370
+ constructor(config = {}) {
371
+ super("tables");
372
+ this.#filter = config.filter;
373
+ this.#forward = config.forward;
374
+ this.#backward = config.backward;
375
+ }
376
+ /**
377
+ * Execute the grounding process.
378
+ * Writes discovered tables and relationships to the context.
379
+ */
380
+ async execute(ctx) {
381
+ const seedTables = await this.applyFilter();
382
+ const forward = this.#forward;
383
+ const backward = this.#backward;
384
+ if (!forward && !backward) {
385
+ const tables3 = await Promise.all(
386
+ seedTables.map((name) => this.getTable(name))
387
+ );
388
+ ctx.tables.push(...tables3);
389
+ return () => this.#describeTables(tables3);
390
+ }
391
+ const tables2 = {};
392
+ const allRelationships = [];
393
+ const seenRelationships = /* @__PURE__ */ new Set();
394
+ const forwardQueue = [];
395
+ const backwardQueue = [];
396
+ const forwardVisited = /* @__PURE__ */ new Set();
397
+ const backwardVisited = /* @__PURE__ */ new Set();
398
+ for (const name of seedTables) {
399
+ if (forward) forwardQueue.push({ name, depth: 0 });
400
+ if (backward) backwardQueue.push({ name, depth: 0 });
401
+ }
402
+ const forwardLimit = forward === true ? Infinity : forward || 0;
403
+ while (forwardQueue.length > 0) {
404
+ const item = forwardQueue.shift();
405
+ if (!item) break;
406
+ const { name, depth } = item;
407
+ if (forwardVisited.has(name)) continue;
408
+ forwardVisited.add(name);
409
+ if (!tables2[name]) {
410
+ tables2[name] = await this.getTable(name);
411
+ }
412
+ if (depth < forwardLimit) {
413
+ const rels = await this.findOutgoingRelations(name);
414
+ for (const rel of rels) {
415
+ this.addRelationship(rel, allRelationships, seenRelationships);
416
+ if (!forwardVisited.has(rel.referenced_table)) {
417
+ forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });
418
+ }
419
+ }
420
+ }
421
+ }
422
+ const backwardLimit = backward === true ? Infinity : backward || 0;
423
+ while (backwardQueue.length > 0) {
424
+ const item = backwardQueue.shift();
425
+ if (!item) break;
426
+ const { name, depth } = item;
427
+ if (backwardVisited.has(name)) continue;
428
+ backwardVisited.add(name);
429
+ if (!tables2[name]) {
430
+ tables2[name] = await this.getTable(name);
431
+ }
432
+ if (depth < backwardLimit) {
433
+ const rels = await this.findIncomingRelations(name);
434
+ for (const rel of rels) {
435
+ this.addRelationship(rel, allRelationships, seenRelationships);
436
+ if (!backwardVisited.has(rel.table)) {
437
+ backwardQueue.push({ name: rel.table, depth: depth + 1 });
438
+ }
439
+ }
440
+ }
441
+ }
442
+ const tablesList = Object.values(tables2);
443
+ ctx.tables.push(...tablesList);
444
+ ctx.relationships.push(...allRelationships);
445
+ return () => this.#describeTables(tablesList);
446
+ }
447
+ /**
448
+ * Apply the filter to get seed table names.
449
+ * If filter is an explicit array, skip querying all table names.
450
+ */
451
+ async applyFilter() {
452
+ const filter = this.#filter;
453
+ if (Array.isArray(filter)) {
454
+ return filter;
455
+ }
456
+ const names = await this.getAllTableNames();
457
+ if (!filter) {
458
+ return names;
459
+ }
460
+ if (filter instanceof RegExp) {
461
+ return names.filter((name) => filter.test(name));
462
+ }
463
+ return names.filter(filter);
464
+ }
465
+ /**
466
+ * Add a relationship to the collection, deduplicating by key.
467
+ */
468
+ addRelationship(rel, all, seen) {
469
+ const key = `${rel.table}:${rel.from.join(",")}:${rel.referenced_table}:${rel.to.join(",")}`;
470
+ if (!seen.has(key)) {
471
+ seen.add(key);
472
+ all.push(rel);
473
+ }
474
+ }
475
+ #describeTables(tables2) {
476
+ if (!tables2.length) {
477
+ return "Schema unavailable.";
478
+ }
479
+ return tables2.map((table) => {
480
+ const rowCountInfo = table.rowCount != null ? ` [rows: ${table.rowCount}${table.sizeHint ? `, size: ${table.sizeHint}` : ""}]` : "";
481
+ const pkConstraint = table.constraints?.find((c) => c.type === "PRIMARY_KEY");
482
+ const pkColumns = new Set(pkConstraint?.columns ?? []);
483
+ const columns = table.columns.map((column) => {
484
+ const annotations = [];
485
+ const isPrimaryKey = pkColumns.has(column.name);
486
+ if (isPrimaryKey) {
487
+ annotations.push("PK");
488
+ }
489
+ if (column.isIndexed && !isPrimaryKey) {
490
+ annotations.push("Indexed");
491
+ }
492
+ if (column.kind === "LowCardinality" && column.values?.length) {
493
+ annotations.push(`LowCardinality: ${column.values.join(", ")}`);
494
+ }
495
+ if (column.stats) {
496
+ const statParts = [];
497
+ if (column.stats.min != null || column.stats.max != null) {
498
+ const minText = column.stats.min ?? "n/a";
499
+ const maxText = column.stats.max ?? "n/a";
500
+ statParts.push(`range ${minText} \u2192 ${maxText}`);
501
+ }
502
+ if (column.stats.nullFraction != null && Number.isFinite(column.stats.nullFraction)) {
503
+ const percent = Math.round(column.stats.nullFraction * 1e3) / 10;
504
+ statParts.push(`null\u2248${percent}%`);
505
+ }
506
+ if (statParts.length) {
507
+ annotations.push(statParts.join(", "));
508
+ }
509
+ }
510
+ const annotationText = annotations.length ? ` [${annotations.join(", ")}]` : "";
511
+ return ` - ${column.name} (${column.type})${annotationText}`;
512
+ }).join("\n");
513
+ const indexes2 = table.indexes?.length ? `
514
+ Indexes:
515
+ ${table.indexes.map((index) => {
516
+ const props = [];
517
+ if (index.unique) {
518
+ props.push("UNIQUE");
519
+ }
520
+ if (index.type) {
521
+ props.push(index.type);
522
+ }
523
+ const propsText = props.length ? ` (${props.join(", ")})` : "";
524
+ const columnsText = index.columns?.length ? index.columns.join(", ") : "expression";
525
+ return ` - ${index.name}${propsText}: ${columnsText}`;
526
+ }).join("\n")}` : "";
527
+ return `- Table: ${table.name}${rowCountInfo}
528
+ Columns:
529
+ ${columns}${indexes2}`;
530
+ }).join("\n\n");
531
+ }
532
+ #formatTableLabel = (tableName) => {
533
+ const base = tableName.split(".").pop() ?? tableName;
534
+ return base.replace(/_/g, " ");
535
+ };
536
+ #describeRelationships = (tables2, relationships) => {
537
+ if (!relationships.length) {
538
+ return "None detected";
539
+ }
540
+ const tableMap = new Map(tables2.map((table) => [table.name, table]));
541
+ return relationships.map((relationship) => {
542
+ const sourceLabel = this.#formatTableLabel(relationship.table);
543
+ const targetLabel = this.#formatTableLabel(
544
+ relationship.referenced_table
545
+ );
546
+ const singularSource = pluralize.singular(sourceLabel);
547
+ const pluralSource = pluralize.plural(sourceLabel);
548
+ const singularTarget = pluralize.singular(targetLabel);
549
+ const pluralTarget = pluralize.plural(targetLabel);
550
+ const sourceTable = tableMap.get(relationship.table);
551
+ const targetTable = tableMap.get(relationship.referenced_table);
552
+ const sourceCount = sourceTable?.rowCount;
553
+ const targetCount = targetTable?.rowCount;
554
+ const ratio = sourceCount != null && targetCount != null && targetCount > 0 ? sourceCount / targetCount : null;
555
+ let cardinality = "each";
556
+ if (ratio != null) {
557
+ if (ratio > 5) {
558
+ cardinality = `many-to-one (\u2248${sourceCount} vs ${targetCount})`;
559
+ } else if (ratio < 1.2 && ratio > 0.8) {
560
+ cardinality = `roughly 1:1 (${sourceCount} vs ${targetCount})`;
561
+ } else if (ratio < 0.2) {
562
+ cardinality = `one-to-many (${sourceCount} vs ${targetCount})`;
563
+ }
564
+ }
565
+ const mappings = relationship.from.map((fromCol, idx) => {
566
+ const targetCol = relationship.to[idx] ?? relationship.to[0] ?? fromCol;
567
+ return `${relationship.table}.${fromCol} -> ${relationship.referenced_table}.${targetCol}`;
568
+ }).join(", ");
569
+ return `- ${relationship.table} (${relationship.from.join(", ")}) -> ${relationship.referenced_table} (${relationship.to.join(", ")}) [${cardinality}]`;
570
+ }).join("\n");
571
+ };
572
+ };
573
+
574
+ // packages/text2sql/src/lib/adapters/sqlite/column-stats.sqlite.grounding.ts
575
+ var SqliteColumnStatsGrounding = class extends ColumnStatsGrounding {
576
+ #adapter;
577
+ constructor(adapter, config = {}) {
578
+ super(config);
579
+ this.#adapter = adapter;
580
+ }
581
+ async collectStats(tableName, column) {
582
+ if (!this.#shouldCollectStats(column.type)) {
583
+ return void 0;
584
+ }
585
+ const tableIdentifier = this.#adapter.quoteIdentifier(tableName);
586
+ const columnIdentifier = this.#adapter.quoteIdentifier(column.name);
587
+ const sql = `
588
+ SELECT
589
+ MIN(${columnIdentifier}) AS min_value,
590
+ MAX(${columnIdentifier}) AS max_value,
591
+ AVG(CASE WHEN ${columnIdentifier} IS NULL THEN 1.0 ELSE 0.0 END) AS null_fraction
592
+ FROM ${tableIdentifier}
593
+ `;
594
+ const rows = await this.#adapter.runQuery(sql);
595
+ if (!rows.length) {
596
+ return void 0;
597
+ }
598
+ const min = this.#normalizeValue(rows[0]?.min_value);
599
+ const max = this.#normalizeValue(rows[0]?.max_value);
600
+ const nullFraction = this.#adapter.toNumber(rows[0]?.null_fraction);
601
+ if (min == null && max == null && nullFraction == null) {
602
+ return void 0;
603
+ }
604
+ return {
605
+ min: min ?? void 0,
606
+ max: max ?? void 0,
607
+ nullFraction: nullFraction != null && Number.isFinite(nullFraction) ? Math.max(0, Math.min(1, nullFraction)) : void 0
608
+ };
609
+ }
610
+ #shouldCollectStats(type) {
611
+ if (!type) {
612
+ return false;
613
+ }
614
+ const normalized = type.toLowerCase();
615
+ return /int|real|numeric|double|float|decimal|date|time|bool/.test(
616
+ normalized
617
+ );
618
+ }
619
+ #normalizeValue(value) {
620
+ if (value === null || value === void 0) {
621
+ return null;
622
+ }
623
+ if (typeof value === "string") {
624
+ return value;
625
+ }
626
+ if (typeof value === "number" || typeof value === "bigint") {
627
+ return String(value);
628
+ }
629
+ if (typeof value === "boolean") {
630
+ return value ? "true" : "false";
631
+ }
632
+ if (value instanceof Date) {
633
+ return value.toISOString();
634
+ }
635
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
636
+ return value.toString("utf-8");
637
+ }
638
+ return null;
639
+ }
640
+ };
641
+
642
+ // packages/text2sql/src/lib/adapters/sqlite/constraint.sqlite.grounding.ts
643
+ var SqliteConstraintGrounding = class extends ConstraintGrounding {
644
+ #adapter;
645
+ constructor(adapter, config = {}) {
646
+ super(config);
647
+ this.#adapter = adapter;
648
+ }
649
+ async getConstraints(tableName) {
650
+ const constraints2 = [];
651
+ const columns = await this.#adapter.runQuery(
652
+ `PRAGMA table_info(${this.#quoteIdentifier(tableName)})`
653
+ );
654
+ const pkColumns = columns.filter((col) => col.pk > 0).sort((a, b) => a.pk - b.pk).map((col) => col.name);
655
+ if (pkColumns.length > 0) {
656
+ constraints2.push({
657
+ name: `${tableName}_pkey`,
658
+ type: "PRIMARY_KEY",
659
+ columns: pkColumns
660
+ });
661
+ }
662
+ for (const col of columns) {
663
+ if (col.notnull === 1 && col.pk === 0) {
664
+ constraints2.push({
665
+ name: `${tableName}_${col.name}_notnull`,
666
+ type: "NOT_NULL",
667
+ columns: [col.name]
668
+ });
669
+ }
670
+ if (col.dflt_value != null) {
671
+ constraints2.push({
672
+ name: `${tableName}_${col.name}_default`,
673
+ type: "DEFAULT",
674
+ columns: [col.name],
675
+ defaultValue: col.dflt_value
676
+ });
677
+ }
678
+ }
679
+ const fkRows = await this.#adapter.runQuery(
680
+ `PRAGMA foreign_key_list(${this.#quoteIdentifier(tableName)})`
681
+ );
682
+ const fkGroups = /* @__PURE__ */ new Map();
683
+ for (const row of fkRows) {
684
+ const group = fkGroups.get(row.id) ?? [];
685
+ group.push(row);
686
+ fkGroups.set(row.id, group);
687
+ }
688
+ for (const [id, rows] of fkGroups) {
689
+ rows.sort((a, b) => a.seq - b.seq);
690
+ constraints2.push({
691
+ name: `${tableName}_fkey_${id}`,
692
+ type: "FOREIGN_KEY",
693
+ columns: rows.map((r) => r.from),
694
+ referencedTable: rows[0].table,
695
+ referencedColumns: rows.map((r) => r.to)
696
+ });
697
+ }
698
+ const ddlRows = await this.#adapter.runQuery(
699
+ `SELECT sql FROM sqlite_master WHERE type='table' AND name=${this.#quoteIdentifier(tableName)}`
700
+ );
701
+ if (ddlRows[0]?.sql) {
702
+ const checkConstraints = this.#parseCheckConstraints(ddlRows[0].sql, tableName);
703
+ constraints2.push(...checkConstraints);
704
+ }
705
+ return constraints2;
706
+ }
707
+ #parseCheckConstraints(ddl, tableName) {
708
+ const constraints2 = [];
709
+ const checkRegex = /(?:CONSTRAINT\s+["'`]?(\w+)["'`]?\s+)?CHECK\s*\(([^)]+)\)/gi;
710
+ let match;
711
+ let index = 0;
712
+ while ((match = checkRegex.exec(ddl)) !== null) {
713
+ const name = match[1] || `${tableName}_check_${index}`;
714
+ const definition = match[2]?.trim();
715
+ if (definition) {
716
+ constraints2.push({
717
+ name,
718
+ type: "CHECK",
719
+ definition
720
+ });
721
+ index++;
722
+ }
723
+ }
724
+ const uniqueRegex = /(?:CONSTRAINT\s+["'`]?(\w+)["'`]?\s+)?UNIQUE\s*\(([^)]+)\)/gi;
725
+ let uniqueIndex = 0;
726
+ while ((match = uniqueRegex.exec(ddl)) !== null) {
727
+ const name = match[1] || `${tableName}_unique_${uniqueIndex}`;
728
+ const columnsStr = match[2]?.trim();
729
+ if (columnsStr) {
730
+ const columns = columnsStr.split(",").map((c) => c.trim().replace(/["'`]/g, ""));
731
+ constraints2.push({
732
+ name,
733
+ type: "UNIQUE",
734
+ columns
735
+ });
736
+ uniqueIndex++;
737
+ }
738
+ }
739
+ return constraints2;
740
+ }
741
+ #quoteIdentifier(name) {
742
+ return `'${name.replace(/'/g, "''")}'`;
743
+ }
744
+ };
745
+
746
+ // packages/text2sql/src/lib/adapters/sqlite/indexes.sqlite.grounding.ts
747
+ var SqliteIndexesGrounding = class extends IndexesGrounding {
748
+ #adapter;
749
+ constructor(adapter, config = {}) {
750
+ super(config);
751
+ this.#adapter = adapter;
752
+ }
753
+ async getIndexes(tableName) {
754
+ const indexListRows = await this.#adapter.runQuery(
755
+ `PRAGMA index_list(${this.#quoteIdentifier(tableName)})`
756
+ );
757
+ const indexes2 = [];
758
+ for (const indexRow of indexListRows) {
759
+ if (!indexRow.name) continue;
760
+ const indexInfoRows = await this.#adapter.runQuery(
761
+ `PRAGMA index_info(${this.#quoteIdentifier(indexRow.name)})`
762
+ );
763
+ const columns = indexInfoRows.filter((row) => row.name != null).sort((a, b) => a.seqno - b.seqno).map((row) => row.name);
764
+ if (!columns.length) continue;
765
+ indexes2.push({
766
+ name: indexRow.name,
767
+ columns,
768
+ unique: indexRow.unique === 1,
769
+ type: indexRow.partial === 1 ? "PARTIAL" : void 0
770
+ });
771
+ }
772
+ return indexes2;
773
+ }
774
+ #quoteIdentifier(name) {
775
+ return `'${name.replace(/'/g, "''")}'`;
776
+ }
777
+ };
778
+
779
+ // packages/text2sql/src/lib/adapters/sqlite/info.sqlite.grounding.ts
780
+ var SqliteInfoGrounding = class extends InfoGrounding {
781
+ #adapter;
782
+ constructor(adapter, config = {}) {
783
+ super(config);
784
+ this.#adapter = adapter;
785
+ }
786
+ async collectInfo() {
787
+ const rows = await this.#adapter.runQuery(
788
+ "SELECT sqlite_version() AS version"
789
+ );
790
+ return {
791
+ dialect: "sqlite",
792
+ version: rows[0]?.version
793
+ };
794
+ }
795
+ };
796
+
797
+ // packages/text2sql/src/lib/adapters/sqlite/low-cardinality.sqlite.grounding.ts
798
+ var LOW_CARDINALITY_LIMIT = 20;
799
+ var SqliteLowCardinalityGrounding = class extends LowCardinalityGrounding {
800
+ #adapter;
801
+ constructor(adapter, config = {}) {
802
+ super(config);
803
+ this.#adapter = adapter;
804
+ }
805
+ async collectLowCardinality(tableName, column) {
806
+ const tableIdentifier = this.#adapter.quoteIdentifier(tableName);
807
+ const columnIdentifier = this.#adapter.quoteIdentifier(column.name);
808
+ const limit = LOW_CARDINALITY_LIMIT + 1;
809
+ const sql = `
810
+ SELECT DISTINCT ${columnIdentifier} AS value
811
+ FROM ${tableIdentifier}
812
+ WHERE ${columnIdentifier} IS NOT NULL
813
+ LIMIT ${limit}
814
+ `;
815
+ const rows = await this.#adapter.runQuery(sql);
816
+ if (!rows.length || rows.length > LOW_CARDINALITY_LIMIT) {
817
+ return void 0;
818
+ }
819
+ const values = [];
820
+ for (const row of rows) {
821
+ const formatted = this.#normalizeValue(row.value);
822
+ if (formatted == null) {
823
+ return void 0;
824
+ }
825
+ values.push(formatted);
826
+ }
827
+ if (!values.length) {
828
+ return void 0;
829
+ }
830
+ return { kind: "LowCardinality", values };
831
+ }
832
+ #normalizeValue(value) {
833
+ if (value === null || value === void 0) {
834
+ return null;
835
+ }
836
+ if (typeof value === "string") {
837
+ return value;
838
+ }
839
+ if (typeof value === "number" || typeof value === "bigint") {
840
+ return String(value);
841
+ }
842
+ if (typeof value === "boolean") {
843
+ return value ? "true" : "false";
844
+ }
845
+ if (value instanceof Date) {
846
+ return value.toISOString();
847
+ }
848
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
849
+ return value.toString("utf-8");
850
+ }
851
+ return null;
852
+ }
853
+ };
854
+
855
+ // packages/text2sql/src/lib/adapters/sqlite/row-count.sqlite.grounding.ts
856
+ var SqliteRowCountGrounding = class extends RowCountGrounding {
857
+ #adapter;
858
+ constructor(adapter, config = {}) {
859
+ super(config);
860
+ this.#adapter = adapter;
861
+ }
862
+ async getRowCount(tableName) {
863
+ const rows = await this.#adapter.runQuery(
864
+ `SELECT COUNT(*) as count FROM ${this.#adapter.quoteIdentifier(tableName)}`
865
+ );
866
+ return this.#adapter.toNumber(rows[0]?.count);
867
+ }
868
+ };
869
+
870
+ // packages/text2sql/src/lib/adapters/sqlite/sqlite.ts
871
+ var SQL_ERROR_MAP = [
872
+ {
873
+ pattern: /^no such table: .+$/,
874
+ type: "MISSING_TABLE",
875
+ hint: "Check the database schema for the correct table name. The table you referenced does not exist."
876
+ },
877
+ {
878
+ pattern: /^no such column: .+$/,
879
+ type: "INVALID_COLUMN",
880
+ hint: "Check the table schema for correct column names. The column may not exist or is ambiguous (exists in multiple joined tables)."
881
+ },
882
+ {
883
+ pattern: /^ambiguous column name: .+$/,
884
+ type: "INVALID_COLUMN",
885
+ hint: "Check the table schema for correct column names. The column may not exist or is ambiguous (exists in multiple joined tables)."
886
+ },
887
+ {
888
+ pattern: /^near ".+": syntax error$/,
889
+ type: "SYNTAX_ERROR",
890
+ hint: "There is a SQL syntax error. Review the query structure, keywords, and punctuation."
891
+ },
892
+ {
893
+ pattern: /^no tables specified$/,
894
+ type: "SYNTAX_ERROR",
895
+ hint: "There is a SQL syntax error. Review the query structure, keywords, and punctuation."
896
+ },
897
+ {
898
+ pattern: /^attempt to write a readonly database$/,
899
+ type: "CONSTRAINT_ERROR",
900
+ hint: "A database constraint was violated. This should not happen with read-only queries."
901
+ }
902
+ ];
903
+ function formatError(sql, error) {
904
+ const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error occurred";
905
+ const errorInfo = SQL_ERROR_MAP.find((it) => it.pattern.test(errorMessage));
906
+ if (!errorInfo) {
907
+ return {
908
+ error: errorMessage,
909
+ error_type: "UNKNOWN_ERROR",
910
+ suggestion: "Review the query and try again",
911
+ sql_attempted: sql
912
+ };
913
+ }
914
+ return {
915
+ error: errorMessage,
916
+ error_type: errorInfo.type,
917
+ suggestion: errorInfo.hint,
918
+ sql_attempted: sql
919
+ };
920
+ }
921
+ var Sqlite = class extends Adapter {
922
+ #options;
923
+ grounding;
924
+ defaultSchema = void 0;
925
+ systemSchemas = [];
926
+ constructor(options) {
927
+ super();
928
+ if (!options || typeof options.execute !== "function") {
929
+ throw new Error("Sqlite adapter requires an execute function.");
930
+ }
931
+ this.#options = options;
932
+ this.grounding = options.grounding;
933
+ }
934
+ async execute(sql) {
935
+ return this.#options.execute(sql);
936
+ }
937
+ async validate(sql) {
938
+ const validator = this.#options.validate ?? (async (text) => {
939
+ await this.#options.execute(`EXPLAIN ${text}`);
940
+ });
941
+ try {
942
+ return await validator(sql);
943
+ } catch (error) {
944
+ return JSON.stringify(formatError(sql, error));
945
+ }
946
+ }
947
+ #quoteIdentifier(name) {
948
+ return `'${name.replace(/'/g, "''")}'`;
949
+ }
950
+ async runQuery(sql) {
951
+ const result = await this.#options.execute(sql);
952
+ if (Array.isArray(result)) {
953
+ return result;
954
+ }
955
+ if (result && typeof result === "object" && "rows" in result && Array.isArray(result.rows)) {
956
+ return result.rows;
957
+ }
958
+ throw new Error(
959
+ "Sqlite adapter execute() must return an array of rows or an object with a rows array when introspecting."
960
+ );
961
+ }
962
+ quoteIdentifier(name) {
963
+ return `"${name.replace(/"/g, '""')}"`;
964
+ }
965
+ escape(value) {
966
+ return value.replace(/"/g, '""');
967
+ }
968
+ buildSampleRowsQuery(tableName, columns, limit) {
969
+ const columnList = columns?.length ? columns.map((c) => this.quoteIdentifier(c)).join(", ") : "*";
970
+ return `SELECT ${columnList} FROM ${this.quoteIdentifier(tableName)} LIMIT ${limit}`;
971
+ }
972
+ };
973
+
974
+ // packages/text2sql/src/lib/adapters/sqlite/table.sqlite.grounding.ts
975
+ var SqliteTableGrounding = class extends TableGrounding {
976
+ #adapter;
977
+ #relationshipCache = null;
978
+ constructor(adapter, config = {}) {
979
+ super(config);
980
+ this.#adapter = adapter;
981
+ }
982
+ async getAllTableNames() {
983
+ const rows = await this.#adapter.runQuery(`SELECT name FROM sqlite_master WHERE type='table' ORDER BY name`);
984
+ return rows.map((row) => row.name).filter(
985
+ (name) => typeof name === "string" && !name.startsWith("sqlite_")
986
+ );
987
+ }
988
+ async getTable(tableName) {
989
+ const columns = await this.#adapter.runQuery(
990
+ `PRAGMA table_info(${this.#quoteIdentifier(tableName)})`
991
+ );
992
+ return {
993
+ name: tableName,
994
+ rawName: tableName,
995
+ columns: columns.map((col) => ({
996
+ name: col.name ?? "unknown",
997
+ type: col.type ?? "unknown"
998
+ }))
999
+ };
1000
+ }
1001
+ async findOutgoingRelations(tableName) {
1002
+ const rows = await this.#adapter.runQuery(
1003
+ `PRAGMA foreign_key_list(${this.#quoteIdentifier(tableName)})`
1004
+ );
1005
+ const groups = /* @__PURE__ */ new Map();
1006
+ for (const row of rows) {
1007
+ if (row.id == null || row.table == null || row.from == null || row.to == null) {
1008
+ continue;
1009
+ }
1010
+ const id = Number(row.id);
1011
+ const existing = groups.get(id);
1012
+ if (!existing) {
1013
+ groups.set(id, {
1014
+ table: tableName,
1015
+ from: [String(row.from)],
1016
+ referenced_table: String(row.table),
1017
+ to: [String(row.to)]
1018
+ });
1019
+ } else {
1020
+ existing.from.push(String(row.from));
1021
+ existing.to.push(String(row.to));
1022
+ }
1023
+ }
1024
+ return Array.from(groups.values());
1025
+ }
1026
+ async findIncomingRelations(tableName) {
1027
+ if (!this.#relationshipCache) {
1028
+ this.#relationshipCache = await this.#loadAllRelationships();
1029
+ }
1030
+ return this.#relationshipCache.filter(
1031
+ (r) => r.referenced_table === tableName
1032
+ );
1033
+ }
1034
+ async #loadAllRelationships() {
1035
+ const allNames = await this.getAllTableNames();
1036
+ const results = [];
1037
+ for (const name of allNames) {
1038
+ results.push(...await this.findOutgoingRelations(name));
1039
+ }
1040
+ return results;
1041
+ }
1042
+ #quoteIdentifier(name) {
1043
+ return `'${name.replace(/'/g, "''")}'`;
1044
+ }
1045
+ };
1046
+
1047
+ // packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
1048
+ var ViewGrounding = class extends AbstractGrounding {
1049
+ #filter;
1050
+ constructor(config = {}) {
1051
+ super("views");
1052
+ this.#filter = config.filter;
1053
+ }
1054
+ /**
1055
+ * Execute the grounding process.
1056
+ * Writes discovered views to the context.
1057
+ */
1058
+ async execute(ctx) {
1059
+ const viewNames = await this.applyFilter();
1060
+ const views2 = await Promise.all(
1061
+ viewNames.map((name) => this.getView(name))
1062
+ );
1063
+ ctx.views.push(...views2);
1064
+ return () => this.#describe(views2);
1065
+ }
1066
+ #describe(views2) {
1067
+ if (!views2.length) {
1068
+ return "No views available.";
1069
+ }
1070
+ return views2.map((view) => {
1071
+ const columns = view.columns.map((column) => {
1072
+ const annotations = [];
1073
+ if (column.kind === "LowCardinality" && column.values?.length) {
1074
+ annotations.push(`LowCardinality: ${column.values.join(", ")}`);
1075
+ }
1076
+ if (column.stats) {
1077
+ const statParts = [];
1078
+ if (column.stats.min != null || column.stats.max != null) {
1079
+ const minText = column.stats.min ?? "n/a";
1080
+ const maxText = column.stats.max ?? "n/a";
1081
+ statParts.push(`range ${minText} \u2192 ${maxText}`);
1082
+ }
1083
+ if (column.stats.nullFraction != null && Number.isFinite(column.stats.nullFraction)) {
1084
+ const percent = Math.round(column.stats.nullFraction * 1e3) / 10;
1085
+ statParts.push(`null\u2248${percent}%`);
1086
+ }
1087
+ if (statParts.length) {
1088
+ annotations.push(statParts.join(", "));
1089
+ }
1090
+ }
1091
+ const annotationText = annotations.length ? ` [${annotations.join(", ")}]` : "";
1092
+ return ` - ${column.name} (${column.type})${annotationText}`;
1093
+ }).join("\n");
1094
+ const definition = view.definition ? `
1095
+ Definition: ${view.definition.length > 200 ? view.definition.slice(0, 200) + "..." : view.definition}` : "";
1096
+ return `- View: ${view.name}${definition}
1097
+ Columns:
1098
+ ${columns}`;
1099
+ }).join("\n\n");
1100
+ }
1101
+ /**
1102
+ * Apply the filter to get view names.
1103
+ * If filter is an explicit array, skip querying all view names.
1104
+ */
1105
+ async applyFilter() {
1106
+ const filter = this.#filter;
1107
+ if (Array.isArray(filter)) {
1108
+ return filter;
1109
+ }
1110
+ const names = await this.getAllViewNames();
1111
+ if (!filter) {
1112
+ return names;
1113
+ }
1114
+ if (filter instanceof RegExp) {
1115
+ return names.filter((name) => filter.test(name));
1116
+ }
1117
+ return names.filter(filter);
1118
+ }
1119
+ };
1120
+
1121
+ // packages/text2sql/src/lib/adapters/sqlite/view.sqlite.grounding.ts
1122
+ var SqliteViewGrounding = class extends ViewGrounding {
1123
+ #adapter;
1124
+ constructor(adapter, config = {}) {
1125
+ super(config);
1126
+ this.#adapter = adapter;
1127
+ }
1128
+ async getAllViewNames() {
1129
+ const rows = await this.#adapter.runQuery(`SELECT name FROM sqlite_master WHERE type='view' ORDER BY name`);
1130
+ return rows.map((row) => row.name).filter((name) => typeof name === "string");
1131
+ }
1132
+ async getView(viewName) {
1133
+ const defRows = await this.#adapter.runQuery(
1134
+ `SELECT sql FROM sqlite_master WHERE type='view' AND name=${this.#quoteIdentifier(viewName)}`
1135
+ );
1136
+ const columns = await this.#adapter.runQuery(
1137
+ `PRAGMA table_info(${this.#quoteIdentifier(viewName)})`
1138
+ );
1139
+ return {
1140
+ name: viewName,
1141
+ definition: defRows[0]?.sql ?? void 0,
1142
+ columns: columns.map((col) => ({
1143
+ name: col.name ?? "unknown",
1144
+ type: col.type ?? "unknown"
1145
+ }))
1146
+ };
1147
+ }
1148
+ #quoteIdentifier(name) {
1149
+ return `'${name.replace(/'/g, "''")}'`;
1150
+ }
1151
+ };
1152
+
1153
+ // packages/text2sql/src/lib/adapters/sqlite/index.ts
1154
+ function tables(config = {}) {
1155
+ return (adapter) => new SqliteTableGrounding(adapter, config);
1156
+ }
1157
+ function info(config = {}) {
1158
+ return (adapter) => new SqliteInfoGrounding(adapter, config);
1159
+ }
1160
+ function views(config = {}) {
1161
+ return (adapter) => {
1162
+ return new SqliteViewGrounding(adapter, config);
1163
+ };
1164
+ }
1165
+ function columnStats(config = {}) {
1166
+ return (adapter) => {
1167
+ return new SqliteColumnStatsGrounding(adapter, config);
1168
+ };
1169
+ }
1170
+ function lowCardinality(config = {}) {
1171
+ return (adapter) => {
1172
+ return new SqliteLowCardinalityGrounding(adapter, config);
1173
+ };
1174
+ }
1175
+ function indexes(config = {}) {
1176
+ return (adapter) => {
1177
+ return new SqliteIndexesGrounding(adapter, config);
1178
+ };
1179
+ }
1180
+ function rowCount(config = {}) {
1181
+ return (adapter) => {
1182
+ return new SqliteRowCountGrounding(adapter, config);
1183
+ };
1184
+ }
1185
+ function constraints(config = {}) {
1186
+ return (adapter) => {
1187
+ return new SqliteConstraintGrounding(adapter, config);
1188
+ };
1189
+ }
1190
+ function report(config = {}) {
1191
+ return (adapter) => new ReportGrounding(adapter, config);
1192
+ }
1193
+ var sqlite_default = {
1194
+ tables,
1195
+ info,
1196
+ views,
1197
+ columnStats,
1198
+ lowCardinality,
1199
+ indexes,
1200
+ rowCount,
1201
+ constraints,
1202
+ report,
1203
+ Sqlite
1204
+ };
1205
+ export {
1206
+ Sqlite,
1207
+ columnStats,
1208
+ constraints,
1209
+ sqlite_default as default,
1210
+ formatError,
1211
+ indexes,
1212
+ info,
1213
+ lowCardinality,
1214
+ report,
1215
+ rowCount,
1216
+ tables,
1217
+ views
1218
+ };
1219
+ //# sourceMappingURL=index.js.map