@deepagents/text2sql 0.37.1 → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +10 -1
  2. package/dist/index.js +165 -44
  3. package/dist/index.js.map +2 -2
  4. package/dist/lib/adapters/adapter.d.ts +4 -4
  5. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  6. package/dist/lib/adapters/bigquery/constraint.bigquery.grounding.d.ts.map +1 -1
  7. package/dist/lib/adapters/bigquery/index.js +160 -31
  8. package/dist/lib/adapters/bigquery/index.js.map +3 -3
  9. package/dist/lib/adapters/bigquery/indexes.bigquery.grounding.d.ts.map +1 -1
  10. package/dist/lib/adapters/bigquery/row-count.bigquery.grounding.d.ts.map +1 -1
  11. package/dist/lib/adapters/groundings/abstract.grounding.d.ts +3 -1
  12. package/dist/lib/adapters/groundings/abstract.grounding.d.ts.map +1 -1
  13. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts.map +1 -1
  14. package/dist/lib/adapters/groundings/column-values.grounding.d.ts.map +1 -1
  15. package/dist/lib/adapters/groundings/constraint.grounding.d.ts.map +1 -1
  16. package/dist/lib/adapters/groundings/context.d.ts +6 -2
  17. package/dist/lib/adapters/groundings/context.d.ts.map +1 -1
  18. package/dist/lib/adapters/groundings/index.js +96 -20
  19. package/dist/lib/adapters/groundings/index.js.map +2 -2
  20. package/dist/lib/adapters/groundings/indexes.grounding.d.ts.map +1 -1
  21. package/dist/lib/adapters/groundings/row-count.grounding.d.ts.map +1 -1
  22. package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
  23. package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
  24. package/dist/lib/adapters/mysql/index.js +145 -35
  25. package/dist/lib/adapters/mysql/index.js.map +3 -3
  26. package/dist/lib/adapters/postgres/index.js +151 -35
  27. package/dist/lib/adapters/postgres/index.js.map +3 -3
  28. package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
  29. package/dist/lib/adapters/spreadsheet/index.js +109 -27
  30. package/dist/lib/adapters/spreadsheet/index.js.map +3 -3
  31. package/dist/lib/adapters/sqlite/index.js +145 -35
  32. package/dist/lib/adapters/sqlite/index.js.map +3 -3
  33. package/dist/lib/adapters/sqlserver/index.js +151 -35
  34. package/dist/lib/adapters/sqlserver/index.js.map +3 -3
  35. package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
  36. package/dist/lib/sql.d.ts +22 -5
  37. package/dist/lib/sql.d.ts.map +1 -1
  38. package/package.json +6 -5
package/README.md CHANGED
@@ -17,6 +17,15 @@ AI-powered natural language to SQL. Ask questions in plain English, get executab
17
17
  npm install @deepagents/text2sql
18
18
  ```
19
19
 
20
+ Install the database driver or client library that matches your adapter:
21
+
22
+ ```bash
23
+ npm install pg # PostgreSQL
24
+ npm install mssql # SQL Server
25
+ npm install mysql2 # MySQL / MariaDB
26
+ npm install @google-cloud/bigquery # BigQuery
27
+ ```
28
+
20
29
  Requires Node.js LTS (20+).
21
30
 
22
31
  ## Quick Start
@@ -187,7 +196,7 @@ Control what schema metadata the AI receives:
187
196
 
188
197
  ## Conversations
189
198
 
190
- `chat()` persists history through the `ContextEngine` returned by your `context` factory. Reuse the same store, `chatId`, and `userId` to continue the same thread:
199
+ `chat()` persists history through the `ContextEngine` returned by your `context` factory. Reuse the same store, `chatId`, and `userId` to continue the same thread. Pass only the new incoming message(s) for the current turn; do not replay earlier turns that are already stored in the context store:
191
200
 
192
201
  ```typescript
193
202
  const stream = await text2sql.chat([
package/dist/index.js CHANGED
@@ -163,13 +163,14 @@ function relationship(input) {
163
163
  }
164
164
 
165
165
  // packages/text2sql/src/lib/adapters/groundings/context.ts
166
- function createGroundingContext() {
166
+ function createGroundingContext(options = {}) {
167
167
  return {
168
168
  tables: [],
169
169
  views: [],
170
170
  relationships: [],
171
171
  info: void 0,
172
- cache: /* @__PURE__ */ new Map()
172
+ cache: /* @__PURE__ */ new Map(),
173
+ onProgress: options.onProgress
173
174
  };
174
175
  }
175
176
 
@@ -315,7 +316,24 @@ var Adapter = class {
315
316
  async introspect(ctx = createGroundingContext()) {
316
317
  for (const fn of this.grounding) {
317
318
  const grounding = fn(this);
318
- await grounding.execute(ctx);
319
+ if (grounding.phase) {
320
+ ctx.onProgress?.({
321
+ type: "phase:start",
322
+ phase: grounding.phase,
323
+ message: `Starting ${grounding.name} introspection...`
324
+ });
325
+ }
326
+ try {
327
+ await grounding.execute(ctx);
328
+ } finally {
329
+ if (grounding.phase) {
330
+ ctx.onProgress?.({
331
+ type: "phase:end",
332
+ phase: grounding.phase,
333
+ message: `Finished ${grounding.name} introspection.`
334
+ });
335
+ }
336
+ }
319
337
  }
320
338
  return this.#toSchemaFragments(ctx);
321
339
  }
@@ -521,26 +539,33 @@ var Adapter = class {
521
539
  async #checkScope(sql, allowedEntities) {
522
540
  const dialect = this.formatterLanguage;
523
541
  const scopeDialects = {
524
- sqlite: "mysql",
525
- postgresql: "postgresql",
526
- bigquery: "bigquery",
527
- transactsql: "transactsql",
528
- mysql: "mysql"
542
+ sqlite: ["sqlite", "mysql"],
543
+ postgresql: ["postgresql"],
544
+ bigquery: ["bigquery"],
545
+ transactsql: ["transactsql"],
546
+ mysql: ["mysql"]
529
547
  };
530
- const scopeDialect = scopeDialects[dialect];
531
- if (!scopeDialect) {
548
+ const candidates = scopeDialects[dialect];
549
+ if (!candidates) {
532
550
  throw new TypeError(
533
551
  `No scope dialect mapping for formatter language "${dialect}". Add it to the scopeDialects map in Adapter.#checkScope.`
534
552
  );
535
553
  }
536
- let references;
537
- try {
538
- references = extractBaseEntityReferences(sql, scopeDialect);
539
- } catch (error) {
554
+ let references = null;
555
+ let lastError;
556
+ for (const candidate of candidates) {
557
+ try {
558
+ references = extractBaseEntityReferences(sql, candidate);
559
+ break;
560
+ } catch (error) {
561
+ lastError = error;
562
+ }
563
+ }
564
+ if (references === null) {
540
565
  return buildScopeParseErrorPayload(
541
566
  sql,
542
567
  dialect,
543
- error
568
+ lastError
544
569
  );
545
570
  }
546
571
  if (references.length === 0) return null;
@@ -5202,6 +5227,9 @@ function sqlSandboxExtension(adapters, options = {}) {
5202
5227
  }
5203
5228
 
5204
5229
  // packages/text2sql/src/lib/sql.ts
5230
+ import {
5231
+ createUIMessageStream
5232
+ } from "ai";
5205
5233
  import "@deepagents/agent";
5206
5234
  import {
5207
5235
  agent as agent2,
@@ -5209,6 +5237,7 @@ import {
5209
5237
  errorRecoveryGuardrail,
5210
5238
  fragment as fragment3
5211
5239
  } from "@deepagents/context";
5240
+ var TEXT2SQL_INDEX_PROGRESS_CHUNK = "data-text2sql-index-progress";
5212
5241
  var Text2Sql = class {
5213
5242
  #config;
5214
5243
  #introspection;
@@ -5251,61 +5280,152 @@ var Text2Sql = class {
5251
5280
  return result.sql;
5252
5281
  }
5253
5282
  async index() {
5283
+ return this.#index();
5284
+ }
5285
+ async #index(onProgress) {
5254
5286
  const entries = Object.entries(this.#config.adapters);
5255
- const wrapped = await Promise.all(
5287
+ onProgress?.({
5288
+ type: "index:start",
5289
+ message: `Indexing ${entries.length} adapter${entries.length === 1 ? "" : "s"}...`,
5290
+ current: 0,
5291
+ total: entries.length
5292
+ });
5293
+ const settled = await Promise.allSettled(
5256
5294
  entries.map(async ([name, adapter]) => {
5257
- const schema = await this.#indexAdapter(name, adapter);
5295
+ const schema = await this.#indexAdapter(name, adapter, onProgress);
5258
5296
  return fragment3(name, ...schema);
5259
5297
  })
5260
5298
  );
5299
+ const failed = settled.find((result) => result.status === "rejected");
5300
+ if (failed) {
5301
+ onProgress?.({
5302
+ type: "index:error",
5303
+ message: failed.reason instanceof Error ? failed.reason.message : String(failed.reason)
5304
+ });
5305
+ throw failed.reason;
5306
+ }
5307
+ const wrapped = settled.map((result) => {
5308
+ if (result.status === "rejected") {
5309
+ throw result.reason;
5310
+ }
5311
+ return result.value;
5312
+ });
5313
+ onProgress?.({
5314
+ type: "index:end",
5315
+ message: "Finished indexing adapters.",
5316
+ current: entries.length,
5317
+ total: entries.length
5318
+ });
5261
5319
  return wrapped;
5262
5320
  }
5263
- async #indexAdapter(name, adapter) {
5321
+ async #indexAdapter(name, adapter, onProgress) {
5322
+ onProgress?.({
5323
+ type: "adapter:start",
5324
+ adapter: name,
5325
+ message: `Indexing adapter "${name}"...`
5326
+ });
5264
5327
  const cache = this.#introspection.get(name);
5265
5328
  if (!cache) {
5266
5329
  throw new Error(`no introspection cache registered for "${name}"`);
5267
5330
  }
5268
- const cached = await cache.read();
5269
- if (cached) {
5270
- return cached;
5271
- }
5272
5331
  try {
5273
- const fragments = await adapter.introspect();
5332
+ const cached = await cache.read();
5333
+ if (cached) {
5334
+ onProgress?.({
5335
+ type: "adapter:cache-hit",
5336
+ adapter: name,
5337
+ message: `Using cached index for adapter "${name}".`,
5338
+ cached: true
5339
+ });
5340
+ onProgress?.({
5341
+ type: "adapter:end",
5342
+ adapter: name,
5343
+ message: `Finished indexing adapter "${name}".`,
5344
+ cached: true
5345
+ });
5346
+ return cached;
5347
+ }
5348
+ onProgress?.({
5349
+ type: "adapter:cache-miss",
5350
+ adapter: name,
5351
+ message: `No cached index for adapter "${name}".`,
5352
+ cached: false
5353
+ });
5354
+ const ctx = createGroundingContext({
5355
+ onProgress: (progress) => onProgress?.(this.#adapterProgressEvent(name, progress))
5356
+ });
5357
+ const fragments = await adapter.introspect(ctx);
5274
5358
  await cache.write(fragments);
5359
+ onProgress?.({
5360
+ type: "adapter:end",
5361
+ adapter: name,
5362
+ message: `Finished indexing adapter "${name}".`,
5363
+ cached: false
5364
+ });
5275
5365
  return fragments;
5276
5366
  } catch (error) {
5277
5367
  const reason = error instanceof Error ? error.message : String(error);
5368
+ onProgress?.({
5369
+ type: "adapter:error",
5370
+ adapter: name,
5371
+ message: `Failed indexing adapter "${name}": ${reason}`
5372
+ });
5278
5373
  throw new Error(`introspecting adapter "${name}": ${reason}`, {
5279
5374
  cause: error
5280
5375
  });
5281
5376
  }
5282
5377
  }
5378
+ #adapterProgressEvent(adapter, progress) {
5379
+ return {
5380
+ type: progress.type,
5381
+ adapter,
5382
+ phase: progress.phase,
5383
+ table: progress.table,
5384
+ message: progress.message,
5385
+ current: progress.current,
5386
+ total: progress.total,
5387
+ cached: false
5388
+ };
5389
+ }
5283
5390
  async toPairs(adapterName, factory) {
5284
5391
  const adapter = this.#requireAdapter(adapterName);
5285
5392
  const producer = factory(adapter);
5286
5393
  return producer.toPairs();
5287
5394
  }
5288
- async chat(messages, options) {
5289
- if (messages.length === 0) {
5290
- throw new Error("messages must not be empty");
5291
- }
5292
- const context = this.#config.context(
5293
- ...guidelines(),
5294
- ...await this.index()
5295
- );
5296
- const chatAgent = agent2({
5297
- name: "text2sql",
5298
- sandbox: this.#config.sandbox,
5299
- model: this.#config.model,
5300
- context,
5301
- tools: this.#config.tools,
5302
- guardrails: [errorRecoveryGuardrail],
5303
- maxGuardrailRetries: 3
5304
- });
5305
- return chat(chatAgent, messages, {
5306
- abortSignal: options?.abortSignal,
5307
- generateTitle: options?.generateTitle,
5308
- transform: this.#config.transform
5395
+ async chat(options) {
5396
+ return createUIMessageStream({
5397
+ execute: async ({ writer }) => {
5398
+ const context = this.#config.context;
5399
+ const head = await context.headMessage();
5400
+ if (!head || head.name !== "assistant") {
5401
+ throw new Error(
5402
+ "text2sql.chat: expected head to be an assistant message \u2014 call context.continue() before chat()"
5403
+ );
5404
+ }
5405
+ writer.write({ type: "start", messageId: head.id });
5406
+ const progress = (event) => {
5407
+ writer.write({
5408
+ type: TEXT2SQL_INDEX_PROGRESS_CHUNK,
5409
+ data: event
5410
+ });
5411
+ };
5412
+ context.set(...guidelines(), ...await this.#index(progress));
5413
+ const chatAgent = agent2({
5414
+ name: "text2sql",
5415
+ sandbox: this.#config.sandbox,
5416
+ model: this.#config.model,
5417
+ context,
5418
+ tools: this.#config.tools,
5419
+ guardrails: [errorRecoveryGuardrail],
5420
+ maxGuardrailRetries: 3
5421
+ });
5422
+ const chatStream = await chat(chatAgent, {
5423
+ abortSignal: options?.abortSignal,
5424
+ generateTitle: options?.generateTitle,
5425
+ transform: this.#config.transform
5426
+ });
5427
+ writer.merge(chatStream);
5428
+ }
5309
5429
  });
5310
5430
  }
5311
5431
  };
@@ -5325,6 +5445,7 @@ export {
5325
5445
  SqlProxyEnforcementPlugin,
5326
5446
  SqlProxyViolationError,
5327
5447
  SqliteFs,
5448
+ TEXT2SQL_INDEX_PROGRESS_CHUNK,
5328
5449
  Text2Sql,
5329
5450
  UnanswerableSQLError,
5330
5451
  applyTablesFilter,