@deepagents/text2sql 7.1.0 → 7.1.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 (47) hide show
  1. package/README.md +63 -76
  2. package/dist/bin/sql.js +42 -584
  3. package/dist/eval/src/evals/models.d.ts +12 -0
  4. package/dist/eval/src/evals/models.d.ts.map +1 -0
  5. package/dist/eval/src/evals/sql-create-context/sql-create-context.eval.d.ts +2 -0
  6. package/dist/eval/src/evals/sql-create-context/sql-create-context.eval.d.ts.map +1 -0
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +130 -211
  10. package/dist/index.js.map +4 -4
  11. package/dist/lib/adapter-index.d.ts +0 -2
  12. package/dist/lib/adapter-index.d.ts.map +1 -1
  13. package/dist/lib/adapters/posthog/index.js.map +2 -2
  14. package/dist/lib/adapters/posthog/posthog.d.ts.map +1 -1
  15. package/dist/lib/adapters/posthog/schema.posthog.grounding.d.ts.map +1 -1
  16. package/dist/lib/adapters/spreadsheet/index.js +4 -4
  17. package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
  18. package/dist/lib/adapters/sqlite/index.js +0 -3
  19. package/dist/lib/adapters/sqlite/index.js.map +2 -2
  20. package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -1
  21. package/dist/lib/adapters/sqlserver/index.js +43 -59
  22. package/dist/lib/adapters/sqlserver/index.js.map +2 -2
  23. package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts +1 -1
  24. package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts.map +1 -1
  25. package/dist/lib/agents/question.agent.d.ts.map +1 -1
  26. package/dist/lib/agents/suggestions.agents.d.ts.map +1 -1
  27. package/dist/lib/fs/sqlite/sqlite-fs.d.ts.map +1 -1
  28. package/dist/lib/index-cache.d.ts +2 -3
  29. package/dist/lib/index-cache.d.ts.map +1 -1
  30. package/dist/lib/sql-command.d.ts +1 -4
  31. package/dist/lib/sql-command.d.ts.map +1 -1
  32. package/dist/lib/sql.d.ts +0 -5
  33. package/dist/lib/sql.d.ts.map +1 -1
  34. package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts.map +1 -1
  35. package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts.map +1 -1
  36. package/dist/lib/synthesis/extractors/sql-extractor.d.ts.map +1 -1
  37. package/dist/lib/synthesis/index.js.map +2 -2
  38. package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts.map +1 -1
  39. package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
  40. package/dist/lib/synthesis/synthesizers/persona-generator.d.ts.map +1 -1
  41. package/dist/scripts/scripts/bigquery-test.d.ts +2 -0
  42. package/dist/scripts/scripts/bigquery-test.d.ts.map +1 -0
  43. package/package.json +4 -6
  44. package/dist/lib/index-lock.d.ts +0 -60
  45. package/dist/lib/index-lock.d.ts.map +0 -1
  46. package/dist/lib/prompt.d.ts +0 -2
  47. package/dist/lib/prompt.d.ts.map +0 -1
package/dist/bin/sql.js CHANGED
@@ -84,54 +84,6 @@ function isFragmentList(value) {
84
84
  return Array.isArray(value) && value.every((item) => isFragment(item) && isFragmentData(item));
85
85
  }
86
86
 
87
- // packages/text2sql/src/lib/index-lock.ts
88
- import { createHash as createHash2 } from "node:crypto";
89
- import { mkdir as mkdir2 } from "node:fs/promises";
90
- import { tmpdir as tmpdir2 } from "node:os";
91
- import * as path2 from "node:path";
92
- import { lock as acquireLock } from "proper-lockfile";
93
- var DEFAULT_STALE_MS = 3e4;
94
- var DEFAULT_RETRIES = {
95
- retries: 10,
96
- factor: 2,
97
- minTimeout: 100,
98
- maxTimeout: 2e3
99
- };
100
- var FileIndexLock = class {
101
- #dir;
102
- #namespace;
103
- #stale;
104
- #retries;
105
- constructor(options = {}) {
106
- this.#dir = options.dir ?? tmpdir2();
107
- this.#namespace = options.namespace;
108
- this.#stale = options.stale ?? DEFAULT_STALE_MS;
109
- this.#retries = options.retries ?? DEFAULT_RETRIES;
110
- }
111
- async run(key, fn) {
112
- await mkdir2(this.#dir, { recursive: true });
113
- const release = await acquireLock(this.#lockPathFor(key), {
114
- realpath: false,
115
- stale: this.#stale,
116
- retries: this.#retries
117
- });
118
- try {
119
- return await fn();
120
- } finally {
121
- await release().catch(() => {
122
- });
123
- }
124
- }
125
- #lockPathFor(key) {
126
- const watermark = this.#namespace ? `${this.#namespace}-${key}` : key;
127
- const hash = createHash2("md5").update(watermark).digest("hex");
128
- return path2.join(this.#dir, `text2sql-lock-${hash}`);
129
- }
130
- };
131
-
132
- // packages/text2sql/src/lib/sql.ts
133
- import "@deepagents/agent";
134
-
135
87
  // packages/text2sql/src/lib/adapter-index.ts
136
88
  import { fragment } from "@deepagents/context";
137
89
 
@@ -255,7 +207,6 @@ function databaseNameFragment(name) {
255
207
  var AdapterIndexer = class {
256
208
  #adapters;
257
209
  #cache;
258
- #lock;
259
210
  constructor(options) {
260
211
  const adapterNames = Object.keys(options.adapters);
261
212
  if (adapterNames.length === 0) {
@@ -264,7 +215,6 @@ var AdapterIndexer = class {
264
215
  validateAdapterNames(adapterNames);
265
216
  this.#adapters = options.adapters;
266
217
  this.#cache = options.cache;
267
- this.#lock = options.lock;
268
218
  }
269
219
  async index(options = {}) {
270
220
  const progress = timestampProgressHandler(options.onProgress);
@@ -322,41 +272,15 @@ var AdapterIndexer = class {
322
272
  message: `Indexing adapter "${name}"...`
323
273
  });
324
274
  const cache = this.#cache;
325
- const key = name;
326
- const emitCacheHit = () => progress({
327
- type: "adapter:cache-hit",
328
- adapter: name,
329
- message: `Using cached index for adapter "${name}".`,
330
- cached: true
331
- });
332
- const emitCacheMiss = () => progress({
333
- type: "adapter:cache-miss",
334
- adapter: name,
335
- message: `No cached index for adapter "${name}".`,
336
- cached: false
337
- });
338
- const introspect = async () => {
339
- const ctx = createGroundingContext({
340
- onProgress: (event) => progress(adapterProgressEvent(name, event))
341
- });
342
- const fragments = await adapter.introspect(ctx);
343
- await cache?.write(key, fragments);
344
- return fragments;
345
- };
346
- let servedFromCache = false;
347
- const populate = async () => {
348
- const recheck = await cache?.read(key);
349
- if (recheck) {
350
- servedFromCache = true;
351
- return recheck;
352
- }
353
- if (cache) emitCacheMiss();
354
- return introspect();
355
- };
356
275
  try {
357
- const cached = await cache?.read(key);
276
+ const cached = await cache?.read(name);
358
277
  if (cached) {
359
- emitCacheHit();
278
+ progress({
279
+ type: "adapter:cache-hit",
280
+ adapter: name,
281
+ message: `Using cached index for adapter "${name}".`,
282
+ cached: true
283
+ });
360
284
  progress({
361
285
  type: "adapter:end",
362
286
  adapter: name,
@@ -365,13 +289,24 @@ var AdapterIndexer = class {
365
289
  });
366
290
  return cached;
367
291
  }
368
- const fragments = await this.#lock.run(key, populate);
369
- if (servedFromCache) emitCacheHit();
292
+ if (cache) {
293
+ progress({
294
+ type: "adapter:cache-miss",
295
+ adapter: name,
296
+ message: `No cached index for adapter "${name}".`,
297
+ cached: false
298
+ });
299
+ }
300
+ const ctx = createGroundingContext({
301
+ onProgress: (event) => progress(adapterProgressEvent(name, event))
302
+ });
303
+ const fragments = await adapter.introspect(ctx);
304
+ await cache?.write(name, fragments);
370
305
  progress({
371
306
  type: "adapter:end",
372
307
  adapter: name,
373
308
  message: `Finished indexing adapter "${name}".`,
374
- cached: servedFromCache
309
+ cached: false
375
310
  });
376
311
  return fragments;
377
312
  } catch (error) {
@@ -416,456 +351,6 @@ function errorMessage(error) {
416
351
  return error instanceof Error ? error.message : String(error);
417
352
  }
418
353
 
419
- // packages/text2sql/src/lib/agents/sql.agent.ts
420
- import { groq } from "@ai-sdk/groq";
421
- import {
422
- APICallError,
423
- JSONParseError,
424
- NoContentGeneratedError,
425
- NoObjectGeneratedError,
426
- NoOutputGeneratedError,
427
- TypeValidationError,
428
- defaultSettingsMiddleware,
429
- wrapLanguageModel
430
- } from "ai";
431
- import dedent from "dedent";
432
- import pRetry from "p-retry";
433
- import z2 from "zod";
434
- import "@deepagents/agent";
435
- import {
436
- ContextEngine,
437
- InMemoryContextStore,
438
- example,
439
- fragment as fragment2,
440
- guardrail,
441
- hint,
442
- persona,
443
- policy,
444
- structuredOutput,
445
- user,
446
- workflow
447
- } from "@deepagents/context";
448
-
449
- // packages/text2sql/src/lib/sql-scope-error.ts
450
- function buildScopeParseErrorPayload(sql, parserDialect, error) {
451
- const parserError = error instanceof Error ? error.message : String(error ?? "Unknown error");
452
- return {
453
- error: `SQL scope analysis failed before validation/execution: ${parserError}`,
454
- error_type: "SQL_SCOPE_PARSE_ERROR",
455
- suggestion: "Rewrite the query into simpler SQL that can be analyzed safely, or extend parser coverage for this dialect feature.",
456
- sql_attempted: sql,
457
- parser_dialect: parserDialect,
458
- parser_error: parserError
459
- };
460
- }
461
-
462
- // packages/text2sql/src/lib/agents/sql.agent.ts
463
- var RETRY_TEMPERATURES = [0, 0.2, 0.3];
464
- var SQL_AGENT_ROLE = "Expert SQL query generator.";
465
- var SQL_AGENT_OBJECTIVE = "Generate precise SQL grounded in provided schema.";
466
- var SQL_AGENT_POLICIES = [
467
- fragment2(
468
- "schema_mapping",
469
- policy({
470
- rule: "Translate natural language into precise SQL grounded in available schema entities."
471
- }),
472
- hint("Preserve schema spelling exactly, including typos in column names.")
473
- ),
474
- fragment2(
475
- "projection_minimality",
476
- policy({
477
- rule: "Return only columns requested by the question; do not add helper columns unless explicitly requested."
478
- }),
479
- policy({
480
- rule: 'For requests of the form "X sorted/ordered by Y", project X only unless Y is explicitly requested as an output field.'
481
- }),
482
- policy({
483
- rule: "Prefer selecting schema columns directly without derived expressions when direct selection answers the request."
484
- }),
485
- hint(
486
- "Do not include ORDER BY, GROUP BY, or JOIN helper columns in SELECT output unless the question explicitly asks for them."
487
- ),
488
- policy({
489
- rule: "Use DISTINCT only when uniqueness is explicitly requested (for example distinct/unique/different/no duplicates)."
490
- }),
491
- hint(
492
- 'Do not infer DISTINCT from generic wording such as "some", plural nouns, or entity-set phrasing; for transactional/attendance-style tables, default to raw rows unless uniqueness is explicitly requested.'
493
- )
494
- ),
495
- fragment2(
496
- "date_transform_safety",
497
- policy({
498
- rule: "Do not assume VARCHAR/TEXT values are parseable dates. Avoid date extraction functions on text columns by default."
499
- }),
500
- policy({
501
- rule: "Use date-part extraction only when both conditions hold: the question explicitly asks for transformation and schema values require transformation to produce that unit."
502
- }),
503
- hint(
504
- "Do not apply SUBSTR, STRFTIME, DATE_PART, YEAR, or similar extraction functions unless the question explicitly asks for transformation and schema values require it."
505
- ),
506
- hint(
507
- "If a column already represents the requested concept (for example a stored year-like value), use the column as-is."
508
- )
509
- ),
510
- fragment2(
511
- "sql_minimality",
512
- guardrail({
513
- rule: "Never hallucinate tables or columns.",
514
- reason: "Schema fidelity is required.",
515
- action: "Use only available schema entities."
516
- }),
517
- guardrail({
518
- rule: "Avoid unnecessary transformations and derived projections.",
519
- reason: "Extra transformations frequently change semantics and reduce correctness.",
520
- action: "Do not add date parsing, substring extraction, or derived columns unless explicitly required by the question or schema."
521
- })
522
- ),
523
- fragment2(
524
- "preflight_checklist",
525
- workflow({
526
- task: "Final SQL preflight before returning output",
527
- steps: [
528
- "Verify selected columns match the question and remove unrequested helper projections.",
529
- "If aggregate values are used only for ranking/filtering, keep them out of SELECT unless explicitly requested.",
530
- "Prefer raw schema columns over derived expressions when raw columns already satisfy the request.",
531
- "If a candidate query uses STRFTIME, SUBSTR, DATE_PART, YEAR, or similar extraction on text-like columns, remove that transformation unless explicitly required by the question.",
532
- "Return only schema-grounded SQL using existing tables and columns."
533
- ]
534
- })
535
- ),
536
- fragment2(
537
- "set_semantics",
538
- policy({
539
- rule: "For questions asking where both condition A and condition B hold over an attribute, compute the intersection of qualifying sets for that attribute."
540
- }),
541
- policy({
542
- rule: "Do not force the same entity instance to satisfy both conditions unless the question explicitly requests the same person/row/entity."
543
- }),
544
- hint(
545
- "Prefer INTERSECT (or logically equivalent set-based shape) over requiring the same physical row/entity to satisfy both conditions unless explicitly requested."
546
- ),
547
- hint(
548
- "When two conditions describe different row groups whose shared attribute is requested, build each group separately and intersect the attribute values."
549
- ),
550
- hint(
551
- "Do not collapse cross-group conditions into a single-row AND predicate when the intent is shared values across groups."
552
- ),
553
- policy({
554
- rule: "If two predicates on the same field cannot both be true for one row, do not combine them with AND; use set operations across separate filtered subsets when shared values are requested."
555
- })
556
- ),
557
- fragment2(
558
- "predicate_column_alignment",
559
- policy({
560
- rule: "Match literal values to semantically compatible columns. Do not compare descriptive names to identifier columns."
561
- }),
562
- hint(
563
- "When a filter value is a descriptive label (for example a department name), join through the lookup table and filter on its name/title column, not on *_id columns."
564
- ),
565
- hint(
566
- "When relation roles are explicit in wording (for example host/home/source/destination), prefer foreign keys with matching role qualifiers over generic similarly named columns."
567
- ),
568
- policy({
569
- rule: "When multiple foreign-key candidates exist, select the column whose qualifier best matches the relationship described in the question."
570
- }),
571
- policy({
572
- rule: "For hosting/held semantics, prefer host_* relationship columns when available over generic *_id alternatives."
573
- }),
574
- hint(
575
- 'Interpret wording like "held/hosted a competition or event" as a hosting relationship and map to host_* foreign keys when present.'
576
- ),
577
- policy({
578
- rule: "Do not compare descriptive labels or names to *_id columns; join to the table containing the descriptive field and filter there."
579
- }),
580
- policy({
581
- rule: "Keep numeric identifiers unquoted when used as numeric equality filters unless schema indicates text identifiers."
582
- }),
583
- policy({
584
- rule: "When filtering by a descriptive label value and a related table exposes a corresponding *_name or title column, join to that table and filter on the descriptive column."
585
- })
586
- ),
587
- fragment2(
588
- "ordering_semantics",
589
- policy({
590
- rule: "Respect explicit sort direction terms. If direction is not specified, use ascending order unless a superlative intent (most/least/highest/lowest) implies direction."
591
- }),
592
- policy({
593
- rule: "When ranking categories by frequency, use COUNT for ordering but keep output focused on requested category fields unless counts are explicitly requested."
594
- }),
595
- policy({
596
- rule: "Do not use DESC unless descending direction is explicit or a superlative intent requires descending ranking."
597
- }),
598
- policy({
599
- rule: 'For "most common/frequent <attribute>" requests, return the attribute value(s) only; use counts only for ordering/filtering unless the question explicitly asks to return counts.'
600
- }),
601
- hint(
602
- 'Use DESC with LIMIT 1 for "most/highest/largest"; use ASC with LIMIT 1 for "least/lowest/smallest".'
603
- )
604
- ),
605
- fragment2(
606
- "negative_membership_queries",
607
- policy({
608
- rule: "For requests asking entities that did not participate/host/appear in related records, prefer NOT IN or NOT EXISTS against the related foreign-key set."
609
- }),
610
- hint(
611
- "Map role-bearing relationship columns carefully (for example host_* foreign keys for hosting relationships) instead of generic IDs when role wording is explicit."
612
- ),
613
- hint(
614
- 'For "never had/never exceeded" conditions over history tables, exclude entities via NOT IN/NOT EXISTS against the disqualifying entity-id set (often built with GROUP BY/HAVING MAX(...)).'
615
- )
616
- ),
617
- fragment2(
618
- "join_completeness",
619
- policy({
620
- rule: "Preserve entity-restricting joins implied by the question. Do not widen results by querying only a broader attribute table when a subset entity table is available."
621
- }),
622
- policy({
623
- rule: "If an entity term in the question maps to a table, keep that table in query scope and join to attribute tables rather than dropping the entity table."
624
- }),
625
- hint(
626
- "If the question targets a specific entity group, include that entity table and its join conditions even when selected columns come from a related table."
627
- ),
628
- hint(
629
- "When the question names an entity type and a relation table links to that entity via *_id, include the entity table in scope instead of counting only relation rows."
630
- ),
631
- hint(
632
- "Prefer INNER JOIN by default; use LEFT JOIN only when the question explicitly requests including unmatched rows or zero-related entities."
633
- )
634
- ),
635
- fragment2(
636
- "aggregation_exactness",
637
- policy({
638
- rule: "Preserve requested aggregation semantics exactly: use COUNT(*) by default for total rows, use COUNT(DISTINCT ...) only when uniqueness is explicitly requested, and group by stable entity keys when computing per-entity aggregates."
639
- }),
640
- policy({
641
- rule: "For questions asking which entity has lowest/highest average of a metric, compute AVG(metric) per entity (GROUP BY entity) and rank those aggregates."
642
- }),
643
- hint(
644
- 'For "how many <entities>" questions over relation records, default to COUNT(*) on qualifying rows unless explicit uniqueness language is present.'
645
- )
646
- ),
647
- fragment2(
648
- "query_shape_examples",
649
- example({
650
- question: "List categories ordered by how many records belong to each category.",
651
- answer: "SELECT category FROM records GROUP BY category ORDER BY COUNT(*)"
652
- }),
653
- example({
654
- question: "Show labels shared by rows with metric > 100 and rows with metric < 10.",
655
- answer: "SELECT label FROM records WHERE metric > 100 INTERSECT SELECT label FROM records WHERE metric < 10"
656
- }),
657
- example({
658
- question: "List locations that have not hosted any event.",
659
- answer: "SELECT location_name FROM locations WHERE location_id NOT IN (SELECT host_location_id FROM events)"
660
- }),
661
- example({
662
- question: "List the most common category across records.",
663
- answer: "SELECT category FROM records GROUP BY category ORDER BY COUNT(*) DESC LIMIT 1"
664
- })
665
- )
666
- ];
667
- function extractSql(output) {
668
- const match = output.match(/```sql\n?([\s\S]*?)```/);
669
- return match ? match[1].trim() : output.trim();
670
- }
671
- async function toSql(options) {
672
- const { maxRetries = 3 } = options;
673
- return withRetry(
674
- async (attemptNumber, errors, attempts) => {
675
- const context = new ContextEngine({
676
- store: new InMemoryContextStore(),
677
- chatId: `sql-gen-${crypto.randomUUID()}`,
678
- userId: "system"
679
- });
680
- context.set(
681
- persona({
682
- name: "Freya",
683
- role: SQL_AGENT_ROLE,
684
- objective: SQL_AGENT_OBJECTIVE
685
- // role: `You are a data science expert that provides well-reasoned and detailed responses.`,
686
- // objective: `Your task is to understand the schema and generate a valid SQL query to answer the question. You first think about the reasoning process as an internal monologue and then provide the user with the answer.`,
687
- }),
688
- ...SQL_AGENT_POLICIES,
689
- ...options.fragments
690
- );
691
- if (errors.length) {
692
- const lastError = errors.at(-1);
693
- context.set(
694
- user(dedent`
695
- Answer the following question with the SQL code. Use the piece of evidence and base your answer on the database schema.
696
- Given the question, the evidence and the database schema, return the SQL script that addresses the question.
697
-
698
- Question: ${options.input}
699
- `),
700
- UnanswerableSQLError.isInstance(lastError) ? user(
701
- `<retry_instruction>Your previous response marked the task as unanswerable. Re-evaluate using best-effort schema mapping. If the core intent is answerable with existing tables/columns, return SQL. Return error only when required core intent cannot be mapped without inventing schema elements.</retry_instruction>`
702
- ) : user(
703
- `<validation_error>Your previous SQL query had the following error: ${lastError?.message}. Please fix the query.</validation_error>`
704
- )
705
- );
706
- } else {
707
- context.set(
708
- user(dedent`
709
- Answer the following question with the SQL code. Use the piece of evidence and base your answer on the database schema.
710
- Given the question, the evidence and the database schema, return the SQL script that addresses the question.
711
-
712
- Question: ${options.input}
713
- `)
714
- );
715
- }
716
- const temperature = RETRY_TEMPERATURES[attemptNumber - 1] ?? RETRY_TEMPERATURES[RETRY_TEMPERATURES.length - 1];
717
- const baseModel = options.model ?? groq("openai/gpt-oss-20b");
718
- const model = wrapLanguageModel({
719
- model: baseModel,
720
- middleware: defaultSettingsMiddleware({ settings: { temperature } })
721
- });
722
- const sqlOutput = structuredOutput({
723
- model,
724
- context,
725
- schema: z2.object({
726
- result: z2.union([
727
- z2.object({
728
- sql: z2.string().describe("The SQL query that answers the question"),
729
- reasoning: z2.string().describe("The reasoning steps taken to generate the SQL")
730
- }),
731
- z2.object({
732
- error: z2.string().describe(
733
- "Error message explaining why the question cannot be answered with the given schema"
734
- )
735
- })
736
- ])
737
- })
738
- });
739
- const { result: output } = await sqlOutput.generate();
740
- const finalizeSql = async (rawSql) => {
741
- const extractedSql = extractSql(rawSql);
742
- const errorsOrUndefined = errors.length ? errors.map(formatErrorMessage) : void 0;
743
- if (extractedSql === "") {
744
- return {
745
- attempts,
746
- sql: "",
747
- errors: errorsOrUndefined
748
- };
749
- }
750
- const sql = options.adapter.format(extractedSql);
751
- if (rawSql.trimStart().startsWith("```") && extractedSql === rawSql.trim()) {
752
- throw new SQLValidationError(
753
- JSON.stringify(
754
- buildScopeParseErrorPayload(
755
- sql,
756
- options.adapter.formatterLanguage,
757
- new Error(
758
- "SQL response was fenced markdown, not executable SQL."
759
- )
760
- )
761
- )
762
- );
763
- }
764
- const validationError = await options.adapter.validate(sql);
765
- if (validationError) {
766
- throw new SQLValidationError(validationError);
767
- }
768
- return {
769
- attempts,
770
- sql,
771
- errors: errorsOrUndefined
772
- };
773
- };
774
- if ("error" in output) {
775
- context.set(
776
- user(
777
- "<best_effort_fallback>Do not return unanswerable. Produce the best valid SQL query that answers the core intent using only available schema entities.</best_effort_fallback>"
778
- )
779
- );
780
- const forcedSqlOutput = structuredOutput({
781
- model,
782
- context,
783
- schema: z2.object({
784
- sql: z2.string().describe(
785
- "Best-effort SQL query that answers the core intent using only available schema entities."
786
- ),
787
- reasoning: z2.string().describe("Reasoning steps for best-effort schema mapping.")
788
- })
789
- });
790
- try {
791
- const forced = await forcedSqlOutput.generate();
792
- return await finalizeSql(forced.sql);
793
- } catch (error) {
794
- if (SQLValidationError.isInstance(error) || APICallError.isInstance(error) || JSONParseError.isInstance(error) || TypeValidationError.isInstance(error) || NoObjectGeneratedError.isInstance(error) || NoOutputGeneratedError.isInstance(error) || NoContentGeneratedError.isInstance(error)) {
795
- throw error;
796
- }
797
- throw new UnanswerableSQLError(output.error);
798
- }
799
- }
800
- return await finalizeSql(output.sql);
801
- },
802
- { retries: maxRetries - 1 }
803
- );
804
- }
805
- function formatErrorMessage(error) {
806
- if (APICallError.isInstance(error)) {
807
- if (error.message.startsWith("Failed to validate JSON")) {
808
- return `Schema validation failed: ${error.message}`;
809
- }
810
- return error.message;
811
- }
812
- if (SQLValidationError.isInstance(error)) {
813
- return `SQL Validation Error: ${error.message}`;
814
- }
815
- return error.message;
816
- }
817
- function isModelUnavailableError(error) {
818
- if (!APICallError.isInstance(error)) {
819
- return false;
820
- }
821
- const message = error.message.toLowerCase();
822
- const responseBody = (error.responseBody ?? "").toLowerCase();
823
- const is404ModelError = error.statusCode === 404 && (message.includes("model") || responseBody.includes("model_not_found"));
824
- const errorCode = typeof error.data === "object" && error.data !== null && "error" in error.data && typeof error.data.error === "object" && error.data.error !== null && "code" in error.data.error && typeof error.data.error.code === "string" ? error.data.error.code.toLowerCase() : void 0;
825
- return is404ModelError || errorCode === "model_not_found" || responseBody.includes('"code":"model_not_found"') || message.includes("model") && message.includes("does not exist or you do not have access to it");
826
- }
827
- async function withRetry(computation, options = { retries: 3 }) {
828
- const errors = [];
829
- let attempts = 0;
830
- return pRetry(
831
- (attemptNumber) => {
832
- return computation(attemptNumber, errors, ++attempts);
833
- },
834
- {
835
- retries: options.retries,
836
- shouldRetry: (context) => {
837
- if (UnanswerableSQLError.isInstance(context.error)) {
838
- return false;
839
- }
840
- if (isModelUnavailableError(context.error)) {
841
- return false;
842
- }
843
- if (SQLValidationError.isInstance(context.error)) {
844
- return true;
845
- }
846
- console.log({
847
- NoObjectGeneratedError: NoObjectGeneratedError.isInstance(
848
- context.error
849
- ),
850
- NoOutputGeneratedError: NoOutputGeneratedError.isInstance(
851
- context.error
852
- ),
853
- APICallError: APICallError.isInstance(context.error),
854
- JSONParseError: JSONParseError.isInstance(context.error),
855
- TypeValidationError: TypeValidationError.isInstance(context.error),
856
- NoContentGeneratedError: NoContentGeneratedError.isInstance(
857
- context.error
858
- )
859
- });
860
- return APICallError.isInstance(context.error) || JSONParseError.isInstance(context.error) || TypeValidationError.isInstance(context.error) || NoObjectGeneratedError.isInstance(context.error) || NoOutputGeneratedError.isInstance(context.error) || NoContentGeneratedError.isInstance(context.error);
861
- },
862
- onFailedAttempt(context) {
863
- errors.push(context.error);
864
- }
865
- }
866
- );
867
- }
868
-
869
354
  // packages/text2sql/src/lib/sql.ts
870
355
  var text2SqlValidationMarker = /* @__PURE__ */ Symbol("Text2SqlValidationError");
871
356
  var text2SqlUnknownAdapterMarker = /* @__PURE__ */ Symbol("Text2SqlUnknownAdapterError");
@@ -927,8 +412,7 @@ var Text2Sql = class {
927
412
  this.#config = config;
928
413
  this.#indexer = new AdapterIndexer({
929
414
  adapters: config.adapters,
930
- cache: config.cache,
931
- lock: config.lock
415
+ cache: config.cache
932
416
  });
933
417
  }
934
418
  #requireAdapter(name) {
@@ -938,13 +422,6 @@ var Text2Sql = class {
938
422
  }
939
423
  return adapter;
940
424
  }
941
- #requireModel(op) {
942
- const model = this.#config.model;
943
- if (!model) {
944
- throw new Error(`Text2Sql.${op}() requires a model in Text2SqlConfig`);
945
- }
946
- return model;
947
- }
948
425
  adapterNames() {
949
426
  return Object.keys(this.#config.adapters);
950
427
  }
@@ -1003,18 +480,6 @@ var Text2Sql = class {
1003
480
  onProgress: options.onProgress
1004
481
  });
1005
482
  }
1006
- async toSql(input, adapterName) {
1007
- const adapter = this.#requireAdapter(adapterName);
1008
- const model = this.#requireModel("toSql");
1009
- const fragments = await this.#indexer.indexAdapter(adapterName);
1010
- const result = await toSql({
1011
- input,
1012
- adapter,
1013
- fragments,
1014
- model
1015
- });
1016
- return result.sql;
1017
- }
1018
483
  async toPairs(adapterName, factory) {
1019
484
  const adapter = this.#requireAdapter(adapterName);
1020
485
  const producer = factory(adapter);
@@ -1023,18 +488,18 @@ var Text2Sql = class {
1023
488
  };
1024
489
 
1025
490
  // packages/text2sql/src/bin/command.ts
1026
- import * as path3 from "node:path";
1027
- import z3 from "zod";
1028
- var cliWord = z3.union([z3.string(), z3.number()]).transform(String);
1029
- var cliOptions = z3.record(z3.string(), z3.unknown());
1030
- var queryArgs = z3.tuple([cliWord, z3.array(cliWord)]);
491
+ import * as path2 from "node:path";
492
+ import z2 from "zod";
493
+ var cliWord = z2.union([z2.string(), z2.number()]).transform(String);
494
+ var cliOptions = z2.record(z2.string(), z2.unknown());
495
+ var queryArgs = z2.tuple([cliWord, z2.array(cliWord)]);
1031
496
  var OUT_DIR_OPTION = {
1032
497
  flag: "--out-dir <path>",
1033
498
  description: "Output directory (default: $TEXT2SQL_OUT_DIR or ./sql)"
1034
499
  };
1035
500
  function resolveOutputDir(ctx, options) {
1036
501
  const explicit = typeof options.outDir === "string" ? options.outDir : void 0;
1037
- return path3.resolve(ctx.cwd, explicit ?? ctx.env.TEXT2SQL_OUT_DIR ?? "./sql");
502
+ return path2.resolve(ctx.cwd, explicit ?? ctx.env.TEXT2SQL_OUT_DIR ?? "./sql");
1038
503
  }
1039
504
  var CommandError = class extends Error {
1040
505
  command;
@@ -1087,15 +552,15 @@ var SqlQueryCommand = class extends SqlCommand {
1087
552
  // packages/text2sql/src/bin/commands/index.ts
1088
553
  import { constants, createWriteStream } from "node:fs";
1089
554
  import {
1090
- mkdir as mkdir3,
555
+ mkdir as mkdir2,
1091
556
  open,
1092
557
  stat,
1093
558
  writeFile as writeFile2
1094
559
  } from "node:fs/promises";
1095
- import * as path4 from "node:path";
560
+ import * as path3 from "node:path";
1096
561
  import { finished } from "node:stream/promises";
1097
562
  import { v7 } from "uuid";
1098
- import z4 from "zod";
563
+ import z3 from "zod";
1099
564
 
1100
565
  // packages/text2sql/src/bin/commands/indexing-formatter.ts
1101
566
  var REDUNDANT_MESSAGE_TYPES = /* @__PURE__ */ new Set([
@@ -1116,7 +581,7 @@ function formatPretty(event) {
1116
581
  }
1117
582
 
1118
583
  // packages/text2sql/src/bin/commands/index.ts
1119
- var indexArgs = z4.tuple([z4.array(cliWord)]);
584
+ var indexArgs = z3.tuple([z3.array(cliWord)]);
1120
585
  var IndexCommand = class extends SqlCommand {
1121
586
  name = "index";
1122
587
  description = "Index adapter schemas and write context artifacts";
@@ -1138,13 +603,13 @@ var IndexCommand = class extends SqlCommand {
1138
603
  const verbose = this.resolveVerbose(options.verbose);
1139
604
  const outputDir = resolveOutputDir(ctx, options);
1140
605
  const id = v7();
1141
- const fragmentsPath = path4.join(outputDir, `index-${id}.json`);
606
+ const fragmentsPath = path3.join(outputDir, `index-${id}.json`);
1142
607
  const explicitEventsPath = resolveExplicitEventsPath(ctx);
1143
- const eventsPath = explicitEventsPath ?? path4.join(outputDir, `index-${id}.events.ndjson`);
608
+ const eventsPath = explicitEventsPath ?? path3.join(outputDir, `index-${id}.events.ndjson`);
1144
609
  try {
1145
- await mkdir3(outputDir, { recursive: true });
610
+ await mkdir2(outputDir, { recursive: true });
1146
611
  if (explicitEventsPath) {
1147
- await mkdir3(path4.dirname(explicitEventsPath), { recursive: true });
612
+ await mkdir2(path3.dirname(explicitEventsPath), { recursive: true });
1148
613
  }
1149
614
  const eventsStream = await openEventsWriter(
1150
615
  eventsPath,
@@ -1204,7 +669,7 @@ function createProgressHandler(eventsStream, verbose, stderr) {
1204
669
  }
1205
670
  function resolveExplicitEventsPath(ctx) {
1206
671
  const explicit = ctx.env.TEXT2SQL_INDEX_EVENTS_PATH?.trim();
1207
- return explicit ? path4.resolve(ctx.cwd, explicit) : null;
672
+ return explicit ? path3.resolve(ctx.cwd, explicit) : null;
1208
673
  }
1209
674
  async function openEventsWriter(eventsPath, explicitEventsPath, stderr) {
1210
675
  const probe = explicitEventsPath ? await probeFifoWriter(explicitEventsPath, stderr) : null;
@@ -1269,8 +734,8 @@ function dedupe(values) {
1269
734
  }
1270
735
 
1271
736
  // packages/text2sql/src/bin/commands/run.ts
1272
- import { mkdir as mkdir4, writeFile as writeFile3 } from "node:fs/promises";
1273
- import * as path5 from "node:path";
737
+ import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
738
+ import * as path4 from "node:path";
1274
739
  import { v7 as v72 } from "uuid";
1275
740
  var RunCommand = class extends SqlQueryCommand {
1276
741
  name = "run";
@@ -1281,9 +746,9 @@ var RunCommand = class extends SqlQueryCommand {
1281
746
  async runQuery(ctx, name, sql, options) {
1282
747
  const outputDir = resolveOutputDir(ctx, options);
1283
748
  const { rows, columns } = await ctx.text2Sql.run(name, sql);
1284
- const outPath = path5.join(outputDir, `${v72()}.json`);
749
+ const outPath = path4.join(outputDir, `${v72()}.json`);
1285
750
  try {
1286
- await mkdir4(outputDir, { recursive: true });
751
+ await mkdir3(outputDir, { recursive: true });
1287
752
  await writeFile3(outPath, JSON.stringify(rows, null, 2));
1288
753
  } catch (error) {
1289
754
  this.fail(errorMessage2(error));
@@ -1425,19 +890,12 @@ function resolveIndexCache() {
1425
890
  }
1426
891
  return new FileIndexCache({ dir, namespace });
1427
892
  }
1428
- function resolveIndexLock() {
1429
- return new FileIndexLock({
1430
- dir: process.env.TEXT2SQL_INDEX_LOCK_DIR ?? process.env.TEXT2SQL_INDEX_CACHE_DIR,
1431
- namespace: process.env.TEXT2SQL_INDEX_VERSION
1432
- });
1433
- }
1434
893
  async function runCommand(command, positional, options) {
1435
894
  let ctx;
1436
895
  try {
1437
896
  const text2Sql = new Text2Sql({
1438
897
  adapters: await loadAdapters(),
1439
- cache: resolveIndexCache(),
1440
- lock: resolveIndexLock()
898
+ cache: resolveIndexCache()
1441
899
  });
1442
900
  ctx = {
1443
901
  text2Sql,