@zenera/rag 1.1.34 → 1.1.35

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.
@@ -69,4 +69,19 @@ export interface Journal<S, M, P extends string> {
69
69
  * interleave their store writes and leave one that neither of them describes.
70
70
  */
71
71
  export declare function beginBuild<S, M, P extends string>(plan: BuildPlan<S, M, P>): Journal<S, M, P>;
72
+ export declare function span(ms: number): string;
73
+ /**
74
+ * How long is left at the pace since `first`, or undefined until there is a
75
+ * pace. Measured from the first report rather than the start, so vectors the
76
+ * cache already held do not make the rest look free.
77
+ */
78
+ export declare function remaining(first: {
79
+ at: number;
80
+ done: number;
81
+ }, done: number, total: number, now: number): number | undefined;
82
+ /**
83
+ * The embedding narration. A model that takes one text per request reports
84
+ * once per text, so it is throttled; the last report always gets through.
85
+ */
86
+ export declare function embedReporter(say: (line: string) => void, started: number, clock?: () => number): (done: number, total: number) => void;
72
87
  //# sourceMappingURL=progress.d.ts.map
@@ -142,4 +142,43 @@ export function beginBuild(plan) {
142
142
  function claim(path, lock) {
143
143
  claimLock(path, lock, (held) => new CliError(`this index is already being built (pid ${held.pid}, since ${held.startedAt})`, EXIT.failed, `wait for it, or build elsewhere with --out`));
144
144
  }
145
+ export function span(ms) {
146
+ const seconds = Math.round(ms / 1000);
147
+ if (seconds < 60) {
148
+ return `${seconds}s`;
149
+ }
150
+ const minutes = Math.floor(seconds / 60);
151
+ return minutes < 60
152
+ ? `${minutes}m${seconds % 60}s`
153
+ : `${Math.floor(minutes / 60)}h${minutes % 60}m`;
154
+ }
155
+ /**
156
+ * How long is left at the pace since `first`, or undefined until there is a
157
+ * pace. Measured from the first report rather than the start, so vectors the
158
+ * cache already held do not make the rest look free.
159
+ */
160
+ export function remaining(first, done, total, now) {
161
+ const went = done - first.done;
162
+ const took = now - first.at;
163
+ return went > 0 && took > 0 && done < total ? ((total - done) * took) / went : undefined;
164
+ }
165
+ /**
166
+ * The embedding narration. A model that takes one text per request reports
167
+ * once per text, so it is throttled; the last report always gets through.
168
+ */
169
+ export function embedReporter(say, started, clock = Date.now) {
170
+ let first;
171
+ let saidAt = -Infinity;
172
+ return (done, total) => {
173
+ const now = clock();
174
+ first ??= { at: now, done };
175
+ if (done < total && now - saidAt < INTERVAL_MS) {
176
+ return;
177
+ }
178
+ saidAt = now;
179
+ const left = remaining(first, done, total, now);
180
+ say(` embedded ${done}/${total} · ${Math.floor((done / total) * 100)}% · ${span(now - started)}` +
181
+ (left === undefined ? '' : ` · ~${span(left)} left`));
182
+ };
183
+ }
145
184
  //# sourceMappingURL=progress.js.map
@@ -5,7 +5,7 @@ import { join, relative, resolve } from 'node:path';
5
5
  import { resolveEmbedder } from "../common/embedder.js";
6
6
  import { locateIndex, outputDir } from "../common/locate.js";
7
7
  import { assertSameEmbedding, inspectIndex } from "../common/manifest.js";
8
- import { INTERVAL_MS } from "../common/progress.js";
8
+ import { embedReporter, INTERVAL_MS, span } from "../common/progress.js";
9
9
  import { breakdown, grid, indexing } from "../common/prose.js";
10
10
  import { announce, reportReady, stagingFor, swapIn } from "../common/restore.js";
11
11
  import { assemble, DEFAULT_MAX_LINES } from "./assemble.js";
@@ -282,7 +282,7 @@ async function index(args, ctx) {
282
282
  cacheDir: values['cache-dir'] ? cacheDir : undefined,
283
283
  onReading: loud
284
284
  ? throttled((done, total, pending) => note(dim(` parsed ${done}/${total} · ${Math.floor((done / total) * 100)}% · ` +
285
- `${elapsed(started)}${still(pending)}`)))
285
+ `${span(Date.now() - started)}${still(pending)}`)))
286
286
  : undefined,
287
287
  onRead: loud
288
288
  ? (summary) => {
@@ -296,9 +296,7 @@ async function index(args, ctx) {
296
296
  note(dim(` embedding ${summary.counts.chunks} chunks with ${chosen.id} …`));
297
297
  }
298
298
  : undefined,
299
- onProgress: loud
300
- ? (done, total) => note(dim(` embedded ${done}/${total} · ${Math.floor((done / total) * 100)}% · ${elapsed(started)}`))
301
- : undefined,
299
+ onProgress: loud ? embedReporter((line) => note(dim(line)), started) : undefined,
302
300
  onWriting: loud ? (rows) => notes(indexing(rows, 'chunks').map(aside)) : undefined,
303
301
  });
304
302
  if (ctx.json) {
@@ -376,9 +374,7 @@ async function restore(args, ctx) {
376
374
  dimensions,
377
375
  cache: !values['no-cache'],
378
376
  cacheDir: values['cache-dir'] ? cacheDir : undefined,
379
- onProgress: loud
380
- ? (done, total) => note(dim(` embedded ${done}/${total} · ${Math.floor((done / total) * 100)}% · ${elapsed(started)}`))
381
- : undefined,
377
+ onProgress: loud ? embedReporter((line) => note(dim(line)), started) : undefined,
382
378
  onWriting: loud ? (rows) => notes(indexing(rows, 'chunks').map(aside)) : undefined,
383
379
  });
384
380
  await swapIn(dir, staging);
@@ -428,10 +424,6 @@ function printSources(sources) {
428
424
  note();
429
425
  }
430
426
  const column = (source, at) => [source.lines, source.sections, source.tables, source.chunks][at] ?? 0;
431
- function elapsed(since) {
432
- const seconds = Math.round((Date.now() - since) / 1000);
433
- return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m${seconds % 60}s`;
434
- }
435
427
  /** Reading fires per document, and a line per document is not a narration. */
436
428
  function throttled(say) {
437
429
  let saidAt = 0;
@@ -5,6 +5,7 @@ import { join, relative, resolve } from 'node:path';
5
5
  import { resolveEmbedder } from "../common/embedder.js";
6
6
  import { locateIndex, outputDir } from "../common/locate.js";
7
7
  import { assertSameEmbedding, inspectIndex } from "../common/manifest.js";
8
+ import { embedReporter } from "../common/progress.js";
8
9
  import { breakdown, indexing } from "../common/prose.js";
9
10
  import { announce, reportReady, stagingFor, swapIn } from "../common/restore.js";
10
11
  import { buildIndex } from "./build.js";
@@ -267,9 +268,7 @@ async function index(args, ctx) {
267
268
  note(dim(` embedding ${summary.counts.entities} entities with ${chosen.id} …`));
268
269
  }
269
270
  : undefined,
270
- onProgress: loud
271
- ? (done, total) => note(dim(` embedded ${done}/${total} · ${Math.floor((done / total) * 100)}% · ${elapsed(started)}`))
272
- : undefined,
271
+ onProgress: loud ? embedReporter((line) => note(dim(line)), started) : undefined,
273
272
  onWriting: loud ? (rows) => notes(indexing(rows, 'entities').map(aside)) : undefined,
274
273
  });
275
274
  if (ctx.json) {
@@ -330,9 +329,7 @@ async function restore(args, ctx) {
330
329
  dimensions,
331
330
  cache: !values['no-cache'],
332
331
  cacheDir: values['cache-dir'] ? cacheDir : undefined,
333
- onProgress: loud
334
- ? (done, total) => note(dim(` embedded ${done}/${total} · ${Math.floor((done / total) * 100)}% · ${elapsed(started)}`))
335
- : undefined,
332
+ onProgress: loud ? embedReporter((line) => note(dim(line)), started) : undefined,
336
333
  onWriting: loud ? (rows) => notes(indexing(rows, 'entities').map(aside)) : undefined,
337
334
  });
338
335
  await swapIn(dir, staging);
@@ -373,10 +370,6 @@ async function ready(args, ctx) {
373
370
  reportReady(health, ctx, values.quiet === true);
374
371
  }
375
372
  const HEADERS = ['PATHS', 'OPERATIONS', 'SCHEMAS', 'FIELDS'];
376
- function elapsed(since) {
377
- const seconds = Math.round((Date.now() - since) / 1000);
378
- return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m${seconds % 60}s`;
379
- }
380
373
  function printSources(sources) {
381
374
  const rows = sources.map((s) => ({
382
375
  name: s.file,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenera/rag",
3
- "version": "1.1.34",
3
+ "version": "1.1.35",
4
4
  "description": "Retrieval over a corpus: openapi/swagger documents as a searchable graph, markdown and text as searchable passages.",
5
5
  "keywords": [
6
6
  "agents",
@@ -61,8 +61,8 @@
61
61
  "dependencies": {
62
62
  "@apidevtools/swagger-parser": "^12.0.0",
63
63
  "@lancedb/lancedb": "^0.38.0",
64
- "@zenera/cli": "^1.1.34",
65
- "@zenera/neo": "^1.1.34",
64
+ "@zenera/cli": "^1.1.35",
65
+ "@zenera/neo": "^1.1.35",
66
66
  "graphology": "^0.26.0",
67
67
  "remark-frontmatter": "^5.0.0",
68
68
  "remark-gfm": "^4.0.1",