abb-opencode-local-rag 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.
- package/LICENSE +21 -0
- package/README.de.md +416 -0
- package/README.es.md +416 -0
- package/README.fr.md +416 -0
- package/README.md +491 -0
- package/README.pt-BR.md +416 -0
- package/README.zh-CN.md +416 -0
- package/dist/bin/install-skills.d.ts +20 -0
- package/dist/bin/install-skills.d.ts.map +1 -0
- package/dist/bin/install-skills.js +195 -0
- package/dist/bin/install-skills.js.map +1 -0
- package/dist/chunker/index.d.ts +24 -0
- package/dist/chunker/index.d.ts.map +1 -0
- package/dist/chunker/index.js +2 -0
- package/dist/chunker/index.js.map +1 -0
- package/dist/chunker/semantic-chunker.d.ts +97 -0
- package/dist/chunker/semantic-chunker.d.ts.map +1 -0
- package/dist/chunker/semantic-chunker.js +294 -0
- package/dist/chunker/semantic-chunker.js.map +1 -0
- package/dist/chunker/sentence-splitter.d.ts +28 -0
- package/dist/chunker/sentence-splitter.d.ts.map +1 -0
- package/dist/chunker/sentence-splitter.js +219 -0
- package/dist/chunker/sentence-splitter.js.map +1 -0
- package/dist/cli/common.d.ts +65 -0
- package/dist/cli/common.d.ts.map +1 -0
- package/dist/cli/common.js +138 -0
- package/dist/cli/common.js.map +1 -0
- package/dist/cli/delete.d.ts +8 -0
- package/dist/cli/delete.d.ts.map +1 -0
- package/dist/cli/delete.js +173 -0
- package/dist/cli/delete.js.map +1 -0
- package/dist/cli/file-collection.d.ts +2 -0
- package/dist/cli/file-collection.d.ts.map +1 -0
- package/dist/cli/file-collection.js +53 -0
- package/dist/cli/file-collection.js.map +1 -0
- package/dist/cli/ingest.d.ts +100 -0
- package/dist/cli/ingest.d.ts.map +1 -0
- package/dist/cli/ingest.js +363 -0
- package/dist/cli/ingest.js.map +1 -0
- package/dist/cli/list.d.ts +35 -0
- package/dist/cli/list.d.ts.map +1 -0
- package/dist/cli/list.js +210 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/options.d.ts +100 -0
- package/dist/cli/options.d.ts.map +1 -0
- package/dist/cli/options.js +241 -0
- package/dist/cli/options.js.map +1 -0
- package/dist/cli/query.d.ts +24 -0
- package/dist/cli/query.d.ts.map +1 -0
- package/dist/cli/query.js +191 -0
- package/dist/cli/query.js.map +1 -0
- package/dist/cli/read-neighbors.d.ts +11 -0
- package/dist/cli/read-neighbors.d.ts.map +1 -0
- package/dist/cli/read-neighbors.js +224 -0
- package/dist/cli/read-neighbors.js.map +1 -0
- package/dist/cli/status.d.ts +8 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +80 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/sync.d.ts +8 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +244 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli-main.d.ts +12 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +63 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/embedder/index.d.ts +85 -0
- package/dist/embedder/index.d.ts.map +1 -0
- package/dist/embedder/index.js +284 -0
- package/dist/embedder/index.js.map +1 -0
- package/dist/features/list.d.ts +37 -0
- package/dist/features/list.d.ts.map +1 -0
- package/dist/features/list.js +40 -0
- package/dist/features/list.js.map +1 -0
- package/dist/features/sync.d.ts +207 -0
- package/dist/features/sync.d.ts.map +1 -0
- package/dist/features/sync.js +380 -0
- package/dist/features/sync.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/compute.d.ts +86 -0
- package/dist/ingest/compute.d.ts.map +1 -0
- package/dist/ingest/compute.js +177 -0
- package/dist/ingest/compute.js.map +1 -0
- package/dist/ingest/file.d.ts +27 -0
- package/dist/ingest/file.d.ts.map +1 -0
- package/dist/ingest/file.js +67 -0
- package/dist/ingest/file.js.map +1 -0
- package/dist/ingest/visual.d.ts +45 -0
- package/dist/ingest/visual.d.ts.map +1 -0
- package/dist/ingest/visual.js +234 -0
- package/dist/ingest/visual.js.map +1 -0
- package/dist/parser/docx-parser.d.ts +12 -0
- package/dist/parser/docx-parser.d.ts.map +1 -0
- package/dist/parser/docx-parser.js +328 -0
- package/dist/parser/docx-parser.js.map +1 -0
- package/dist/parser/html-parser.d.ts +18 -0
- package/dist/parser/html-parser.d.ts.map +1 -0
- package/dist/parser/html-parser.js +102 -0
- package/dist/parser/html-parser.js.map +1 -0
- package/dist/parser/index.d.ts +214 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +454 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/pdf-extract.d.ts +81 -0
- package/dist/parser/pdf-extract.d.ts.map +1 -0
- package/dist/parser/pdf-extract.js +112 -0
- package/dist/parser/pdf-extract.js.map +1 -0
- package/dist/parser/pdf-filter.d.ts +117 -0
- package/dist/parser/pdf-filter.d.ts.map +1 -0
- package/dist/parser/pdf-filter.js +528 -0
- package/dist/parser/pdf-filter.js.map +1 -0
- package/dist/parser/title-extractor.d.ts +69 -0
- package/dist/parser/title-extractor.d.ts.map +1 -0
- package/dist/parser/title-extractor.js +145 -0
- package/dist/parser/title-extractor.js.map +1 -0
- package/dist/pdf-visual/captioner.d.ts +16 -0
- package/dist/pdf-visual/captioner.d.ts.map +1 -0
- package/dist/pdf-visual/captioner.js +63 -0
- package/dist/pdf-visual/captioner.js.map +1 -0
- package/dist/pdf-visual/captioners/fast.d.ts +7 -0
- package/dist/pdf-visual/captioners/fast.d.ts.map +1 -0
- package/dist/pdf-visual/captioners/fast.js +103 -0
- package/dist/pdf-visual/captioners/fast.js.map +1 -0
- package/dist/pdf-visual/captioners/quality.d.ts +7 -0
- package/dist/pdf-visual/captioners/quality.d.ts.map +1 -0
- package/dist/pdf-visual/captioners/quality.js +127 -0
- package/dist/pdf-visual/captioners/quality.js.map +1 -0
- package/dist/pdf-visual/captioners/shared.d.ts +44 -0
- package/dist/pdf-visual/captioners/shared.d.ts.map +1 -0
- package/dist/pdf-visual/captioners/shared.js +104 -0
- package/dist/pdf-visual/captioners/shared.js.map +1 -0
- package/dist/pdf-visual/detector.d.ts +9 -0
- package/dist/pdf-visual/detector.d.ts.map +1 -0
- package/dist/pdf-visual/detector.js +234 -0
- package/dist/pdf-visual/detector.js.map +1 -0
- package/dist/pdf-visual/index.d.ts +13 -0
- package/dist/pdf-visual/index.d.ts.map +1 -0
- package/dist/pdf-visual/index.js +45 -0
- package/dist/pdf-visual/index.js.map +1 -0
- package/dist/pdf-visual/renderer.d.ts +9 -0
- package/dist/pdf-visual/renderer.d.ts.map +1 -0
- package/dist/pdf-visual/renderer.js +177 -0
- package/dist/pdf-visual/renderer.js.map +1 -0
- package/dist/pdf-visual/types.d.ts +62 -0
- package/dist/pdf-visual/types.d.ts.map +1 -0
- package/dist/pdf-visual/types.js +32 -0
- package/dist/pdf-visual/types.js.map +1 -0
- package/dist/server/error-utils.d.ts +79 -0
- package/dist/server/error-utils.d.ts.map +1 -0
- package/dist/server/error-utils.js +148 -0
- package/dist/server/error-utils.js.map +1 -0
- package/dist/server/index.d.ts +258 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1104 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/list-scanner.d.ts +52 -0
- package/dist/server/list-scanner.d.ts.map +1 -0
- package/dist/server/list-scanner.js +72 -0
- package/dist/server/list-scanner.js.map +1 -0
- package/dist/server/tool-definitions.d.ts +8 -0
- package/dist/server/tool-definitions.d.ts.map +1 -0
- package/dist/server/tool-definitions.js +181 -0
- package/dist/server/tool-definitions.js.map +1 -0
- package/dist/server/tool-input.d.ts +37 -0
- package/dist/server/tool-input.d.ts.map +1 -0
- package/dist/server/tool-input.js +216 -0
- package/dist/server/tool-input.js.map +1 -0
- package/dist/server/types.d.ts +331 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +3 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server-main.d.ts +46 -0
- package/dist/server-main.d.ts.map +1 -0
- package/dist/server-main.js +242 -0
- package/dist/server-main.js.map +1 -0
- package/dist/utils/base-dirs.d.ts +212 -0
- package/dist/utils/base-dirs.d.ts.map +1 -0
- package/dist/utils/base-dirs.js +422 -0
- package/dist/utils/base-dirs.js.map +1 -0
- package/dist/utils/errors.d.ts +24 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +53 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/limits.d.ts +26 -0
- package/dist/utils/limits.d.ts.map +1 -0
- package/dist/utils/limits.js +28 -0
- package/dist/utils/limits.js.map +1 -0
- package/dist/utils/list-sources.d.ts +47 -0
- package/dist/utils/list-sources.d.ts.map +1 -0
- package/dist/utils/list-sources.js +50 -0
- package/dist/utils/list-sources.js.map +1 -0
- package/dist/utils/raw-data-utils.d.ts +131 -0
- package/dist/utils/raw-data-utils.d.ts.map +1 -0
- package/dist/utils/raw-data-utils.js +255 -0
- package/dist/utils/raw-data-utils.js.map +1 -0
- package/dist/utils/scan.d.ts +126 -0
- package/dist/utils/scan.d.ts.map +1 -0
- package/dist/utils/scan.js +221 -0
- package/dist/utils/scan.js.map +1 -0
- package/dist/utils/scope-match.d.ts +43 -0
- package/dist/utils/scope-match.d.ts.map +1 -0
- package/dist/utils/scope-match.js +87 -0
- package/dist/utils/scope-match.js.map +1 -0
- package/dist/utils/sensitive-path.d.ts +23 -0
- package/dist/utils/sensitive-path.d.ts.map +1 -0
- package/dist/utils/sensitive-path.js +91 -0
- package/dist/utils/sensitive-path.js.map +1 -0
- package/dist/utils/sync-path-key.d.ts +20 -0
- package/dist/utils/sync-path-key.d.ts.map +1 -0
- package/dist/utils/sync-path-key.js +33 -0
- package/dist/utils/sync-path-key.js.map +1 -0
- package/dist/vectordb/index.d.ts +168 -0
- package/dist/vectordb/index.d.ts.map +1 -0
- package/dist/vectordb/index.js +619 -0
- package/dist/vectordb/index.js.map +1 -0
- package/dist/vectordb/search-filters.d.ts +39 -0
- package/dist/vectordb/search-filters.d.ts.map +1 -0
- package/dist/vectordb/search-filters.js +136 -0
- package/dist/vectordb/search-filters.js.map +1 -0
- package/dist/vectordb/types.d.ts +196 -0
- package/dist/vectordb/types.d.ts.map +1 -0
- package/dist/vectordb/types.js +224 -0
- package/dist/vectordb/types.js.map +1 -0
- package/package.json +105 -0
- package/skills/mcp-local-rag/SKILL.md +308 -0
- package/skills/mcp-local-rag/references/cli-reference.md +175 -0
- package/skills/mcp-local-rag/references/html-ingestion.md +78 -0
- package/skills/mcp-local-rag/references/query-optimization.md +57 -0
- package/skills/mcp-local-rag/references/result-refinement.md +56 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { GroupingMode, SearchResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Apply grouping algorithm to filter results by detecting group boundaries.
|
|
4
|
+
*
|
|
5
|
+
* Uses statistical threshold (mean + k*std) to identify significant gaps (group boundaries).
|
|
6
|
+
* - 'similar': Returns only the first group (cuts at first boundary)
|
|
7
|
+
* - 'related': Returns up to 2 groups (cuts at second boundary)
|
|
8
|
+
*
|
|
9
|
+
* @param results - Search results sorted by distance (ascending)
|
|
10
|
+
* @param mode - Grouping mode ('similar' = 1 group, 'related' = 2 groups)
|
|
11
|
+
* @returns Filtered results
|
|
12
|
+
*/
|
|
13
|
+
export declare function applyGrouping(results: SearchResult[], mode: GroupingMode): SearchResult[];
|
|
14
|
+
/**
|
|
15
|
+
* Apply file-based filter to limit results to chunks from the top N files.
|
|
16
|
+
*
|
|
17
|
+
* Ranks files by their best (lowest distance) chunk score and keeps only
|
|
18
|
+
* chunks belonging to the top `maxFiles` files.
|
|
19
|
+
*
|
|
20
|
+
* @param results - Search results sorted by distance (ascending)
|
|
21
|
+
* @param maxFiles - Maximum number of files to keep
|
|
22
|
+
* @returns Filtered results preserving original order
|
|
23
|
+
*/
|
|
24
|
+
export declare function applyFileFilter(results: SearchResult[], maxFiles: number): SearchResult[];
|
|
25
|
+
/**
|
|
26
|
+
* Apply keyword boost to rerank vector search results
|
|
27
|
+
* Uses multiplicative formula: final_distance = distance / (1 + keyword_normalized * weight)
|
|
28
|
+
*
|
|
29
|
+
* This proportional boost ensures:
|
|
30
|
+
* - Keyword matches improve ranking without dominating semantic similarity
|
|
31
|
+
* - Documents without keyword matches keep their original vector distance
|
|
32
|
+
* - Higher weight = stronger influence of keyword matching
|
|
33
|
+
*
|
|
34
|
+
* @param vectorResults - Results from vector search (already filtered by maxDistance/grouping)
|
|
35
|
+
* @param ftsResults - Raw FTS results with BM25 scores
|
|
36
|
+
* @param weight - Boost weight (0-1, from hybridWeight config)
|
|
37
|
+
*/
|
|
38
|
+
export declare function applyKeywordBoost(vectorResults: SearchResult[], ftsResults: Record<string, unknown>[], weight: number): SearchResult[];
|
|
39
|
+
//# sourceMappingURL=search-filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-filters.d.ts","sourceRoot":"","sources":["../../src/vectordb/search-filters.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAS5D;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,YAAY,EAAE,CAyCzF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CAyBzF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,YAAY,EAAE,EAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACrC,MAAM,EAAE,MAAM,GACb,YAAY,EAAE,CAoChB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Search filter functions extracted from VectorStore for testability
|
|
2
|
+
/**
|
|
3
|
+
* Standard deviation multiplier for detecting group boundaries.
|
|
4
|
+
* A gap is considered a "boundary" if it exceeds mean + k*std.
|
|
5
|
+
* Value of 1.5 means gaps > 1.5 standard deviations above mean are boundaries.
|
|
6
|
+
*/
|
|
7
|
+
const GROUPING_BOUNDARY_STD_MULTIPLIER = 1.5;
|
|
8
|
+
/**
|
|
9
|
+
* Apply grouping algorithm to filter results by detecting group boundaries.
|
|
10
|
+
*
|
|
11
|
+
* Uses statistical threshold (mean + k*std) to identify significant gaps (group boundaries).
|
|
12
|
+
* - 'similar': Returns only the first group (cuts at first boundary)
|
|
13
|
+
* - 'related': Returns up to 2 groups (cuts at second boundary)
|
|
14
|
+
*
|
|
15
|
+
* @param results - Search results sorted by distance (ascending)
|
|
16
|
+
* @param mode - Grouping mode ('similar' = 1 group, 'related' = 2 groups)
|
|
17
|
+
* @returns Filtered results
|
|
18
|
+
*/
|
|
19
|
+
export function applyGrouping(results, mode) {
|
|
20
|
+
if (results.length <= 1)
|
|
21
|
+
return results;
|
|
22
|
+
// Calculate gaps between consecutive results with their indices
|
|
23
|
+
const gaps = [];
|
|
24
|
+
for (let i = 0; i < results.length - 1; i++) {
|
|
25
|
+
const current = results[i];
|
|
26
|
+
const next = results[i + 1];
|
|
27
|
+
if (current !== undefined && next !== undefined) {
|
|
28
|
+
gaps.push({ index: i + 1, gap: next.score - current.score });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (gaps.length === 0)
|
|
32
|
+
return results;
|
|
33
|
+
// Calculate statistical threshold to identify significant gaps (group boundaries)
|
|
34
|
+
const gapValues = gaps.map((g) => g.gap);
|
|
35
|
+
const mean = gapValues.reduce((a, b) => a + b, 0) / gapValues.length;
|
|
36
|
+
const variance = gapValues.reduce((a, b) => a + (b - mean) ** 2, 0) / gapValues.length;
|
|
37
|
+
const std = Math.sqrt(variance);
|
|
38
|
+
const threshold = mean + GROUPING_BOUNDARY_STD_MULTIPLIER * std;
|
|
39
|
+
// Find all significant gaps (group boundaries)
|
|
40
|
+
const boundaries = gaps.filter((g) => g.gap > threshold).map((g) => g.index);
|
|
41
|
+
// If no boundaries found, return all results
|
|
42
|
+
if (boundaries.length === 0)
|
|
43
|
+
return results;
|
|
44
|
+
// Determine how many groups to include based on mode
|
|
45
|
+
// 'similar': 1 group (cut at first boundary)
|
|
46
|
+
// 'related': 2 groups (cut at second boundary, or return all if only 1 boundary)
|
|
47
|
+
const groupsToInclude = mode === 'similar' ? 1 : 2;
|
|
48
|
+
const boundaryIndex = groupsToInclude - 1;
|
|
49
|
+
// If we don't have enough boundaries, return all results for 'related' mode
|
|
50
|
+
if (boundaryIndex >= boundaries.length) {
|
|
51
|
+
return mode === 'related' ? results : results.slice(0, boundaries[0]);
|
|
52
|
+
}
|
|
53
|
+
// Cut at the appropriate boundary
|
|
54
|
+
return results.slice(0, boundaries[boundaryIndex]);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Apply file-based filter to limit results to chunks from the top N files.
|
|
58
|
+
*
|
|
59
|
+
* Ranks files by their best (lowest distance) chunk score and keeps only
|
|
60
|
+
* chunks belonging to the top `maxFiles` files.
|
|
61
|
+
*
|
|
62
|
+
* @param results - Search results sorted by distance (ascending)
|
|
63
|
+
* @param maxFiles - Maximum number of files to keep
|
|
64
|
+
* @returns Filtered results preserving original order
|
|
65
|
+
*/
|
|
66
|
+
export function applyFileFilter(results, maxFiles) {
|
|
67
|
+
if (results.length === 0)
|
|
68
|
+
return results;
|
|
69
|
+
// Find the best (lowest) score per file
|
|
70
|
+
const fileScores = new Map();
|
|
71
|
+
for (const result of results) {
|
|
72
|
+
const current = fileScores.get(result.filePath);
|
|
73
|
+
if (current === undefined || result.score < current) {
|
|
74
|
+
fileScores.set(result.filePath, result.score);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// If we have fewer or equal files than maxFiles, return all
|
|
78
|
+
if (fileScores.size <= maxFiles)
|
|
79
|
+
return results;
|
|
80
|
+
// Sort files by best score (ascending) and take top N
|
|
81
|
+
const topFiles = new Set([...fileScores.entries()]
|
|
82
|
+
.sort((a, b) => a[1] - b[1])
|
|
83
|
+
.slice(0, maxFiles)
|
|
84
|
+
.map(([filePath]) => filePath));
|
|
85
|
+
// Filter results to only include chunks from top files
|
|
86
|
+
return results.filter((result) => topFiles.has(result.filePath));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Apply keyword boost to rerank vector search results
|
|
90
|
+
* Uses multiplicative formula: final_distance = distance / (1 + keyword_normalized * weight)
|
|
91
|
+
*
|
|
92
|
+
* This proportional boost ensures:
|
|
93
|
+
* - Keyword matches improve ranking without dominating semantic similarity
|
|
94
|
+
* - Documents without keyword matches keep their original vector distance
|
|
95
|
+
* - Higher weight = stronger influence of keyword matching
|
|
96
|
+
*
|
|
97
|
+
* @param vectorResults - Results from vector search (already filtered by maxDistance/grouping)
|
|
98
|
+
* @param ftsResults - Raw FTS results with BM25 scores
|
|
99
|
+
* @param weight - Boost weight (0-1, from hybridWeight config)
|
|
100
|
+
*/
|
|
101
|
+
export function applyKeywordBoost(vectorResults, ftsResults, weight) {
|
|
102
|
+
// Build FTS score map with normalized scores (0-1)
|
|
103
|
+
let maxBm25Score = 0;
|
|
104
|
+
for (const result of ftsResults) {
|
|
105
|
+
if (!result)
|
|
106
|
+
continue;
|
|
107
|
+
const score = result['_score'] ?? 0;
|
|
108
|
+
if (score > maxBm25Score)
|
|
109
|
+
maxBm25Score = score;
|
|
110
|
+
}
|
|
111
|
+
const ftsScoreMap = new Map();
|
|
112
|
+
for (const result of ftsResults) {
|
|
113
|
+
if (!result)
|
|
114
|
+
continue;
|
|
115
|
+
const key = `${result['filePath']}:${result['chunkIndex']}`;
|
|
116
|
+
const rawScore = result['_score'] ?? 0;
|
|
117
|
+
const normalized = maxBm25Score > 0 ? rawScore / maxBm25Score : 0;
|
|
118
|
+
ftsScoreMap.set(key, normalized);
|
|
119
|
+
}
|
|
120
|
+
// Apply multiplicative boost to vector results
|
|
121
|
+
const boostedResults = vectorResults.map((result) => {
|
|
122
|
+
const key = `${result.filePath}:${result.chunkIndex}`;
|
|
123
|
+
const keywordScore = ftsScoreMap.get(key) ?? 0;
|
|
124
|
+
// Multiplicative boost: distance / (1 + keyword * weight)
|
|
125
|
+
// - If keyword matches (score=1) and weight=1: distance halved
|
|
126
|
+
// - If no keyword match (score=0): distance unchanged
|
|
127
|
+
const boostedDistance = result.score / (1 + keywordScore * weight);
|
|
128
|
+
return {
|
|
129
|
+
...result,
|
|
130
|
+
score: boostedDistance,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
// Re-sort by boosted distance (ascending = better)
|
|
134
|
+
return boostedResults.sort((a, b) => a.score - b.score);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=search-filters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-filters.js","sourceRoot":"","sources":["../../src/vectordb/search-filters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { AppError } from '../utils/errors.js';
|
|
2
|
+
/** Multiplier for candidate count in hybrid search (to allow reranking) */
|
|
3
|
+
export declare const HYBRID_SEARCH_CANDIDATE_MULTIPLIER = 2;
|
|
4
|
+
/** FTS index name (bump version when changing tokenizer settings) */
|
|
5
|
+
export declare const FTS_INDEX_NAME = "fts_index_v2";
|
|
6
|
+
/** Threshold for cleaning up old index versions (1 minute) */
|
|
7
|
+
export declare const FTS_CLEANUP_THRESHOLD_MS: number;
|
|
8
|
+
/** Default hybrid-search weight (vector vs FTS blend) when not configured */
|
|
9
|
+
export declare const DEFAULT_HYBRID_WEIGHT = 0.6;
|
|
10
|
+
/**
|
|
11
|
+
* Grouping mode for quality filtering
|
|
12
|
+
* - 'similar': Only return the most similar group (stops at first distance jump)
|
|
13
|
+
* - 'related': Include related groups (stops at second distance jump)
|
|
14
|
+
*/
|
|
15
|
+
export type GroupingMode = 'similar' | 'related';
|
|
16
|
+
/**
|
|
17
|
+
* VectorStore configuration
|
|
18
|
+
*/
|
|
19
|
+
export interface VectorStoreConfig {
|
|
20
|
+
/** LanceDB database path */
|
|
21
|
+
dbPath: string;
|
|
22
|
+
/** Table name */
|
|
23
|
+
tableName: string;
|
|
24
|
+
/** Maximum distance threshold for filtering results (optional) */
|
|
25
|
+
maxDistance?: number;
|
|
26
|
+
/** Grouping mode for quality filtering (optional) */
|
|
27
|
+
grouping?: GroupingMode;
|
|
28
|
+
/** Hybrid search weight for BM25 (0.0 = vector only, 1.0 = BM25 only, default 0.6) */
|
|
29
|
+
hybridWeight?: number;
|
|
30
|
+
/** Maximum number of files to keep in results (optional, filters by best score per file) */
|
|
31
|
+
maxFiles?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Per-call options for {@link VectorStore.search}.
|
|
35
|
+
* Grouped into an object (instead of positional params) so the caller can pass
|
|
36
|
+
* any subset and so adding options (like `scope`) is not a breaking signature
|
|
37
|
+
* change.
|
|
38
|
+
*/
|
|
39
|
+
export interface SearchOptions {
|
|
40
|
+
/** Optional query text for keyword boost (BM25) */
|
|
41
|
+
queryText?: string;
|
|
42
|
+
/** Number of results to retrieve (default 10, valid range 1-20) */
|
|
43
|
+
limit?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Optional path-prefix scope (exact-or-descendant, prefixes unioned). Omitted
|
|
46
|
+
* = no prefilter (backward compatible).
|
|
47
|
+
*/
|
|
48
|
+
scope?: string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Document metadata
|
|
52
|
+
*/
|
|
53
|
+
export interface DocumentMetadata {
|
|
54
|
+
/** File name */
|
|
55
|
+
fileName: string;
|
|
56
|
+
/** File size in bytes */
|
|
57
|
+
fileSize: number;
|
|
58
|
+
/** File type (extension) */
|
|
59
|
+
fileType: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validated, bounded image stored in a chunk's ordered attachment JSON.
|
|
63
|
+
*/
|
|
64
|
+
export interface VisualAttachment {
|
|
65
|
+
imageIndex: number;
|
|
66
|
+
mimeType: 'image/png' | 'image/jpeg';
|
|
67
|
+
data: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Vector chunk
|
|
71
|
+
*/
|
|
72
|
+
export interface VectorChunk {
|
|
73
|
+
/** Chunk ID (UUID) */
|
|
74
|
+
id: string;
|
|
75
|
+
/** File path (absolute) */
|
|
76
|
+
filePath: string;
|
|
77
|
+
/** Chunk index (zero-based) */
|
|
78
|
+
chunkIndex: number;
|
|
79
|
+
/** Chunk text */
|
|
80
|
+
text: string;
|
|
81
|
+
/** Embedding vector (dimension depends on model) */
|
|
82
|
+
vector: number[];
|
|
83
|
+
/** Metadata */
|
|
84
|
+
metadata: DocumentMetadata;
|
|
85
|
+
/** Document title extracted from file content (display-only, not used for scoring) */
|
|
86
|
+
fileTitle: string | null;
|
|
87
|
+
/** SHA-256 of the source file bytes; absent for chunks not ingested from a file. */
|
|
88
|
+
contentHash?: string;
|
|
89
|
+
/** Ordered `JSON.stringify(VisualAttachment[])`; omitted means no attachments. */
|
|
90
|
+
visualAttachments?: string;
|
|
91
|
+
/** Ingestion timestamp (ISO 8601 format) */
|
|
92
|
+
timestamp: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Search result
|
|
96
|
+
*/
|
|
97
|
+
export interface SearchResult {
|
|
98
|
+
/** Stable persisted row identity used for attachment hydration. */
|
|
99
|
+
id: string;
|
|
100
|
+
/** File path */
|
|
101
|
+
filePath: string;
|
|
102
|
+
/** Chunk index */
|
|
103
|
+
chunkIndex: number;
|
|
104
|
+
/** Chunk text */
|
|
105
|
+
text: string;
|
|
106
|
+
/** Distance score using dot product (0 = identical, 1 = orthogonal, 2 = opposite) */
|
|
107
|
+
score: number;
|
|
108
|
+
/** Metadata */
|
|
109
|
+
metadata: DocumentMetadata;
|
|
110
|
+
/** Document title extracted from file content (display-only, not used for scoring) */
|
|
111
|
+
fileTitle: string | null;
|
|
112
|
+
}
|
|
113
|
+
/** Validated attachments for one persisted row, in visual order. */
|
|
114
|
+
export interface HydratedChunkAttachments {
|
|
115
|
+
id: string;
|
|
116
|
+
attachments: VisualAttachment[];
|
|
117
|
+
}
|
|
118
|
+
/** Result of one final-identity hydration query. */
|
|
119
|
+
export interface AttachmentHydrationResult {
|
|
120
|
+
rows: HydratedChunkAttachments[];
|
|
121
|
+
omittedCount: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Row returned by VectorStore.getChunksByRange.
|
|
125
|
+
* Distinct from SearchResult: no score (not a ranked result) and no metadata
|
|
126
|
+
* (not needed for index-adjacent retrieval). Consumed by
|
|
127
|
+
* handleReadChunkNeighbors and runReadNeighbors.
|
|
128
|
+
*/
|
|
129
|
+
export interface ChunkRow {
|
|
130
|
+
/** File path (absolute) */
|
|
131
|
+
filePath: string;
|
|
132
|
+
/** Chunk index (zero-based) */
|
|
133
|
+
chunkIndex: number;
|
|
134
|
+
/** Chunk text */
|
|
135
|
+
text: string;
|
|
136
|
+
/** Document title extracted from file content (display-only, not used for scoring) */
|
|
137
|
+
fileTitle: string | null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Raw result from LanceDB query (internal type)
|
|
141
|
+
*/
|
|
142
|
+
export interface LanceDBRawResult {
|
|
143
|
+
id: string;
|
|
144
|
+
filePath: string;
|
|
145
|
+
chunkIndex: number;
|
|
146
|
+
text: string;
|
|
147
|
+
metadata: DocumentMetadata;
|
|
148
|
+
/** Document title (optional - existing rows lack this field before migration) */
|
|
149
|
+
fileTitle?: string | null;
|
|
150
|
+
_distance?: number;
|
|
151
|
+
_score?: number;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Type guard for LanceDB raw search result
|
|
155
|
+
*/
|
|
156
|
+
export declare function isLanceDBRawResult(value: unknown): value is LanceDBRawResult;
|
|
157
|
+
/**
|
|
158
|
+
* Convert LanceDB raw result to SearchResult with type validation
|
|
159
|
+
* @throws DatabaseError if the result is invalid
|
|
160
|
+
*/
|
|
161
|
+
export declare function toSearchResult(raw: unknown): SearchResult;
|
|
162
|
+
/**
|
|
163
|
+
* Map a raw LanceDB row to a full {@link VectorChunk}, including the stored
|
|
164
|
+
* embedding vector and metadata. Used for backup/restore (ingest rollback),
|
|
165
|
+
* where the row must round-trip back through `insertChunks` intact — unlike
|
|
166
|
+
* {@link toChunkRow} / {@link toSearchResult}, which drop the vector. The
|
|
167
|
+
* embedding is normalized to `number[]` (LanceDB returns a typed array).
|
|
168
|
+
*/
|
|
169
|
+
export declare function toVectorChunk(raw: unknown): VectorChunk;
|
|
170
|
+
/** Normalize only the defined legacy no-image sentinels; keep malformed JSON observable. */
|
|
171
|
+
export declare function normalizeVisualAttachments(value: unknown): string;
|
|
172
|
+
/** Parse one persisted attachment cell while keeping malformed siblings observable. */
|
|
173
|
+
export declare function parseHydratedVisualAttachments(value: unknown): {
|
|
174
|
+
attachments: VisualAttachment[];
|
|
175
|
+
omittedCount: number;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Convert LanceDB raw row to ChunkRow with type validation.
|
|
179
|
+
* Mirrors toSearchResult but returns the minimal range-read shape: no score
|
|
180
|
+
* (not ranked) and no metadata (not needed for index-adjacent retrieval).
|
|
181
|
+
*
|
|
182
|
+
* Uses a narrower shape check than isLanceDBRawResult: only
|
|
183
|
+
* filePath/chunkIndex/text are required because getChunksByRange
|
|
184
|
+
* does not project metadata. The empty-string-or-missing fileTitle
|
|
185
|
+
* is normalized to null per §Field Propagation Map.
|
|
186
|
+
*
|
|
187
|
+
* @throws DatabaseError if the raw row is missing required fields
|
|
188
|
+
*/
|
|
189
|
+
export declare function toChunkRow(raw: unknown): ChunkRow;
|
|
190
|
+
/**
|
|
191
|
+
* Database error
|
|
192
|
+
*/
|
|
193
|
+
export declare class DatabaseError extends AppError {
|
|
194
|
+
constructor(message: string, cause?: Error);
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/vectordb/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// VectorDB type definitions, constants, type guards, and error classes
|
|
2
|
+
import { AppError } from '../utils/errors.js';
|
|
3
|
+
import { MAX_VISUAL_RENDITION_BYTES } from '../utils/limits.js';
|
|
4
|
+
// ============================================
|
|
5
|
+
// Constants
|
|
6
|
+
// ============================================
|
|
7
|
+
/** Multiplier for candidate count in hybrid search (to allow reranking) */
|
|
8
|
+
export const HYBRID_SEARCH_CANDIDATE_MULTIPLIER = 2;
|
|
9
|
+
/** FTS index name (bump version when changing tokenizer settings) */
|
|
10
|
+
export const FTS_INDEX_NAME = 'fts_index_v2';
|
|
11
|
+
/** Threshold for cleaning up old index versions (1 minute) */
|
|
12
|
+
export const FTS_CLEANUP_THRESHOLD_MS = 60 * 1000;
|
|
13
|
+
/** Default hybrid-search weight (vector vs FTS blend) when not configured */
|
|
14
|
+
export const DEFAULT_HYBRID_WEIGHT = 0.6;
|
|
15
|
+
// ============================================
|
|
16
|
+
// Type Guards
|
|
17
|
+
// ============================================
|
|
18
|
+
/**
|
|
19
|
+
* Type guard for DocumentMetadata
|
|
20
|
+
*/
|
|
21
|
+
function isDocumentMetadata(value) {
|
|
22
|
+
if (typeof value !== 'object' || value === null)
|
|
23
|
+
return false;
|
|
24
|
+
const obj = value;
|
|
25
|
+
return (typeof obj['fileName'] === 'string' &&
|
|
26
|
+
typeof obj['fileSize'] === 'number' &&
|
|
27
|
+
typeof obj['fileType'] === 'string');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Type guard for LanceDB raw search result
|
|
31
|
+
*/
|
|
32
|
+
export function isLanceDBRawResult(value) {
|
|
33
|
+
if (typeof value !== 'object' || value === null)
|
|
34
|
+
return false;
|
|
35
|
+
const obj = value;
|
|
36
|
+
return (typeof obj['id'] === 'string' &&
|
|
37
|
+
typeof obj['filePath'] === 'string' &&
|
|
38
|
+
typeof obj['chunkIndex'] === 'number' &&
|
|
39
|
+
typeof obj['text'] === 'string' &&
|
|
40
|
+
isDocumentMetadata(obj['metadata']));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Convert LanceDB raw result to SearchResult with type validation
|
|
44
|
+
* @throws DatabaseError if the result is invalid
|
|
45
|
+
*/
|
|
46
|
+
export function toSearchResult(raw) {
|
|
47
|
+
if (!isLanceDBRawResult(raw)) {
|
|
48
|
+
throw new DatabaseError('Invalid search result format from LanceDB');
|
|
49
|
+
}
|
|
50
|
+
// Score source: vector search rows carry `_distance` (dot distance, the
|
|
51
|
+
// normal path). `_score` is a defensive fallback for any FTS-shaped row that
|
|
52
|
+
// reaches here (the live FTS path consumes `_score` directly in
|
|
53
|
+
// applyKeywordBoost, not via this mapper). The final `?? 0` is an
|
|
54
|
+
// effectively-unreachable guard: vectorSearch always returns `_distance`. It
|
|
55
|
+
// is kept defensive rather than throwing, since a missing score is not worth
|
|
56
|
+
// failing a whole search over.
|
|
57
|
+
return {
|
|
58
|
+
id: raw.id,
|
|
59
|
+
filePath: raw.filePath,
|
|
60
|
+
chunkIndex: raw.chunkIndex,
|
|
61
|
+
text: raw.text,
|
|
62
|
+
score: raw._distance ?? raw._score ?? 0,
|
|
63
|
+
metadata: raw.metadata,
|
|
64
|
+
fileTitle: raw.fileTitle || null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Map a raw LanceDB row to a full {@link VectorChunk}, including the stored
|
|
69
|
+
* embedding vector and metadata. Used for backup/restore (ingest rollback),
|
|
70
|
+
* where the row must round-trip back through `insertChunks` intact — unlike
|
|
71
|
+
* {@link toChunkRow} / {@link toSearchResult}, which drop the vector. The
|
|
72
|
+
* embedding is normalized to `number[]` (LanceDB returns a typed array).
|
|
73
|
+
*/
|
|
74
|
+
export function toVectorChunk(raw) {
|
|
75
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
76
|
+
throw new DatabaseError('Invalid chunk row shape from LanceDB');
|
|
77
|
+
}
|
|
78
|
+
const obj = raw;
|
|
79
|
+
const { id, filePath, chunkIndex, text, vector, metadata, fileTitle, contentHash, visualAttachments, timestamp, } = obj;
|
|
80
|
+
if (typeof id !== 'string' ||
|
|
81
|
+
typeof filePath !== 'string' ||
|
|
82
|
+
typeof chunkIndex !== 'number' ||
|
|
83
|
+
typeof text !== 'string' ||
|
|
84
|
+
typeof timestamp !== 'string') {
|
|
85
|
+
throw new DatabaseError('Invalid chunk row shape from LanceDB (scalar fields)');
|
|
86
|
+
}
|
|
87
|
+
if (!isDocumentMetadata(metadata)) {
|
|
88
|
+
throw new DatabaseError('Invalid chunk row shape from LanceDB (metadata)');
|
|
89
|
+
}
|
|
90
|
+
if (vector == null || typeof vector.length !== 'number') {
|
|
91
|
+
throw new DatabaseError('Invalid chunk row shape from LanceDB (vector)');
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
id,
|
|
95
|
+
filePath,
|
|
96
|
+
chunkIndex,
|
|
97
|
+
text,
|
|
98
|
+
vector: Array.from(vector),
|
|
99
|
+
metadata,
|
|
100
|
+
fileTitle: typeof fileTitle === 'string' && fileTitle.length > 0 ? fileTitle : null,
|
|
101
|
+
// Omit the key rather than store '' or undefined: the create path seeds ''
|
|
102
|
+
// for schema inference, and a '' that survived to a caller would read as a
|
|
103
|
+
// real hash equal to nothing on disk.
|
|
104
|
+
...(typeof contentHash === 'string' && contentHash.length > 0 ? { contentHash } : {}),
|
|
105
|
+
visualAttachments: normalizeVisualAttachments(visualAttachments),
|
|
106
|
+
timestamp,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** Normalize only the defined legacy no-image sentinels; keep malformed JSON observable. */
|
|
110
|
+
export function normalizeVisualAttachments(value) {
|
|
111
|
+
return typeof value === 'string' && value.length > 0 ? value : '[]';
|
|
112
|
+
}
|
|
113
|
+
const VISUAL_RENDITION_MAX_BASE64_LENGTH = Math.ceil(MAX_VISUAL_RENDITION_BYTES / 3) * 4;
|
|
114
|
+
function decodeStrictBase64(value) {
|
|
115
|
+
if (typeof value !== 'string' ||
|
|
116
|
+
value.length === 0 ||
|
|
117
|
+
value.length > VISUAL_RENDITION_MAX_BASE64_LENGTH ||
|
|
118
|
+
value.length % 4 !== 0 ||
|
|
119
|
+
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const decoded = Buffer.from(value, 'base64');
|
|
123
|
+
return decoded.toString('base64') === value ? decoded : null;
|
|
124
|
+
}
|
|
125
|
+
function isVisualAttachment(value) {
|
|
126
|
+
if (typeof value !== 'object' || value === null)
|
|
127
|
+
return false;
|
|
128
|
+
const attachment = value;
|
|
129
|
+
if (!Number.isInteger(attachment.imageIndex) ||
|
|
130
|
+
attachment.imageIndex < 0 ||
|
|
131
|
+
(attachment.mimeType !== 'image/png' && attachment.mimeType !== 'image/jpeg')) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const bytes = decodeStrictBase64(attachment.data);
|
|
135
|
+
if (!bytes || bytes.byteLength > MAX_VISUAL_RENDITION_BYTES)
|
|
136
|
+
return false;
|
|
137
|
+
return attachment.mimeType === 'image/png'
|
|
138
|
+
? bytes.length >= 8 &&
|
|
139
|
+
bytes[0] === 0x89 &&
|
|
140
|
+
bytes[1] === 0x50 &&
|
|
141
|
+
bytes[2] === 0x4e &&
|
|
142
|
+
bytes[3] === 0x47 &&
|
|
143
|
+
bytes[4] === 0x0d &&
|
|
144
|
+
bytes[5] === 0x0a &&
|
|
145
|
+
bytes[6] === 0x1a &&
|
|
146
|
+
bytes[7] === 0x0a
|
|
147
|
+
: bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff;
|
|
148
|
+
}
|
|
149
|
+
/** Parse one persisted attachment cell while keeping malformed siblings observable. */
|
|
150
|
+
export function parseHydratedVisualAttachments(value) {
|
|
151
|
+
if (value === null || value === undefined || value === '' || value === '[]') {
|
|
152
|
+
return { attachments: [], omittedCount: 0 };
|
|
153
|
+
}
|
|
154
|
+
if (typeof value !== 'string')
|
|
155
|
+
return { attachments: [], omittedCount: 1 };
|
|
156
|
+
let parsed;
|
|
157
|
+
try {
|
|
158
|
+
parsed = JSON.parse(value);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return { attachments: [], omittedCount: 1 };
|
|
162
|
+
}
|
|
163
|
+
if (!Array.isArray(parsed))
|
|
164
|
+
return { attachments: [], omittedCount: 1 };
|
|
165
|
+
const attachments = [];
|
|
166
|
+
let omittedCount = 0;
|
|
167
|
+
for (const item of parsed) {
|
|
168
|
+
if (!isVisualAttachment(item)) {
|
|
169
|
+
omittedCount += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
attachments.push({
|
|
173
|
+
imageIndex: item.imageIndex,
|
|
174
|
+
mimeType: item.mimeType,
|
|
175
|
+
data: item.data,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
attachments.sort((left, right) => left.imageIndex - right.imageIndex);
|
|
179
|
+
return { attachments, omittedCount };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Convert LanceDB raw row to ChunkRow with type validation.
|
|
183
|
+
* Mirrors toSearchResult but returns the minimal range-read shape: no score
|
|
184
|
+
* (not ranked) and no metadata (not needed for index-adjacent retrieval).
|
|
185
|
+
*
|
|
186
|
+
* Uses a narrower shape check than isLanceDBRawResult: only
|
|
187
|
+
* filePath/chunkIndex/text are required because getChunksByRange
|
|
188
|
+
* does not project metadata. The empty-string-or-missing fileTitle
|
|
189
|
+
* is normalized to null per §Field Propagation Map.
|
|
190
|
+
*
|
|
191
|
+
* @throws DatabaseError if the raw row is missing required fields
|
|
192
|
+
*/
|
|
193
|
+
export function toChunkRow(raw) {
|
|
194
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
195
|
+
throw new DatabaseError('Invalid chunk row shape from LanceDB');
|
|
196
|
+
}
|
|
197
|
+
const obj = raw;
|
|
198
|
+
if (typeof obj['filePath'] !== 'string' ||
|
|
199
|
+
typeof obj['chunkIndex'] !== 'number' ||
|
|
200
|
+
typeof obj['text'] !== 'string') {
|
|
201
|
+
throw new DatabaseError('Invalid chunk row shape from LanceDB');
|
|
202
|
+
}
|
|
203
|
+
const rawFileTitle = obj['fileTitle'];
|
|
204
|
+
const fileTitle = typeof rawFileTitle === 'string' && rawFileTitle.length > 0 ? rawFileTitle : null;
|
|
205
|
+
return {
|
|
206
|
+
filePath: obj['filePath'],
|
|
207
|
+
chunkIndex: obj['chunkIndex'],
|
|
208
|
+
text: obj['text'],
|
|
209
|
+
fileTitle,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// ============================================
|
|
213
|
+
// Error Classes
|
|
214
|
+
// ============================================
|
|
215
|
+
/**
|
|
216
|
+
* Database error
|
|
217
|
+
*/
|
|
218
|
+
export class DatabaseError extends AppError {
|
|
219
|
+
constructor(message, cause) {
|
|
220
|
+
super(message, 'vectordb', 'internal', cause);
|
|
221
|
+
this.name = 'DatabaseError';
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/vectordb/types.ts"],"names":[],"mappings":"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"}
|