@deepagents/text2sql 0.30.0 → 0.31.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.js +18 -2
- package/dist/index.js.map +3 -3
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +62 -5
- package/dist/lib/adapters/bigquery/index.js.map +3 -3
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts +23 -0
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/index.js +48 -5
- package/dist/lib/adapters/groundings/index.js.map +2 -2
- package/dist/lib/adapters/groundings/table.grounding.d.ts +9 -1
- package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts +7 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/mysql/index.js +62 -5
- package/dist/lib/adapters/mysql/index.js.map +3 -3
- package/dist/lib/adapters/postgres/index.js +62 -5
- package/dist/lib/adapters/postgres/index.js.map +3 -3
- package/dist/lib/adapters/spreadsheet/index.js +57 -4
- package/dist/lib/adapters/spreadsheet/index.js.map +3 -3
- package/dist/lib/adapters/sqlite/index.js +62 -5
- package/dist/lib/adapters/sqlite/index.js.map +3 -3
- package/dist/lib/adapters/sqlserver/index.js +62 -5
- package/dist/lib/adapters/sqlserver/index.js.map +3 -3
- package/package.json +4 -4
|
@@ -7,6 +7,29 @@ import type { GroundingContext } from './context.ts';
|
|
|
7
7
|
* - function: predicate to filter table names
|
|
8
8
|
*/
|
|
9
9
|
export type Filter = string[] | RegExp | ((tableName: string) => boolean);
|
|
10
|
+
/**
|
|
11
|
+
* Per-entity column filter.
|
|
12
|
+
* Maps entity name (table or view) to a Filter that selects which columns to keep.
|
|
13
|
+
* Entities not listed in the record keep all their columns.
|
|
14
|
+
*/
|
|
15
|
+
export type ColumnsFilter = Record<string, Filter>;
|
|
16
|
+
/**
|
|
17
|
+
* Filter a columns array using a Filter.
|
|
18
|
+
* Keeps columns whose name matches the filter.
|
|
19
|
+
*/
|
|
20
|
+
export declare function filterColumns<T extends {
|
|
21
|
+
name: string;
|
|
22
|
+
}>(columns: T[], filter: Filter): T[];
|
|
23
|
+
/**
|
|
24
|
+
* Apply per-entity column filtering.
|
|
25
|
+
* Returns the entity unchanged if no filter matches its name.
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyColumnFilter<T extends {
|
|
28
|
+
name: string;
|
|
29
|
+
columns: {
|
|
30
|
+
name: string;
|
|
31
|
+
}[];
|
|
32
|
+
}>(entity: T, columnsConfig?: ColumnsFilter): T;
|
|
10
33
|
export interface AdapterInfo {
|
|
11
34
|
dialect: string;
|
|
12
35
|
version?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstract.grounding.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/groundings/abstract.grounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AACD,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC;AAE/C;;;;;GAKG;AACH,8BAAsB,iBAAiB;IACrC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;IAIxB;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CACvD"}
|
|
1
|
+
{"version":3,"file":"abstract.grounding.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/groundings/abstract.grounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EACtD,OAAO,EAAE,CAAC,EAAE,EACZ,MAAM,EAAE,MAAM,GACb,CAAC,EAAE,CAQL;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,EACvD,MAAM,EAAE,CAAC,EAAE,aAAa,CAAC,EAAE,aAAa,GAAG,CAAC,CAK7C;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AACD,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC;AAE/C;;;;;GAKG;AACH,8BAAsB,iBAAiB;IACrC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;IAIxB;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CACvD"}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
// packages/text2sql/src/lib/adapters/groundings/abstract.grounding.ts
|
|
2
|
+
function filterColumns(columns, filter) {
|
|
3
|
+
if (Array.isArray(filter)) {
|
|
4
|
+
return columns.filter((col) => filter.includes(col.name));
|
|
5
|
+
}
|
|
6
|
+
if (filter instanceof RegExp) {
|
|
7
|
+
return columns.filter((col) => filter.test(col.name));
|
|
8
|
+
}
|
|
9
|
+
return columns.filter((col) => filter(col.name));
|
|
10
|
+
}
|
|
11
|
+
function applyColumnFilter(entity, columnsConfig) {
|
|
12
|
+
if (!columnsConfig) return entity;
|
|
13
|
+
const filter = columnsConfig[entity.name];
|
|
14
|
+
if (!filter) return entity;
|
|
15
|
+
return { ...entity, columns: filterColumns(entity.columns, filter) };
|
|
16
|
+
}
|
|
2
17
|
var AbstractGrounding = class {
|
|
3
18
|
/**
|
|
4
19
|
* Grounding identifier for debugging/logging.
|
|
@@ -357,11 +372,13 @@ var RowCountGrounding = class extends AbstractGrounding {
|
|
|
357
372
|
// packages/text2sql/src/lib/adapters/groundings/table.grounding.ts
|
|
358
373
|
var TableGrounding = class extends AbstractGrounding {
|
|
359
374
|
#filter;
|
|
375
|
+
#columns;
|
|
360
376
|
#forward;
|
|
361
377
|
#backward;
|
|
362
378
|
constructor(config = {}) {
|
|
363
379
|
super("table");
|
|
364
380
|
this.#filter = config.filter;
|
|
381
|
+
this.#columns = config.columns;
|
|
365
382
|
this.#forward = config.forward;
|
|
366
383
|
this.#backward = config.backward;
|
|
367
384
|
}
|
|
@@ -375,7 +392,9 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
375
392
|
const backward = this.#backward;
|
|
376
393
|
if (!forward && !backward) {
|
|
377
394
|
const tables2 = await Promise.all(
|
|
378
|
-
seedTables.map(
|
|
395
|
+
seedTables.map(
|
|
396
|
+
async (name) => applyColumnFilter(await this.getTable(name), this.#columns)
|
|
397
|
+
)
|
|
379
398
|
);
|
|
380
399
|
ctx.tables.push(...tables2);
|
|
381
400
|
return;
|
|
@@ -399,11 +418,15 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
399
418
|
if (forwardVisited.has(name)) continue;
|
|
400
419
|
forwardVisited.add(name);
|
|
401
420
|
if (!tables[name]) {
|
|
402
|
-
tables[name] =
|
|
421
|
+
tables[name] = applyColumnFilter(
|
|
422
|
+
await this.getTable(name),
|
|
423
|
+
this.#columns
|
|
424
|
+
);
|
|
403
425
|
}
|
|
404
426
|
if (depth < forwardLimit) {
|
|
405
427
|
const rels = await this.findOutgoingRelations(name);
|
|
406
428
|
for (const rel of rels) {
|
|
429
|
+
if (!this.isRelationshipVisible(rel)) continue;
|
|
407
430
|
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
408
431
|
if (!forwardVisited.has(rel.referenced_table)) {
|
|
409
432
|
forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });
|
|
@@ -419,11 +442,15 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
419
442
|
if (backwardVisited.has(name)) continue;
|
|
420
443
|
backwardVisited.add(name);
|
|
421
444
|
if (!tables[name]) {
|
|
422
|
-
tables[name] =
|
|
445
|
+
tables[name] = applyColumnFilter(
|
|
446
|
+
await this.getTable(name),
|
|
447
|
+
this.#columns
|
|
448
|
+
);
|
|
423
449
|
}
|
|
424
450
|
if (depth < backwardLimit) {
|
|
425
451
|
const rels = await this.findIncomingRelations(name);
|
|
426
452
|
for (const rel of rels) {
|
|
453
|
+
if (!this.isRelationshipVisible(rel)) continue;
|
|
427
454
|
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
428
455
|
if (!backwardVisited.has(rel.table)) {
|
|
429
456
|
backwardQueue.push({ name: rel.table, depth: depth + 1 });
|
|
@@ -463,15 +490,27 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
463
490
|
all.push(rel);
|
|
464
491
|
}
|
|
465
492
|
}
|
|
493
|
+
isRelationshipVisible(rel) {
|
|
494
|
+
return this.areColumnsVisible(rel.from, this.#columns?.[rel.table]) && this.areColumnsVisible(rel.to, this.#columns?.[rel.referenced_table]);
|
|
495
|
+
}
|
|
496
|
+
areColumnsVisible(names, filter) {
|
|
497
|
+
if (!filter) return true;
|
|
498
|
+
return filterColumns(
|
|
499
|
+
names.map((name) => ({ name })),
|
|
500
|
+
filter
|
|
501
|
+
).length === names.length;
|
|
502
|
+
}
|
|
466
503
|
};
|
|
467
504
|
|
|
468
505
|
// packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
|
|
469
506
|
var ViewGrounding = class extends AbstractGrounding {
|
|
470
507
|
#filter;
|
|
508
|
+
#columns;
|
|
471
509
|
includeDefinition;
|
|
472
510
|
constructor(config = {}) {
|
|
473
511
|
super("view");
|
|
474
512
|
this.#filter = config.filter;
|
|
513
|
+
this.#columns = config.columns;
|
|
475
514
|
this.includeDefinition = config.includeDefinition ?? true;
|
|
476
515
|
}
|
|
477
516
|
/**
|
|
@@ -481,7 +520,9 @@ var ViewGrounding = class extends AbstractGrounding {
|
|
|
481
520
|
async execute(ctx) {
|
|
482
521
|
const viewNames = await this.applyFilter();
|
|
483
522
|
const views = await Promise.all(
|
|
484
|
-
viewNames.map(
|
|
523
|
+
viewNames.map(
|
|
524
|
+
async (name) => applyColumnFilter(await this.getView(name), this.#columns)
|
|
525
|
+
)
|
|
485
526
|
);
|
|
486
527
|
ctx.views.push(...views);
|
|
487
528
|
}
|
|
@@ -514,6 +555,8 @@ export {
|
|
|
514
555
|
RowCountGrounding,
|
|
515
556
|
TableGrounding,
|
|
516
557
|
ViewGrounding,
|
|
517
|
-
|
|
558
|
+
applyColumnFilter,
|
|
559
|
+
createGroundingContext,
|
|
560
|
+
filterColumns
|
|
518
561
|
};
|
|
519
562
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/adapters/groundings/abstract.grounding.ts", "../../../../src/lib/adapters/groundings/column-stats.grounding.ts", "../../../../src/lib/adapters/groundings/column-values.grounding.ts", "../../../../src/lib/adapters/groundings/constraint.grounding.ts", "../../../../src/lib/adapters/groundings/context.ts", "../../../../src/lib/adapters/groundings/info.grounding.ts", "../../../../src/lib/adapters/groundings/report.grounding.ts", "../../../../src/lib/adapters/groundings/row-count.grounding.ts", "../../../../src/lib/adapters/groundings/table.grounding.ts", "../../../../src/lib/adapters/groundings/view.grounding.ts"],
|
|
4
|
-
"sourcesContent": ["import type { FragmentObject } from '@deepagents/context';\n\nimport type { Adapter } from '../adapter.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Filter type for table names.\n * - string[]: explicit list of table names\n * - RegExp: pattern to match table names\n * - function: predicate to filter table names\n */\nexport type Filter = string[] | RegExp | ((tableName: string) => boolean);\n\nexport interface AdapterInfo {\n dialect: string;\n version?: string;\n database?: string;\n details?: FragmentObject;\n}\nexport type AdapterInfoProvider =\n | AdapterInfo\n | (() => Promise<AdapterInfo> | AdapterInfo);\n\n/**\n * Abstract base class for database schema groundings.\n *\n * Groundings collect schema metadata into the shared GroundingContext.\n * Fragment generation is centralized in Adapter.introspect().\n */\nexport abstract class AbstractGrounding {\n /**\n * Grounding identifier for debugging/logging.\n */\n name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Execute grounding to populate the shared context.\n * Groundings mutate ctx to add their collected data (tables, views, indexes, etc).\n * Fragment generation happens centrally in Adapter after all groundings complete.\n *\n * @param ctx - Shared context for accumulating schema data\n */\n abstract execute(ctx: GroundingContext): Promise<void>;\n}\n\nclass SampleDataGrounding {\n // this will fetch sample data for tables matching the filter\n}\n\nclass FunctionGrounding {\n #filter: Filter;\n #adapter: Adapter;\n constructor(adapter: Adapter, filter: Filter) {\n this.#filter = filter;\n this.#adapter = adapter;\n }\n}\n", "import type { ColumnStats } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { Column, ColumnContainer, GroundingContext } from './context.ts';\n\n/**\n * Configuration for ColumnStatsGrounding.\n */\nexport interface ColumnStatsGroundingConfig {\n // Future: filter which tables/columns to collect stats for\n}\n\n/**\n * Abstract base class for column statistics grounding.\n *\n * Reads tables and views from the context and annotates their columns\n * with statistics (min, max, nullFraction).\n *\n * Subclasses implement database-specific hooks:\n * - `collectStats()` - collect min/max/nullFraction for a column\n */\nexport abstract class ColumnStatsGrounding extends AbstractGrounding {\n constructor(config: ColumnStatsGroundingConfig = {}) {\n super('columnStats');\n }\n\n /**\n * Collect min/max/nullFraction statistics for a column.\n * Return undefined to skip this column.\n */\n protected abstract collectStats(\n tableName: string,\n column: Column,\n ): Promise<ColumnStats | undefined>;\n\n /**\n * Execute the grounding process.\n * Annotates columns in ctx.tables and ctx.views with statistics.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n // Process both tables and views\n const allContainers: ColumnContainer[] = [...ctx.tables, ...ctx.views];\n for (const container of allContainers) {\n for (const column of container.columns) {\n // Collect min/max/nullFraction\n try {\n const stats = await this.collectStats(container.name, column);\n if (stats) {\n column.stats = stats;\n }\n } catch (error) {\n // Skip on error\n console.warn(\n 'Error collecting stats for',\n container.name,\n column.name,\n error,\n );\n }\n }\n }\n }\n}\n", "import type { Table, TableConstraint } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { Column, ColumnContainer, GroundingContext } from './context.ts';\n\nexport type { Column, ColumnContainer };\n\n/**\n * Result of column value detection.\n */\nexport type ColumnValuesResult = {\n kind: 'Enum' | 'LowCardinality';\n values: string[];\n};\n\n/**\n * Configuration for ColumnValuesGrounding.\n */\nexport interface ColumnValuesGroundingConfig {\n /** Maximum number of distinct values to consider low cardinality (default: 20) */\n lowCardinalityLimit?: number;\n /** Maximum character length for individual values. Columns with any value exceeding this are skipped entirely (default: 100) */\n maxValueLength?: number;\n}\n\n/**\n * Abstract base class for column values grounding.\n *\n * Discovers possible values for columns from three sources (in priority order):\n * 1. Native ENUM types (PostgreSQL, MySQL) \u2192 kind: 'Enum'\n * 2. CHECK constraints with IN clauses \u2192 kind: 'Enum'\n * 3. Low cardinality data scan \u2192 kind: 'LowCardinality'\n *\n * Subclasses implement database-specific hooks:\n * - `collectEnumValues()` - get values for native ENUM columns\n * - `collectLowCardinality()` - collect distinct values via data scan\n */\nexport abstract class ColumnValuesGrounding extends AbstractGrounding {\n protected lowCardinalityLimit: number;\n protected maxValueLength: number;\n\n constructor(config: ColumnValuesGroundingConfig = {}) {\n super('columnValues');\n this.lowCardinalityLimit = config.lowCardinalityLimit ?? 20;\n this.maxValueLength = config.maxValueLength ?? 100;\n }\n\n /**\n * Get values for native ENUM type columns.\n * Return undefined if column is not an ENUM type.\n * Default implementation returns undefined (no native ENUM support).\n */\n protected async collectEnumValues(\n _tableName: string,\n _column: Column,\n ): Promise<string[] | undefined> {\n return undefined;\n }\n\n /**\n * Collect distinct values for low cardinality columns via data scan.\n * Return undefined if column has too many distinct values.\n */\n protected abstract collectLowCardinality(\n tableName: string,\n column: Column,\n ): Promise<string[] | undefined>;\n\n /**\n * Parse CHECK constraint for enum-like IN clause.\n * Extracts values from patterns like:\n * - CHECK (status IN ('active', 'inactive'))\n * - CHECK ((status)::text = ANY (ARRAY['a'::text, 'b'::text]))\n * - CHECK (status = 'active' OR status = 'inactive')\n */\n protected parseCheckConstraint(\n constraint: TableConstraint,\n columnName: string,\n ): string[] | undefined {\n if (constraint.type !== 'CHECK' || !constraint.definition) {\n return undefined;\n }\n\n // Check if constraint applies to this column\n if (constraint.columns && !constraint.columns.includes(columnName)) {\n return undefined;\n }\n\n const def = constraint.definition;\n const escapedCol = this.escapeRegex(columnName);\n\n // Column pattern: matches column name with optional parens and type cast\n // e.g., \"status\", \"(status)\", \"((status)::text)\"\n const colPattern = `(?:\\\\(?\\\\(?${escapedCol}\\\\)?(?:::(?:text|varchar|character varying))?\\\\)?)`;\n\n // Pattern 1: column IN ('val1', 'val2', ...)\n const inMatch = def.match(\n new RegExp(`${colPattern}\\\\s+IN\\\\s*\\\\(([^)]+)\\\\)`, 'i'),\n );\n if (inMatch) {\n return this.extractStringValues(inMatch[1]);\n }\n\n // Pattern 2: PostgreSQL ANY(ARRAY[...])\n const anyMatch = def.match(\n new RegExp(\n `${colPattern}\\\\s*=\\\\s*ANY\\\\s*\\\\(\\\\s*(?:ARRAY)?\\\\s*\\\\[([^\\\\]]+)\\\\]`,\n 'i',\n ),\n );\n if (anyMatch) {\n return this.extractStringValues(anyMatch[1]);\n }\n\n // Pattern 3: column = 'val1' OR column = 'val2' ...\n const orPattern = new RegExp(\n `\\\\b${this.escapeRegex(columnName)}\\\\b\\\\s*=\\\\s*'([^']*)'`,\n 'gi',\n );\n const orMatches = [...def.matchAll(orPattern)];\n if (orMatches.length >= 2) {\n return orMatches.map((m) => m[1]);\n }\n\n return undefined;\n }\n\n /**\n * Extract string values from a comma-separated list.\n */\n private extractStringValues(input: string): string[] | undefined {\n const values: string[] = [];\n // Match quoted strings: 'value' or 'value'::type\n const matches = input.matchAll(/'([^']*)'/g);\n for (const match of matches) {\n values.push(match[1]);\n }\n return values.length > 0 ? values : undefined;\n }\n\n /**\n * Escape special regex characters in a string.\n */\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Get the table from context by name.\n */\n private getTable(ctx: GroundingContext, name: string): Table | undefined {\n return ctx.tables.find((t) => t.name === name);\n }\n\n /**\n * Execute the grounding process.\n * Annotates columns in ctx.tables and ctx.views with values.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n // Process both tables and views\n const allContainers: ColumnContainer[] = [...ctx.tables, ...ctx.views];\n\n for (const container of allContainers) {\n const table = this.getTable(ctx, container.name);\n\n for (const column of container.columns) {\n try {\n const result = await this.resolveColumnValues(\n container.name,\n column,\n table?.constraints,\n );\n if (result) {\n column.kind = result.kind;\n column.values = result.values;\n }\n } catch (error) {\n console.warn(\n 'Error collecting column values for',\n container.name,\n column.name,\n error,\n );\n }\n }\n }\n }\n\n private exceedsMaxValueLength(values: string[]): boolean {\n return values.some((v) => v.length > this.maxValueLength);\n }\n\n /**\n * Resolve column values from all sources in priority order.\n */\n private async resolveColumnValues(\n tableName: string,\n column: Column,\n constraints?: TableConstraint[],\n ): Promise<ColumnValuesResult | undefined> {\n // Priority 1: Native ENUM type\n const enumValues = await this.collectEnumValues(tableName, column);\n if (enumValues?.length && !this.exceedsMaxValueLength(enumValues)) {\n return { kind: 'Enum', values: enumValues };\n }\n\n // Priority 2: CHECK constraint with IN clause\n if (constraints) {\n for (const constraint of constraints) {\n const checkValues = this.parseCheckConstraint(constraint, column.name);\n if (checkValues?.length && !this.exceedsMaxValueLength(checkValues)) {\n return { kind: 'Enum', values: checkValues };\n }\n }\n }\n\n // Priority 3: Low cardinality data scan\n const lowCardValues = await this.collectLowCardinality(tableName, column);\n if (lowCardValues?.length && !this.exceedsMaxValueLength(lowCardValues)) {\n return { kind: 'LowCardinality', values: lowCardValues };\n }\n\n return undefined;\n }\n}\n", "import type { TableConstraint } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for ConstraintGrounding.\n */\nexport interface ConstraintGroundingConfig {\n // Future: filter which tables/constraint types to collect\n}\n\n/**\n * Abstract base class for constraint grounding.\n *\n * Reads tables from the context and annotates them with constraints\n * (CHECK, UNIQUE, NOT_NULL, DEFAULT).\n * This grounding must run AFTER TableGrounding since it reads from ctx.tables.\n *\n * Subclasses implement the database-specific hook:\n * - `getConstraints()` - fetch constraints for a table\n */\nexport abstract class ConstraintGrounding extends AbstractGrounding {\n constructor(config: ConstraintGroundingConfig = {}) {\n super('constraint');\n }\n\n /**\n * Fetch constraints for a specific table.\n */\n protected abstract getConstraints(\n tableName: string,\n ): Promise<TableConstraint[]>;\n\n /**\n * Execute the grounding process.\n * Annotates tables in ctx.tables with their constraints.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n for (const table of ctx.tables) {\n try {\n table.constraints = await this.getConstraints(table.name);\n } catch (error) {\n // Skip on error - table might not exist or be inaccessible\n console.warn('Error collecting constraints for', table.name, error);\n }\n }\n }\n}\n", "import type {\n AdapterInfo,\n ColumnStats,\n Relationship,\n Table,\n} from '../adapter.ts';\nimport type { View } from './view.grounding.ts';\n\n/**\n * Column type for grounding operations.\n * Common interface between Table.columns and View.columns.\n */\nexport interface Column {\n name: string;\n type: string;\n kind?: 'LowCardinality' | 'Enum';\n values?: string[];\n stats?: ColumnStats;\n}\n\n/**\n * Entity with columns (Table or View).\n */\nexport interface ColumnContainer {\n name: string;\n columns: Column[];\n}\n\n/**\n * Shared context object passed to all groundings.\n * Groundings read from and write to this context.\n */\nexport interface GroundingContext {\n /** Tables discovered by TableGrounding */\n tables: Table[];\n\n /** Views discovered by ViewGrounding */\n views: View[];\n\n /** Relationships discovered by TableGrounding */\n relationships: Relationship[];\n\n /** Database info collected by InfoGrounding */\n info?: AdapterInfo;\n\n /** Business context report generated by ReportGrounding */\n report?: string;\n\n /** Shared cache for cross-grounding deduplication. Keyed by `type:key`. */\n cache: Map<string, unknown>;\n}\n\n/**\n * Create a new empty grounding context.\n */\nexport function createGroundingContext(): GroundingContext {\n return {\n tables: [],\n views: [],\n relationships: [],\n info: undefined,\n cache: new Map(),\n };\n}\n", "import type { AdapterInfo } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for InfoGrounding.\n */\nexport interface InfoGroundingConfig {\n // Future: options to control what info to collect\n}\n\n/**\n * Abstract base class for database info grounding.\n *\n * Collects database dialect, version, and connection info.\n *\n * Subclasses implement the database-specific hook:\n * - `collectInfo()` - collect database info\n */\nexport abstract class InfoGrounding extends AbstractGrounding {\n constructor(config: InfoGroundingConfig = {}) {\n super('dialectInfo');\n }\n\n /**\n * Collect database dialect, version, and other info.\n */\n protected abstract collectInfo(): Promise<AdapterInfo>;\n\n /**\n * Execute the grounding process.\n * Writes database info to ctx.info.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n ctx.info = await this.collectInfo();\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport { tool } from 'ai';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n agent,\n fragment,\n persona,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Cache interface for storing generated reports.\n */\nexport interface ReportCache {\n get(): Promise<string | null>;\n set(value: string): Promise<void>;\n}\n\n/**\n * Configuration for ReportGrounding.\n */\nexport interface ReportGroundingConfig {\n /** LLM model to use for generating the report */\n model?: AgentModel;\n /** Optional cache for storing generated reports */\n cache?: ReportCache;\n /** Force regeneration even if cached */\n forceRefresh?: boolean;\n}\n\n/**\n * Grounding that generates a business context report about the database.\n *\n * Uses an LLM agent to:\n * 1. Query COUNT(*) for each table\n * 2. Query SELECT * LIMIT 3 for sample data\n * 3. Generate a 400-600 word business context report\n *\n * The report helps downstream agents understand what the database represents.\n */\nexport class ReportGrounding extends AbstractGrounding {\n #adapter: Adapter;\n #model: AgentModel;\n #cache?: ReportCache;\n #forceRefresh: boolean;\n\n constructor(adapter: Adapter, config: ReportGroundingConfig = {}) {\n super('business_context');\n this.#adapter = adapter;\n this.#model = config.model ?? groq('openai/gpt-oss-20b');\n this.#cache = config.cache;\n this.#forceRefresh = config.forceRefresh ?? false;\n }\n\n async execute(ctx: GroundingContext): Promise<void> {\n // Check cache first (unless forcing refresh)\n if (!this.#forceRefresh && this.#cache) {\n const cached = await this.#cache.get();\n if (cached) {\n ctx.report = cached;\n return;\n }\n }\n\n // Generate report using LLM\n const report = await this.#generateReport();\n ctx.report = report;\n\n // Cache the result\n if (this.#cache) {\n await this.#cache.set(report);\n }\n }\n\n async #generateReport(): Promise<string> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `report-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'db-report-agent',\n role: 'Database analyst',\n objective:\n 'Analyze the database and write a contextual report about what it represents',\n }),\n fragment(\n 'instructions',\n dedent`\n Write a business context that helps another agent answer questions accurately.\n\n For EACH table, do queries ONE AT A TIME:\n 1. SELECT COUNT(*) to get row count\n 2. SELECT * LIMIT 3 to see sample data\n\n Then write a report with:\n - What business this database is for\n - For each table: purpose, row count, and example of what the data looks like\n\n Include concrete examples like \"Track prices are $0.99\",\n \"Customer names like 'Lu\u00EDs Gon\u00E7alves'\", etc.\n\n Keep it 400-600 words, conversational style.\n `,\n ),\n user(\n 'Please analyze the database and write a contextual report about what this database represents.',\n ),\n );\n\n const adapter = this.#adapter;\n\n const reportAgent = agent({\n name: 'db-report-agent',\n model: this.#model,\n context,\n tools: {\n query_database: tool({\n description:\n 'Execute a SELECT query to explore the database and gather insights.',\n inputSchema: z.object({\n sql: z.string().describe('The SELECT query to execute'),\n purpose: z\n .string()\n .describe(\n 'What insight you are trying to gather with this query',\n ),\n }),\n execute: ({ sql }) => {\n return adapter.execute(sql);\n },\n }),\n },\n });\n\n const result = await reportAgent.generate({});\n return result.text;\n }\n}\n", "import type { Table } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for RowCountGrounding.\n */\nexport interface RowCountGroundingConfig {\n // Future: filter which tables to count\n}\n\n/**\n * Abstract base class for row count grounding.\n *\n * Reads tables from the context and annotates them with row counts and size hints.\n * This grounding must run AFTER TableGrounding since it reads from ctx.tables.\n *\n * Subclasses implement the database-specific hook:\n * - `getRowCount()` - get row count for a table\n */\nexport abstract class RowCountGrounding extends AbstractGrounding {\n constructor(config: RowCountGroundingConfig = {}) {\n super('rowCount');\n }\n\n /**\n * Get row count for a specific table.\n */\n protected abstract getRowCount(\n tableName: string,\n ): Promise<number | undefined>;\n\n /**\n * Execute the grounding process.\n * Annotates tables in ctx.tables with row counts and size hints.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n for (const table of ctx.tables) {\n const count = await this.getRowCount(table.name);\n if (count != null) {\n table.rowCount = count;\n table.sizeHint = this.#classifyRowCount(count);\n }\n }\n }\n\n /**\n * Classify row count into a size hint category.\n */\n #classifyRowCount(count: number): Table['sizeHint'] {\n if (count < 100) return 'tiny';\n if (count < 1000) return 'small';\n if (count < 10000) return 'medium';\n if (count < 100000) return 'large';\n return 'huge';\n }\n}\n", "import type { Filter, Relationship, Table } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for TableGrounding.\n */\nexport interface TableGroundingConfig {\n /** Filter to select seed tables */\n filter?: Filter;\n /**\n * Traverse forward (child\u2192parent) following FK direction.\n * - true: unlimited depth\n * - number: maximum depth\n * - false/undefined: no forward traversal\n */\n forward?: boolean | number;\n /**\n * Traverse backward (parent\u2192child) finding tables that reference us.\n * - true: unlimited depth\n * - number: maximum depth\n * - false/undefined: no backward traversal\n */\n backward?: boolean | number;\n}\n\n/**\n * Abstract base class for table grounding.\n *\n * The `execute()` method implements a BFS traversal algorithm that discovers\n * tables and relationships. Subclasses implement the database-specific hooks:\n * - `getAllTableNames()` - list all tables\n * - `getTable()` - get table metadata\n * - `findOutgoingRelations()` - find FKs FROM a table\n * - `findIncomingRelations()` - find FKs TO a table\n */\nexport abstract class TableGrounding extends AbstractGrounding {\n #filter?: Filter;\n #forward?: boolean | number;\n #backward?: boolean | number;\n\n constructor(config: TableGroundingConfig = {}) {\n super('table');\n this.#filter = config.filter;\n this.#forward = config.forward;\n this.#backward = config.backward;\n }\n\n /** Get all table names in the database */\n protected abstract getAllTableNames(): Promise<string[]>;\n\n /** Get full table metadata for a single table */\n protected abstract getTable(tableName: string): Promise<Table>;\n\n /** Find FKs FROM this table (outgoing relationships) */\n protected abstract findOutgoingRelations(\n tableName: string,\n ): Promise<Relationship[]>;\n\n /** Find FKs TO this table (incoming relationships) */\n protected abstract findIncomingRelations(\n tableName: string,\n ): Promise<Relationship[]>;\n\n /**\n * Execute the grounding process.\n * Writes discovered tables and relationships to the context.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n const seedTables = await this.applyFilter();\n const forward = this.#forward;\n const backward = this.#backward;\n\n // No traversal at all - just add the seed tables\n if (!forward && !backward) {\n const tables = await Promise.all(\n seedTables.map((name) => this.getTable(name)),\n );\n ctx.tables.push(...tables);\n return;\n }\n\n const tables: Record<string, Table> = {};\n const allRelationships: Relationship[] = [];\n const seenRelationships = new Set<string>();\n\n // Track depth separately for forward/backward using BFS\n const forwardQueue: Array<{ name: string; depth: number }> = [];\n const backwardQueue: Array<{ name: string; depth: number }> = [];\n const forwardVisited = new Set<string>();\n const backwardVisited = new Set<string>();\n\n // Initialize queues with seed tables at depth 0\n for (const name of seedTables) {\n if (forward) forwardQueue.push({ name, depth: 0 });\n if (backward) backwardQueue.push({ name, depth: 0 });\n }\n\n // Process forward (child\u2192parent)\n const forwardLimit = forward === true ? Infinity : forward || 0;\n while (forwardQueue.length > 0) {\n const item = forwardQueue.shift();\n if (!item) break;\n const { name, depth } = item;\n\n if (forwardVisited.has(name)) continue;\n forwardVisited.add(name);\n\n if (!tables[name]) {\n tables[name] = await this.getTable(name);\n }\n\n if (depth < forwardLimit) {\n const rels = await this.findOutgoingRelations(name);\n for (const rel of rels) {\n this.addRelationship(rel, allRelationships, seenRelationships);\n if (!forwardVisited.has(rel.referenced_table)) {\n forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });\n }\n }\n }\n }\n\n // Process backward (parent\u2192child)\n const backwardLimit = backward === true ? Infinity : backward || 0;\n while (backwardQueue.length > 0) {\n const item = backwardQueue.shift();\n if (!item) break;\n const { name, depth } = item;\n\n if (backwardVisited.has(name)) continue;\n backwardVisited.add(name);\n\n if (!tables[name]) {\n tables[name] = await this.getTable(name);\n }\n\n if (depth < backwardLimit) {\n const rels = await this.findIncomingRelations(name);\n for (const rel of rels) {\n this.addRelationship(rel, allRelationships, seenRelationships);\n if (!backwardVisited.has(rel.table)) {\n backwardQueue.push({ name: rel.table, depth: depth + 1 });\n }\n }\n }\n }\n\n // Write to context\n const tablesList = Object.values(tables);\n ctx.tables.push(...tablesList);\n ctx.relationships.push(...allRelationships);\n }\n\n /**\n * Apply the filter to get seed table names.\n * If filter is an explicit array, skip querying all table names.\n */\n protected async applyFilter(): Promise<string[]> {\n const filter = this.#filter;\n if (Array.isArray(filter)) {\n return filter;\n }\n const names = await this.getAllTableNames();\n if (!filter) {\n return names;\n }\n if (filter instanceof RegExp) {\n return names.filter((name) => filter.test(name));\n }\n return names.filter(filter);\n }\n\n /**\n * Add a relationship to the collection, deduplicating by key.\n */\n protected addRelationship(\n rel: Relationship,\n all: Relationship[],\n seen: Set<string>,\n ): void {\n const key = `${rel.table}:${rel.from.join(',')}:${rel.referenced_table}:${rel.to.join(',')}`;\n if (!seen.has(key)) {\n seen.add(key);\n all.push(rel);\n }\n }\n}\n", "import type { ColumnStats, Filter } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Represents a database view with its metadata.\n */\nexport interface View {\n name: string;\n schema?: string;\n rawName?: string;\n /** The SQL definition of the view (CREATE VIEW statement or query) */\n definition?: string;\n columns: {\n name: string;\n type: string;\n /** Low cardinality marker */\n kind?: 'LowCardinality';\n /** Distinct values for low cardinality columns */\n values?: string[];\n /** Column statistics (min, max, nullFraction) */\n stats?: ColumnStats;\n }[];\n}\n\n/**\n * Configuration for ViewGrounding.\n */\nexport interface ViewGroundingConfig {\n /** Filter to select views */\n filter?: Filter;\n /** Whether to fetch the SQL definition of each view (default: true) */\n includeDefinition?: boolean;\n}\n\n/**\n * Abstract base class for view grounding\n *\n * The `execute()` method implements the algorithm that discovers views.\n * Subclasses implement the database-specific hooks:\n * - `getAllViewNames()` - list all views\n * - `getView()` - get view metadata\n */\nexport abstract class ViewGrounding extends AbstractGrounding {\n #filter?: Filter;\n protected includeDefinition: boolean;\n\n constructor(config: ViewGroundingConfig = {}) {\n super('view');\n this.#filter = config.filter;\n this.includeDefinition = config.includeDefinition ?? true;\n }\n\n /** Get all view names in the database */\n protected abstract getAllViewNames(): Promise<string[]>;\n\n /** Get full view metadata for a single view */\n protected abstract getView(viewName: string): Promise<View>;\n\n /**\n * Execute the grounding process.\n * Writes discovered views to the context.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n const viewNames = await this.applyFilter();\n const views = await Promise.all(\n viewNames.map((name) => this.getView(name)),\n );\n ctx.views.push(...views);\n }\n\n /**\n * Apply the filter to get view names.\n * If filter is an explicit array, skip querying all view names.\n */\n protected async applyFilter(): Promise<string[]> {\n const filter = this.#filter;\n if (Array.isArray(filter)) {\n return filter;\n }\n const names = await this.getAllViewNames();\n if (!filter) {\n return names;\n }\n if (filter instanceof RegExp) {\n return names.filter((name) => filter.test(name));\n }\n return names.filter(filter);\n }\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["import type { FragmentObject } from '@deepagents/context';\n\nimport type { Adapter } from '../adapter.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Filter type for table names.\n * - string[]: explicit list of table names\n * - RegExp: pattern to match table names\n * - function: predicate to filter table names\n */\nexport type Filter = string[] | RegExp | ((tableName: string) => boolean);\n\n/**\n * Per-entity column filter.\n * Maps entity name (table or view) to a Filter that selects which columns to keep.\n * Entities not listed in the record keep all their columns.\n */\nexport type ColumnsFilter = Record<string, Filter>;\n\n/**\n * Filter a columns array using a Filter.\n * Keeps columns whose name matches the filter.\n */\nexport function filterColumns<T extends { name: string }>(\n columns: T[],\n filter: Filter,\n): T[] {\n if (Array.isArray(filter)) {\n return columns.filter((col) => filter.includes(col.name));\n }\n if (filter instanceof RegExp) {\n return columns.filter((col) => filter.test(col.name));\n }\n return columns.filter((col) => filter(col.name));\n}\n\n/**\n * Apply per-entity column filtering.\n * Returns the entity unchanged if no filter matches its name.\n */\nexport function applyColumnFilter<\n T extends { name: string; columns: { name: string }[] },\n>(entity: T, columnsConfig?: ColumnsFilter): T {\n if (!columnsConfig) return entity;\n const filter = columnsConfig[entity.name];\n if (!filter) return entity;\n return { ...entity, columns: filterColumns(entity.columns, filter) } as T;\n}\n\nexport interface AdapterInfo {\n dialect: string;\n version?: string;\n database?: string;\n details?: FragmentObject;\n}\nexport type AdapterInfoProvider =\n | AdapterInfo\n | (() => Promise<AdapterInfo> | AdapterInfo);\n\n/**\n * Abstract base class for database schema groundings.\n *\n * Groundings collect schema metadata into the shared GroundingContext.\n * Fragment generation is centralized in Adapter.introspect().\n */\nexport abstract class AbstractGrounding {\n /**\n * Grounding identifier for debugging/logging.\n */\n name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Execute grounding to populate the shared context.\n * Groundings mutate ctx to add their collected data (tables, views, indexes, etc).\n * Fragment generation happens centrally in Adapter after all groundings complete.\n *\n * @param ctx - Shared context for accumulating schema data\n */\n abstract execute(ctx: GroundingContext): Promise<void>;\n}\n\nclass SampleDataGrounding {\n // this will fetch sample data for tables matching the filter\n}\n\nclass FunctionGrounding {\n #filter: Filter;\n #adapter: Adapter;\n constructor(adapter: Adapter, filter: Filter) {\n this.#filter = filter;\n this.#adapter = adapter;\n }\n}\n", "import type { ColumnStats } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { Column, ColumnContainer, GroundingContext } from './context.ts';\n\n/**\n * Configuration for ColumnStatsGrounding.\n */\nexport interface ColumnStatsGroundingConfig {\n // Future: filter which tables/columns to collect stats for\n}\n\n/**\n * Abstract base class for column statistics grounding.\n *\n * Reads tables and views from the context and annotates their columns\n * with statistics (min, max, nullFraction).\n *\n * Subclasses implement database-specific hooks:\n * - `collectStats()` - collect min/max/nullFraction for a column\n */\nexport abstract class ColumnStatsGrounding extends AbstractGrounding {\n constructor(config: ColumnStatsGroundingConfig = {}) {\n super('columnStats');\n }\n\n /**\n * Collect min/max/nullFraction statistics for a column.\n * Return undefined to skip this column.\n */\n protected abstract collectStats(\n tableName: string,\n column: Column,\n ): Promise<ColumnStats | undefined>;\n\n /**\n * Execute the grounding process.\n * Annotates columns in ctx.tables and ctx.views with statistics.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n // Process both tables and views\n const allContainers: ColumnContainer[] = [...ctx.tables, ...ctx.views];\n for (const container of allContainers) {\n for (const column of container.columns) {\n // Collect min/max/nullFraction\n try {\n const stats = await this.collectStats(container.name, column);\n if (stats) {\n column.stats = stats;\n }\n } catch (error) {\n // Skip on error\n console.warn(\n 'Error collecting stats for',\n container.name,\n column.name,\n error,\n );\n }\n }\n }\n }\n}\n", "import type { Table, TableConstraint } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { Column, ColumnContainer, GroundingContext } from './context.ts';\n\nexport type { Column, ColumnContainer };\n\n/**\n * Result of column value detection.\n */\nexport type ColumnValuesResult = {\n kind: 'Enum' | 'LowCardinality';\n values: string[];\n};\n\n/**\n * Configuration for ColumnValuesGrounding.\n */\nexport interface ColumnValuesGroundingConfig {\n /** Maximum number of distinct values to consider low cardinality (default: 20) */\n lowCardinalityLimit?: number;\n /** Maximum character length for individual values. Columns with any value exceeding this are skipped entirely (default: 100) */\n maxValueLength?: number;\n}\n\n/**\n * Abstract base class for column values grounding.\n *\n * Discovers possible values for columns from three sources (in priority order):\n * 1. Native ENUM types (PostgreSQL, MySQL) \u2192 kind: 'Enum'\n * 2. CHECK constraints with IN clauses \u2192 kind: 'Enum'\n * 3. Low cardinality data scan \u2192 kind: 'LowCardinality'\n *\n * Subclasses implement database-specific hooks:\n * - `collectEnumValues()` - get values for native ENUM columns\n * - `collectLowCardinality()` - collect distinct values via data scan\n */\nexport abstract class ColumnValuesGrounding extends AbstractGrounding {\n protected lowCardinalityLimit: number;\n protected maxValueLength: number;\n\n constructor(config: ColumnValuesGroundingConfig = {}) {\n super('columnValues');\n this.lowCardinalityLimit = config.lowCardinalityLimit ?? 20;\n this.maxValueLength = config.maxValueLength ?? 100;\n }\n\n /**\n * Get values for native ENUM type columns.\n * Return undefined if column is not an ENUM type.\n * Default implementation returns undefined (no native ENUM support).\n */\n protected async collectEnumValues(\n _tableName: string,\n _column: Column,\n ): Promise<string[] | undefined> {\n return undefined;\n }\n\n /**\n * Collect distinct values for low cardinality columns via data scan.\n * Return undefined if column has too many distinct values.\n */\n protected abstract collectLowCardinality(\n tableName: string,\n column: Column,\n ): Promise<string[] | undefined>;\n\n /**\n * Parse CHECK constraint for enum-like IN clause.\n * Extracts values from patterns like:\n * - CHECK (status IN ('active', 'inactive'))\n * - CHECK ((status)::text = ANY (ARRAY['a'::text, 'b'::text]))\n * - CHECK (status = 'active' OR status = 'inactive')\n */\n protected parseCheckConstraint(\n constraint: TableConstraint,\n columnName: string,\n ): string[] | undefined {\n if (constraint.type !== 'CHECK' || !constraint.definition) {\n return undefined;\n }\n\n // Check if constraint applies to this column\n if (constraint.columns && !constraint.columns.includes(columnName)) {\n return undefined;\n }\n\n const def = constraint.definition;\n const escapedCol = this.escapeRegex(columnName);\n\n // Column pattern: matches column name with optional parens and type cast\n // e.g., \"status\", \"(status)\", \"((status)::text)\"\n const colPattern = `(?:\\\\(?\\\\(?${escapedCol}\\\\)?(?:::(?:text|varchar|character varying))?\\\\)?)`;\n\n // Pattern 1: column IN ('val1', 'val2', ...)\n const inMatch = def.match(\n new RegExp(`${colPattern}\\\\s+IN\\\\s*\\\\(([^)]+)\\\\)`, 'i'),\n );\n if (inMatch) {\n return this.extractStringValues(inMatch[1]);\n }\n\n // Pattern 2: PostgreSQL ANY(ARRAY[...])\n const anyMatch = def.match(\n new RegExp(\n `${colPattern}\\\\s*=\\\\s*ANY\\\\s*\\\\(\\\\s*(?:ARRAY)?\\\\s*\\\\[([^\\\\]]+)\\\\]`,\n 'i',\n ),\n );\n if (anyMatch) {\n return this.extractStringValues(anyMatch[1]);\n }\n\n // Pattern 3: column = 'val1' OR column = 'val2' ...\n const orPattern = new RegExp(\n `\\\\b${this.escapeRegex(columnName)}\\\\b\\\\s*=\\\\s*'([^']*)'`,\n 'gi',\n );\n const orMatches = [...def.matchAll(orPattern)];\n if (orMatches.length >= 2) {\n return orMatches.map((m) => m[1]);\n }\n\n return undefined;\n }\n\n /**\n * Extract string values from a comma-separated list.\n */\n private extractStringValues(input: string): string[] | undefined {\n const values: string[] = [];\n // Match quoted strings: 'value' or 'value'::type\n const matches = input.matchAll(/'([^']*)'/g);\n for (const match of matches) {\n values.push(match[1]);\n }\n return values.length > 0 ? values : undefined;\n }\n\n /**\n * Escape special regex characters in a string.\n */\n private escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Get the table from context by name.\n */\n private getTable(ctx: GroundingContext, name: string): Table | undefined {\n return ctx.tables.find((t) => t.name === name);\n }\n\n /**\n * Execute the grounding process.\n * Annotates columns in ctx.tables and ctx.views with values.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n // Process both tables and views\n const allContainers: ColumnContainer[] = [...ctx.tables, ...ctx.views];\n\n for (const container of allContainers) {\n const table = this.getTable(ctx, container.name);\n\n for (const column of container.columns) {\n try {\n const result = await this.resolveColumnValues(\n container.name,\n column,\n table?.constraints,\n );\n if (result) {\n column.kind = result.kind;\n column.values = result.values;\n }\n } catch (error) {\n console.warn(\n 'Error collecting column values for',\n container.name,\n column.name,\n error,\n );\n }\n }\n }\n }\n\n private exceedsMaxValueLength(values: string[]): boolean {\n return values.some((v) => v.length > this.maxValueLength);\n }\n\n /**\n * Resolve column values from all sources in priority order.\n */\n private async resolveColumnValues(\n tableName: string,\n column: Column,\n constraints?: TableConstraint[],\n ): Promise<ColumnValuesResult | undefined> {\n // Priority 1: Native ENUM type\n const enumValues = await this.collectEnumValues(tableName, column);\n if (enumValues?.length && !this.exceedsMaxValueLength(enumValues)) {\n return { kind: 'Enum', values: enumValues };\n }\n\n // Priority 2: CHECK constraint with IN clause\n if (constraints) {\n for (const constraint of constraints) {\n const checkValues = this.parseCheckConstraint(constraint, column.name);\n if (checkValues?.length && !this.exceedsMaxValueLength(checkValues)) {\n return { kind: 'Enum', values: checkValues };\n }\n }\n }\n\n // Priority 3: Low cardinality data scan\n const lowCardValues = await this.collectLowCardinality(tableName, column);\n if (lowCardValues?.length && !this.exceedsMaxValueLength(lowCardValues)) {\n return { kind: 'LowCardinality', values: lowCardValues };\n }\n\n return undefined;\n }\n}\n", "import type { TableConstraint } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for ConstraintGrounding.\n */\nexport interface ConstraintGroundingConfig {\n // Future: filter which tables/constraint types to collect\n}\n\n/**\n * Abstract base class for constraint grounding.\n *\n * Reads tables from the context and annotates them with constraints\n * (CHECK, UNIQUE, NOT_NULL, DEFAULT).\n * This grounding must run AFTER TableGrounding since it reads from ctx.tables.\n *\n * Subclasses implement the database-specific hook:\n * - `getConstraints()` - fetch constraints for a table\n */\nexport abstract class ConstraintGrounding extends AbstractGrounding {\n constructor(config: ConstraintGroundingConfig = {}) {\n super('constraint');\n }\n\n /**\n * Fetch constraints for a specific table.\n */\n protected abstract getConstraints(\n tableName: string,\n ): Promise<TableConstraint[]>;\n\n /**\n * Execute the grounding process.\n * Annotates tables in ctx.tables with their constraints.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n for (const table of ctx.tables) {\n try {\n table.constraints = await this.getConstraints(table.name);\n } catch (error) {\n // Skip on error - table might not exist or be inaccessible\n console.warn('Error collecting constraints for', table.name, error);\n }\n }\n }\n}\n", "import type {\n AdapterInfo,\n ColumnStats,\n Relationship,\n Table,\n} from '../adapter.ts';\nimport type { View } from './view.grounding.ts';\n\n/**\n * Column type for grounding operations.\n * Common interface between Table.columns and View.columns.\n */\nexport interface Column {\n name: string;\n type: string;\n kind?: 'LowCardinality' | 'Enum';\n values?: string[];\n stats?: ColumnStats;\n}\n\n/**\n * Entity with columns (Table or View).\n */\nexport interface ColumnContainer {\n name: string;\n columns: Column[];\n}\n\n/**\n * Shared context object passed to all groundings.\n * Groundings read from and write to this context.\n */\nexport interface GroundingContext {\n /** Tables discovered by TableGrounding */\n tables: Table[];\n\n /** Views discovered by ViewGrounding */\n views: View[];\n\n /** Relationships discovered by TableGrounding */\n relationships: Relationship[];\n\n /** Database info collected by InfoGrounding */\n info?: AdapterInfo;\n\n /** Business context report generated by ReportGrounding */\n report?: string;\n\n /** Shared cache for cross-grounding deduplication. Keyed by `type:key`. */\n cache: Map<string, unknown>;\n}\n\n/**\n * Create a new empty grounding context.\n */\nexport function createGroundingContext(): GroundingContext {\n return {\n tables: [],\n views: [],\n relationships: [],\n info: undefined,\n cache: new Map(),\n };\n}\n", "import type { AdapterInfo } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for InfoGrounding.\n */\nexport interface InfoGroundingConfig {\n // Future: options to control what info to collect\n}\n\n/**\n * Abstract base class for database info grounding.\n *\n * Collects database dialect, version, and connection info.\n *\n * Subclasses implement the database-specific hook:\n * - `collectInfo()` - collect database info\n */\nexport abstract class InfoGrounding extends AbstractGrounding {\n constructor(config: InfoGroundingConfig = {}) {\n super('dialectInfo');\n }\n\n /**\n * Collect database dialect, version, and other info.\n */\n protected abstract collectInfo(): Promise<AdapterInfo>;\n\n /**\n * Execute the grounding process.\n * Writes database info to ctx.info.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n ctx.info = await this.collectInfo();\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport { tool } from 'ai';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n agent,\n fragment,\n persona,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Cache interface for storing generated reports.\n */\nexport interface ReportCache {\n get(): Promise<string | null>;\n set(value: string): Promise<void>;\n}\n\n/**\n * Configuration for ReportGrounding.\n */\nexport interface ReportGroundingConfig {\n /** LLM model to use for generating the report */\n model?: AgentModel;\n /** Optional cache for storing generated reports */\n cache?: ReportCache;\n /** Force regeneration even if cached */\n forceRefresh?: boolean;\n}\n\n/**\n * Grounding that generates a business context report about the database.\n *\n * Uses an LLM agent to:\n * 1. Query COUNT(*) for each table\n * 2. Query SELECT * LIMIT 3 for sample data\n * 3. Generate a 400-600 word business context report\n *\n * The report helps downstream agents understand what the database represents.\n */\nexport class ReportGrounding extends AbstractGrounding {\n #adapter: Adapter;\n #model: AgentModel;\n #cache?: ReportCache;\n #forceRefresh: boolean;\n\n constructor(adapter: Adapter, config: ReportGroundingConfig = {}) {\n super('business_context');\n this.#adapter = adapter;\n this.#model = config.model ?? groq('openai/gpt-oss-20b');\n this.#cache = config.cache;\n this.#forceRefresh = config.forceRefresh ?? false;\n }\n\n async execute(ctx: GroundingContext): Promise<void> {\n // Check cache first (unless forcing refresh)\n if (!this.#forceRefresh && this.#cache) {\n const cached = await this.#cache.get();\n if (cached) {\n ctx.report = cached;\n return;\n }\n }\n\n // Generate report using LLM\n const report = await this.#generateReport();\n ctx.report = report;\n\n // Cache the result\n if (this.#cache) {\n await this.#cache.set(report);\n }\n }\n\n async #generateReport(): Promise<string> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `report-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'db-report-agent',\n role: 'Database analyst',\n objective:\n 'Analyze the database and write a contextual report about what it represents',\n }),\n fragment(\n 'instructions',\n dedent`\n Write a business context that helps another agent answer questions accurately.\n\n For EACH table, do queries ONE AT A TIME:\n 1. SELECT COUNT(*) to get row count\n 2. SELECT * LIMIT 3 to see sample data\n\n Then write a report with:\n - What business this database is for\n - For each table: purpose, row count, and example of what the data looks like\n\n Include concrete examples like \"Track prices are $0.99\",\n \"Customer names like 'Lu\u00EDs Gon\u00E7alves'\", etc.\n\n Keep it 400-600 words, conversational style.\n `,\n ),\n user(\n 'Please analyze the database and write a contextual report about what this database represents.',\n ),\n );\n\n const adapter = this.#adapter;\n\n const reportAgent = agent({\n name: 'db-report-agent',\n model: this.#model,\n context,\n tools: {\n query_database: tool({\n description:\n 'Execute a SELECT query to explore the database and gather insights.',\n inputSchema: z.object({\n sql: z.string().describe('The SELECT query to execute'),\n purpose: z\n .string()\n .describe(\n 'What insight you are trying to gather with this query',\n ),\n }),\n execute: ({ sql }) => {\n return adapter.execute(sql);\n },\n }),\n },\n });\n\n const result = await reportAgent.generate({});\n return result.text;\n }\n}\n", "import type { Table } from '../adapter.ts';\nimport { AbstractGrounding } from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for RowCountGrounding.\n */\nexport interface RowCountGroundingConfig {\n // Future: filter which tables to count\n}\n\n/**\n * Abstract base class for row count grounding.\n *\n * Reads tables from the context and annotates them with row counts and size hints.\n * This grounding must run AFTER TableGrounding since it reads from ctx.tables.\n *\n * Subclasses implement the database-specific hook:\n * - `getRowCount()` - get row count for a table\n */\nexport abstract class RowCountGrounding extends AbstractGrounding {\n constructor(config: RowCountGroundingConfig = {}) {\n super('rowCount');\n }\n\n /**\n * Get row count for a specific table.\n */\n protected abstract getRowCount(\n tableName: string,\n ): Promise<number | undefined>;\n\n /**\n * Execute the grounding process.\n * Annotates tables in ctx.tables with row counts and size hints.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n for (const table of ctx.tables) {\n const count = await this.getRowCount(table.name);\n if (count != null) {\n table.rowCount = count;\n table.sizeHint = this.#classifyRowCount(count);\n }\n }\n }\n\n /**\n * Classify row count into a size hint category.\n */\n #classifyRowCount(count: number): Table['sizeHint'] {\n if (count < 100) return 'tiny';\n if (count < 1000) return 'small';\n if (count < 10000) return 'medium';\n if (count < 100000) return 'large';\n return 'huge';\n }\n}\n", "import type { Filter, Relationship, Table } from '../adapter.ts';\nimport {\n AbstractGrounding,\n type ColumnsFilter,\n applyColumnFilter,\n filterColumns,\n} from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Configuration for TableGrounding.\n */\nexport interface TableGroundingConfig {\n /** Filter to select seed tables */\n filter?: Filter;\n /**\n * Per-table column filter.\n * Maps table name to a Filter that selects which columns to keep.\n * Tables not listed keep all their columns.\n */\n columns?: ColumnsFilter;\n /**\n * Traverse forward (child\u2192parent) following FK direction.\n * - true: unlimited depth\n * - number: maximum depth\n * - false/undefined: no forward traversal\n */\n forward?: boolean | number;\n /**\n * Traverse backward (parent\u2192child) finding tables that reference us.\n * - true: unlimited depth\n * - number: maximum depth\n * - false/undefined: no backward traversal\n */\n backward?: boolean | number;\n}\n\n/**\n * Abstract base class for table grounding.\n *\n * The `execute()` method implements a BFS traversal algorithm that discovers\n * tables and relationships. Subclasses implement the database-specific hooks:\n * - `getAllTableNames()` - list all tables\n * - `getTable()` - get table metadata\n * - `findOutgoingRelations()` - find FKs FROM a table\n * - `findIncomingRelations()` - find FKs TO a table\n */\nexport abstract class TableGrounding extends AbstractGrounding {\n #filter?: Filter;\n #columns?: ColumnsFilter;\n #forward?: boolean | number;\n #backward?: boolean | number;\n\n constructor(config: TableGroundingConfig = {}) {\n super('table');\n this.#filter = config.filter;\n this.#columns = config.columns;\n this.#forward = config.forward;\n this.#backward = config.backward;\n }\n\n /** Get all table names in the database */\n protected abstract getAllTableNames(): Promise<string[]>;\n\n /** Get full table metadata for a single table */\n protected abstract getTable(tableName: string): Promise<Table>;\n\n /** Find FKs FROM this table (outgoing relationships) */\n protected abstract findOutgoingRelations(\n tableName: string,\n ): Promise<Relationship[]>;\n\n /** Find FKs TO this table (incoming relationships) */\n protected abstract findIncomingRelations(\n tableName: string,\n ): Promise<Relationship[]>;\n\n /**\n * Execute the grounding process.\n * Writes discovered tables and relationships to the context.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n const seedTables = await this.applyFilter();\n const forward = this.#forward;\n const backward = this.#backward;\n\n // No traversal at all - just add the seed tables\n if (!forward && !backward) {\n const tables = await Promise.all(\n seedTables.map(async (name) =>\n applyColumnFilter(await this.getTable(name), this.#columns),\n ),\n );\n ctx.tables.push(...tables);\n return;\n }\n\n const tables: Record<string, Table> = {};\n const allRelationships: Relationship[] = [];\n const seenRelationships = new Set<string>();\n\n // Track depth separately for forward/backward using BFS\n const forwardQueue: Array<{ name: string; depth: number }> = [];\n const backwardQueue: Array<{ name: string; depth: number }> = [];\n const forwardVisited = new Set<string>();\n const backwardVisited = new Set<string>();\n\n // Initialize queues with seed tables at depth 0\n for (const name of seedTables) {\n if (forward) forwardQueue.push({ name, depth: 0 });\n if (backward) backwardQueue.push({ name, depth: 0 });\n }\n\n // Process forward (child\u2192parent)\n const forwardLimit = forward === true ? Infinity : forward || 0;\n while (forwardQueue.length > 0) {\n const item = forwardQueue.shift();\n if (!item) break;\n const { name, depth } = item;\n\n if (forwardVisited.has(name)) continue;\n forwardVisited.add(name);\n\n if (!tables[name]) {\n tables[name] = applyColumnFilter(\n await this.getTable(name),\n this.#columns,\n );\n }\n\n if (depth < forwardLimit) {\n const rels = await this.findOutgoingRelations(name);\n for (const rel of rels) {\n if (!this.isRelationshipVisible(rel)) continue;\n this.addRelationship(rel, allRelationships, seenRelationships);\n if (!forwardVisited.has(rel.referenced_table)) {\n forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });\n }\n }\n }\n }\n\n // Process backward (parent\u2192child)\n const backwardLimit = backward === true ? Infinity : backward || 0;\n while (backwardQueue.length > 0) {\n const item = backwardQueue.shift();\n if (!item) break;\n const { name, depth } = item;\n\n if (backwardVisited.has(name)) continue;\n backwardVisited.add(name);\n\n if (!tables[name]) {\n tables[name] = applyColumnFilter(\n await this.getTable(name),\n this.#columns,\n );\n }\n\n if (depth < backwardLimit) {\n const rels = await this.findIncomingRelations(name);\n for (const rel of rels) {\n if (!this.isRelationshipVisible(rel)) continue;\n this.addRelationship(rel, allRelationships, seenRelationships);\n if (!backwardVisited.has(rel.table)) {\n backwardQueue.push({ name: rel.table, depth: depth + 1 });\n }\n }\n }\n }\n\n // Write to context\n const tablesList = Object.values(tables);\n ctx.tables.push(...tablesList);\n ctx.relationships.push(...allRelationships);\n }\n\n /**\n * Apply the filter to get seed table names.\n * If filter is an explicit array, skip querying all table names.\n */\n protected async applyFilter(): Promise<string[]> {\n const filter = this.#filter;\n if (Array.isArray(filter)) {\n return filter;\n }\n const names = await this.getAllTableNames();\n if (!filter) {\n return names;\n }\n if (filter instanceof RegExp) {\n return names.filter((name) => filter.test(name));\n }\n return names.filter(filter);\n }\n\n /**\n * Add a relationship to the collection, deduplicating by key.\n */\n protected addRelationship(\n rel: Relationship,\n all: Relationship[],\n seen: Set<string>,\n ): void {\n const key = `${rel.table}:${rel.from.join(',')}:${rel.referenced_table}:${rel.to.join(',')}`;\n if (!seen.has(key)) {\n seen.add(key);\n all.push(rel);\n }\n }\n\n protected isRelationshipVisible(rel: Relationship): boolean {\n return (\n this.areColumnsVisible(rel.from, this.#columns?.[rel.table]) &&\n this.areColumnsVisible(rel.to, this.#columns?.[rel.referenced_table])\n );\n }\n\n protected areColumnsVisible(names: string[], filter?: Filter): boolean {\n if (!filter) return true;\n return (\n filterColumns(\n names.map((name) => ({ name })),\n filter,\n ).length === names.length\n );\n }\n}\n", "import type { ColumnStats, Filter } from '../adapter.ts';\nimport {\n AbstractGrounding,\n type ColumnsFilter,\n applyColumnFilter,\n} from './abstract.grounding.ts';\nimport type { GroundingContext } from './context.ts';\n\n/**\n * Represents a database view with its metadata.\n */\nexport interface View {\n name: string;\n schema?: string;\n rawName?: string;\n /** The SQL definition of the view (CREATE VIEW statement or query) */\n definition?: string;\n columns: {\n name: string;\n type: string;\n /** Low cardinality marker */\n kind?: 'LowCardinality';\n /** Distinct values for low cardinality columns */\n values?: string[];\n /** Column statistics (min, max, nullFraction) */\n stats?: ColumnStats;\n }[];\n}\n\n/**\n * Configuration for ViewGrounding.\n */\nexport interface ViewGroundingConfig {\n /** Filter to select views */\n filter?: Filter;\n /**\n * Per-view column filter.\n * Maps view name to a Filter that selects which columns to keep.\n * Views not listed keep all their columns.\n */\n columns?: ColumnsFilter;\n /** Whether to fetch the SQL definition of each view (default: true) */\n includeDefinition?: boolean;\n}\n\n/**\n * Abstract base class for view grounding\n *\n * The `execute()` method implements the algorithm that discovers views.\n * Subclasses implement the database-specific hooks:\n * - `getAllViewNames()` - list all views\n * - `getView()` - get view metadata\n */\nexport abstract class ViewGrounding extends AbstractGrounding {\n #filter?: Filter;\n #columns?: ColumnsFilter;\n protected includeDefinition: boolean;\n\n constructor(config: ViewGroundingConfig = {}) {\n super('view');\n this.#filter = config.filter;\n this.#columns = config.columns;\n this.includeDefinition = config.includeDefinition ?? true;\n }\n\n /** Get all view names in the database */\n protected abstract getAllViewNames(): Promise<string[]>;\n\n /** Get full view metadata for a single view */\n protected abstract getView(viewName: string): Promise<View>;\n\n /**\n * Execute the grounding process.\n * Writes discovered views to the context.\n */\n async execute(ctx: GroundingContext): Promise<void> {\n const viewNames = await this.applyFilter();\n const views = await Promise.all(\n viewNames.map(async (name) =>\n applyColumnFilter(await this.getView(name), this.#columns),\n ),\n );\n ctx.views.push(...views);\n }\n\n /**\n * Apply the filter to get view names.\n * If filter is an explicit array, skip querying all view names.\n */\n protected async applyFilter(): Promise<string[]> {\n const filter = this.#filter;\n if (Array.isArray(filter)) {\n return filter;\n }\n const names = await this.getAllViewNames();\n if (!filter) {\n return names;\n }\n if (filter instanceof RegExp) {\n return names.filter((name) => filter.test(name));\n }\n return names.filter(filter);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["tables"]
|
|
7
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Filter, Relationship, Table } from '../adapter.ts';
|
|
2
|
-
import { AbstractGrounding } from './abstract.grounding.ts';
|
|
2
|
+
import { AbstractGrounding, type ColumnsFilter } from './abstract.grounding.ts';
|
|
3
3
|
import type { GroundingContext } from './context.ts';
|
|
4
4
|
/**
|
|
5
5
|
* Configuration for TableGrounding.
|
|
@@ -7,6 +7,12 @@ import type { GroundingContext } from './context.ts';
|
|
|
7
7
|
export interface TableGroundingConfig {
|
|
8
8
|
/** Filter to select seed tables */
|
|
9
9
|
filter?: Filter;
|
|
10
|
+
/**
|
|
11
|
+
* Per-table column filter.
|
|
12
|
+
* Maps table name to a Filter that selects which columns to keep.
|
|
13
|
+
* Tables not listed keep all their columns.
|
|
14
|
+
*/
|
|
15
|
+
columns?: ColumnsFilter;
|
|
10
16
|
/**
|
|
11
17
|
* Traverse forward (child→parent) following FK direction.
|
|
12
18
|
* - true: unlimited depth
|
|
@@ -57,5 +63,7 @@ export declare abstract class TableGrounding extends AbstractGrounding {
|
|
|
57
63
|
* Add a relationship to the collection, deduplicating by key.
|
|
58
64
|
*/
|
|
59
65
|
protected addRelationship(rel: Relationship, all: Relationship[], seen: Set<string>): void;
|
|
66
|
+
protected isRelationshipVisible(rel: Relationship): boolean;
|
|
67
|
+
protected areColumnsVisible(names: string[], filter?: Filter): boolean;
|
|
60
68
|
}
|
|
61
69
|
//# sourceMappingURL=table.grounding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.grounding.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/groundings/table.grounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,
|
|
1
|
+
{"version":3,"file":"table.grounding.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/groundings/table.grounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EACL,iBAAiB,EACjB,KAAK,aAAa,EAGnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,8BAAsB,cAAe,SAAQ,iBAAiB;;gBAMhD,MAAM,GAAE,oBAAyB;IAQ7C,0CAA0C;IAC1C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAExD,iDAAiD;IACjD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAE9D,wDAAwD;IACxD,SAAS,CAAC,QAAQ,CAAC,qBAAqB,CACtC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC;IAE1B,sDAAsD;IACtD,SAAS,CAAC,QAAQ,CAAC,qBAAqB,CACtC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC;IAE1B;;;OAGG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgGnD;;;OAGG;cACa,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAehD;;OAEG;IACH,SAAS,CAAC,eAAe,CACvB,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,YAAY,EAAE,EACnB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAChB,IAAI;IAQP,SAAS,CAAC,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO;IAO3D,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO;CASvE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ColumnStats, Filter } from '../adapter.ts';
|
|
2
|
-
import { AbstractGrounding } from './abstract.grounding.ts';
|
|
2
|
+
import { AbstractGrounding, type ColumnsFilter } from './abstract.grounding.ts';
|
|
3
3
|
import type { GroundingContext } from './context.ts';
|
|
4
4
|
/**
|
|
5
5
|
* Represents a database view with its metadata.
|
|
@@ -27,6 +27,12 @@ export interface View {
|
|
|
27
27
|
export interface ViewGroundingConfig {
|
|
28
28
|
/** Filter to select views */
|
|
29
29
|
filter?: Filter;
|
|
30
|
+
/**
|
|
31
|
+
* Per-view column filter.
|
|
32
|
+
* Maps view name to a Filter that selects which columns to keep.
|
|
33
|
+
* Views not listed keep all their columns.
|
|
34
|
+
*/
|
|
35
|
+
columns?: ColumnsFilter;
|
|
30
36
|
/** Whether to fetch the SQL definition of each view (default: true) */
|
|
31
37
|
includeDefinition?: boolean;
|
|
32
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view.grounding.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/groundings/view.grounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,
|
|
1
|
+
{"version":3,"file":"view.grounding.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/groundings/view.grounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,KAAK,aAAa,EAEnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,6BAA6B;QAC7B,IAAI,CAAC,EAAE,gBAAgB,CAAC;QACxB,kDAAkD;QAClD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,iDAAiD;QACjD,KAAK,CAAC,EAAE,WAAW,CAAC;KACrB,EAAE,CAAC;CACL;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,8BAAsB,aAAc,SAAQ,iBAAiB;;IAG3D,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAC;gBAEzB,MAAM,GAAE,mBAAwB;IAO5C,yCAAyC;IACzC,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAEvD,+CAA+C;IAC/C,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE3D;;;OAGG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD;;;OAGG;cACa,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAcjD"}
|
|
@@ -328,7 +328,18 @@ var Adapter = class {
|
|
|
328
328
|
fragments.push(this.#viewToFragment(v));
|
|
329
329
|
}
|
|
330
330
|
const tableMap = new Map(ctx.tables.map((t) => [t.name, t]));
|
|
331
|
+
const tableColumnSets = new Map(
|
|
332
|
+
ctx.tables.map((t) => [t.name, new Set(t.columns.map((c) => c.name))])
|
|
333
|
+
);
|
|
331
334
|
for (const rel of ctx.relationships) {
|
|
335
|
+
const sourceColumns = tableColumnSets.get(rel.table);
|
|
336
|
+
const targetColumns = tableColumnSets.get(rel.referenced_table);
|
|
337
|
+
if (sourceColumns && rel.from.some((column2) => !sourceColumns.has(column2))) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (targetColumns && rel.to.some((column2) => !targetColumns.has(column2))) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
332
343
|
const sourceTable = tableMap.get(rel.table);
|
|
333
344
|
const targetTable = tableMap.get(rel.referenced_table);
|
|
334
345
|
fragments.push(
|
|
@@ -383,7 +394,10 @@ var Adapter = class {
|
|
|
383
394
|
stats: col.stats
|
|
384
395
|
})
|
|
385
396
|
);
|
|
386
|
-
const
|
|
397
|
+
const presentColumns = new Set(t.columns.map((c) => c.name));
|
|
398
|
+
const indexFragments = (t.indexes ?? []).filter(
|
|
399
|
+
(idx) => idx.columns.every((column2) => presentColumns.has(column2))
|
|
400
|
+
).map(
|
|
387
401
|
(idx) => index({
|
|
388
402
|
name: idx.name,
|
|
389
403
|
columns: idx.columns,
|
|
@@ -393,6 +407,8 @@ var Adapter = class {
|
|
|
393
407
|
);
|
|
394
408
|
const constraintFragments = (t.constraints ?? []).filter(
|
|
395
409
|
(c) => c.type === "CHECK" || c.type === "UNIQUE" && (c.columns?.length ?? 0) > 1
|
|
410
|
+
).filter(
|
|
411
|
+
(c) => !c.columns?.length || c.columns.every((column2) => presentColumns.has(column2))
|
|
396
412
|
).map(
|
|
397
413
|
(c) => constraint({
|
|
398
414
|
name: c.name,
|
|
@@ -586,6 +602,21 @@ var Adapter = class {
|
|
|
586
602
|
};
|
|
587
603
|
|
|
588
604
|
// packages/text2sql/src/lib/adapters/groundings/abstract.grounding.ts
|
|
605
|
+
function filterColumns(columns, filter) {
|
|
606
|
+
if (Array.isArray(filter)) {
|
|
607
|
+
return columns.filter((col) => filter.includes(col.name));
|
|
608
|
+
}
|
|
609
|
+
if (filter instanceof RegExp) {
|
|
610
|
+
return columns.filter((col) => filter.test(col.name));
|
|
611
|
+
}
|
|
612
|
+
return columns.filter((col) => filter(col.name));
|
|
613
|
+
}
|
|
614
|
+
function applyColumnFilter(entity, columnsConfig) {
|
|
615
|
+
if (!columnsConfig) return entity;
|
|
616
|
+
const filter = columnsConfig[entity.name];
|
|
617
|
+
if (!filter) return entity;
|
|
618
|
+
return { ...entity, columns: filterColumns(entity.columns, filter) };
|
|
619
|
+
}
|
|
589
620
|
var AbstractGrounding = class {
|
|
590
621
|
/**
|
|
591
622
|
* Grounding identifier for debugging/logging.
|
|
@@ -957,11 +988,13 @@ var RowCountGrounding = class extends AbstractGrounding {
|
|
|
957
988
|
// packages/text2sql/src/lib/adapters/groundings/table.grounding.ts
|
|
958
989
|
var TableGrounding = class extends AbstractGrounding {
|
|
959
990
|
#filter;
|
|
991
|
+
#columns;
|
|
960
992
|
#forward;
|
|
961
993
|
#backward;
|
|
962
994
|
constructor(config = {}) {
|
|
963
995
|
super("table");
|
|
964
996
|
this.#filter = config.filter;
|
|
997
|
+
this.#columns = config.columns;
|
|
965
998
|
this.#forward = config.forward;
|
|
966
999
|
this.#backward = config.backward;
|
|
967
1000
|
}
|
|
@@ -975,7 +1008,9 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
975
1008
|
const backward = this.#backward;
|
|
976
1009
|
if (!forward && !backward) {
|
|
977
1010
|
const tables3 = await Promise.all(
|
|
978
|
-
seedTables.map(
|
|
1011
|
+
seedTables.map(
|
|
1012
|
+
async (name) => applyColumnFilter(await this.getTable(name), this.#columns)
|
|
1013
|
+
)
|
|
979
1014
|
);
|
|
980
1015
|
ctx.tables.push(...tables3);
|
|
981
1016
|
return;
|
|
@@ -999,11 +1034,15 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
999
1034
|
if (forwardVisited.has(name)) continue;
|
|
1000
1035
|
forwardVisited.add(name);
|
|
1001
1036
|
if (!tables2[name]) {
|
|
1002
|
-
tables2[name] =
|
|
1037
|
+
tables2[name] = applyColumnFilter(
|
|
1038
|
+
await this.getTable(name),
|
|
1039
|
+
this.#columns
|
|
1040
|
+
);
|
|
1003
1041
|
}
|
|
1004
1042
|
if (depth < forwardLimit) {
|
|
1005
1043
|
const rels = await this.findOutgoingRelations(name);
|
|
1006
1044
|
for (const rel of rels) {
|
|
1045
|
+
if (!this.isRelationshipVisible(rel)) continue;
|
|
1007
1046
|
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
1008
1047
|
if (!forwardVisited.has(rel.referenced_table)) {
|
|
1009
1048
|
forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });
|
|
@@ -1019,11 +1058,15 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
1019
1058
|
if (backwardVisited.has(name)) continue;
|
|
1020
1059
|
backwardVisited.add(name);
|
|
1021
1060
|
if (!tables2[name]) {
|
|
1022
|
-
tables2[name] =
|
|
1061
|
+
tables2[name] = applyColumnFilter(
|
|
1062
|
+
await this.getTable(name),
|
|
1063
|
+
this.#columns
|
|
1064
|
+
);
|
|
1023
1065
|
}
|
|
1024
1066
|
if (depth < backwardLimit) {
|
|
1025
1067
|
const rels = await this.findIncomingRelations(name);
|
|
1026
1068
|
for (const rel of rels) {
|
|
1069
|
+
if (!this.isRelationshipVisible(rel)) continue;
|
|
1027
1070
|
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
1028
1071
|
if (!backwardVisited.has(rel.table)) {
|
|
1029
1072
|
backwardQueue.push({ name: rel.table, depth: depth + 1 });
|
|
@@ -1063,6 +1106,16 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
1063
1106
|
all.push(rel);
|
|
1064
1107
|
}
|
|
1065
1108
|
}
|
|
1109
|
+
isRelationshipVisible(rel) {
|
|
1110
|
+
return this.areColumnsVisible(rel.from, this.#columns?.[rel.table]) && this.areColumnsVisible(rel.to, this.#columns?.[rel.referenced_table]);
|
|
1111
|
+
}
|
|
1112
|
+
areColumnsVisible(names, filter) {
|
|
1113
|
+
if (!filter) return true;
|
|
1114
|
+
return filterColumns(
|
|
1115
|
+
names.map((name) => ({ name })),
|
|
1116
|
+
filter
|
|
1117
|
+
).length === names.length;
|
|
1118
|
+
}
|
|
1066
1119
|
};
|
|
1067
1120
|
|
|
1068
1121
|
// packages/text2sql/src/lib/adapters/mysql/column-stats.mysql.grounding.ts
|
|
@@ -1740,10 +1793,12 @@ var MysqlTableGrounding = class extends TableGrounding {
|
|
|
1740
1793
|
// packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
|
|
1741
1794
|
var ViewGrounding = class extends AbstractGrounding {
|
|
1742
1795
|
#filter;
|
|
1796
|
+
#columns;
|
|
1743
1797
|
includeDefinition;
|
|
1744
1798
|
constructor(config = {}) {
|
|
1745
1799
|
super("view");
|
|
1746
1800
|
this.#filter = config.filter;
|
|
1801
|
+
this.#columns = config.columns;
|
|
1747
1802
|
this.includeDefinition = config.includeDefinition ?? true;
|
|
1748
1803
|
}
|
|
1749
1804
|
/**
|
|
@@ -1753,7 +1808,9 @@ var ViewGrounding = class extends AbstractGrounding {
|
|
|
1753
1808
|
async execute(ctx) {
|
|
1754
1809
|
const viewNames = await this.applyFilter();
|
|
1755
1810
|
const views2 = await Promise.all(
|
|
1756
|
-
viewNames.map(
|
|
1811
|
+
viewNames.map(
|
|
1812
|
+
async (name) => applyColumnFilter(await this.getView(name), this.#columns)
|
|
1813
|
+
)
|
|
1757
1814
|
);
|
|
1758
1815
|
ctx.views.push(...views2);
|
|
1759
1816
|
}
|