@customize-agent/knowledge 3.0.1 → 3.0.3
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.
|
@@ -50,7 +50,7 @@ export class FileClassifier {
|
|
|
50
50
|
['.pdf', 'document', 'pdf'], ['.docx', 'document', 'office'], ['.doc', 'document', 'office'], ['.rtf', 'document', 'office'], ['.odt', 'document', 'office'],
|
|
51
51
|
['.pptx', 'document', 'presentation'], ['.ppt', 'document', 'presentation'], ['.odp', 'document', 'presentation'],
|
|
52
52
|
['.md', 'document', 'markdown'], ['.markdown', 'document', 'markdown'], ['.mdx', 'document', 'markdown'],
|
|
53
|
-
['.txt', 'document', 'plaintext'], ['.rst', 'document', 'plaintext'], ['.asciidoc', 'document', 'plaintext'], ['.tex', 'document', 'plaintext'],
|
|
53
|
+
['.txt', 'document', 'plaintext'], ['.textclipping', 'document', 'text_clipping'], ['.rst', 'document', 'plaintext'], ['.asciidoc', 'document', 'plaintext'], ['.tex', 'document', 'plaintext'],
|
|
54
54
|
['.epub', 'document', 'ebook'], ['.mobi', 'document', 'ebook'],
|
|
55
55
|
['.xlsx', 'spreadsheet', 'excel'], ['.xls', 'spreadsheet', 'excel'], ['.xlsm', 'spreadsheet', 'excel'],
|
|
56
56
|
['.csv', 'spreadsheet', 'csv'], ['.tsv', 'spreadsheet', 'csv'], ['.tab', 'spreadsheet', 'csv'], ['.ods', 'spreadsheet', 'opendoc'],
|
|
@@ -90,6 +90,13 @@ export declare class KnowledgeBaseManager {
|
|
|
90
90
|
uploadFile(fileName: string, content: Buffer, targetRelativePath?: string, onProgress?: (progress: KnowledgeIndexProgress) => void, options?: {
|
|
91
91
|
vectorMode?: 'sync' | 'defer';
|
|
92
92
|
}): Promise<DiffResult>;
|
|
93
|
+
uploadFiles(files: Array<{
|
|
94
|
+
fileName: string;
|
|
95
|
+
content: Buffer;
|
|
96
|
+
targetRelativePath?: string;
|
|
97
|
+
}>, onProgress?: (progress: KnowledgeIndexProgress) => void, options?: {
|
|
98
|
+
vectorMode?: 'sync' | 'defer';
|
|
99
|
+
}): Promise<DiffResult>;
|
|
93
100
|
listFailedFiles(): DiffResult['skippedFiles'];
|
|
94
101
|
removeFile(relativePath: string): Promise<void>;
|
|
95
102
|
tagFile(relativePath: string, tags: string[]): void;
|
|
@@ -368,11 +368,16 @@ export class KnowledgeBaseManager {
|
|
|
368
368
|
return targetRelativePath ?? this.defaultUploadRelativePath(fileName);
|
|
369
369
|
}
|
|
370
370
|
async uploadFile(fileName, content, targetRelativePath, onProgress, options = {}) {
|
|
371
|
+
return this.uploadFiles([{ fileName, content, targetRelativePath }], onProgress, options);
|
|
372
|
+
}
|
|
373
|
+
async uploadFiles(files, onProgress, options = {}) {
|
|
371
374
|
this.initialize();
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
375
|
+
for (const file of files) {
|
|
376
|
+
const relativePath = this.getUploadRelativePath(file.fileName, file.targetRelativePath);
|
|
377
|
+
const targetPath = this.resolveKbRelativePath(relativePath);
|
|
378
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
379
|
+
fs.writeFileSync(targetPath, file.content);
|
|
380
|
+
}
|
|
376
381
|
return this.incrementalIndex({ onProgress, vectorMode: options.vectorMode });
|
|
377
382
|
}
|
|
378
383
|
listFailedFiles() {
|
|
@@ -11,14 +11,25 @@ export declare class ContentExtractor {
|
|
|
11
11
|
constructor(externalExtractors?: ExternalExtractorRegistry);
|
|
12
12
|
extract(file: ClassifiedFile): Promise<ExtractionResult>;
|
|
13
13
|
private tryExternalExtractor;
|
|
14
|
+
private extractTextClipping;
|
|
15
|
+
private swapUtf16Bytes;
|
|
16
|
+
private extractReadableFragments;
|
|
17
|
+
private textScore;
|
|
14
18
|
private extractCad;
|
|
19
|
+
private extractDxf;
|
|
20
|
+
private tryConvertDwgToDxf;
|
|
15
21
|
private extractCadMesh;
|
|
22
|
+
private getTempRoot;
|
|
23
|
+
private extractBinaryReadableFragments;
|
|
16
24
|
private extractBinaryStrings;
|
|
17
25
|
private extractData;
|
|
18
26
|
private extractDiagram;
|
|
19
27
|
private parseDelimitedLine;
|
|
20
28
|
private extractDelimitedText;
|
|
21
29
|
private extractOfficeDocument;
|
|
30
|
+
private extractRtf;
|
|
31
|
+
private extractLegacyWordDocument;
|
|
32
|
+
private extractLegacyOfficeBinary;
|
|
22
33
|
private extractSpreadsheet;
|
|
23
34
|
private extractOfficeZip;
|
|
24
35
|
private extractArchive;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
4
5
|
import { ExternalExtractorRegistry } from './external-extractor.js';
|
|
5
6
|
import { resolveAndImport, resolvePackage, getNodeModulesRoot } from './module-resolver.js';
|
|
6
7
|
export class ContentExtractor {
|
|
@@ -24,7 +25,7 @@ export class ContentExtractor {
|
|
|
24
25
|
warnings.push(...external.warnings);
|
|
25
26
|
}
|
|
26
27
|
else if (file.category === 'cad') {
|
|
27
|
-
const result = this.extractCad(file);
|
|
28
|
+
const result = await this.extractCad(file);
|
|
28
29
|
text = result.text;
|
|
29
30
|
Object.assign(metadata, result.metadata);
|
|
30
31
|
warnings.push(...result.warnings);
|
|
@@ -83,6 +84,12 @@ export class ContentExtractor {
|
|
|
83
84
|
Object.assign(metadata, result.metadata);
|
|
84
85
|
warnings.push(...result.warnings);
|
|
85
86
|
}
|
|
87
|
+
else if (file.format === 'text_clipping') {
|
|
88
|
+
const result = this.extractTextClipping(file);
|
|
89
|
+
text = result.text;
|
|
90
|
+
Object.assign(metadata, result.metadata);
|
|
91
|
+
warnings.push(...result.warnings);
|
|
92
|
+
}
|
|
86
93
|
else if (this.isTextReadable(file)) {
|
|
87
94
|
text = fs.readFileSync(file.absolutePath, 'utf8');
|
|
88
95
|
metadata.extractionMode = 'plain_text';
|
|
@@ -133,10 +140,67 @@ export class ContentExtractor {
|
|
|
133
140
|
}
|
|
134
141
|
return undefined;
|
|
135
142
|
}
|
|
136
|
-
|
|
143
|
+
extractTextClipping(file) {
|
|
144
|
+
const buffer = fs.readFileSync(file.absolutePath);
|
|
145
|
+
const candidates = [
|
|
146
|
+
buffer.toString('utf16le'),
|
|
147
|
+
this.swapUtf16Bytes(buffer).toString('utf16le'),
|
|
148
|
+
buffer.toString('utf8'),
|
|
149
|
+
...this.extractBinaryStrings(file.absolutePath),
|
|
150
|
+
];
|
|
151
|
+
const fragments = candidates.flatMap(candidate => this.extractReadableFragments(candidate));
|
|
152
|
+
const unique = Array.from(new Set(fragments))
|
|
153
|
+
.filter(fragment => fragment.length >= 2 && !/^bplist\d+/u.test(fragment))
|
|
154
|
+
.sort((a, b) => this.textScore(b) - this.textScore(a))
|
|
155
|
+
.slice(0, 50);
|
|
156
|
+
const text = unique.join('\n');
|
|
157
|
+
return {
|
|
158
|
+
text: text ? [this.metadataOnlyText(file), text].join('\n') : this.metadataOnlyText(file),
|
|
159
|
+
metadata: {
|
|
160
|
+
extractionMode: 'builtin_text_clipping',
|
|
161
|
+
vectorizable: true,
|
|
162
|
+
contentCoverage: text ? 'text_clipping_payload' : 'metadata',
|
|
163
|
+
fragmentCount: unique.length,
|
|
164
|
+
},
|
|
165
|
+
warnings: text ? [] : ['未从 textClipping 中提取到剪贴文本,仅入库元数据'],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
swapUtf16Bytes(buffer) {
|
|
169
|
+
const swapped = Buffer.from(buffer);
|
|
170
|
+
for (let i = 0; i + 1 < swapped.length; i += 2) {
|
|
171
|
+
const first = swapped[i] ?? 0;
|
|
172
|
+
swapped[i] = swapped[i + 1] ?? 0;
|
|
173
|
+
swapped[i + 1] = first;
|
|
174
|
+
}
|
|
175
|
+
return swapped;
|
|
176
|
+
}
|
|
177
|
+
extractReadableFragments(value) {
|
|
178
|
+
return value
|
|
179
|
+
.replace(/[^\p{L}\p{N}\p{P}\p{S}\s]/gu, '\n')
|
|
180
|
+
.split(/[\r\n]+/u)
|
|
181
|
+
.map(line => line.replace(/\s+/gu, ' ').trim())
|
|
182
|
+
.filter(line => line.length >= 2 && /[\p{L}\p{N}]/u.test(line));
|
|
183
|
+
}
|
|
184
|
+
textScore(value) {
|
|
185
|
+
const cjk = (value.match(/[\p{Script=Han}]/gu) ?? []).length;
|
|
186
|
+
const alnum = (value.match(/[\p{L}\p{N}]/gu) ?? []).length;
|
|
187
|
+
return cjk * 4 + alnum + Math.min(value.length, 200) / 20;
|
|
188
|
+
}
|
|
189
|
+
async extractCad(file) {
|
|
137
190
|
const metadata = { extractionMode: 'builtin_cad_structural', vectorizable: true };
|
|
138
191
|
const warnings = [];
|
|
139
192
|
const ext = path.extname(file.absolutePath).toLowerCase();
|
|
193
|
+
if (ext === '.dxf')
|
|
194
|
+
return await this.extractDxf(file, fs.readFileSync(file.absolutePath, 'utf8'), metadata);
|
|
195
|
+
if (ext === '.dwg') {
|
|
196
|
+
const converted = await this.tryConvertDwgToDxf(file.absolutePath);
|
|
197
|
+
if (converted?.dxfText) {
|
|
198
|
+
const parsed = await this.extractDxf(file, converted.dxfText, { ...metadata, extractionMode: converted.tool, convertedFrom: 'dwg' });
|
|
199
|
+
parsed.warnings.push(...converted.warnings);
|
|
200
|
+
return parsed;
|
|
201
|
+
}
|
|
202
|
+
warnings.push(...(converted?.warnings ?? ['未检测到可用 DWG→DXF 转换器,使用内置图纸可读文本抽取']));
|
|
203
|
+
}
|
|
140
204
|
if (file.format === 'autocad' && ext === '.dxf') {
|
|
141
205
|
const raw = fs.readFileSync(file.absolutePath, 'utf8');
|
|
142
206
|
const layers = this.matchAll(raw, /\n\s*8\s*\n([^\n]+)/gu).slice(0, 300);
|
|
@@ -213,18 +277,82 @@ export class ContentExtractor {
|
|
|
213
277
|
if (result.text.trim())
|
|
214
278
|
return result;
|
|
215
279
|
}
|
|
216
|
-
const
|
|
217
|
-
metadata.extractionMode = '
|
|
218
|
-
metadata.contentCoverage =
|
|
219
|
-
metadata.stringCount =
|
|
220
|
-
if (
|
|
221
|
-
warnings.push(`${file.format} 内置 CAD
|
|
280
|
+
const readable = this.extractBinaryReadableFragments(file.absolutePath).slice(0, 800);
|
|
281
|
+
metadata.extractionMode = 'builtin_cad_readable_fragments';
|
|
282
|
+
metadata.contentCoverage = readable.length > 0 ? 'cad_readable_text_fragments' : 'metadata';
|
|
283
|
+
metadata.stringCount = readable.length;
|
|
284
|
+
if (readable.length === 0)
|
|
285
|
+
warnings.push(`${file.format} 内置 CAD 解析器未提取到可用文本,已入库文件元数据用于检索`);
|
|
286
|
+
else
|
|
287
|
+
warnings.push(`${file.format} 未检测到专业 DWG 转换器,已使用内置可读标注/标题块抽取;如需完整图纸结构,请安装 ODA File Converter 或 LibreDWG 并配置外部解析器`);
|
|
288
|
+
return {
|
|
289
|
+
text: readable.length > 0 ? [this.metadataOnlyText(file), `CAD 图纸可读标注/标题块/属性:\n${readable.join('\n')}`].join('\n') : this.metadataOnlyText(file),
|
|
290
|
+
metadata,
|
|
291
|
+
warnings,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
async extractDxf(file, raw, metadata) {
|
|
295
|
+
const warnings = [];
|
|
296
|
+
let parsed;
|
|
297
|
+
try {
|
|
298
|
+
const mod = await resolveAndImport('dxf-parser');
|
|
299
|
+
const Parser = mod.default ?? mod;
|
|
300
|
+
parsed = new Parser().parseSync(raw);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
warnings.push('dxf-parser 解析失败,已使用 DXF 文本结构抽取回退');
|
|
304
|
+
}
|
|
305
|
+
const layers = this.matchAll(raw, /\n\s*8\s*\n([^\n]+)/gu).slice(0, 300);
|
|
306
|
+
const textEntities = this.matchAll(raw, /\n\s*(?:1|3)\s*\n([^\n]+)/gu).slice(0, 800);
|
|
307
|
+
const blocks = this.matchAll(raw, /\n\s*2\s*\n([^\n]+)/gu).slice(0, 300);
|
|
308
|
+
const entityTypes = this.matchAll(raw, /\n\s*0\s*\n([A-Z][A-Z0-9_]+)/gu).slice(0, 1200);
|
|
309
|
+
const uniqueLayers = Array.from(new Set(layers));
|
|
310
|
+
const uniqueBlocks = Array.from(new Set(blocks));
|
|
311
|
+
const uniqueEntityTypes = Array.from(new Set(entityTypes));
|
|
312
|
+
metadata.layerCount = uniqueLayers.length;
|
|
313
|
+
metadata.layerNames = uniqueLayers.slice(0, 80);
|
|
314
|
+
metadata.textEntityCount = textEntities.length;
|
|
315
|
+
metadata.blockCount = uniqueBlocks.length;
|
|
316
|
+
metadata.blockNames = uniqueBlocks.slice(0, 80);
|
|
317
|
+
metadata.entityTypeCount = uniqueEntityTypes.length;
|
|
318
|
+
metadata.entityTypes = uniqueEntityTypes.slice(0, 80);
|
|
319
|
+
metadata.contentCoverage = 'dxf_layers_blocks_entities_text';
|
|
320
|
+
metadata.parsedByDxfParser = Boolean(parsed);
|
|
222
321
|
return {
|
|
223
|
-
text:
|
|
322
|
+
text: [
|
|
323
|
+
this.metadataOnlyText(file),
|
|
324
|
+
`CAD DXF 图层: ${uniqueLayers.join(', ')}`,
|
|
325
|
+
`CAD DXF 块/符号: ${uniqueBlocks.join(', ')}`,
|
|
326
|
+
`CAD DXF 实体类型: ${uniqueEntityTypes.join(', ')}`,
|
|
327
|
+
`CAD DXF 标注/文本:\n${textEntities.join('\n')}`,
|
|
328
|
+
].join('\n'),
|
|
224
329
|
metadata,
|
|
225
330
|
warnings,
|
|
226
331
|
};
|
|
227
332
|
}
|
|
333
|
+
async tryConvertDwgToDxf(filePath) {
|
|
334
|
+
const tmpDir = fs.mkdtempSync(path.join(this.getTempRoot(), 'customize-dwg-'));
|
|
335
|
+
const outputPath = path.join(tmpDir, `${path.basename(filePath, path.extname(filePath))}.dxf`);
|
|
336
|
+
try {
|
|
337
|
+
const customCmd = process.env.CUSTOMIZE_DWG_TO_DXF_CMD;
|
|
338
|
+
if (customCmd) {
|
|
339
|
+
const command = customCmd.replace(/\{input\}/gu, filePath).replace(/\{output\}/gu, outputPath);
|
|
340
|
+
const result = spawnSync(command, { shell: true, encoding: 'utf8', timeout: 120_000 });
|
|
341
|
+
if (result.status === 0 && fs.existsSync(outputPath))
|
|
342
|
+
return { dxfText: fs.readFileSync(outputPath, 'utf8'), tool: 'external_dwg_to_dxf', warnings: [] };
|
|
343
|
+
return { tool: 'external_dwg_to_dxf', warnings: [`CUSTOMIZE_DWG_TO_DXF_CMD 转换失败: ${result.stderr || result.stdout || result.error?.message || 'unknown error'}`] };
|
|
344
|
+
}
|
|
345
|
+
for (const bin of ['dwgread', 'dwg2dxf']) {
|
|
346
|
+
const result = spawnSync(bin, bin === 'dwgread' ? ['-O', 'DXF', '-o', outputPath, filePath] : [filePath, outputPath], { encoding: 'utf8', timeout: 120_000 });
|
|
347
|
+
if (result.status === 0 && fs.existsSync(outputPath))
|
|
348
|
+
return { dxfText: fs.readFileSync(outputPath, 'utf8'), tool: bin, warnings: [] };
|
|
349
|
+
}
|
|
350
|
+
return { tool: 'builtin_fallback', warnings: ['未检测到可用 DWG→DXF 转换器(dwgread/dwg2dxf/CUSTOMIZE_DWG_TO_DXF_CMD),使用内置图纸可读文本抽取'] };
|
|
351
|
+
}
|
|
352
|
+
finally {
|
|
353
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
228
356
|
extractCadMesh(file, ext, metadata) {
|
|
229
357
|
const warnings = [];
|
|
230
358
|
if (['.obj', '.gltf'].includes(ext)) {
|
|
@@ -258,13 +386,25 @@ export class ContentExtractor {
|
|
|
258
386
|
metadata.contentCoverage = binaryStrings.length > 0 ? 'mesh_binary_strings' : 'metadata';
|
|
259
387
|
return { text: binaryStrings.length > 0 ? [this.metadataOnlyText(file), `Mesh 二进制字符串:\n${binaryStrings.join('\n')}`].join('\n') : '', metadata, warnings };
|
|
260
388
|
}
|
|
261
|
-
|
|
389
|
+
getTempRoot() {
|
|
390
|
+
return process.env.CUSTOMIZE_TMPDIR || process.env.TMPDIR || tmpdir();
|
|
391
|
+
}
|
|
392
|
+
extractBinaryReadableFragments(filePath) {
|
|
262
393
|
const buffer = fs.readFileSync(filePath);
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
.
|
|
394
|
+
const candidates = [
|
|
395
|
+
buffer.toString('utf8'),
|
|
396
|
+
buffer.toString('utf16le'),
|
|
397
|
+
this.swapUtf16Bytes(buffer).toString('utf16le'),
|
|
398
|
+
buffer.toString('latin1'),
|
|
399
|
+
];
|
|
400
|
+
return Array.from(new Set(candidates.flatMap(candidate => this.extractReadableFragments(candidate))))
|
|
401
|
+
.filter(value => value.length >= 3 && !/^\d+$/u.test(value))
|
|
402
|
+
.sort((a, b) => this.textScore(b) - this.textScore(a))
|
|
266
403
|
.slice(0, 2_000);
|
|
267
404
|
}
|
|
405
|
+
extractBinaryStrings(filePath) {
|
|
406
|
+
return this.extractBinaryReadableFragments(filePath);
|
|
407
|
+
}
|
|
268
408
|
extractData(file) {
|
|
269
409
|
const raw = fs.readFileSync(file.absolutePath, 'utf8');
|
|
270
410
|
const metadata = { extractionMode: 'structured_data', vectorizable: true };
|
|
@@ -378,7 +518,14 @@ export class ContentExtractor {
|
|
|
378
518
|
};
|
|
379
519
|
}
|
|
380
520
|
async extractOfficeDocument(file) {
|
|
381
|
-
|
|
521
|
+
const ext = path.extname(file.absolutePath).toLowerCase();
|
|
522
|
+
if (ext === '.rtf')
|
|
523
|
+
return this.extractRtf(file);
|
|
524
|
+
if (ext === '.doc')
|
|
525
|
+
return this.extractLegacyWordDocument(file);
|
|
526
|
+
if (ext === '.ppt')
|
|
527
|
+
return this.extractLegacyOfficeBinary(file);
|
|
528
|
+
if (ext === '.docx') {
|
|
382
529
|
try {
|
|
383
530
|
const mammoth = await resolveAndImport('mammoth');
|
|
384
531
|
const result = await mammoth.extractRawText({ path: file.absolutePath });
|
|
@@ -397,7 +544,50 @@ export class ContentExtractor {
|
|
|
397
544
|
}
|
|
398
545
|
return this.extractOfficeZip(file);
|
|
399
546
|
}
|
|
547
|
+
extractRtf(file) {
|
|
548
|
+
const raw = fs.readFileSync(file.absolutePath, 'utf8');
|
|
549
|
+
const text = raw
|
|
550
|
+
.replace(/\\'[0-9a-fA-F]{2}/gu, ' ')
|
|
551
|
+
.replace(/\\[a-zA-Z]+-?\d* ?/gu, ' ')
|
|
552
|
+
.replace(/[{}]/gu, ' ')
|
|
553
|
+
.replace(/\s+/gu, ' ')
|
|
554
|
+
.trim();
|
|
555
|
+
return {
|
|
556
|
+
text,
|
|
557
|
+
metadata: { extractionMode: 'builtin_rtf_text', vectorizable: true, contentCoverage: 'rtf_text' },
|
|
558
|
+
warnings: text ? [] : ['RTF 解析未提取到正文,未入库'],
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
async extractLegacyWordDocument(file) {
|
|
562
|
+
try {
|
|
563
|
+
const mod = await resolveAndImport('word-extractor');
|
|
564
|
+
const WordExtractor = mod.default ?? mod;
|
|
565
|
+
const document = await new WordExtractor().extract(file.absolutePath);
|
|
566
|
+
const text = document.getBody().trim();
|
|
567
|
+
if (text) {
|
|
568
|
+
return {
|
|
569
|
+
text,
|
|
570
|
+
metadata: { extractionMode: 'builtin_word_extractor', vectorizable: true, contentCoverage: 'legacy_word_full_text', textLength: text.length },
|
|
571
|
+
warnings: [],
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
catch {
|
|
576
|
+
// fallback below
|
|
577
|
+
}
|
|
578
|
+
return this.extractLegacyOfficeBinary(file);
|
|
579
|
+
}
|
|
580
|
+
extractLegacyOfficeBinary(file) {
|
|
581
|
+
const strings = this.extractBinaryStrings(file.absolutePath).slice(0, 1_000);
|
|
582
|
+
const text = strings.join('\n').trim();
|
|
583
|
+
return {
|
|
584
|
+
text,
|
|
585
|
+
metadata: { extractionMode: 'builtin_legacy_office_binary_strings', vectorizable: true, contentCoverage: 'legacy_office_binary_strings', stringCount: strings.length },
|
|
586
|
+
warnings: text ? [] : ['旧版 Office 二进制文件未提取到正文,未入库'],
|
|
587
|
+
};
|
|
588
|
+
}
|
|
400
589
|
async extractSpreadsheet(file) {
|
|
590
|
+
const ext = path.extname(file.absolutePath).toLowerCase();
|
|
401
591
|
try {
|
|
402
592
|
const XLSX = await resolveAndImport('xlsx');
|
|
403
593
|
const workbook = XLSX.readFile(file.absolutePath, { cellDates: true, cellFormula: true, cellNF: true, cellStyles: true });
|
|
@@ -442,8 +632,12 @@ export class ContentExtractor {
|
|
|
442
632
|
}
|
|
443
633
|
}
|
|
444
634
|
catch {
|
|
635
|
+
if (ext === '.xls')
|
|
636
|
+
return this.extractLegacyOfficeBinary(file);
|
|
445
637
|
// fallback below
|
|
446
638
|
}
|
|
639
|
+
if (ext === '.xls')
|
|
640
|
+
return this.extractLegacyOfficeBinary(file);
|
|
447
641
|
return this.extractOfficeZip(file);
|
|
448
642
|
}
|
|
449
643
|
async extractOfficeZip(file) {
|
|
@@ -461,13 +655,14 @@ export class ContentExtractor {
|
|
|
461
655
|
texts.push(`${entry.name}: ${stripped.slice(0, 8_000)}`);
|
|
462
656
|
}
|
|
463
657
|
metadata.entryCount = Object.keys(zip.files).length;
|
|
464
|
-
metadata.contentCoverage = texts.length > 0 ? 'office_zip_xml_text' : '
|
|
465
|
-
return { text:
|
|
658
|
+
metadata.contentCoverage = texts.length > 0 ? 'office_zip_xml_text' : 'office_zip_empty_text';
|
|
659
|
+
return { text: texts.join('\n'), metadata, warnings: texts.length ? [] : ['Office/表格/演示文件未提取到正文,未入库'] };
|
|
466
660
|
}
|
|
467
661
|
catch (error) {
|
|
468
662
|
metadata.extractionMode = 'office_zip_failed';
|
|
469
663
|
metadata.parseError = error instanceof Error ? error.message : String(error);
|
|
470
|
-
|
|
664
|
+
metadata.contentCoverage = 'office_zip_failed';
|
|
665
|
+
return { text: '', metadata, warnings: [`Office/表格/演示文件解析失败: ${metadata.parseError},未入库`] };
|
|
471
666
|
}
|
|
472
667
|
}
|
|
473
668
|
async extractArchive(file) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@customize-agent/knowledge",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Local knowledge base infrastructure for customize-agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,15 +32,17 @@
|
|
|
32
32
|
"knowledge-base"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@napi-rs/canvas": "^0.1.82",
|
|
35
36
|
"better-sqlite3": "^12.10.0",
|
|
36
|
-
"
|
|
37
|
+
"dxf-parser": "^1.1.2",
|
|
37
38
|
"fast-glob": "^3.3.3",
|
|
38
39
|
"jszip": "^3.10.1",
|
|
39
|
-
"@napi-rs/canvas": "^0.1.82",
|
|
40
40
|
"mammoth": "^1.12.0",
|
|
41
41
|
"pdf-parse": "^2.4.5",
|
|
42
42
|
"pdfjs-dist": "^5.4.394",
|
|
43
|
+
"sqlite-vec": "^0.1.9",
|
|
43
44
|
"tesseract.js": "^7.0.0",
|
|
45
|
+
"word-extractor": "^1.0.4",
|
|
44
46
|
"xlsx": "^0.18.5"
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|