@zenera/rag 1.1.5 → 1.1.8

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 (41) hide show
  1. package/README.md +68 -6
  2. package/dist/command.js +41 -674
  3. package/dist/common/embedder.d.ts +3 -0
  4. package/dist/common/embedder.js +64 -0
  5. package/dist/common/locate.d.ts +18 -0
  6. package/dist/common/locate.js +155 -0
  7. package/dist/common/manifest.d.ts +50 -0
  8. package/dist/common/manifest.js +62 -0
  9. package/dist/{schema → common}/match.d.ts +4 -0
  10. package/dist/{schema → common}/match.js +7 -0
  11. package/dist/common/progress.d.ts +57 -0
  12. package/dist/common/progress.js +155 -0
  13. package/dist/common/prose.d.ts +13 -0
  14. package/dist/common/prose.js +56 -0
  15. package/dist/index.d.ts +6 -3
  16. package/dist/index.js +6 -3
  17. package/dist/schema/build.js +6 -2
  18. package/dist/schema/command.d.ts +3 -0
  19. package/dist/schema/command.js +819 -0
  20. package/dist/schema/files.d.ts +5 -27
  21. package/dist/schema/files.js +9 -29
  22. package/dist/schema/lookup.d.ts +12 -2
  23. package/dist/schema/lookup.js +29 -2
  24. package/dist/{present.d.ts → schema/present.d.ts} +5 -5
  25. package/dist/{present.js → schema/present.js} +2 -2
  26. package/dist/{query.d.ts → schema/query.d.ts} +1 -1
  27. package/dist/schema/readme.d.ts +6 -0
  28. package/dist/schema/readme.js +122 -0
  29. package/dist/schema/render.d.ts +8 -0
  30. package/dist/schema/render.js +12 -2
  31. package/dist/{repl.d.ts → schema/repl.d.ts} +1 -1
  32. package/dist/schema/search.js +2 -1
  33. package/dist/schema/tools.d.ts +3 -1
  34. package/dist/schema/tools.js +152 -19
  35. package/dist/schema/trace.d.ts +52 -0
  36. package/dist/schema/trace.js +144 -0
  37. package/package.json +3 -3
  38. package/dist/schema/progress.d.ts +0 -26
  39. package/dist/schema/progress.js +0 -316
  40. /package/dist/{query.js → schema/query.js} +0 -0
  41. /package/dist/{repl.js → schema/repl.js} +0 -0
@@ -1,15 +1,17 @@
1
1
  import { tool } from '@zenera/neo';
2
- import { FORMATS, isFormat, present } from "../present.js";
3
- import { isEmpty, parseQuery, QueryError } from "../query.js";
2
+ import { loose, matcher, PatternError } from "../common/match.js";
3
+ import { FORMATS, isFormat, present } from "./present.js";
4
+ import { isEmpty, parseQuery, QueryError } from "./query.js";
4
5
  import { toTypeScript } from "./hydrate.js";
5
6
  import { fields, grepNodes, listNodes, propertyCount } from "./lookup.js";
6
- import { loose, matcher, PatternError } from "./match.js";
7
+ import { sourceTag } from "./render.js";
7
8
  import { stitch } from "./subgraph.js";
9
+ import { chainOf, traceNodes } from "./trace.js";
8
10
  // ---------------------------------------------------------------------------
9
11
  // The same index, given to an agent
10
12
  //
11
- // Five tools over one engine, and only one of them ranks anything. `search_api`
12
- // is the way in when the question is vague; the other four are exact, because
13
+ // Six tools over one engine, and only one of them ranks anything. `search_api`
14
+ // is the way in when the question is vague; the other five are exact, because
13
15
  // a model that has been told "no results" by a vector search has learned
14
16
  // nothing — a ranking returns the top of a list, so an empty answer and an
15
17
  // absent thing look identical.
@@ -19,6 +21,12 @@ import { stitch } from "./subgraph.js";
19
21
  // model does not need to be reminded what a password is — it needs the list of
20
22
  // types that have one. `grep_api` is the same instinct widened: every literal
21
23
  // occurrence, counted in full, so "it is not there" can actually be concluded.
24
+ //
25
+ // `trace_api` is the other direction entirely. Having found a field, the next
26
+ // question is always which call carries it. `search_api` stitches part of the
27
+ // way there, but only between the nodes that ranked — and the operation and
28
+ // the field usually share no word at all, which is why the edge between them
29
+ // was built. `trace_api` follows that edge instead of ranking anything.
22
30
  // ---------------------------------------------------------------------------
23
31
  const GROUP = 'schema';
24
32
  /** Kept small on purpose: a tool result is prompt, and the model asked for one thing. */
@@ -28,9 +36,17 @@ const MAX_CANDIDATES = 25;
28
36
  /** A listing is lines rather than subgraphs, so it can afford more of them. */
29
37
  const DEFAULT_ROWS = 50;
30
38
  const MAX_ROWS = 200;
39
+ /** A trace is a paragraph per node, so fewer of them, and fewer routes each. */
40
+ const DEFAULT_TRACES = 5;
41
+ const MAX_TRACES = 25;
42
+ const DEFAULT_ROUTES = 10;
31
43
  export function schemaTools(index, options = {}) {
32
44
  const fallback = options.format ?? 'text';
33
45
  const docs = options.docs ?? true;
46
+ // With one document there is nothing to disambiguate and naming it on every
47
+ // line is prompt spent saying the same word; with several it is the only
48
+ // way to tell two revisions of one API apart.
49
+ const source = options.source ?? index.manifest.sources.length > 1;
34
50
  const searchApi = tool({
35
51
  name: 'search_api',
36
52
  group: GROUP,
@@ -107,7 +123,10 @@ export function schemaTools(index, options = {}) {
107
123
  found: result.subgraphs.length,
108
124
  ids: result.subgraphs.flatMap((s) => s.hits),
109
125
  truncated: result.subgraphs.some((s) => s.truncated),
110
- api: await present(index, result.subgraphs, chosen(format, fallback), { docs }),
126
+ api: await present(index, result.subgraphs, chosen(format, fallback), {
127
+ docs,
128
+ source,
129
+ }),
111
130
  };
112
131
  },
113
132
  });
@@ -209,12 +228,24 @@ export function schemaTools(index, options = {}) {
209
228
  description: 'Match the name. A plain word matches anywhere in it; use * and ? ' +
210
229
  'to match the whole name, e.g. "*Password*".',
211
230
  },
212
- path: { type: 'string', description: 'Match the route, e.g. "/users*".' },
231
+ path: {
232
+ type: 'string',
233
+ description: 'Match the route, e.g. "/users*". Keeps operations and their ' +
234
+ 'parameters; a schema sits on no one route, so it is left out.',
235
+ },
236
+ regex: {
237
+ type: 'boolean',
238
+ description: 'Read `name` and `path` as regular expressions instead.',
239
+ },
213
240
  method_type: {
214
241
  type: 'string',
215
242
  enum: ['read_only', 'read_write', 'any'],
216
243
  },
217
244
  direction: { type: 'string', enum: ['input', 'output', 'any'] },
245
+ source: {
246
+ type: 'string',
247
+ description: 'Only this document, when the index holds more than one.',
248
+ },
218
249
  limit: {
219
250
  type: 'integer',
220
251
  description: `Rows to return. Default ${DEFAULT_ROWS}.`,
@@ -222,7 +253,7 @@ export function schemaTools(index, options = {}) {
222
253
  },
223
254
  additionalProperties: false,
224
255
  },
225
- execute: async ({ kind, name, path, method_type, direction, limit }) => {
256
+ execute: async ({ kind, name, path, regex, method_type, direction, source: only, limit, }) => {
226
257
  const subject = SUBJECTS[kind ?? 'methods'];
227
258
  if (!subject) {
228
259
  return { error: `cannot list "${kind}"`, hint: 'kind is methods, types or fields' };
@@ -231,8 +262,9 @@ export function schemaTools(index, options = {}) {
231
262
  try {
232
263
  result = listNodes(index.graph, {
233
264
  kind: subject,
234
- name: name ? [loose(name)] : undefined,
235
- path: path ? [loose(path)] : undefined,
265
+ name: name ? [loose(name, { regex })] : undefined,
266
+ path: path ? [loose(path, { regex })] : undefined,
267
+ source: only,
236
268
  methodType: enumerated(method_type),
237
269
  direction: enumerated(direction),
238
270
  limit: Math.min(limit ?? DEFAULT_ROWS, MAX_ROWS),
@@ -244,7 +276,7 @@ export function schemaTools(index, options = {}) {
244
276
  return {
245
277
  found: result.found,
246
278
  truncated: result.truncated,
247
- [PLURALS[subject]]: result.rows.map((r) => line(index.graph, subject, r)),
279
+ [PLURALS[subject]]: result.rows.map((r) => line(index.graph, subject, r, source)),
248
280
  };
249
281
  },
250
282
  });
@@ -255,7 +287,8 @@ export function schemaTools(index, options = {}) {
255
287
  'operations, schemas and fields alike. No embeddings and no ranking, so ' +
256
288
  'nothing is missed for being an unusual word or an odd spelling. This is the ' +
257
289
  'tool for "does X exist anywhere", and for checking that a search which ' +
258
- 'returned nothing really means there is nothing.',
290
+ 'returned nothing really means there is nothing. Narrow it with `path` or ' +
291
+ '`name` when the word is common and only one corner of the API is meant.',
259
292
  parameters: {
260
293
  type: 'object',
261
294
  properties: {
@@ -268,6 +301,18 @@ export function schemaTools(index, options = {}) {
268
301
  description: 'Read the pattern as a regular expression instead.',
269
302
  },
270
303
  kind: { type: 'string', enum: ['method', 'type', 'property'] },
304
+ name: {
305
+ type: 'string',
306
+ description: 'Only nodes whose own name matches this. * and ? allowed.',
307
+ },
308
+ path: {
309
+ type: 'string',
310
+ description: 'Only what sits on a matching route, e.g. "/users*".',
311
+ },
312
+ source: {
313
+ type: 'string',
314
+ description: 'Only this document, when the index holds more than one.',
315
+ },
271
316
  limit: {
272
317
  type: 'integer',
273
318
  description: `Matches to return. Default ${DEFAULT_ROWS}. \`found\` always counts them all.`,
@@ -276,11 +321,14 @@ export function schemaTools(index, options = {}) {
276
321
  required: ['pattern'],
277
322
  additionalProperties: false,
278
323
  },
279
- execute: async ({ pattern, regex, kind, limit }) => {
324
+ execute: async ({ pattern, regex, kind, name, path, source: only, limit }) => {
280
325
  let result;
281
326
  try {
282
327
  result = grepNodes(index.graph, matcher(pattern, { regex }), {
283
328
  kinds: kind ? [kind] : undefined,
329
+ name: name ? [loose(name)] : undefined,
330
+ path: path ? [loose(path)] : undefined,
331
+ source: only,
284
332
  limit: Math.min(limit ?? DEFAULT_ROWS, MAX_ROWS),
285
333
  });
286
334
  }
@@ -296,11 +344,95 @@ export function schemaTools(index, options = {}) {
296
344
  return {
297
345
  found: result.found,
298
346
  truncated: result.truncated,
299
- matches: result.matches.map((m) => ({ id: m.id, text: m.text })),
347
+ matches: result.matches.map((m) => ({
348
+ id: m.id,
349
+ ...(source ? { source: m.attributes.source } : {}),
350
+ text: m.text,
351
+ })),
352
+ };
353
+ },
354
+ });
355
+ const traceApi = tool({
356
+ name: 'trace_api',
357
+ group: GROUP,
358
+ description: 'Answers "which calls can reach this?" for a field or a schema: walks up the ' +
359
+ 'graph from everything of that name to the operations that accept or return it, ' +
360
+ 'and gives the chain in between. Use it whenever a field has been found and the ' +
361
+ 'endpoint to call is what is actually wanted — searching for the operation will ' +
362
+ 'not work, because a call almost never repeats the name of a field nested inside ' +
363
+ 'its body. An empty answer means nothing in the API carries it.',
364
+ parameters: {
365
+ type: 'object',
366
+ properties: {
367
+ of: {
368
+ type: 'string',
369
+ description: 'The field or schema name to trace up from, or a node id such as ' +
370
+ '"Type:User". A plain word matches anywhere in the name; * and ? ' +
371
+ 'match the whole of it.',
372
+ },
373
+ kind: {
374
+ type: 'string',
375
+ enum: ['type', 'property'],
376
+ description: 'Only trace from schemas, or only from fields.',
377
+ },
378
+ direction: {
379
+ type: 'string',
380
+ enum: ['input', 'output', 'any'],
381
+ description: 'Keep only the calls that accept it, or that return it.',
382
+ },
383
+ source: {
384
+ type: 'string',
385
+ description: 'Only this document, when the index holds more than one.',
386
+ },
387
+ limit: {
388
+ type: 'integer',
389
+ description: `Nodes to trace from. Default ${DEFAULT_TRACES}.`,
390
+ },
391
+ routes: {
392
+ type: 'integer',
393
+ description: `Operations per node. Default ${DEFAULT_ROUTES}.`,
394
+ },
395
+ },
396
+ required: ['of'],
397
+ additionalProperties: false,
398
+ },
399
+ execute: async ({ of: wanted, kind, direction, source: only, limit, routes }) => {
400
+ let result;
401
+ try {
402
+ result = traceNodes(index.graph, {
403
+ ids: index.graph.hasNode(wanted) ? [wanted] : undefined,
404
+ kinds: kind ? [kind] : undefined,
405
+ name: index.graph.hasNode(wanted) ? undefined : [loose(wanted)],
406
+ source: only,
407
+ limit: Math.min(limit ?? DEFAULT_TRACES, MAX_TRACES),
408
+ maxRoutes: Math.min(routes ?? DEFAULT_ROUTES, MAX_ROWS),
409
+ });
410
+ }
411
+ catch (err) {
412
+ return { error: err instanceof PatternError ? err.message : String(err) };
413
+ }
414
+ if (result.found === 0) {
415
+ return {
416
+ found: 0,
417
+ hint: `nothing in the API is called "${wanted}" — try grep_api for it`,
418
+ };
419
+ }
420
+ const side = enumerated(direction);
421
+ return {
422
+ found: result.found,
423
+ truncated: result.truncated,
424
+ traced: result.traces.map((t) => ({
425
+ id: t.id,
426
+ reached_by: t.found,
427
+ operations: t.routes
428
+ .filter((r) => !side || r.direction === side || r.direction === 'both')
429
+ .map((r) => `${r.attributes.httpMethod} ${r.attributes.path} ` +
430
+ `${r.attributes.name} (${r.direction})${source ? ` ${sourceTag(r.attributes.source)}` : ''} ${chainOf(index.graph, t.id, r)}`),
431
+ })),
300
432
  };
301
433
  },
302
434
  });
303
- return [searchApi, describeTypes, findTypesWithProperty, listApi, grepApi];
435
+ return [searchApi, describeTypes, findTypesWithProperty, listApi, grepApi, traceApi];
304
436
  }
305
437
  // ---------------------------------------------------------------------------
306
438
  const SUBJECTS = {
@@ -315,16 +447,17 @@ const PLURALS = {
315
447
  property: 'properties',
316
448
  };
317
449
  /** One row, as the line a model reads rather than an object it has to walk. */
318
- function line(graph, kind, row) {
450
+ function line(graph, kind, row, source = false) {
451
+ const from = source ? ` ${sourceTag(row.source)}` : '';
319
452
  if (kind === 'method') {
320
- return `${row.httpMethod} ${row.path} ${row.name}${row.doc ? ` — ${row.doc}` : ''}`;
453
+ return `${row.httpMethod} ${row.path} ${row.name}${from}${row.doc ? ` — ${row.doc}` : ''}`;
321
454
  }
322
455
  if (kind === 'type') {
323
456
  const side = row.direction === 'none' ? '' : ` (${row.direction})`;
324
- return `${row.name}${side} ${fields(propertyCount(graph, row.id))}${row.doc ? ` — ${row.doc}` : ''}`;
457
+ return `${row.name}${side} ${fields(propertyCount(graph, row.id))}${from}${row.doc ? ` — ${row.doc}` : ''}`;
325
458
  }
326
459
  const owner = row.parent ? `${row.parent}.` : '';
327
- return `${owner}${row.name}${row.required ? '' : '?'}: ${row.signature || 'unknown'}`;
460
+ return `${owner}${row.name}${row.required ? '' : '?'}: ${row.signature || 'unknown'}${from}`;
328
461
  }
329
462
  const enumerated = (value) => value && value !== 'any' ? value : undefined;
330
463
  function list(description) {
@@ -0,0 +1,52 @@
1
+ import type { Matcher } from '../common/match.ts';
2
+ import type { ApiGraph, NodeAttrs, NodeKind } from './graph.ts';
3
+ export type Side = 'input' | 'output' | 'both';
4
+ export interface Route {
5
+ /** the operation node */
6
+ id: string;
7
+ attributes: NodeAttrs;
8
+ /** whether the call accepts what was traced, returns it, or both */
9
+ direction: Side;
10
+ /** the nodes in between, nearest the operation first */
11
+ via: string[];
12
+ hops: number;
13
+ }
14
+ export interface Trace {
15
+ id: string;
16
+ attributes: NodeAttrs;
17
+ /** how many operations reach it, whatever was kept */
18
+ found: number;
19
+ routes: Route[];
20
+ truncated: boolean;
21
+ }
22
+ export interface Traced {
23
+ /** how many nodes were traced, whatever was kept */
24
+ found: number;
25
+ traces: Trace[];
26
+ truncated: boolean;
27
+ }
28
+ export interface TraceFilter {
29
+ /** node ids to start from, taken as given */
30
+ ids?: readonly string[];
31
+ /** which kinds a `name` may match; all three when unset */
32
+ kinds?: readonly NodeKind[];
33
+ name?: readonly Matcher[];
34
+ source?: string;
35
+ /** starting nodes kept */
36
+ limit?: number;
37
+ /** routes kept per starting node */
38
+ maxRoutes?: number;
39
+ maxHops?: number;
40
+ }
41
+ /** How far containment is read backwards before a route is too indirect to mean anything. */
42
+ export declare const DEFAULT_TRACE_HOPS = 8;
43
+ export declare function traceNodes(graph: ApiGraph, filter: TraceFilter): Traced;
44
+ export declare function traceOne(graph: ApiGraph, id: string, filter?: TraceFilter): Trace;
45
+ /**
46
+ * The chain from an operation down to the node that was traced, as one line.
47
+ * A field is written onto the type above it (`User.email`) and a parameter is
48
+ * marked (`?page_size`), so the shape of the call can be read off the route
49
+ * without going and looking any of it up.
50
+ */
51
+ export declare function chainOf(graph: ApiGraph, start: string, route: Route): string;
52
+ //# sourceMappingURL=trace.d.ts.map
@@ -0,0 +1,144 @@
1
+ import { listNodes } from "./lookup.js";
2
+ /** How far containment is read backwards before a route is too indirect to mean anything. */
3
+ export const DEFAULT_TRACE_HOPS = 8;
4
+ /** A walk is bounded; a document can always be larger than the one this was written against. */
5
+ const MAX_VISITS = 20_000;
6
+ /**
7
+ * What a bare pattern matches. Operations are left out on purpose: they are
8
+ * where a trace ends, so starting one at an operationId that happens to share
9
+ * a word adds a row saying only that it found itself. Naming a `Method:` id
10
+ * outright still works.
11
+ */
12
+ const KINDS = ['type', 'property'];
13
+ /** The edges an operation holds its payload by. Reaching one of these is arriving. */
14
+ const ENTRY = new Set(['TAKES_INPUT', 'RETURNS_OUTPUT', 'HAS_PARAM']);
15
+ /** Containment, read backwards: a type to the fields that hold it, a field to its owner. */
16
+ const UPWARD = new Set(['HAS_PROPERTY', 'OF_TYPE', 'COMPOSES', 'ITEM_OF']);
17
+ export function traceNodes(graph, filter) {
18
+ const starts = select(graph, filter);
19
+ const kept = filter.limit && filter.limit < starts.length ? starts.slice(0, filter.limit) : starts;
20
+ return {
21
+ found: starts.length,
22
+ truncated: kept.length < starts.length,
23
+ traces: kept.map((id) => traceOne(graph, id, filter)),
24
+ };
25
+ }
26
+ export function traceOne(graph, id, filter = {}) {
27
+ const attributes = graph.getNodeAttributes(id);
28
+ // An operation is already where a trace ends; it reaches itself and nothing above it.
29
+ const routes = attributes.kind === 'method'
30
+ ? [{ id, attributes, direction: 'both', via: [], hops: 0 }]
31
+ : climb(graph, id, filter.maxHops ?? DEFAULT_TRACE_HOPS);
32
+ const limit = filter.maxRoutes;
33
+ return {
34
+ id,
35
+ attributes,
36
+ found: routes.length,
37
+ routes: limit && limit < routes.length ? routes.slice(0, limit) : routes,
38
+ truncated: Boolean(limit && limit < routes.length),
39
+ };
40
+ }
41
+ /**
42
+ * The chain from an operation down to the node that was traced, as one line.
43
+ * A field is written onto the type above it (`User.email`) and a parameter is
44
+ * marked (`?page_size`), so the shape of the call can be read off the route
45
+ * without going and looking any of it up.
46
+ */
47
+ export function chainOf(graph, start, route) {
48
+ let out = '';
49
+ for (const id of [...route.via, start]) {
50
+ const label = labelOf(graph, id);
51
+ out += out === '' || label.startsWith('.') ? label : ` → ${label}`;
52
+ }
53
+ return out;
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ function labelOf(graph, id) {
57
+ const a = graph.getNodeAttributes(id);
58
+ if (a.kind !== 'property') {
59
+ return a.name;
60
+ }
61
+ // A parameter belongs to a call rather than to a type, so it cannot be
62
+ // written as a field of the thing before it.
63
+ return id.includes('#') ? `?${a.name}` : `.${a.name}`;
64
+ }
65
+ function select(graph, filter) {
66
+ const out = new Set((filter.ids ?? []).filter((id) => graph.hasNode(id)));
67
+ if (filter.name) {
68
+ for (const kind of filter.kinds ?? KINDS) {
69
+ for (const row of listNodes(graph, { kind, name: filter.name, source: filter.source })
70
+ .rows) {
71
+ out.add(row.id);
72
+ }
73
+ }
74
+ }
75
+ return [...out].sort((a, b) => a.localeCompare(b));
76
+ }
77
+ /**
78
+ * Breadth-first up containment, recording an operation the moment one is
79
+ * reached. Breadth-first is what makes the answer the *shortest* way in: a
80
+ * type held by a wrapper held by a request body should be reported through
81
+ * the body, not through whichever branch happened to be walked first.
82
+ */
83
+ function climb(graph, start, maxHops) {
84
+ const previous = new Map();
85
+ const seen = new Set([start]);
86
+ const found = new Map();
87
+ let frontier = [start];
88
+ for (let hop = 0; hop <= maxHops && frontier.length > 0 && seen.size < MAX_VISITS; hop++) {
89
+ const next = [];
90
+ for (const node of frontier) {
91
+ for (const edge of graph.inEdges(node)) {
92
+ const relation = graph.getEdgeAttribute(edge, 'relation');
93
+ const from = graph.source(edge);
94
+ if (ENTRY.has(relation)) {
95
+ arrive(found, {
96
+ id: from,
97
+ attributes: graph.getNodeAttributes(from),
98
+ direction: sideOf(relation),
99
+ via: upward(previous, start, node),
100
+ hops: hop + 1,
101
+ });
102
+ }
103
+ else if (UPWARD.has(relation) && !seen.has(from)) {
104
+ seen.add(from);
105
+ previous.set(from, node);
106
+ next.push(from);
107
+ }
108
+ }
109
+ }
110
+ frontier = next;
111
+ }
112
+ return [...found.values()].sort((a, b) => a.hops - b.hops ||
113
+ a.attributes.path.localeCompare(b.attributes.path) ||
114
+ a.attributes.httpMethod.localeCompare(b.attributes.httpMethod));
115
+ }
116
+ /**
117
+ * One operation, once. A call that both accepts and returns the same type is
118
+ * two edges and one answer, and the honest word for that answer is `both`.
119
+ */
120
+ function arrive(found, route) {
121
+ const existing = found.get(route.id);
122
+ if (!existing) {
123
+ found.set(route.id, route);
124
+ }
125
+ else if (existing.direction !== route.direction) {
126
+ existing.direction = 'both';
127
+ }
128
+ }
129
+ const sideOf = (relation) => (relation === 'RETURNS_OUTPUT' ? 'output' : 'input');
130
+ /** The nodes walked through, from the one holding the entry edge back down towards the start. */
131
+ function upward(previous, start, from) {
132
+ const out = [];
133
+ let at = from;
134
+ while (at !== start) {
135
+ out.push(at);
136
+ const below = previous.get(at);
137
+ if (below === undefined) {
138
+ break;
139
+ }
140
+ at = below;
141
+ }
142
+ return out;
143
+ }
144
+ //# sourceMappingURL=trace.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenera/rag",
3
- "version": "1.1.5",
3
+ "version": "1.1.8",
4
4
  "description": "Retrieval over API descriptions: openapi/swagger documents as a searchable graph.",
5
5
  "keywords": [
6
6
  "agents",
@@ -53,7 +53,7 @@
53
53
  "@apidevtools/swagger-parser": "^12.0.0",
54
54
  "@lancedb/lancedb": "^0.38.0",
55
55
  "graphology": "^0.26.0",
56
- "@zenera/cli": "^1.1.5",
57
- "@zenera/neo": "^1.1.5"
56
+ "@zenera/cli": "^1.1.8",
57
+ "@zenera/neo": "^1.1.8"
58
58
  }
59
59
  }
@@ -1,26 +0,0 @@
1
- import type { Counts, Manifest } from './files.ts';
2
- export declare const LOCK_FILE = ".lock";
3
- export declare const README_FILE = "README.md";
4
- export type Phase = 'reading' | 'graph' | 'embedding' | 'writing';
5
- export interface BuildPlan {
6
- /** where the index goes */
7
- dir: string;
8
- files: readonly string[];
9
- /** the embedding reference as it was typed */
10
- embedding: string;
11
- indexer: string;
12
- }
13
- export interface Journal {
14
- phase(name: Phase): void;
15
- /** what the documents turned out to hold, once they have been read */
16
- read(counts: Counts): void;
17
- progress(done: number, total: number): void;
18
- finish(manifest: Manifest): void;
19
- fail(reason: unknown): void;
20
- }
21
- /**
22
- * Takes the directory, or refuses it. Two builds writing one index would
23
- * interleave their LanceDB writes and leave a store neither of them describes.
24
- */
25
- export declare function beginBuild(plan: BuildPlan): Journal;
26
- //# sourceMappingURL=progress.d.ts.map