@deepagents/text2sql 0.38.1 → 0.39.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/README.md +81 -44
- package/dist/index.js +40 -16
- package/dist/index.js.map +2 -2
- package/dist/lib/adapters/adapter.d.ts +1 -0
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +24 -13
- package/dist/lib/adapters/bigquery/index.js.map +2 -2
- package/dist/lib/adapters/groundings/context.d.ts +2 -2
- package/dist/lib/adapters/groundings/context.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/index.js +20 -9
- package/dist/lib/adapters/groundings/index.js.map +2 -2
- package/dist/lib/adapters/mysql/index.js +23 -12
- package/dist/lib/adapters/mysql/index.js.map +2 -2
- package/dist/lib/adapters/postgres/index.js +31 -18
- package/dist/lib/adapters/postgres/index.js.map +3 -3
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +20 -9
- package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
- package/dist/lib/adapters/sqlite/index.js +23 -12
- package/dist/lib/adapters/sqlite/index.js.map +2 -2
- package/dist/lib/adapters/sqlserver/index.js +31 -18
- package/dist/lib/adapters/sqlserver/index.js.map +3 -3
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +1 -0
- package/dist/lib/sql.d.ts.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
createBashTool,
|
|
42
42
|
createRoutingSandbox,
|
|
43
43
|
createVirtualSandbox,
|
|
44
|
+
user,
|
|
44
45
|
} from '@deepagents/context';
|
|
45
46
|
import { Text2Sql, sqlSandboxExtension } from '@deepagents/text2sql';
|
|
46
47
|
import {
|
|
@@ -59,6 +60,11 @@ const pool = new pg.Pool({
|
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
const store = new InMemoryContextStore();
|
|
63
|
+
const engine = new ContextEngine({
|
|
64
|
+
store,
|
|
65
|
+
chatId: 'chat-123',
|
|
66
|
+
userId: 'user-456',
|
|
67
|
+
});
|
|
62
68
|
const adapter = new Postgres({
|
|
63
69
|
execute: async (sql) => {
|
|
64
70
|
const result = await pool.query(sql);
|
|
@@ -86,15 +92,7 @@ const text2sql = new Text2Sql({
|
|
|
86
92
|
model: groq('openai/gpt-oss-20b'),
|
|
87
93
|
sandbox,
|
|
88
94
|
adapters: { main: adapter },
|
|
89
|
-
context:
|
|
90
|
-
const engine = new ContextEngine({
|
|
91
|
-
store,
|
|
92
|
-
chatId: 'chat-123',
|
|
93
|
-
userId: 'user-456',
|
|
94
|
-
});
|
|
95
|
-
engine.set(...fragments);
|
|
96
|
-
return engine;
|
|
97
|
-
},
|
|
95
|
+
context: engine,
|
|
98
96
|
});
|
|
99
97
|
|
|
100
98
|
// Generate SQL
|
|
@@ -130,10 +128,10 @@ for composition patterns.
|
|
|
130
128
|
|
|
131
129
|
## Fragments
|
|
132
130
|
|
|
133
|
-
Inject domain knowledge
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
Inject domain knowledge by setting fragments on the `ContextEngine` you pass to
|
|
132
|
+
`Text2Sql`. Those fragments affect `chat()` sessions. If you need direct SQL
|
|
133
|
+
generation with extra fragments, use the lower-level `toSql({ fragments })`
|
|
134
|
+
helper exported from `@deepagents/text2sql`.
|
|
137
135
|
|
|
138
136
|
```typescript
|
|
139
137
|
import {
|
|
@@ -146,31 +144,25 @@ import {
|
|
|
146
144
|
} from '@deepagents/context';
|
|
147
145
|
|
|
148
146
|
const store = new InMemoryContextStore();
|
|
147
|
+
const engine = new ContextEngine({
|
|
148
|
+
store,
|
|
149
|
+
chatId: 'chat-123',
|
|
150
|
+
userId: 'user-456',
|
|
151
|
+
});
|
|
149
152
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
action: 'Aggregate by department instead',
|
|
164
|
-
}),
|
|
165
|
-
example({
|
|
166
|
-
question: 'show me churned customers',
|
|
167
|
-
answer: `SELECT * FROM customers WHERE status = 'churned' ORDER BY churned_at DESC`,
|
|
168
|
-
}),
|
|
169
|
-
...fragments,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return engine;
|
|
173
|
-
};
|
|
153
|
+
engine.set(
|
|
154
|
+
term('MRR', 'monthly recurring revenue'),
|
|
155
|
+
hint('Always exclude test accounts with email ending in @test.com'),
|
|
156
|
+
guardrail({
|
|
157
|
+
rule: 'Never expose individual salaries',
|
|
158
|
+
reason: 'Confidential HR data',
|
|
159
|
+
action: 'Aggregate by department instead',
|
|
160
|
+
}),
|
|
161
|
+
example({
|
|
162
|
+
question: 'show me churned customers',
|
|
163
|
+
answer: `SELECT * FROM customers WHERE status = 'churned' ORDER BY churned_at DESC`,
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
174
166
|
```
|
|
175
167
|
|
|
176
168
|
**Domain fragments** (11 types): `term`, `hint`, `guardrail`, `example`, `explain`, `clarification`, `workflow`, `quirk`, `styleGuide`, `analogy`, `glossary`.
|
|
@@ -196,23 +188,68 @@ Control what schema metadata the AI receives:
|
|
|
196
188
|
|
|
197
189
|
## Conversations
|
|
198
190
|
|
|
199
|
-
`chat()` persists history through the `ContextEngine`
|
|
191
|
+
`chat()` persists history through the `ContextEngine` you pass to `Text2Sql`.
|
|
192
|
+
Reuse the same store, `chatId`, and `userId` to continue the same thread.
|
|
193
|
+
Before each turn, append only the new incoming message to the engine; do not
|
|
194
|
+
replay earlier turns that are already stored in the context store:
|
|
200
195
|
|
|
201
196
|
```typescript
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
]);
|
|
197
|
+
await engine.continue(user('Show me orders from last month'));
|
|
198
|
+
const stream = await text2sql.chat();
|
|
205
199
|
|
|
206
200
|
for await (const chunk of stream) {
|
|
207
201
|
// handle streaming response
|
|
208
202
|
}
|
|
209
203
|
|
|
210
204
|
// Continue the same conversation
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
205
|
+
await engine.continue(user('Now filter to only completed ones'));
|
|
206
|
+
const followUp = await text2sql.chat();
|
|
207
|
+
for await (const chunk of followUp) {
|
|
208
|
+
// handle streaming response
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
While `chat()` builds or reads cached schema context, it emits progress chunks
|
|
213
|
+
before assistant text starts:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import {
|
|
217
|
+
TEXT2SQL_INDEX_PROGRESS_CHUNK,
|
|
218
|
+
type Text2SqlIndexProgressEvent,
|
|
219
|
+
} from '@deepagents/text2sql';
|
|
220
|
+
|
|
221
|
+
await engine.continue(user('Show me top 10 customers'));
|
|
222
|
+
const stream = await text2sql.chat();
|
|
223
|
+
const startedAt = new Map<string, number>();
|
|
224
|
+
|
|
225
|
+
for await (const chunk of stream) {
|
|
226
|
+
if (chunk.type === TEXT2SQL_INDEX_PROGRESS_CHUNK) {
|
|
227
|
+
const event = chunk.data as Text2SqlIndexProgressEvent;
|
|
228
|
+
const scope =
|
|
229
|
+
[event.adapter, event.phase].filter(Boolean).join(':') || 'index';
|
|
230
|
+
|
|
231
|
+
if (event.type.endsWith(':start') || event.type === 'index:start') {
|
|
232
|
+
startedAt.set(scope, event.timestampMs ?? Date.now());
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (event.type.endsWith(':end') || event.type.endsWith(':error')) {
|
|
236
|
+
const durationMs =
|
|
237
|
+
(event.timestampMs ?? Date.now()) -
|
|
238
|
+
(startedAt.get(scope) ?? event.timestampMs ?? Date.now());
|
|
239
|
+
console.log(event.message, durationMs);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// handle assistant stream chunks
|
|
246
|
+
}
|
|
214
247
|
```
|
|
215
248
|
|
|
249
|
+
Each progress event includes a generic Unix epoch `timestampMs` so clients can
|
|
250
|
+
calculate per-index, per-adapter, or per-phase durations without Text2SQL
|
|
251
|
+
imposing one duration policy.
|
|
252
|
+
|
|
216
253
|
`toSql()` is stateless. It reads the current schema fragments, but it does not
|
|
217
254
|
write messages, titles, or usage into your context store.
|
|
218
255
|
|
package/dist/index.js
CHANGED
|
@@ -163,6 +163,17 @@ function relationship(input) {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// packages/text2sql/src/lib/adapters/groundings/context.ts
|
|
166
|
+
var noopProgress = () => {
|
|
167
|
+
};
|
|
168
|
+
function timestampProgress(onProgress) {
|
|
169
|
+
if (!onProgress) return noopProgress;
|
|
170
|
+
return (progress) => {
|
|
171
|
+
onProgress({
|
|
172
|
+
...progress,
|
|
173
|
+
timestampMs: progress.timestampMs ?? Date.now()
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
}
|
|
166
177
|
function createGroundingContext(options = {}) {
|
|
167
178
|
return {
|
|
168
179
|
tables: [],
|
|
@@ -170,7 +181,7 @@ function createGroundingContext(options = {}) {
|
|
|
170
181
|
relationships: [],
|
|
171
182
|
info: void 0,
|
|
172
183
|
cache: /* @__PURE__ */ new Map(),
|
|
173
|
-
onProgress: options.onProgress
|
|
184
|
+
onProgress: timestampProgress(options.onProgress)
|
|
174
185
|
};
|
|
175
186
|
}
|
|
176
187
|
|
|
@@ -317,7 +328,7 @@ var Adapter = class {
|
|
|
317
328
|
for (const fn of this.grounding) {
|
|
318
329
|
const grounding = fn(this);
|
|
319
330
|
if (grounding.phase) {
|
|
320
|
-
ctx.onProgress
|
|
331
|
+
ctx.onProgress({
|
|
321
332
|
type: "phase:start",
|
|
322
333
|
phase: grounding.phase,
|
|
323
334
|
message: `Starting ${grounding.name} introspection...`
|
|
@@ -327,7 +338,7 @@ var Adapter = class {
|
|
|
327
338
|
await grounding.execute(ctx);
|
|
328
339
|
} finally {
|
|
329
340
|
if (grounding.phase) {
|
|
330
|
-
ctx.onProgress
|
|
341
|
+
ctx.onProgress({
|
|
331
342
|
type: "phase:end",
|
|
332
343
|
phase: grounding.phase,
|
|
333
344
|
message: `Finished ${grounding.name} introspection.`
|
|
@@ -5238,6 +5249,17 @@ import {
|
|
|
5238
5249
|
fragment as fragment3
|
|
5239
5250
|
} from "@deepagents/context";
|
|
5240
5251
|
var TEXT2SQL_INDEX_PROGRESS_CHUNK = "data-text2sql-index-progress";
|
|
5252
|
+
var noopProgressHandler = () => {
|
|
5253
|
+
};
|
|
5254
|
+
function timestampProgressHandler(onProgress) {
|
|
5255
|
+
if (!onProgress) return noopProgressHandler;
|
|
5256
|
+
return (event) => {
|
|
5257
|
+
onProgress({
|
|
5258
|
+
...event,
|
|
5259
|
+
timestampMs: event.timestampMs ?? Date.now()
|
|
5260
|
+
});
|
|
5261
|
+
};
|
|
5262
|
+
}
|
|
5241
5263
|
var Text2Sql = class {
|
|
5242
5264
|
#config;
|
|
5243
5265
|
#introspection;
|
|
@@ -5283,8 +5305,9 @@ var Text2Sql = class {
|
|
|
5283
5305
|
return this.#index();
|
|
5284
5306
|
}
|
|
5285
5307
|
async #index(onProgress) {
|
|
5308
|
+
const progress = timestampProgressHandler(onProgress);
|
|
5286
5309
|
const entries = Object.entries(this.#config.adapters);
|
|
5287
|
-
|
|
5310
|
+
progress({
|
|
5288
5311
|
type: "index:start",
|
|
5289
5312
|
message: `Indexing ${entries.length} adapter${entries.length === 1 ? "" : "s"}...`,
|
|
5290
5313
|
current: 0,
|
|
@@ -5292,13 +5315,13 @@ var Text2Sql = class {
|
|
|
5292
5315
|
});
|
|
5293
5316
|
const settled = await Promise.allSettled(
|
|
5294
5317
|
entries.map(async ([name, adapter]) => {
|
|
5295
|
-
const schema = await this.#indexAdapter(name, adapter,
|
|
5318
|
+
const schema = await this.#indexAdapter(name, adapter, progress);
|
|
5296
5319
|
return fragment3(name, ...schema);
|
|
5297
5320
|
})
|
|
5298
5321
|
);
|
|
5299
5322
|
const failed = settled.find((result) => result.status === "rejected");
|
|
5300
5323
|
if (failed) {
|
|
5301
|
-
|
|
5324
|
+
progress({
|
|
5302
5325
|
type: "index:error",
|
|
5303
5326
|
message: failed.reason instanceof Error ? failed.reason.message : String(failed.reason)
|
|
5304
5327
|
});
|
|
@@ -5310,7 +5333,7 @@ var Text2Sql = class {
|
|
|
5310
5333
|
}
|
|
5311
5334
|
return result.value;
|
|
5312
5335
|
});
|
|
5313
|
-
|
|
5336
|
+
progress({
|
|
5314
5337
|
type: "index:end",
|
|
5315
5338
|
message: "Finished indexing adapters.",
|
|
5316
5339
|
current: entries.length,
|
|
@@ -5318,8 +5341,8 @@ var Text2Sql = class {
|
|
|
5318
5341
|
});
|
|
5319
5342
|
return wrapped;
|
|
5320
5343
|
}
|
|
5321
|
-
async #indexAdapter(name, adapter, onProgress) {
|
|
5322
|
-
onProgress
|
|
5344
|
+
async #indexAdapter(name, adapter, onProgress = noopProgressHandler) {
|
|
5345
|
+
onProgress({
|
|
5323
5346
|
type: "adapter:start",
|
|
5324
5347
|
adapter: name,
|
|
5325
5348
|
message: `Indexing adapter "${name}"...`
|
|
@@ -5331,13 +5354,13 @@ var Text2Sql = class {
|
|
|
5331
5354
|
try {
|
|
5332
5355
|
const cached = await cache.read();
|
|
5333
5356
|
if (cached) {
|
|
5334
|
-
onProgress
|
|
5357
|
+
onProgress({
|
|
5335
5358
|
type: "adapter:cache-hit",
|
|
5336
5359
|
adapter: name,
|
|
5337
5360
|
message: `Using cached index for adapter "${name}".`,
|
|
5338
5361
|
cached: true
|
|
5339
5362
|
});
|
|
5340
|
-
onProgress
|
|
5363
|
+
onProgress({
|
|
5341
5364
|
type: "adapter:end",
|
|
5342
5365
|
adapter: name,
|
|
5343
5366
|
message: `Finished indexing adapter "${name}".`,
|
|
@@ -5345,18 +5368,18 @@ var Text2Sql = class {
|
|
|
5345
5368
|
});
|
|
5346
5369
|
return cached;
|
|
5347
5370
|
}
|
|
5348
|
-
onProgress
|
|
5371
|
+
onProgress({
|
|
5349
5372
|
type: "adapter:cache-miss",
|
|
5350
5373
|
adapter: name,
|
|
5351
5374
|
message: `No cached index for adapter "${name}".`,
|
|
5352
5375
|
cached: false
|
|
5353
5376
|
});
|
|
5354
5377
|
const ctx = createGroundingContext({
|
|
5355
|
-
onProgress: (
|
|
5378
|
+
onProgress: (event) => onProgress(this.#adapterProgressEvent(name, event))
|
|
5356
5379
|
});
|
|
5357
5380
|
const fragments = await adapter.introspect(ctx);
|
|
5358
5381
|
await cache.write(fragments);
|
|
5359
|
-
onProgress
|
|
5382
|
+
onProgress({
|
|
5360
5383
|
type: "adapter:end",
|
|
5361
5384
|
adapter: name,
|
|
5362
5385
|
message: `Finished indexing adapter "${name}".`,
|
|
@@ -5365,7 +5388,7 @@ var Text2Sql = class {
|
|
|
5365
5388
|
return fragments;
|
|
5366
5389
|
} catch (error) {
|
|
5367
5390
|
const reason = error instanceof Error ? error.message : String(error);
|
|
5368
|
-
onProgress
|
|
5391
|
+
onProgress({
|
|
5369
5392
|
type: "adapter:error",
|
|
5370
5393
|
adapter: name,
|
|
5371
5394
|
message: `Failed indexing adapter "${name}": ${reason}`
|
|
@@ -5384,7 +5407,8 @@ var Text2Sql = class {
|
|
|
5384
5407
|
message: progress.message,
|
|
5385
5408
|
current: progress.current,
|
|
5386
5409
|
total: progress.total,
|
|
5387
|
-
cached: false
|
|
5410
|
+
cached: false,
|
|
5411
|
+
timestampMs: progress.timestampMs
|
|
5388
5412
|
};
|
|
5389
5413
|
}
|
|
5390
5414
|
async toPairs(adapterName, factory) {
|