@deepagents/text2sql 2.3.0 → 3.0.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 +70 -4
- package/dist/bin/sql.js +122 -82
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +81 -48
- package/dist/index.js.map +3 -3
- package/dist/lib/adapter-index.d.ts +4 -2
- package/dist/lib/adapter-index.d.ts.map +1 -1
- package/dist/lib/file-cache.d.ts +2 -2
- package/dist/lib/file-cache.d.ts.map +1 -1
- package/dist/lib/index-cache.d.ts +40 -0
- package/dist/lib/index-cache.d.ts.map +1 -0
- package/dist/lib/index-lock.d.ts +31 -0
- package/dist/lib/index-lock.d.ts.map +1 -0
- package/dist/lib/sql.d.ts +4 -1
- package/dist/lib/sql.d.ts.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -67,7 +67,6 @@ const adapter = new Postgres({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
const text2sql = new Text2Sql({
|
|
70
|
-
version: 'v1',
|
|
71
70
|
model: groq('openai/gpt-oss-20b'),
|
|
72
71
|
adapters: { main: adapter },
|
|
73
72
|
});
|
|
@@ -194,8 +193,12 @@ sandbox was not prepared correctly.
|
|
|
194
193
|
- `--out-dir <path>`: writes artifacts under that path (default:
|
|
195
194
|
`$TEXT2SQL_OUT_DIR` or `./sql`).
|
|
196
195
|
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
The `sql` CLI caches introspected schema only when you opt in via env: set
|
|
197
|
+
`TEXT2SQL_INDEX_CACHE_DIR` (where cache files live) and/or
|
|
198
|
+
`TEXT2SQL_INDEX_VERSION` (an invalidation token — bump it when the schema
|
|
199
|
+
changes). With neither set, every `sql index` introspects fresh. See
|
|
200
|
+
[Schema index caching & coordination](#schema-index-caching--coordination) for
|
|
201
|
+
the underlying injectable primitives.
|
|
199
202
|
|
|
200
203
|
For in-process or virtual-sandbox usage (without installing the package CLI),
|
|
201
204
|
wrap an existing `Text2Sql` instance as a just-bash custom command:
|
|
@@ -328,6 +331,69 @@ for await (const chunk of followUp) {
|
|
|
328
331
|
}
|
|
329
332
|
```
|
|
330
333
|
|
|
334
|
+
## Schema index caching & coordination
|
|
335
|
+
|
|
336
|
+
Schema introspection is the expensive part of indexing. `Text2Sql` and
|
|
337
|
+
`AdapterIndexer` own no storage and no locking — you inject both as optional
|
|
338
|
+
primitives, keyed per adapter:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
export interface IndexCache {
|
|
342
|
+
read(key: string): Promise<ContextFragment[] | null>;
|
|
343
|
+
write(key: string, fragments: ContextFragment[]): Promise<void>;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export interface IndexLock {
|
|
347
|
+
// Acquire (waiting if held elsewhere), run fn, release — even on throw.
|
|
348
|
+
// Reject if the lock can't be acquired (callers fail closed).
|
|
349
|
+
run<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
- **No `cache`** → every `index()` introspects fresh (no cache events).
|
|
354
|
+
- **`cache` only** → warm reads skip introspection within whatever the cache
|
|
355
|
+
spans.
|
|
356
|
+
- **`cache` + `lock`** → introspection is single-flight: concurrent callers for
|
|
357
|
+
the same adapter wait on the lock, then read the warm cache.
|
|
358
|
+
|
|
359
|
+
A file-backed `IndexCache` ships as a composable primitive:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { FileIndexCache, Text2Sql } from '@deepagents/text2sql';
|
|
363
|
+
|
|
364
|
+
const text2sql = new Text2Sql({
|
|
365
|
+
model,
|
|
366
|
+
adapters: { main: adapter },
|
|
367
|
+
// Point `dir` at a shared volume to share one cache across processes.
|
|
368
|
+
cache: new FileIndexCache({ dir: '/var/cache/text2sql', namespace: 'v1' }),
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
`FileIndexCache` writes atomically (temp + rename) and treats an unparseable
|
|
373
|
+
file as a miss, so a torn read self-heals into a re-introspect.
|
|
374
|
+
|
|
375
|
+
### Horizontally-scaled deployments
|
|
376
|
+
|
|
377
|
+
Running many processes/containers against one database (e.g. a fleet of
|
|
378
|
+
daemons)? Two coordination tiers:
|
|
379
|
+
|
|
380
|
+
- The **lock alone** serializes introspection — never two concurrent
|
|
381
|
+
introspections of the same database (caps peak DB load). Back it with a
|
|
382
|
+
distributed lock you already operate (a Redis lock, a Postgres
|
|
383
|
+
`pg_advisory_lock`, etc.).
|
|
384
|
+
- The **lock plus a shared cache directory** gives fleet-wide single-flight:
|
|
385
|
+
the lock holder writes the cache on the shared volume, and every waiter reads
|
|
386
|
+
it instead of re-introspecting.
|
|
387
|
+
|
|
388
|
+
> On object-storage-backed volumes (GCS/S3 FUSE) `rename` is not atomic and
|
|
389
|
+
> file locks are unreliable — that is exactly why the lock and cache are
|
|
390
|
+
> injectable. Use a real distributed lock; the `FileIndexCache` parse-as-miss
|
|
391
|
+
> behavior plus the post-lock recheck tolerate a non-atomic write.
|
|
392
|
+
|
|
393
|
+
See `demo/text2sql-daemon` for a daemon that injects a `FileIndexCache`
|
|
394
|
+
(`TEXT2SQL_INDEX_CACHE_DIR` / `TEXT2SQL_INDEX_VERSION`) and a `pg_advisory_lock`
|
|
395
|
+
backed `IndexLock`.
|
|
396
|
+
|
|
331
397
|
## Streaming Index Progress
|
|
332
398
|
|
|
333
399
|
`AdapterIndexer#index({ onProgress })` emits progress events while it warms or reads
|
|
@@ -343,7 +409,7 @@ import {
|
|
|
343
409
|
type Text2SqlIndexProgressEvent,
|
|
344
410
|
} from '@deepagents/text2sql';
|
|
345
411
|
|
|
346
|
-
const indexer = new AdapterIndexer({ adapters
|
|
412
|
+
const indexer = new AdapterIndexer({ adapters });
|
|
347
413
|
|
|
348
414
|
await context.continue(user('Show me top 10 customers'));
|
|
349
415
|
|
package/dist/bin/sql.js
CHANGED
|
@@ -3,6 +3,71 @@
|
|
|
3
3
|
// packages/text2sql/src/bin/sql.ts
|
|
4
4
|
import { cac } from "cac";
|
|
5
5
|
|
|
6
|
+
// packages/text2sql/src/lib/file-cache.ts
|
|
7
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
var FileCache = class {
|
|
13
|
+
path;
|
|
14
|
+
constructor(watermark, extension = ".txt", baseDir = tmpdir()) {
|
|
15
|
+
const hash = createHash("md5").update(watermark).digest("hex");
|
|
16
|
+
this.path = path.join(baseDir, `text2sql-${hash}${extension}`);
|
|
17
|
+
}
|
|
18
|
+
async get() {
|
|
19
|
+
if (existsSync(this.path)) {
|
|
20
|
+
return readFile(this.path, "utf-8");
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
async set(content) {
|
|
25
|
+
await mkdir(path.dirname(this.path), { recursive: true });
|
|
26
|
+
const tempPath = `${this.path}.${randomUUID()}.tmp`;
|
|
27
|
+
await writeFile(tempPath, content, "utf-8");
|
|
28
|
+
await rename(tempPath, this.path);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var JsonCache = class extends FileCache {
|
|
32
|
+
constructor(watermark, baseDir) {
|
|
33
|
+
super(watermark, ".json", baseDir);
|
|
34
|
+
}
|
|
35
|
+
async read() {
|
|
36
|
+
const content = await this.get();
|
|
37
|
+
if (!content) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(content);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
write(data) {
|
|
47
|
+
return this.set(JSON.stringify(data));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// packages/text2sql/src/lib/index-cache.ts
|
|
52
|
+
var FileIndexCache = class {
|
|
53
|
+
#dir;
|
|
54
|
+
#namespace;
|
|
55
|
+
constructor(options = {}) {
|
|
56
|
+
this.#dir = options.dir;
|
|
57
|
+
this.#namespace = options.namespace;
|
|
58
|
+
}
|
|
59
|
+
read(key) {
|
|
60
|
+
return this.#cacheFor(key).read();
|
|
61
|
+
}
|
|
62
|
+
write(key, fragments) {
|
|
63
|
+
return this.#cacheFor(key).write(fragments);
|
|
64
|
+
}
|
|
65
|
+
#cacheFor(key) {
|
|
66
|
+
const watermark = this.#namespace ? `${this.#namespace}-${key}` : key;
|
|
67
|
+
return new JsonCache(watermark, this.#dir);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
6
71
|
// packages/text2sql/src/lib/sql.ts
|
|
7
72
|
import "@deepagents/agent";
|
|
8
73
|
|
|
@@ -120,44 +185,6 @@ function buildScopeParseErrorPayload(sql, dialect, error) {
|
|
|
120
185
|
};
|
|
121
186
|
}
|
|
122
187
|
|
|
123
|
-
// packages/text2sql/src/lib/file-cache.ts
|
|
124
|
-
import { createHash } from "node:crypto";
|
|
125
|
-
import { existsSync } from "node:fs";
|
|
126
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
127
|
-
import { tmpdir } from "node:os";
|
|
128
|
-
import path from "node:path";
|
|
129
|
-
var FileCache = class {
|
|
130
|
-
path;
|
|
131
|
-
constructor(watermark, extension = ".txt") {
|
|
132
|
-
const hash = createHash("md5").update(watermark).digest("hex");
|
|
133
|
-
this.path = path.join(tmpdir(), `text2sql-${hash}${extension}`);
|
|
134
|
-
}
|
|
135
|
-
async get() {
|
|
136
|
-
if (existsSync(this.path)) {
|
|
137
|
-
return readFile(this.path, "utf-8");
|
|
138
|
-
}
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
set(content) {
|
|
142
|
-
return writeFile(this.path, content, "utf-8");
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
var JsonCache = class extends FileCache {
|
|
146
|
-
constructor(watermark) {
|
|
147
|
-
super(watermark, ".json");
|
|
148
|
-
}
|
|
149
|
-
async read() {
|
|
150
|
-
const content = await this.get();
|
|
151
|
-
if (content) {
|
|
152
|
-
return JSON.parse(content);
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
write(data) {
|
|
157
|
-
return this.set(JSON.stringify(data));
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
188
|
// packages/text2sql/src/lib/adapter-index.ts
|
|
162
189
|
var noopProgressHandler = () => {
|
|
163
190
|
};
|
|
@@ -172,8 +199,8 @@ function timestampProgressHandler(onProgress) {
|
|
|
172
199
|
}
|
|
173
200
|
var AdapterIndexer = class {
|
|
174
201
|
#adapters;
|
|
175
|
-
#
|
|
176
|
-
#
|
|
202
|
+
#cache;
|
|
203
|
+
#lock;
|
|
177
204
|
constructor(options) {
|
|
178
205
|
const adapterNames = Object.keys(options.adapters);
|
|
179
206
|
if (adapterNames.length === 0) {
|
|
@@ -181,8 +208,8 @@ var AdapterIndexer = class {
|
|
|
181
208
|
}
|
|
182
209
|
validateAdapterNames(adapterNames);
|
|
183
210
|
this.#adapters = options.adapters;
|
|
184
|
-
this.#
|
|
185
|
-
this.#
|
|
211
|
+
this.#cache = options.cache;
|
|
212
|
+
this.#lock = options.lock;
|
|
186
213
|
}
|
|
187
214
|
async index(options = {}) {
|
|
188
215
|
const progress = timestampProgressHandler(options.onProgress);
|
|
@@ -239,17 +266,42 @@ var AdapterIndexer = class {
|
|
|
239
266
|
adapter: name,
|
|
240
267
|
message: `Indexing adapter "${name}"...`
|
|
241
268
|
});
|
|
242
|
-
const
|
|
243
|
-
const
|
|
269
|
+
const cache = this.#cache;
|
|
270
|
+
const key = name;
|
|
271
|
+
const emitCacheHit = () => progress({
|
|
272
|
+
type: "adapter:cache-hit",
|
|
273
|
+
adapter: name,
|
|
274
|
+
message: `Using cached index for adapter "${name}".`,
|
|
275
|
+
cached: true
|
|
276
|
+
});
|
|
277
|
+
const emitCacheMiss = () => progress({
|
|
278
|
+
type: "adapter:cache-miss",
|
|
279
|
+
adapter: name,
|
|
280
|
+
message: `No cached index for adapter "${name}".`,
|
|
281
|
+
cached: false
|
|
282
|
+
});
|
|
283
|
+
const introspect = async () => {
|
|
284
|
+
const ctx = createGroundingContext({
|
|
285
|
+
onProgress: (event) => progress(adapterProgressEvent(name, event))
|
|
286
|
+
});
|
|
287
|
+
const fragments = await adapter.introspect(ctx);
|
|
288
|
+
await cache?.write(key, fragments);
|
|
289
|
+
return fragments;
|
|
290
|
+
};
|
|
291
|
+
let servedFromCache = false;
|
|
292
|
+
const populate = async () => {
|
|
293
|
+
const recheck = await cache?.read(key);
|
|
294
|
+
if (recheck) {
|
|
295
|
+
servedFromCache = true;
|
|
296
|
+
return recheck;
|
|
297
|
+
}
|
|
298
|
+
if (cache) emitCacheMiss();
|
|
299
|
+
return introspect();
|
|
300
|
+
};
|
|
244
301
|
try {
|
|
245
|
-
const cached = await cache?.read();
|
|
302
|
+
const cached = await cache?.read(key);
|
|
246
303
|
if (cached) {
|
|
247
|
-
|
|
248
|
-
type: "adapter:cache-hit",
|
|
249
|
-
adapter: name,
|
|
250
|
-
message: `Using cached index for adapter "${name}".`,
|
|
251
|
-
cached: true
|
|
252
|
-
});
|
|
304
|
+
emitCacheHit();
|
|
253
305
|
progress({
|
|
254
306
|
type: "adapter:end",
|
|
255
307
|
adapter: name,
|
|
@@ -258,24 +310,13 @@ var AdapterIndexer = class {
|
|
|
258
310
|
});
|
|
259
311
|
return cached;
|
|
260
312
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
type: "adapter:cache-miss",
|
|
264
|
-
adapter: name,
|
|
265
|
-
message: `No cached index for adapter "${name}".`,
|
|
266
|
-
cached: false
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
const ctx = createGroundingContext({
|
|
270
|
-
onProgress: (event) => progress(adapterProgressEvent(name, event))
|
|
271
|
-
});
|
|
272
|
-
const fragments = await adapter.introspect(ctx);
|
|
273
|
-
await cache?.write(fragments);
|
|
313
|
+
const fragments = this.#lock ? await this.#lock.run(key, populate) : await populate();
|
|
314
|
+
if (servedFromCache) emitCacheHit();
|
|
274
315
|
progress({
|
|
275
316
|
type: "adapter:end",
|
|
276
317
|
adapter: name,
|
|
277
318
|
message: `Finished indexing adapter "${name}".`,
|
|
278
|
-
cached:
|
|
319
|
+
cached: servedFromCache
|
|
279
320
|
});
|
|
280
321
|
return fragments;
|
|
281
322
|
} catch (error) {
|
|
@@ -302,16 +343,6 @@ var AdapterIndexer = class {
|
|
|
302
343
|
return [name, adapter];
|
|
303
344
|
});
|
|
304
345
|
}
|
|
305
|
-
#adapterCacheKey(name) {
|
|
306
|
-
const configured = this.#cacheKey?.(name);
|
|
307
|
-
if (configured !== void 0) {
|
|
308
|
-
return configured;
|
|
309
|
-
}
|
|
310
|
-
if (this.#version) {
|
|
311
|
-
return `index-${this.#version}-${name}`;
|
|
312
|
-
}
|
|
313
|
-
return void 0;
|
|
314
|
-
}
|
|
315
346
|
};
|
|
316
347
|
function adapterProgressEvent(adapter, progress) {
|
|
317
348
|
return {
|
|
@@ -824,7 +855,8 @@ var Text2Sql = class {
|
|
|
824
855
|
this.#config = config;
|
|
825
856
|
this.#indexer = new AdapterIndexer({
|
|
826
857
|
adapters: config.adapters,
|
|
827
|
-
|
|
858
|
+
cache: config.cache,
|
|
859
|
+
lock: config.lock
|
|
828
860
|
});
|
|
829
861
|
}
|
|
830
862
|
#requireAdapter(name) {
|
|
@@ -959,7 +991,7 @@ var SqlQueryCommand = class extends SqlCommand {
|
|
|
959
991
|
// packages/text2sql/src/bin/commands/index.ts
|
|
960
992
|
import { constants, createWriteStream } from "node:fs";
|
|
961
993
|
import {
|
|
962
|
-
mkdir,
|
|
994
|
+
mkdir as mkdir2,
|
|
963
995
|
open,
|
|
964
996
|
stat,
|
|
965
997
|
writeFile as writeFile2
|
|
@@ -1012,9 +1044,9 @@ var IndexCommand = class extends SqlCommand {
|
|
|
1012
1044
|
const explicitEventsPath = resolveExplicitEventsPath(ctx);
|
|
1013
1045
|
const eventsPath = explicitEventsPath ?? path3.join(outputDir, `index-${id}.events.ndjson`);
|
|
1014
1046
|
try {
|
|
1015
|
-
await
|
|
1047
|
+
await mkdir2(outputDir, { recursive: true });
|
|
1016
1048
|
if (explicitEventsPath) {
|
|
1017
|
-
await
|
|
1049
|
+
await mkdir2(path3.dirname(explicitEventsPath), { recursive: true });
|
|
1018
1050
|
}
|
|
1019
1051
|
const eventsStream = await openEventsWriter(
|
|
1020
1052
|
eventsPath,
|
|
@@ -1145,7 +1177,7 @@ function dedupe(values) {
|
|
|
1145
1177
|
}
|
|
1146
1178
|
|
|
1147
1179
|
// packages/text2sql/src/bin/commands/run.ts
|
|
1148
|
-
import { mkdir as
|
|
1180
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
1149
1181
|
import * as path4 from "node:path";
|
|
1150
1182
|
import { v7 as v72 } from "uuid";
|
|
1151
1183
|
var RunCommand = class extends SqlQueryCommand {
|
|
@@ -1159,7 +1191,7 @@ var RunCommand = class extends SqlQueryCommand {
|
|
|
1159
1191
|
const { rows, columns } = await ctx.text2Sql.run(name, sql);
|
|
1160
1192
|
const outPath = path4.join(outputDir, `${v72()}.json`);
|
|
1161
1193
|
try {
|
|
1162
|
-
await
|
|
1194
|
+
await mkdir3(outputDir, { recursive: true });
|
|
1163
1195
|
await writeFile3(outPath, JSON.stringify(rows, null, 2));
|
|
1164
1196
|
} catch (error) {
|
|
1165
1197
|
this.fail(errorMessage2(error));
|
|
@@ -1296,12 +1328,20 @@ function registerCommand(cli2, command) {
|
|
|
1296
1328
|
return runCommand(command, positional, options);
|
|
1297
1329
|
});
|
|
1298
1330
|
}
|
|
1331
|
+
function resolveIndexCache() {
|
|
1332
|
+
const dir = process.env.TEXT2SQL_INDEX_CACHE_DIR;
|
|
1333
|
+
const namespace = process.env.TEXT2SQL_INDEX_VERSION;
|
|
1334
|
+
if (!dir && !namespace) {
|
|
1335
|
+
return void 0;
|
|
1336
|
+
}
|
|
1337
|
+
return new FileIndexCache({ dir, namespace });
|
|
1338
|
+
}
|
|
1299
1339
|
async function runCommand(command, positional, options) {
|
|
1300
1340
|
let ctx;
|
|
1301
1341
|
try {
|
|
1302
1342
|
const text2Sql = new Text2Sql({
|
|
1303
1343
|
adapters: await loadAdapters(),
|
|
1304
|
-
|
|
1344
|
+
cache: resolveIndexCache()
|
|
1305
1345
|
});
|
|
1306
1346
|
ctx = {
|
|
1307
1347
|
text2Sql,
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from './lib/agents/suggestions.agents.ts';
|
|
|
6
6
|
export * from './lib/adapter-index.ts';
|
|
7
7
|
export * from './lib/checkpoint.ts';
|
|
8
8
|
export * from './lib/file-cache.ts';
|
|
9
|
+
export * from './lib/index-cache.ts';
|
|
10
|
+
export * from './lib/index-lock.ts';
|
|
9
11
|
export * from './lib/fragments/schema.ts';
|
|
10
12
|
export * from './lib/fs/index.ts';
|
|
11
13
|
export * from './lib/instructions.ts';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1301,16 +1301,16 @@ var suggestionsAgent = agent({
|
|
|
1301
1301
|
import { fragment as fragment2 } from "@deepagents/context";
|
|
1302
1302
|
|
|
1303
1303
|
// packages/text2sql/src/lib/file-cache.ts
|
|
1304
|
-
import { createHash } from "node:crypto";
|
|
1304
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
1305
1305
|
import { existsSync } from "node:fs";
|
|
1306
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
1306
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
1307
1307
|
import { tmpdir } from "node:os";
|
|
1308
1308
|
import path from "node:path";
|
|
1309
1309
|
var FileCache = class {
|
|
1310
1310
|
path;
|
|
1311
|
-
constructor(watermark, extension = ".txt") {
|
|
1311
|
+
constructor(watermark, extension = ".txt", baseDir = tmpdir()) {
|
|
1312
1312
|
const hash2 = createHash("md5").update(watermark).digest("hex");
|
|
1313
|
-
this.path = path.join(
|
|
1313
|
+
this.path = path.join(baseDir, `text2sql-${hash2}${extension}`);
|
|
1314
1314
|
}
|
|
1315
1315
|
async get() {
|
|
1316
1316
|
if (existsSync(this.path)) {
|
|
@@ -1318,26 +1318,53 @@ var FileCache = class {
|
|
|
1318
1318
|
}
|
|
1319
1319
|
return null;
|
|
1320
1320
|
}
|
|
1321
|
-
set(content) {
|
|
1322
|
-
|
|
1321
|
+
async set(content) {
|
|
1322
|
+
await mkdir(path.dirname(this.path), { recursive: true });
|
|
1323
|
+
const tempPath = `${this.path}.${randomUUID()}.tmp`;
|
|
1324
|
+
await writeFile(tempPath, content, "utf-8");
|
|
1325
|
+
await rename(tempPath, this.path);
|
|
1323
1326
|
}
|
|
1324
1327
|
};
|
|
1325
1328
|
var JsonCache = class extends FileCache {
|
|
1326
|
-
constructor(watermark) {
|
|
1327
|
-
super(watermark, ".json");
|
|
1329
|
+
constructor(watermark, baseDir) {
|
|
1330
|
+
super(watermark, ".json", baseDir);
|
|
1328
1331
|
}
|
|
1329
1332
|
async read() {
|
|
1330
1333
|
const content = await this.get();
|
|
1331
|
-
if (content) {
|
|
1334
|
+
if (!content) {
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1337
|
+
try {
|
|
1332
1338
|
return JSON.parse(content);
|
|
1339
|
+
} catch {
|
|
1340
|
+
return null;
|
|
1333
1341
|
}
|
|
1334
|
-
return null;
|
|
1335
1342
|
}
|
|
1336
1343
|
write(data) {
|
|
1337
1344
|
return this.set(JSON.stringify(data));
|
|
1338
1345
|
}
|
|
1339
1346
|
};
|
|
1340
1347
|
|
|
1348
|
+
// packages/text2sql/src/lib/index-cache.ts
|
|
1349
|
+
var FileIndexCache = class {
|
|
1350
|
+
#dir;
|
|
1351
|
+
#namespace;
|
|
1352
|
+
constructor(options = {}) {
|
|
1353
|
+
this.#dir = options.dir;
|
|
1354
|
+
this.#namespace = options.namespace;
|
|
1355
|
+
}
|
|
1356
|
+
read(key) {
|
|
1357
|
+
return this.#cacheFor(key).read();
|
|
1358
|
+
}
|
|
1359
|
+
write(key, fragments) {
|
|
1360
|
+
return this.#cacheFor(key).write(fragments);
|
|
1361
|
+
}
|
|
1362
|
+
#cacheFor(key) {
|
|
1363
|
+
const watermark = this.#namespace ? `${this.#namespace}-${key}` : key;
|
|
1364
|
+
return new JsonCache(watermark, this.#dir);
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1341
1368
|
// packages/text2sql/src/lib/adapter-index.ts
|
|
1342
1369
|
var TEXT2SQL_INDEX_PROGRESS_CHUNK = "data-text2sql-index-progress";
|
|
1343
1370
|
var noopProgressHandler = () => {
|
|
@@ -1353,8 +1380,8 @@ function timestampProgressHandler(onProgress) {
|
|
|
1353
1380
|
}
|
|
1354
1381
|
var AdapterIndexer = class {
|
|
1355
1382
|
#adapters;
|
|
1356
|
-
#
|
|
1357
|
-
#
|
|
1383
|
+
#cache;
|
|
1384
|
+
#lock;
|
|
1358
1385
|
constructor(options) {
|
|
1359
1386
|
const adapterNames = Object.keys(options.adapters);
|
|
1360
1387
|
if (adapterNames.length === 0) {
|
|
@@ -1362,8 +1389,8 @@ var AdapterIndexer = class {
|
|
|
1362
1389
|
}
|
|
1363
1390
|
validateAdapterNames(adapterNames);
|
|
1364
1391
|
this.#adapters = options.adapters;
|
|
1365
|
-
this.#
|
|
1366
|
-
this.#
|
|
1392
|
+
this.#cache = options.cache;
|
|
1393
|
+
this.#lock = options.lock;
|
|
1367
1394
|
}
|
|
1368
1395
|
async index(options = {}) {
|
|
1369
1396
|
const progress = timestampProgressHandler(options.onProgress);
|
|
@@ -1420,17 +1447,42 @@ var AdapterIndexer = class {
|
|
|
1420
1447
|
adapter: name,
|
|
1421
1448
|
message: `Indexing adapter "${name}"...`
|
|
1422
1449
|
});
|
|
1423
|
-
const
|
|
1424
|
-
const
|
|
1450
|
+
const cache = this.#cache;
|
|
1451
|
+
const key = name;
|
|
1452
|
+
const emitCacheHit = () => progress({
|
|
1453
|
+
type: "adapter:cache-hit",
|
|
1454
|
+
adapter: name,
|
|
1455
|
+
message: `Using cached index for adapter "${name}".`,
|
|
1456
|
+
cached: true
|
|
1457
|
+
});
|
|
1458
|
+
const emitCacheMiss = () => progress({
|
|
1459
|
+
type: "adapter:cache-miss",
|
|
1460
|
+
adapter: name,
|
|
1461
|
+
message: `No cached index for adapter "${name}".`,
|
|
1462
|
+
cached: false
|
|
1463
|
+
});
|
|
1464
|
+
const introspect = async () => {
|
|
1465
|
+
const ctx = createGroundingContext({
|
|
1466
|
+
onProgress: (event) => progress(adapterProgressEvent(name, event))
|
|
1467
|
+
});
|
|
1468
|
+
const fragments = await adapter.introspect(ctx);
|
|
1469
|
+
await cache?.write(key, fragments);
|
|
1470
|
+
return fragments;
|
|
1471
|
+
};
|
|
1472
|
+
let servedFromCache = false;
|
|
1473
|
+
const populate = async () => {
|
|
1474
|
+
const recheck = await cache?.read(key);
|
|
1475
|
+
if (recheck) {
|
|
1476
|
+
servedFromCache = true;
|
|
1477
|
+
return recheck;
|
|
1478
|
+
}
|
|
1479
|
+
if (cache) emitCacheMiss();
|
|
1480
|
+
return introspect();
|
|
1481
|
+
};
|
|
1425
1482
|
try {
|
|
1426
|
-
const cached = await cache?.read();
|
|
1483
|
+
const cached = await cache?.read(key);
|
|
1427
1484
|
if (cached) {
|
|
1428
|
-
|
|
1429
|
-
type: "adapter:cache-hit",
|
|
1430
|
-
adapter: name,
|
|
1431
|
-
message: `Using cached index for adapter "${name}".`,
|
|
1432
|
-
cached: true
|
|
1433
|
-
});
|
|
1485
|
+
emitCacheHit();
|
|
1434
1486
|
progress({
|
|
1435
1487
|
type: "adapter:end",
|
|
1436
1488
|
adapter: name,
|
|
@@ -1439,24 +1491,13 @@ var AdapterIndexer = class {
|
|
|
1439
1491
|
});
|
|
1440
1492
|
return cached;
|
|
1441
1493
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
type: "adapter:cache-miss",
|
|
1445
|
-
adapter: name,
|
|
1446
|
-
message: `No cached index for adapter "${name}".`,
|
|
1447
|
-
cached: false
|
|
1448
|
-
});
|
|
1449
|
-
}
|
|
1450
|
-
const ctx = createGroundingContext({
|
|
1451
|
-
onProgress: (event) => progress(adapterProgressEvent(name, event))
|
|
1452
|
-
});
|
|
1453
|
-
const fragments = await adapter.introspect(ctx);
|
|
1454
|
-
await cache?.write(fragments);
|
|
1494
|
+
const fragments = this.#lock ? await this.#lock.run(key, populate) : await populate();
|
|
1495
|
+
if (servedFromCache) emitCacheHit();
|
|
1455
1496
|
progress({
|
|
1456
1497
|
type: "adapter:end",
|
|
1457
1498
|
adapter: name,
|
|
1458
1499
|
message: `Finished indexing adapter "${name}".`,
|
|
1459
|
-
cached:
|
|
1500
|
+
cached: servedFromCache
|
|
1460
1501
|
});
|
|
1461
1502
|
return fragments;
|
|
1462
1503
|
} catch (error) {
|
|
@@ -1483,16 +1524,6 @@ var AdapterIndexer = class {
|
|
|
1483
1524
|
return [name, adapter];
|
|
1484
1525
|
});
|
|
1485
1526
|
}
|
|
1486
|
-
#adapterCacheKey(name) {
|
|
1487
|
-
const configured = this.#cacheKey?.(name);
|
|
1488
|
-
if (configured !== void 0) {
|
|
1489
|
-
return configured;
|
|
1490
|
-
}
|
|
1491
|
-
if (this.#version) {
|
|
1492
|
-
return `index-${this.#version}-${name}`;
|
|
1493
|
-
}
|
|
1494
|
-
return void 0;
|
|
1495
|
-
}
|
|
1496
1527
|
};
|
|
1497
1528
|
function adapterProgressEvent(adapter, progress) {
|
|
1498
1529
|
return {
|
|
@@ -5580,7 +5611,8 @@ var Text2Sql = class {
|
|
|
5580
5611
|
this.#config = config;
|
|
5581
5612
|
this.#indexer = new AdapterIndexer({
|
|
5582
5613
|
adapters: config.adapters,
|
|
5583
|
-
|
|
5614
|
+
cache: config.cache,
|
|
5615
|
+
lock: config.lock
|
|
5584
5616
|
});
|
|
5585
5617
|
}
|
|
5586
5618
|
#requireAdapter(name) {
|
|
@@ -5790,6 +5822,7 @@ export {
|
|
|
5790
5822
|
AdapterIndexer,
|
|
5791
5823
|
Checkpoint,
|
|
5792
5824
|
FileCache,
|
|
5825
|
+
FileIndexCache,
|
|
5793
5826
|
JsonCache,
|
|
5794
5827
|
MssqlFs,
|
|
5795
5828
|
Point,
|