@speakeasy-api/docs-mcp-core 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +27 -0
  3. package/dist/chunking.d.ts +3 -0
  4. package/dist/chunking.d.ts.map +1 -0
  5. package/dist/chunking.js +332 -0
  6. package/dist/chunking.js.map +1 -0
  7. package/dist/cursor.d.ts +12 -0
  8. package/dist/cursor.d.ts.map +1 -0
  9. package/dist/cursor.js +83 -0
  10. package/dist/cursor.js.map +1 -0
  11. package/dist/embedding-cache.d.ts +46 -0
  12. package/dist/embedding-cache.d.ts.map +1 -0
  13. package/dist/embedding-cache.js +230 -0
  14. package/dist/embedding-cache.js.map +1 -0
  15. package/dist/embedding.d.ts +66 -0
  16. package/dist/embedding.d.ts.map +1 -0
  17. package/dist/embedding.js +264 -0
  18. package/dist/embedding.js.map +1 -0
  19. package/dist/index.d.ts +11 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +11 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lancedb.d.ts +42 -0
  24. package/dist/lancedb.d.ts.map +1 -0
  25. package/dist/lancedb.js +442 -0
  26. package/dist/lancedb.js.map +1 -0
  27. package/dist/manifest.d.ts +19 -0
  28. package/dist/manifest.d.ts.map +1 -0
  29. package/dist/manifest.js +245 -0
  30. package/dist/manifest.js.map +1 -0
  31. package/dist/metadata.d.ts +6 -0
  32. package/dist/metadata.d.ts.map +1 -0
  33. package/dist/metadata.js +152 -0
  34. package/dist/metadata.js.map +1 -0
  35. package/dist/parser.d.ts +6 -0
  36. package/dist/parser.d.ts.map +1 -0
  37. package/dist/parser.js +21 -0
  38. package/dist/parser.js.map +1 -0
  39. package/dist/search-common.d.ts +7 -0
  40. package/dist/search-common.d.ts.map +1 -0
  41. package/dist/search-common.js +81 -0
  42. package/dist/search-common.js.map +1 -0
  43. package/dist/search.d.ts +13 -0
  44. package/dist/search.d.ts.map +1 -0
  45. package/dist/search.js +166 -0
  46. package/dist/search.js.map +1 -0
  47. package/dist/types.d.ts +125 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +49 -0
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # @speakeasy-api/docs-mcp-core
2
+
3
+ Core retrieval and indexing primitives for [Speakeasy Docs MCP](https://github.com/speakeasy-api/docs-mcp): markdown chunking, hybrid search, and LanceDB integration.
4
+
5
+ **Beta.** Part of the [`docs-mcp`](https://github.com/speakeasy-api/docs-mcp) monorepo.
6
+
7
+ ## What This Package Does
8
+
9
+ - AST-based markdown parsing and intelligent chunking (respects heading boundaries and code blocks)
10
+ - Hybrid search combining full-text search with semantic vector search via Reciprocal Rank Fusion
11
+ - LanceDB integration with memory-mapped IO for fast retrieval
12
+ - Embedding generation and caching (OpenAI or deterministic hash)
13
+ - Manifest resolution and metadata extraction from `.mcp-manifest.json` files
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @speakeasy-api/docs-mcp-core
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ This package is primarily consumed by [`@speakeasy-api/docs-mcp-server`](https://www.npmjs.com/package/@speakeasy-api/docs-mcp-server) and [`@speakeasy-api/docs-mcp-cli`](https://www.npmjs.com/package/@speakeasy-api/docs-mcp-cli). See the [monorepo README](https://github.com/speakeasy-api/docs-mcp) for end-to-end usage.
24
+
25
+ ## License
26
+
27
+ [AGPL-3.0](https://github.com/speakeasy-api/docs-mcp/blob/main/LICENSE)
@@ -0,0 +1,3 @@
1
+ import type { BuildChunksInput, Chunk } from "./types.js";
2
+ export declare function buildChunks(input: BuildChunksInput): Chunk[];
3
+ //# sourceMappingURL=chunking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../src/chunking.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAC;AAqC5E,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,KAAK,EAAE,CAiC5D"}
@@ -0,0 +1,332 @@
1
+ import { toString } from "mdast-util-to-string";
2
+ import { parseMarkdown } from "./parser.js";
3
+ const CHUNK_LEVEL_MAP = {
4
+ h1: 1,
5
+ h2: 2,
6
+ h3: 3
7
+ };
8
+ // ─── Public API ──────────────────────────────────────────────────
9
+ export function buildChunks(input) {
10
+ if (!input.filepath.trim()) {
11
+ throw new Error("filepath is required");
12
+ }
13
+ const ast = parseMarkdown(input.markdown);
14
+ if (input.strategy.chunk_by === "file") {
15
+ const contentNodes = ast.children.filter((n) => n.type !== "yaml");
16
+ const segments = applySizeRules([
17
+ {
18
+ kind: "file",
19
+ heading: "",
20
+ headingLevel: 0,
21
+ ancestorTexts: [],
22
+ ancestorSlugs: [],
23
+ slug: "",
24
+ nodes: contentNodes,
25
+ fullMarkdown: input.markdown,
26
+ part: 1
27
+ }
28
+ ], input.strategy);
29
+ return materializeChunks(input.filepath, segments, input.metadata ?? {});
30
+ }
31
+ const targetLevel = CHUNK_LEVEL_MAP[input.strategy.chunk_by];
32
+ const segments = splitByHeadingLevel(ast, input.markdown, targetLevel);
33
+ const adjusted = applySizeRules(segments, input.strategy);
34
+ return materializeChunks(input.filepath, adjusted, input.metadata ?? {});
35
+ }
36
+ // ─── Heading-based splitting ─────────────────────────────────────
37
+ function splitByHeadingLevel(ast, markdown, targetLevel) {
38
+ const boundaries = [];
39
+ const stack = new Array(7).fill(undefined);
40
+ const slugCountsByParent = new Map();
41
+ // Filter out YAML frontmatter nodes for processing
42
+ const contentChildren = ast.children.filter((n) => n.type !== "yaml");
43
+ for (let childIdx = 0; childIdx < contentChildren.length; childIdx += 1) {
44
+ const node = contentChildren[childIdx];
45
+ if (node.type !== "heading") {
46
+ continue;
47
+ }
48
+ const heading = toString(node).trim() || "section";
49
+ const baseSlug = slugify(heading) || "section";
50
+ const parentSlugs = [];
51
+ for (let depth = 1; depth < node.depth; depth += 1) {
52
+ const current = stack[depth];
53
+ if (current) {
54
+ parentSlugs.push(current.slug);
55
+ }
56
+ }
57
+ const slug = dedupeSlug(parentSlugs, baseSlug, slugCountsByParent);
58
+ stack[node.depth] = { text: heading, slug };
59
+ for (let depth = node.depth + 1; depth < stack.length; depth += 1) {
60
+ stack[depth] = undefined;
61
+ }
62
+ if (node.depth !== targetLevel) {
63
+ continue;
64
+ }
65
+ const ancestorTexts = [];
66
+ const ancestorSlugs = [];
67
+ for (let depth = 1; depth < targetLevel; depth += 1) {
68
+ const current = stack[depth];
69
+ if (!current) {
70
+ continue;
71
+ }
72
+ ancestorTexts.push(current.text);
73
+ ancestorSlugs.push(current.slug);
74
+ }
75
+ boundaries.push({
76
+ childIndex: childIdx,
77
+ heading,
78
+ headingLevel: node.depth,
79
+ ancestorTexts,
80
+ ancestorSlugs,
81
+ slug
82
+ });
83
+ }
84
+ if (boundaries.length === 0) {
85
+ return [
86
+ {
87
+ kind: "preamble",
88
+ heading: "",
89
+ headingLevel: 0,
90
+ ancestorTexts: [],
91
+ ancestorSlugs: [],
92
+ slug: "_preamble",
93
+ nodes: contentChildren,
94
+ fullMarkdown: markdown,
95
+ part: 1
96
+ }
97
+ ];
98
+ }
99
+ const segments = [];
100
+ // Preamble: nodes before the first target-level heading
101
+ const preambleNodes = contentChildren.slice(0, boundaries[0].childIndex);
102
+ if (preambleNodes.length > 0) {
103
+ const preambleContent = rawMarkdown(preambleNodes, markdown);
104
+ if (preambleContent.trim()) {
105
+ segments.push({
106
+ kind: "preamble",
107
+ heading: "",
108
+ headingLevel: 0,
109
+ ancestorTexts: [],
110
+ ancestorSlugs: [],
111
+ slug: "_preamble",
112
+ nodes: preambleNodes,
113
+ fullMarkdown: markdown,
114
+ part: 1
115
+ });
116
+ }
117
+ }
118
+ for (let index = 0; index < boundaries.length; index += 1) {
119
+ const boundary = boundaries[index];
120
+ const next = boundaries[index + 1];
121
+ const startIdx = boundary.childIndex;
122
+ const endIdx = next ? next.childIndex : contentChildren.length;
123
+ const sectionNodes = contentChildren.slice(startIdx, endIdx);
124
+ const content = rawMarkdown(sectionNodes, markdown);
125
+ if (!content.trim()) {
126
+ continue;
127
+ }
128
+ segments.push({
129
+ kind: "heading",
130
+ heading: boundary.heading,
131
+ headingLevel: boundary.headingLevel,
132
+ ancestorTexts: boundary.ancestorTexts,
133
+ ancestorSlugs: boundary.ancestorSlugs,
134
+ slug: boundary.slug,
135
+ nodes: sectionNodes,
136
+ fullMarkdown: markdown,
137
+ part: 1
138
+ });
139
+ }
140
+ return segments.length > 0
141
+ ? segments
142
+ : [
143
+ {
144
+ kind: "preamble",
145
+ heading: "",
146
+ headingLevel: 0,
147
+ ancestorTexts: [],
148
+ ancestorSlugs: [],
149
+ slug: "_preamble",
150
+ nodes: contentChildren,
151
+ fullMarkdown: markdown,
152
+ part: 1
153
+ }
154
+ ];
155
+ }
156
+ // ─── Slug helpers ────────────────────────────────────────────────
157
+ function dedupeSlug(ancestorSlugs, baseSlug, slugCountsByParent) {
158
+ const parentPath = ancestorSlugs.join("/") || "__root__";
159
+ const counterBySlug = slugCountsByParent.get(parentPath) ?? new Map();
160
+ slugCountsByParent.set(parentPath, counterBySlug);
161
+ const count = (counterBySlug.get(baseSlug) ?? 0) + 1;
162
+ counterBySlug.set(baseSlug, count);
163
+ return count === 1 ? baseSlug : `${baseSlug}-${count}`;
164
+ }
165
+ function slugify(value) {
166
+ return value
167
+ .toLowerCase()
168
+ .replace(/[^a-z0-9 -]/g, "")
169
+ .replace(/\s+/g, "-")
170
+ .replace(/-+/g, "-")
171
+ .replace(/^-|-$/g, "");
172
+ }
173
+ // ─── AST-safe size rules ─────────────────────────────────────────
174
+ function applySizeRules(segments, strategy) {
175
+ const max = strategy.max_chunk_size;
176
+ const min = strategy.min_chunk_size;
177
+ // Phase 1: split oversized segments using AST node boundaries
178
+ const expanded = [];
179
+ for (const segment of segments) {
180
+ const contentLength = rawMarkdown(segment.nodes, segment.fullMarkdown).length;
181
+ if (!max || contentLength <= max) {
182
+ expanded.push(segment);
183
+ continue;
184
+ }
185
+ const nodeGroups = splitByNodeSize(segment.nodes, segment.fullMarkdown, max);
186
+ nodeGroups.forEach((groupNodes, partIndex) => {
187
+ expanded.push({
188
+ ...segment,
189
+ nodes: groupNodes,
190
+ part: partIndex + 1
191
+ });
192
+ });
193
+ }
194
+ // Phase 2: merge undersized segments into previous (Opus-style breadcrumb check)
195
+ if (!min || expanded.length <= 1) {
196
+ return expanded;
197
+ }
198
+ const merged = [];
199
+ for (const segment of expanded) {
200
+ const previous = merged[merged.length - 1];
201
+ const segmentContentLength = rawMarkdown(segment.nodes, segment.fullMarkdown).length;
202
+ if (previous &&
203
+ segmentContentLength < min &&
204
+ canMerge(previous, segment)) {
205
+ // Merge nodes together
206
+ previous.nodes = [...previous.nodes, ...segment.nodes];
207
+ continue;
208
+ }
209
+ merged.push({ ...segment, nodes: [...segment.nodes] });
210
+ }
211
+ return merged;
212
+ }
213
+ /**
214
+ * AST-safe max-size splitting (from Gemini approach).
215
+ *
216
+ * Iterates through AST nodes: if adding the next node would exceed the limit
217
+ * AND we already have accumulated nodes, flush current accumulation as a sub-chunk.
218
+ * Single nodes that exceed the limit on their own stay intact -- a large code
219
+ * block is never bisected.
220
+ */
221
+ function splitByNodeSize(nodes, fullMarkdown, maxSize) {
222
+ const groups = [];
223
+ let current = [];
224
+ let currentSize = 0;
225
+ for (const node of nodes) {
226
+ const nodeSize = rawMarkdown([node], fullMarkdown).length;
227
+ if (current.length > 0 && currentSize + nodeSize > maxSize) {
228
+ groups.push(current);
229
+ current = [node];
230
+ currentSize = nodeSize;
231
+ }
232
+ else {
233
+ current.push(node);
234
+ currentSize += nodeSize;
235
+ }
236
+ }
237
+ if (current.length > 0) {
238
+ groups.push(current);
239
+ }
240
+ return groups;
241
+ }
242
+ /**
243
+ * Determine whether a small segment can be merged into the previous one.
244
+ *
245
+ * Only merges segments that originated from the same heading section
246
+ * (same slug, heading, heading level, and ancestor path). This prevents
247
+ * merging across heading boundaries while allowing sub-chunk parts
248
+ * (from max-size splitting) to be recombined.
249
+ */
250
+ function canMerge(previous, current) {
251
+ if (previous.kind !== current.kind) {
252
+ return false;
253
+ }
254
+ if (previous.slug !== current.slug) {
255
+ return false;
256
+ }
257
+ if (previous.heading !== current.heading || previous.headingLevel !== current.headingLevel) {
258
+ return false;
259
+ }
260
+ return ancestorSlugsMatch(previous.ancestorSlugs, current.ancestorSlugs);
261
+ }
262
+ function ancestorSlugsMatch(a, b) {
263
+ if (a.length !== b.length) {
264
+ return false;
265
+ }
266
+ for (let i = 0; i < a.length; i += 1) {
267
+ if (a[i] !== b[i]) {
268
+ return false;
269
+ }
270
+ }
271
+ return true;
272
+ }
273
+ // ─── Chunk materialization ───────────────────────────────────────
274
+ function materializeChunks(filepath, segments, metadata) {
275
+ const chunks = [];
276
+ for (const [index, segment] of segments.entries()) {
277
+ const chunkId = computeChunkId(filepath, segment);
278
+ const breadcrumbParts = [filepath];
279
+ if (segment.ancestorTexts.length > 0) {
280
+ breadcrumbParts.push(...segment.ancestorTexts);
281
+ }
282
+ if (segment.heading) {
283
+ breadcrumbParts.push(segment.heading);
284
+ }
285
+ const content = rawMarkdown(segment.nodes, segment.fullMarkdown);
286
+ const contentText = segment.nodes.map((n) => toString(n)).join("\n\n");
287
+ chunks.push({
288
+ chunk_id: chunkId,
289
+ filepath,
290
+ heading: segment.heading,
291
+ heading_level: segment.headingLevel,
292
+ content,
293
+ content_text: contentText,
294
+ breadcrumb: breadcrumbParts.join(" > "),
295
+ chunk_index: index,
296
+ metadata: { ...metadata }
297
+ });
298
+ }
299
+ return chunks;
300
+ }
301
+ function computeChunkId(filepath, segment) {
302
+ if (segment.kind === "file") {
303
+ return segment.part === 1 ? filepath : `${filepath}#_part-${segment.part}`;
304
+ }
305
+ if (segment.kind === "preamble") {
306
+ return segment.part === 1
307
+ ? `${filepath}#_preamble`
308
+ : `${filepath}#_preamble-part-${segment.part}`;
309
+ }
310
+ const partSuffix = segment.part > 1 ? `-part-${segment.part}` : "";
311
+ const slug = `${segment.slug}${partSuffix}`;
312
+ const fullPath = [...segment.ancestorSlugs, slug].join("/");
313
+ return `${filepath}#${fullPath}`;
314
+ }
315
+ // ─── AST helpers ─────────────────────────────────────────────────
316
+ /**
317
+ * Reconstruct raw markdown for a slice of AST nodes by extracting
318
+ * the original source range from the full content string.
319
+ * Falls back to mdast-util-to-string if position info is unavailable.
320
+ */
321
+ function rawMarkdown(nodes, fullContent) {
322
+ if (nodes.length === 0)
323
+ return "";
324
+ const first = nodes[0];
325
+ const last = nodes[nodes.length - 1];
326
+ if (first.position?.start && last.position?.end) {
327
+ return fullContent.slice(first.position.start.offset, last.position.end.offset);
328
+ }
329
+ // Fallback
330
+ return nodes.map((n) => toString(n)).join("\n\n");
331
+ }
332
+ //# sourceMappingURL=chunking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.js","sourceRoot":"","sources":["../src/chunking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { CursorPayload } from "./types.js";
2
+ export declare function encodeCursor(payload: CursorPayload): string;
3
+ export declare function decodeCursor(cursor: string): CursorPayload;
4
+ export declare function encodeSearchCursor(payload: CursorPayload, context: {
5
+ query: string;
6
+ filters: Record<string, string>;
7
+ }): string;
8
+ export declare function decodeSearchCursor(cursor: string, context: {
9
+ query: string;
10
+ filters: Record<string, string>;
11
+ }): CursorPayload;
12
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAE3D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAO1D;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC1D,MAAM,CAOR;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC1D,aAAa,CAef"}
package/dist/cursor.js ADDED
@@ -0,0 +1,83 @@
1
+ import { createHash } from "node:crypto";
2
+ export function encodeCursor(payload) {
3
+ return encodePayload(payload);
4
+ }
5
+ export function decodeCursor(cursor) {
6
+ const payload = decodePayload(cursor);
7
+ if (!isCursorPayload(payload)) {
8
+ throw new Error("Invalid cursor: expected { offset:number, limit:number }");
9
+ }
10
+ return payload;
11
+ }
12
+ export function encodeSearchCursor(payload, context) {
13
+ const searchPayload = {
14
+ ...payload,
15
+ v: 1,
16
+ sig: computeSearchCursorSignature(context)
17
+ };
18
+ return encodePayload(searchPayload);
19
+ }
20
+ export function decodeSearchCursor(cursor, context) {
21
+ const payload = decodePayload(cursor);
22
+ if (!isSearchCursorPayload(payload)) {
23
+ throw new Error("Invalid cursor: expected a search cursor payload");
24
+ }
25
+ const expectedSignature = computeSearchCursorSignature(context);
26
+ if (payload.sig !== expectedSignature) {
27
+ throw new Error("Invalid cursor: does not match current query or filters");
28
+ }
29
+ return {
30
+ offset: payload.offset,
31
+ limit: payload.limit
32
+ };
33
+ }
34
+ function encodePayload(payload) {
35
+ const body = JSON.stringify(payload);
36
+ return Buffer.from(body, "utf8").toString("base64url");
37
+ }
38
+ function decodePayload(cursor) {
39
+ let decoded;
40
+ try {
41
+ decoded = Buffer.from(cursor, "base64url").toString("utf8");
42
+ }
43
+ catch {
44
+ throw new Error("Invalid cursor: not valid base64url");
45
+ }
46
+ let payload;
47
+ try {
48
+ payload = JSON.parse(decoded);
49
+ }
50
+ catch {
51
+ throw new Error("Invalid cursor: malformed JSON payload");
52
+ }
53
+ return payload;
54
+ }
55
+ function isCursorPayload(value) {
56
+ if (!value || typeof value !== "object") {
57
+ return false;
58
+ }
59
+ const payload = value;
60
+ return (Number.isInteger(payload.offset) &&
61
+ Number.isInteger(payload.limit) &&
62
+ payload.offset >= 0 &&
63
+ payload.limit > 0);
64
+ }
65
+ function isSearchCursorPayload(value) {
66
+ if (!isCursorPayload(value)) {
67
+ return false;
68
+ }
69
+ const payload = value;
70
+ return payload.v === 1 && typeof payload.sig === "string" && payload.sig.length > 0;
71
+ }
72
+ function computeSearchCursorSignature(input) {
73
+ const query = input.query.trim();
74
+ const normalizedFilters = Object.entries(input.filters)
75
+ .sort(([a], [b]) => a.localeCompare(b))
76
+ .map(([key, value]) => [key, value]);
77
+ const serialized = JSON.stringify({
78
+ query,
79
+ filters: normalizedFilters
80
+ });
81
+ return createHash("sha1").update(serialized, "utf8").digest("base64url");
82
+ }
83
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { Chunk, EmbeddingConfig } from "./types.js";
2
+ /**
3
+ * Bumped only if toEmbeddingInput() changes in a way that isn't captured
4
+ * by the input text itself (e.g., normalization, encoding).
5
+ */
6
+ export declare const EMBEDDING_FORMAT_VERSION = "1";
7
+ export interface CacheEntry {
8
+ fingerprint: string;
9
+ chunk_id: string;
10
+ vector: number[];
11
+ }
12
+ export interface EmbeddingCache {
13
+ entries: Map<string, CacheEntry>;
14
+ }
15
+ export interface EmbedIncrementalStats {
16
+ total: number;
17
+ hits: number;
18
+ misses: number;
19
+ }
20
+ /**
21
+ * Compute a fingerprint for a chunk that captures both the embedding model config
22
+ * and the exact text that would be sent to the model.
23
+ */
24
+ export declare function computeFingerprint(chunk: Chunk, config: EmbeddingConfig): string;
25
+ /**
26
+ * Load the embedding cache from disk.
27
+ * Returns null (with a warning to stderr) if the cache is missing, corrupt, or incompatible.
28
+ */
29
+ export declare function loadCache(baseDir: string, currentConfig: EmbeddingConfig): Promise<EmbeddingCache | null>;
30
+ /**
31
+ * Save the embedding cache to disk using the rotate-and-rename pattern
32
+ * for crash safety.
33
+ */
34
+ export declare function saveCache(baseDir: string, cache: EmbeddingCache, config: EmbeddingConfig): Promise<void>;
35
+ /**
36
+ * Embed chunks incrementally: reuse cached vectors for unchanged chunks,
37
+ * only call the provider for misses.
38
+ */
39
+ export declare function embedChunksIncremental(chunks: Chunk[], provider: EmbeddingConfig & {
40
+ embed(texts: string[]): Promise<number[][]>;
41
+ }, cache: EmbeddingCache | null): Promise<{
42
+ vectorsByChunkId: Map<string, number[]>;
43
+ updatedCache: EmbeddingCache;
44
+ stats: EmbedIncrementalStats;
45
+ }>;
46
+ //# sourceMappingURL=embedding-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding-cache.d.ts","sourceRoot":"","sources":["../src/embedding-cache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAe5C,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CAOhF;AAoBD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,eAAe,GAC7B,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAiFhC;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,KAAK,EAAE,EACf,QAAQ,EAAE,eAAe,GAAG;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;CAAE,EAC3E,KAAK,EAAE,cAAc,GAAG,IAAI,GAC3B,OAAO,CAAC;IACT,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,YAAY,EAAE,cAAc,CAAC;IAC7B,KAAK,EAAE,qBAAqB,CAAC;CAC9B,CAAC,CA+DD"}