@customize-agent/knowledge 1.0.1 → 2.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/dist/chunking/text-chunker.d.ts +12 -1
- package/dist/chunking/text-chunker.js +219 -41
- package/dist/core/change-tracker.d.ts +1 -0
- package/dist/core/change-tracker.js +21 -0
- package/dist/core/index-state-store.d.ts +37 -1
- package/dist/core/index-state-store.js +300 -28
- package/dist/core/knowledge-base-manager.d.ts +70 -3
- package/dist/core/knowledge-base-manager.js +547 -125
- package/dist/core/multi-project-manager.d.ts +7 -4
- package/dist/core/multi-project-manager.js +35 -23
- package/dist/dedup/dedup-engine.d.ts +3 -0
- package/dist/dedup/dedup-engine.js +12 -2
- package/dist/embedding/embedding-provider.d.ts +1 -0
- package/dist/embedding/embedding-provider.js +16 -1
- package/dist/extraction/content-extractor.d.ts +2 -0
- package/dist/extraction/content-extractor.js +209 -42
- package/dist/extraction/module-resolver.d.ts +17 -0
- package/dist/extraction/module-resolver.js +113 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -3
- package/dist/llm/llm-search-provider.d.ts +23 -0
- package/dist/llm/llm-search-provider.js +1 -0
- package/dist/search/federation-search.d.ts +29 -0
- package/dist/search/federation-search.js +8 -1
- package/dist/vector/chroma-store.d.ts +2 -0
- package/dist/vector/chroma-store.js +53 -22
- package/dist/vector/vector-indexer.d.ts +3 -0
- package/dist/vector/vector-indexer.js +23 -0
- package/package.json +11 -3
- package/dist/server/dashboard-client.d.ts +0 -2
- package/dist/server/dashboard-client.js +0 -396
- package/dist/server/dashboard-i18n.d.ts +0 -112
- package/dist/server/dashboard-i18n.js +0 -220
- package/dist/server/dashboard-page.d.ts +0 -6
- package/dist/server/dashboard-page.js +0 -138
- package/dist/server/dashboard-server.d.ts +0 -13
- package/dist/server/dashboard-server.js +0 -225
- package/dist/server/dashboard-styles.d.ts +0 -1
- package/dist/server/dashboard-styles.js +0 -152
|
@@ -15,9 +15,20 @@ export interface ChunkConfig {
|
|
|
15
15
|
}
|
|
16
16
|
export declare class TextChunker {
|
|
17
17
|
chunk(text: string, file: ClassifiedFile, metadata?: Record<string, unknown>): TextChunk[];
|
|
18
|
+
private createCandidates;
|
|
19
|
+
private createTextCandidates;
|
|
20
|
+
private createTableCandidates;
|
|
21
|
+
private createDataCandidates;
|
|
22
|
+
private createCodeCandidates;
|
|
23
|
+
private splitIntoSections;
|
|
24
|
+
private mergeLeadingHeader;
|
|
25
|
+
private recursiveSplit;
|
|
26
|
+
private mergeParts;
|
|
27
|
+
private splitByWindow;
|
|
28
|
+
private enforceCandidateLimit;
|
|
18
29
|
private withHeader;
|
|
19
|
-
private splitBySemanticBoundary;
|
|
20
30
|
private createChunk;
|
|
31
|
+
private kindForCategory;
|
|
21
32
|
private extractSectionTitle;
|
|
22
33
|
private takeOverlap;
|
|
23
34
|
private estimateTokens;
|
|
@@ -1,75 +1,253 @@
|
|
|
1
1
|
const DEFAULT_CONFIGS = {
|
|
2
2
|
document: { maxChunkSize: 800, overlap: 100, headerInjection: true },
|
|
3
|
-
spreadsheet: { maxChunkSize: 1000, overlap:
|
|
3
|
+
spreadsheet: { maxChunkSize: 1000, overlap: 120, headerInjection: true },
|
|
4
4
|
image: { maxChunkSize: 512, overlap: 0, headerInjection: true },
|
|
5
|
-
cad: { maxChunkSize: 600, overlap:
|
|
6
|
-
code: { maxChunkSize: 1000, overlap:
|
|
7
|
-
data: { maxChunkSize: 600, overlap:
|
|
5
|
+
cad: { maxChunkSize: 600, overlap: 80, headerInjection: true },
|
|
6
|
+
code: { maxChunkSize: 1000, overlap: 120, headerInjection: true },
|
|
7
|
+
data: { maxChunkSize: 600, overlap: 80, headerInjection: true },
|
|
8
8
|
web: { maxChunkSize: 800, overlap: 100, headerInjection: true },
|
|
9
9
|
diagram: { maxChunkSize: 512, overlap: 0, headerInjection: true },
|
|
10
10
|
archive: { maxChunkSize: 500, overlap: 50, headerInjection: false },
|
|
11
11
|
other: { maxChunkSize: 500, overlap: 50, headerInjection: false },
|
|
12
12
|
};
|
|
13
|
+
const RECURSIVE_SEPARATORS = [
|
|
14
|
+
/\n(?=#{1,6}\s)/u,
|
|
15
|
+
/\n{2,}/u,
|
|
16
|
+
/\n(?=(?:第[一二三四五六七八九十百千万\d]+[章节条]|[一二三四五六七八九十]+、|\d+[.)、]))/u,
|
|
17
|
+
/(?<=[。!?;])\s*/u,
|
|
18
|
+
/(?<=[,、])\s*/u,
|
|
19
|
+
/\s+/u,
|
|
20
|
+
];
|
|
13
21
|
export class TextChunker {
|
|
14
22
|
chunk(text, file, metadata = {}) {
|
|
15
|
-
|
|
23
|
+
const source = text.trim();
|
|
24
|
+
if (source.length === 0)
|
|
16
25
|
return [];
|
|
17
26
|
const config = DEFAULT_CONFIGS[file.category];
|
|
18
|
-
const normalized = this.withHeader(
|
|
19
|
-
const
|
|
27
|
+
const normalized = this.withHeader(source, file, config);
|
|
28
|
+
const candidates = this.enforceCandidateLimit(this.createCandidates(normalized, file, config), config);
|
|
29
|
+
return candidates.map((candidate, index) => this.createChunk(index, candidate, file, metadata));
|
|
30
|
+
}
|
|
31
|
+
createCandidates(text, file, config) {
|
|
32
|
+
if (file.category === 'spreadsheet')
|
|
33
|
+
return this.createTableCandidates(text, config);
|
|
34
|
+
if (file.category === 'data')
|
|
35
|
+
return this.createDataCandidates(text, config);
|
|
36
|
+
if (file.category === 'code')
|
|
37
|
+
return this.createCodeCandidates(text, config);
|
|
38
|
+
return this.createTextCandidates(text, file.category, config);
|
|
39
|
+
}
|
|
40
|
+
createTextCandidates(text, category, config) {
|
|
41
|
+
const sections = this.splitIntoSections(text, category);
|
|
42
|
+
const candidates = [];
|
|
43
|
+
sections.forEach((section, parentIndex) => {
|
|
44
|
+
const parentId = `p${parentIndex}`;
|
|
45
|
+
const parts = this.mergeLeadingHeader(this.recursiveSplit(section.text, config.maxChunkSize));
|
|
46
|
+
const merged = this.mergeParts(parts, config.maxChunkSize, config.overlap);
|
|
47
|
+
merged.forEach((part, childIndex) => {
|
|
48
|
+
const localStart = section.text.indexOf(part.replace(/^\s+/u, '').slice(0, 40));
|
|
49
|
+
const startChar = section.startChar + Math.max(0, localStart);
|
|
50
|
+
candidates.push({
|
|
51
|
+
text: part,
|
|
52
|
+
startChar,
|
|
53
|
+
endChar: startChar + part.length,
|
|
54
|
+
sectionTitle: section.title,
|
|
55
|
+
kind: this.kindForCategory(category),
|
|
56
|
+
parentId,
|
|
57
|
+
parentIndex,
|
|
58
|
+
childIndex,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
return candidates;
|
|
63
|
+
}
|
|
64
|
+
createTableCandidates(text, config) {
|
|
65
|
+
const lines = text.split(/\r?\n/u).map(line => line.trim()).filter(Boolean);
|
|
66
|
+
const headerLines = lines.filter(line => /^(文件|类型|Sheet|表格|列|Columns?)[::]/iu.test(line));
|
|
67
|
+
const dataLines = lines.filter(line => !headerLines.includes(line));
|
|
68
|
+
if (dataLines.length === 0)
|
|
69
|
+
return this.createTextCandidates(text, 'spreadsheet', config);
|
|
70
|
+
const header = headerLines.join('\n');
|
|
71
|
+
const candidates = [];
|
|
72
|
+
let rowStart = 0;
|
|
73
|
+
let parentIndex = 0;
|
|
74
|
+
while (rowStart < dataLines.length) {
|
|
75
|
+
const rows = [];
|
|
76
|
+
while (rowStart + rows.length < dataLines.length) {
|
|
77
|
+
const nextLine = dataLines[rowStart + rows.length];
|
|
78
|
+
if (!nextLine)
|
|
79
|
+
break;
|
|
80
|
+
const nextRows = [...rows, nextLine];
|
|
81
|
+
const candidate = [header, `行范围: ${rowStart + 1}-${rowStart + nextRows.length}`, ...nextRows].filter(Boolean).join('\n');
|
|
82
|
+
if (rows.length > 0 && this.estimateTokens(candidate) > config.maxChunkSize)
|
|
83
|
+
break;
|
|
84
|
+
rows.push(nextLine);
|
|
85
|
+
}
|
|
86
|
+
const textChunk = [header, `行范围: ${rowStart + 1}-${rowStart + rows.length}`, ...rows].filter(Boolean).join('\n');
|
|
87
|
+
candidates.push({
|
|
88
|
+
text: textChunk,
|
|
89
|
+
startChar: text.indexOf(rows[0] ?? ''),
|
|
90
|
+
endChar: text.indexOf(rows.at(-1) ?? '') + (rows.at(-1)?.length ?? 0),
|
|
91
|
+
sectionTitle: '表格数据',
|
|
92
|
+
kind: 'table',
|
|
93
|
+
parentId: `table-${parentIndex}`,
|
|
94
|
+
parentIndex,
|
|
95
|
+
childIndex: 0,
|
|
96
|
+
rowRange: `${rowStart + 1}-${rowStart + rows.length}`,
|
|
97
|
+
});
|
|
98
|
+
rowStart += Math.max(1, rows.length);
|
|
99
|
+
parentIndex += 1;
|
|
100
|
+
}
|
|
101
|
+
return candidates;
|
|
102
|
+
}
|
|
103
|
+
createDataCandidates(text, config) {
|
|
104
|
+
const sections = text.split(/\n(?=[\w.[\]-]+[::]\s)|\n{2,}/u).map(part => part.trim()).filter(Boolean);
|
|
105
|
+
const parts = sections.length > 1 ? sections : this.recursiveSplit(text, config.maxChunkSize);
|
|
106
|
+
return this.mergeParts(parts, config.maxChunkSize, config.overlap).map((part, index) => ({
|
|
107
|
+
text: part,
|
|
108
|
+
startChar: Math.max(0, text.indexOf(part.slice(0, 40))),
|
|
109
|
+
endChar: Math.max(0, text.indexOf(part.slice(0, 40))) + part.length,
|
|
110
|
+
sectionTitle: this.extractSectionTitle(part),
|
|
111
|
+
kind: 'data',
|
|
112
|
+
parentId: `data-${index}`,
|
|
113
|
+
parentIndex: index,
|
|
114
|
+
childIndex: 0,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
createCodeCandidates(text, config) {
|
|
118
|
+
const blocks = text.split(/\n(?=(?:export\s+)?(?:async\s+)?(?:function|class|interface|type|const|let|var)\s)/u).map(part => part.trim()).filter(Boolean);
|
|
119
|
+
const parts = blocks.length > 1 ? blocks : this.recursiveSplit(text, config.maxChunkSize);
|
|
120
|
+
return this.mergeParts(parts, config.maxChunkSize, config.overlap).map((part, index) => ({
|
|
121
|
+
text: part,
|
|
122
|
+
startChar: Math.max(0, text.indexOf(part.slice(0, 40))),
|
|
123
|
+
endChar: Math.max(0, text.indexOf(part.slice(0, 40))) + part.length,
|
|
124
|
+
sectionTitle: this.extractSectionTitle(part),
|
|
125
|
+
kind: 'code',
|
|
126
|
+
parentId: `code-${index}`,
|
|
127
|
+
parentIndex: index,
|
|
128
|
+
childIndex: 0,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
splitIntoSections(text, category) {
|
|
132
|
+
const pattern = category === 'cad' || category === 'diagram'
|
|
133
|
+
? /\n{2,}/u
|
|
134
|
+
: /\n(?=#{1,6}\s)|\n{2,}/u;
|
|
135
|
+
const rawSections = this.mergeLeadingHeader(text.split(pattern).map(part => part.trim()).filter(Boolean));
|
|
136
|
+
let cursor = 0;
|
|
137
|
+
return rawSections.map(section => {
|
|
138
|
+
const startChar = Math.max(cursor, text.indexOf(section, cursor));
|
|
139
|
+
cursor = startChar + section.length;
|
|
140
|
+
return { text: section, startChar, title: this.extractSectionTitle(section) };
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
mergeLeadingHeader(sections) {
|
|
144
|
+
if (sections.length < 2)
|
|
145
|
+
return sections;
|
|
146
|
+
const first = sections[0];
|
|
147
|
+
if (!first)
|
|
148
|
+
return sections;
|
|
149
|
+
const isHeader = /^文件[::].+\n类型[::]/u.test(first) && this.estimateTokens(first) < 80;
|
|
150
|
+
if (!isHeader)
|
|
151
|
+
return sections;
|
|
152
|
+
return [`${first}\n\n${sections[1]}`, ...sections.slice(2)];
|
|
153
|
+
}
|
|
154
|
+
recursiveSplit(text, maxTokens, separatorIndex = 0) {
|
|
155
|
+
if (this.estimateTokens(text) <= maxTokens)
|
|
156
|
+
return [text.trim()].filter(Boolean);
|
|
157
|
+
if (separatorIndex >= RECURSIVE_SEPARATORS.length)
|
|
158
|
+
return this.splitByWindow(text, maxTokens);
|
|
159
|
+
const separator = RECURSIVE_SEPARATORS[separatorIndex];
|
|
160
|
+
if (!separator)
|
|
161
|
+
return this.splitByWindow(text, maxTokens);
|
|
162
|
+
const parts = text.split(separator).map(part => part.trim()).filter(Boolean);
|
|
163
|
+
if (parts.length <= 1)
|
|
164
|
+
return this.recursiveSplit(text, maxTokens, separatorIndex + 1);
|
|
165
|
+
return parts.flatMap(part => this.recursiveSplit(part, maxTokens, separatorIndex + 1));
|
|
166
|
+
}
|
|
167
|
+
mergeParts(parts, maxTokens, overlapTokens) {
|
|
20
168
|
const chunks = [];
|
|
21
169
|
let buffer = '';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const overlapText = this.takeOverlap(buffer, config.overlap);
|
|
29
|
-
buffer = overlapText.length > 0 ? `${overlapText}\n\n${paragraph}` : paragraph;
|
|
30
|
-
chunkStart = Math.max(0, cursor - overlapText.length);
|
|
170
|
+
for (const part of parts) {
|
|
171
|
+
const candidate = buffer ? `${buffer}\n${part}` : part;
|
|
172
|
+
if (buffer && this.estimateTokens(candidate) > maxTokens) {
|
|
173
|
+
chunks.push(buffer);
|
|
174
|
+
const overlap = this.takeOverlap(buffer, overlapTokens);
|
|
175
|
+
buffer = overlap ? `${overlap}\n${part}` : part;
|
|
31
176
|
}
|
|
32
177
|
else {
|
|
33
178
|
buffer = candidate;
|
|
34
179
|
}
|
|
35
|
-
cursor += paragraph.length + 2;
|
|
36
180
|
}
|
|
37
|
-
if (buffer.trim()
|
|
38
|
-
chunks.push(
|
|
181
|
+
if (buffer.trim())
|
|
182
|
+
chunks.push(buffer);
|
|
183
|
+
return chunks.flatMap(chunk => this.estimateTokens(chunk) > maxTokens ? this.splitByWindow(chunk, maxTokens, overlapTokens) : [chunk]);
|
|
184
|
+
}
|
|
185
|
+
splitByWindow(text, maxTokens, overlapTokens = 0) {
|
|
186
|
+
const maxChars = Math.max(200, maxTokens * 4);
|
|
187
|
+
const overlapChars = Math.max(0, overlapTokens * 4);
|
|
188
|
+
const step = Math.max(1, maxChars - overlapChars);
|
|
189
|
+
const chunks = [];
|
|
190
|
+
for (let start = 0; start < text.length; start += step) {
|
|
191
|
+
chunks.push(text.slice(start, start + maxChars).trim());
|
|
192
|
+
if (start + maxChars >= text.length)
|
|
193
|
+
break;
|
|
39
194
|
}
|
|
40
|
-
return chunks;
|
|
195
|
+
return chunks.filter(Boolean);
|
|
196
|
+
}
|
|
197
|
+
enforceCandidateLimit(candidates, config) {
|
|
198
|
+
return candidates.flatMap(candidate => {
|
|
199
|
+
if (this.estimateTokens(candidate.text) <= config.maxChunkSize)
|
|
200
|
+
return [candidate];
|
|
201
|
+
return this.splitByWindow(candidate.text, config.maxChunkSize, config.overlap).map((text, index) => ({
|
|
202
|
+
...candidate,
|
|
203
|
+
text,
|
|
204
|
+
childIndex: candidate.childIndex + index,
|
|
205
|
+
startChar: candidate.startChar + Math.max(0, candidate.text.indexOf(text.slice(0, 40))),
|
|
206
|
+
endChar: candidate.startChar + Math.max(0, candidate.text.indexOf(text.slice(0, 40))) + text.length,
|
|
207
|
+
rowRange: candidate.rowRange ? `${candidate.rowRange}#${index + 1}` : undefined,
|
|
208
|
+
}));
|
|
209
|
+
});
|
|
41
210
|
}
|
|
42
211
|
withHeader(text, file, config) {
|
|
43
212
|
if (!config.headerInjection)
|
|
44
213
|
return text;
|
|
45
214
|
return `文件: ${file.relativePath}\n类型: ${file.category}/${file.format}\n\n${text}`;
|
|
46
215
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return text.split(/\n(?=#{1,6}\s)|\n{2,}/u).map(part => part.trim()).filter(Boolean);
|
|
50
|
-
}
|
|
51
|
-
if (category === 'code') {
|
|
52
|
-
return text.split(/\n(?=(export\s+)?(async\s+)?(function|class|interface|type|const|let|var)\s)/u).map(part => part.trim()).filter(Boolean);
|
|
53
|
-
}
|
|
54
|
-
if (category === 'data') {
|
|
55
|
-
return text.split(/\n(?=[\w.[\]-]+:\s)|\n{2,}/u).map(part => part.trim()).filter(Boolean);
|
|
56
|
-
}
|
|
57
|
-
if (category === 'cad' || category === 'diagram') {
|
|
58
|
-
return text.split(/\n(?=(?:CAD|STEP|IGES|Mesh|Draw\.io|Excalidraw|SVG)\b)|\n{2,}/u).map(part => part.trim()).filter(Boolean);
|
|
59
|
-
}
|
|
60
|
-
return text.split(/\n{2,}/u).map(part => part.trim()).filter(Boolean);
|
|
61
|
-
}
|
|
62
|
-
createChunk(index, text, startChar, endChar, metadata) {
|
|
216
|
+
createChunk(index, candidate, file, metadata) {
|
|
217
|
+
const text = candidate.text.trim();
|
|
63
218
|
return {
|
|
64
219
|
index,
|
|
65
|
-
text
|
|
66
|
-
startChar,
|
|
67
|
-
endChar,
|
|
220
|
+
text,
|
|
221
|
+
startChar: candidate.startChar,
|
|
222
|
+
endChar: candidate.endChar,
|
|
68
223
|
tokenCount: this.estimateTokens(text),
|
|
69
|
-
sectionTitle: this.extractSectionTitle(text),
|
|
70
|
-
metadata
|
|
224
|
+
sectionTitle: candidate.sectionTitle ?? this.extractSectionTitle(text),
|
|
225
|
+
metadata: {
|
|
226
|
+
...metadata,
|
|
227
|
+
chunkType: 'child',
|
|
228
|
+
chunkKind: candidate.kind,
|
|
229
|
+
parentId: `${file.relativePath}#${candidate.parentId}`,
|
|
230
|
+
parentIndex: candidate.parentIndex,
|
|
231
|
+
childIndex: candidate.childIndex,
|
|
232
|
+
rowRange: candidate.rowRange,
|
|
233
|
+
sectionTitle: candidate.sectionTitle ?? this.extractSectionTitle(text),
|
|
234
|
+
startChar: candidate.startChar,
|
|
235
|
+
endChar: candidate.endChar,
|
|
236
|
+
splitStrategy: 'recursive_parent_child_v1',
|
|
237
|
+
},
|
|
71
238
|
};
|
|
72
239
|
}
|
|
240
|
+
kindForCategory(category) {
|
|
241
|
+
if (category === 'image' || category === 'cad' || category === 'diagram')
|
|
242
|
+
return 'metadata';
|
|
243
|
+
if (category === 'code')
|
|
244
|
+
return 'code';
|
|
245
|
+
if (category === 'data')
|
|
246
|
+
return 'data';
|
|
247
|
+
if (category === 'spreadsheet')
|
|
248
|
+
return 'table';
|
|
249
|
+
return 'text';
|
|
250
|
+
}
|
|
73
251
|
extractSectionTitle(text) {
|
|
74
252
|
const firstLine = text.trim().split(/\r?\n/u)[0]?.trim();
|
|
75
253
|
if (!firstLine)
|
|
@@ -29,6 +29,16 @@ export class ChangeTracker {
|
|
|
29
29
|
newFiles.push(classified);
|
|
30
30
|
continue;
|
|
31
31
|
}
|
|
32
|
+
const metadata = this.parseMetadata(indexed.metadataJson);
|
|
33
|
+
const needsReindex = indexed.status === 'error'
|
|
34
|
+
|| indexed.chunkCount === 0
|
|
35
|
+
|| (classified.format === 'pdf' && indexed.chunkCount <= 1)
|
|
36
|
+
|| metadata.contentCoverage === 'metadata_filename'
|
|
37
|
+
|| metadata.extractionMode === 'pdf_metadata_only';
|
|
38
|
+
if (needsReindex) {
|
|
39
|
+
modifiedFiles.push(classified);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
32
42
|
if (Math.round(diskStat.mtime) !== Math.round(indexed.mtime) || diskStat.size !== indexed.fileSize) {
|
|
33
43
|
const contentHash = this.hashFile(absolutePath);
|
|
34
44
|
if (contentHash !== indexed.contentHash) {
|
|
@@ -63,4 +73,15 @@ export class ChangeTracker {
|
|
|
63
73
|
hashFile(filePath) {
|
|
64
74
|
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
65
75
|
}
|
|
76
|
+
parseMetadata(metadataJson) {
|
|
77
|
+
if (!metadataJson)
|
|
78
|
+
return {};
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(metadataJson);
|
|
81
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
66
87
|
}
|
|
@@ -15,6 +15,24 @@ export interface StoredChunk {
|
|
|
15
15
|
}
|
|
16
16
|
export interface ChunkSearchResult extends StoredChunk {
|
|
17
17
|
score: number;
|
|
18
|
+
scoreDetails?: {
|
|
19
|
+
keywordScore?: number;
|
|
20
|
+
bm25Score?: number;
|
|
21
|
+
exactPhraseBoost?: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface StoredParentChunk {
|
|
25
|
+
id: string;
|
|
26
|
+
relativePath: string;
|
|
27
|
+
parentId: string;
|
|
28
|
+
content: string;
|
|
29
|
+
category: FileCategory;
|
|
30
|
+
format: string;
|
|
31
|
+
collectionName: string;
|
|
32
|
+
sectionTitle?: string;
|
|
33
|
+
chunkCount: number;
|
|
34
|
+
metadataJson?: string;
|
|
35
|
+
createdAt: number;
|
|
18
36
|
}
|
|
19
37
|
export interface FileHashRecord {
|
|
20
38
|
contentHash: string;
|
|
@@ -29,6 +47,7 @@ export interface MinHashRecord {
|
|
|
29
47
|
filePath: string;
|
|
30
48
|
signature: number[];
|
|
31
49
|
shingleCount: number;
|
|
50
|
+
buckets: string[];
|
|
32
51
|
createdAt: number;
|
|
33
52
|
}
|
|
34
53
|
export interface FileRelationship {
|
|
@@ -43,6 +62,7 @@ export interface FileRelationship {
|
|
|
43
62
|
}
|
|
44
63
|
export declare class IndexStateStore {
|
|
45
64
|
private readonly db;
|
|
65
|
+
private ftsEnabled;
|
|
46
66
|
constructor(dbPath: string);
|
|
47
67
|
loadActiveRecords(): Map<string, IndexStateRecord>;
|
|
48
68
|
upsertRecord(record: IndexStateRecord): void;
|
|
@@ -58,12 +78,19 @@ export declare class IndexStateStore {
|
|
|
58
78
|
relativePath?: string;
|
|
59
79
|
limit?: number;
|
|
60
80
|
}): StoredChunk[];
|
|
81
|
+
getContextChunks(relativePath: string, chunkIndex: number, window?: number): StoredChunk[];
|
|
82
|
+
listParentChunks(relativePath: string): StoredParentChunk[];
|
|
83
|
+
getParentChunk(relativePath: string, parentId: string): StoredParentChunk | undefined;
|
|
84
|
+
getChunksByParent(relativePath: string, parentId: string, limit?: number): StoredChunk[];
|
|
61
85
|
searchChunks(query: string, limit?: number): ChunkSearchResult[];
|
|
86
|
+
private searchChunksFts;
|
|
87
|
+
private searchChunksLike;
|
|
62
88
|
findExactDuplicate(contentHash: string, excludePath?: string): FileHashRecord | undefined;
|
|
63
89
|
findNormalizedDuplicate(normalizedHash: string, excludePath?: string): FileHashRecord | undefined;
|
|
64
90
|
upsertFileHash(record: Omit<FileHashRecord, 'createdAt' | 'updatedAt'>): void;
|
|
65
91
|
upsertMinHash(record: Omit<MinHashRecord, 'createdAt'>): void;
|
|
66
92
|
listMinHashes(excludePath?: string): MinHashRecord[];
|
|
93
|
+
listMinHashesByBuckets(buckets: string[], excludePath?: string): MinHashRecord[];
|
|
67
94
|
addRelationship(relationship: Omit<FileRelationship, 'id' | 'createdAt'>): void;
|
|
68
95
|
listRelationships(filePath?: string): FileRelationship[];
|
|
69
96
|
setTags(relativePath: string, tags: string[]): void;
|
|
@@ -81,6 +108,7 @@ export declare class IndexStateStore {
|
|
|
81
108
|
}>;
|
|
82
109
|
deleteRecord(relativePath: string): void;
|
|
83
110
|
setMetadata(key: string, value: string): void;
|
|
111
|
+
getMetadata(key: string): string | undefined;
|
|
84
112
|
getStats(): {
|
|
85
113
|
fileCount: number;
|
|
86
114
|
chunkCount: number;
|
|
@@ -93,11 +121,19 @@ export declare class IndexStateStore {
|
|
|
93
121
|
}>;
|
|
94
122
|
close(): void;
|
|
95
123
|
private initTables;
|
|
124
|
+
private initFts;
|
|
125
|
+
private rebuildFtsIfNeeded;
|
|
96
126
|
private rowToMinHash;
|
|
97
127
|
private rowToFileHash;
|
|
98
128
|
private rowToRelationship;
|
|
129
|
+
private rowToParentChunk;
|
|
130
|
+
private splitParentGroups;
|
|
131
|
+
private metadataString;
|
|
99
132
|
private rowToChunk;
|
|
100
133
|
private expandSearchTerms;
|
|
101
|
-
private
|
|
134
|
+
private toFtsQuery;
|
|
135
|
+
private bm25ToPositiveScore;
|
|
136
|
+
private scoreChunkDetailed;
|
|
137
|
+
private countOccurrences;
|
|
102
138
|
private rowToRecord;
|
|
103
139
|
}
|