@customize-agent/knowledge 1.0.1
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 +24 -0
- package/dist/chunking/text-chunker.js +90 -0
- package/dist/classification/classifier.d.ts +12 -0
- package/dist/classification/classifier.js +95 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.js +40 -0
- package/dist/core/change-tracker.d.ts +10 -0
- package/dist/core/change-tracker.js +66 -0
- package/dist/core/file-scanner.d.ts +8 -0
- package/dist/core/file-scanner.js +32 -0
- package/dist/core/index-state-store.d.ts +103 -0
- package/dist/core/index-state-store.js +439 -0
- package/dist/core/knowledge-base-manager.d.ts +76 -0
- package/dist/core/knowledge-base-manager.js +300 -0
- package/dist/core/multi-project-manager.d.ts +30 -0
- package/dist/core/multi-project-manager.js +163 -0
- package/dist/core/project-config.d.ts +9 -0
- package/dist/core/project-config.js +73 -0
- package/dist/core/project-id.d.ts +1 -0
- package/dist/core/project-id.js +8 -0
- package/dist/core/project-registry.d.ts +10 -0
- package/dist/core/project-registry.js +70 -0
- package/dist/dedup/dedup-engine.d.ts +20 -0
- package/dist/dedup/dedup-engine.js +78 -0
- package/dist/dedup/relationship-detector.d.ts +10 -0
- package/dist/dedup/relationship-detector.js +84 -0
- package/dist/embedding/embedding-provider.d.ts +15 -0
- package/dist/embedding/embedding-provider.js +31 -0
- package/dist/extraction/content-extractor.d.ts +36 -0
- package/dist/extraction/content-extractor.js +655 -0
- package/dist/extraction/external-extractor.d.ts +63 -0
- package/dist/extraction/external-extractor.js +139 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +22 -0
- package/dist/search/federation-search.d.ts +41 -0
- package/dist/search/federation-search.js +102 -0
- package/dist/server/dashboard-client.d.ts +2 -0
- package/dist/server/dashboard-client.js +396 -0
- package/dist/server/dashboard-i18n.d.ts +112 -0
- package/dist/server/dashboard-i18n.js +220 -0
- package/dist/server/dashboard-page.d.ts +6 -0
- package/dist/server/dashboard-page.js +138 -0
- package/dist/server/dashboard-server.d.ts +13 -0
- package/dist/server/dashboard-server.js +225 -0
- package/dist/server/dashboard-styles.d.ts +1 -0
- package/dist/server/dashboard-styles.js +152 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +1 -0
- package/dist/vector/chroma-store.d.ts +39 -0
- package/dist/vector/chroma-store.js +131 -0
- package/dist/vector/collection-manager.d.ts +16 -0
- package/dist/vector/collection-manager.js +77 -0
- package/dist/vector/types.d.ts +33 -0
- package/dist/vector/types.js +1 -0
- package/dist/vector/vector-indexer.d.ts +18 -0
- package/dist/vector/vector-indexer.js +61 -0
- package/package.json +45 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface MinHashSignature {
|
|
2
|
+
signature: number[];
|
|
3
|
+
shingleCount: number;
|
|
4
|
+
}
|
|
5
|
+
export interface SimilarityMatch {
|
|
6
|
+
filePath: string;
|
|
7
|
+
similarity: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class DedupEngine {
|
|
10
|
+
private readonly hashCount;
|
|
11
|
+
normalizeText(text: string): string;
|
|
12
|
+
normalizedHash(text: string): string | undefined;
|
|
13
|
+
relationshipForFormats(sourceFormat: string, targetFormat: string): 'format_variant' | 'translation';
|
|
14
|
+
computeMinHash(text: string, shingleSize?: number): MinHashSignature | undefined;
|
|
15
|
+
estimateSimilarity(a: number[], b: number[]): number;
|
|
16
|
+
relationshipForSimilarity(similarity: number): 'near_duplicate' | 'revision' | undefined;
|
|
17
|
+
private hashToUint32;
|
|
18
|
+
private looksTranslationFormat;
|
|
19
|
+
private isStopWord;
|
|
20
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
export class DedupEngine {
|
|
3
|
+
hashCount = 128;
|
|
4
|
+
normalizeText(text) {
|
|
5
|
+
return text
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.normalize('NFKC')
|
|
8
|
+
.replace(/\b\d{4}\b/gu, 'yyyy')
|
|
9
|
+
.replace(/\b\d{1,2}[-/]\d{1,2}[-/]\d{2,4}\b/gu, 'date')
|
|
10
|
+
.replace(/[\p{P}\p{S}]+/gu, ' ')
|
|
11
|
+
.split(/\s+/u)
|
|
12
|
+
.filter(token => token.length > 0 && !this.isStopWord(token))
|
|
13
|
+
.join(' ')
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
normalizedHash(text) {
|
|
17
|
+
const normalized = this.normalizeText(text);
|
|
18
|
+
if (normalized.length === 0)
|
|
19
|
+
return undefined;
|
|
20
|
+
return crypto.createHash('sha256').update(normalized).digest('hex');
|
|
21
|
+
}
|
|
22
|
+
relationshipForFormats(sourceFormat, targetFormat) {
|
|
23
|
+
if (this.looksTranslationFormat(sourceFormat) || this.looksTranslationFormat(targetFormat))
|
|
24
|
+
return 'translation';
|
|
25
|
+
return 'format_variant';
|
|
26
|
+
}
|
|
27
|
+
computeMinHash(text, shingleSize = 5) {
|
|
28
|
+
const tokens = this.normalizeText(text).split(/\s+/u).filter(Boolean);
|
|
29
|
+
if (tokens.length < shingleSize)
|
|
30
|
+
return undefined;
|
|
31
|
+
const shingles = new Set();
|
|
32
|
+
for (let i = 0; i <= tokens.length - shingleSize; i++) {
|
|
33
|
+
shingles.add(tokens.slice(i, i + shingleSize).join(' '));
|
|
34
|
+
}
|
|
35
|
+
if (shingles.size === 0)
|
|
36
|
+
return undefined;
|
|
37
|
+
const signature = Array.from({ length: this.hashCount }, () => Number.MAX_SAFE_INTEGER);
|
|
38
|
+
for (const shingle of shingles) {
|
|
39
|
+
for (let seed = 0; seed < this.hashCount; seed++) {
|
|
40
|
+
const value = this.hashToUint32(`${seed}:${shingle}`);
|
|
41
|
+
if (value < signature[seed])
|
|
42
|
+
signature[seed] = value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { signature, shingleCount: shingles.size };
|
|
46
|
+
}
|
|
47
|
+
estimateSimilarity(a, b) {
|
|
48
|
+
const length = Math.min(a.length, b.length);
|
|
49
|
+
if (length === 0)
|
|
50
|
+
return 0;
|
|
51
|
+
let equal = 0;
|
|
52
|
+
for (let i = 0; i < length; i++) {
|
|
53
|
+
if (a[i] === b[i])
|
|
54
|
+
equal += 1;
|
|
55
|
+
}
|
|
56
|
+
return equal / length;
|
|
57
|
+
}
|
|
58
|
+
relationshipForSimilarity(similarity) {
|
|
59
|
+
if (similarity >= 0.95)
|
|
60
|
+
return 'near_duplicate';
|
|
61
|
+
if (similarity >= 0.8)
|
|
62
|
+
return 'revision';
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
hashToUint32(input) {
|
|
66
|
+
return crypto.createHash('sha256').update(input).digest().readUInt32BE(0);
|
|
67
|
+
}
|
|
68
|
+
looksTranslationFormat(format) {
|
|
69
|
+
return ['translation', 'bilingual'].includes(format);
|
|
70
|
+
}
|
|
71
|
+
isStopWord(token) {
|
|
72
|
+
return STOP_WORDS.has(token);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const STOP_WORDS = new Set([
|
|
76
|
+
'the', 'a', 'an', 'and', 'or', 'of', 'to', 'in', 'for', 'on', 'with', 'by',
|
|
77
|
+
'是', '的', '了', '和', '与', '及', '或', '在', '为', '对', '中', '本文',
|
|
78
|
+
]);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClassifiedFile, IndexStateRecord } from '../types.js';
|
|
2
|
+
import type { FileRelationship } from '../core/index-state-store.js';
|
|
3
|
+
export declare class RelationshipDetector {
|
|
4
|
+
detect(file: ClassifiedFile, indexedRecords: IndexStateRecord[]): Array<Omit<FileRelationship, 'id' | 'createdAt'>>;
|
|
5
|
+
private detectVersionChain;
|
|
6
|
+
private detectTranslation;
|
|
7
|
+
private detectSameDirectoryComplement;
|
|
8
|
+
private parseVersion;
|
|
9
|
+
private parseLanguageSuffix;
|
|
10
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
export class RelationshipDetector {
|
|
3
|
+
detect(file, indexedRecords) {
|
|
4
|
+
const relationships = [];
|
|
5
|
+
for (const record of indexedRecords) {
|
|
6
|
+
if (record.relativePath === file.relativePath)
|
|
7
|
+
continue;
|
|
8
|
+
const version = this.detectVersionChain(file.relativePath, record.relativePath);
|
|
9
|
+
if (version)
|
|
10
|
+
relationships.push(version);
|
|
11
|
+
const translation = this.detectTranslation(file.relativePath, record.relativePath);
|
|
12
|
+
if (translation)
|
|
13
|
+
relationships.push(translation);
|
|
14
|
+
const complementary = this.detectSameDirectoryComplement(file, record);
|
|
15
|
+
if (complementary)
|
|
16
|
+
relationships.push(complementary);
|
|
17
|
+
}
|
|
18
|
+
return relationships;
|
|
19
|
+
}
|
|
20
|
+
detectVersionChain(source, target) {
|
|
21
|
+
const sourceVersion = this.parseVersion(source);
|
|
22
|
+
const targetVersion = this.parseVersion(target);
|
|
23
|
+
if (!sourceVersion || !targetVersion)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (sourceVersion.base !== targetVersion.base)
|
|
26
|
+
return undefined;
|
|
27
|
+
return {
|
|
28
|
+
sourceFile: source,
|
|
29
|
+
targetFile: target,
|
|
30
|
+
relationshipType: 'version_chain',
|
|
31
|
+
confidence: 0.9,
|
|
32
|
+
detail: `版本链: v${targetVersion.version} → v${sourceVersion.version}`,
|
|
33
|
+
userConfirmed: 0,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
detectTranslation(source, target) {
|
|
37
|
+
const sourceLang = this.parseLanguageSuffix(source);
|
|
38
|
+
const targetLang = this.parseLanguageSuffix(target);
|
|
39
|
+
if (!sourceLang || !targetLang)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (sourceLang.base !== targetLang.base || sourceLang.lang === targetLang.lang)
|
|
42
|
+
return undefined;
|
|
43
|
+
return {
|
|
44
|
+
sourceFile: source,
|
|
45
|
+
targetFile: target,
|
|
46
|
+
relationshipType: 'translation',
|
|
47
|
+
confidence: 0.85,
|
|
48
|
+
detail: `语言版本: ${targetLang.lang} ↔ ${sourceLang.lang}`,
|
|
49
|
+
userConfirmed: 0,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
detectSameDirectoryComplement(file, record) {
|
|
53
|
+
if (path.dirname(file.relativePath) !== path.dirname(record.relativePath))
|
|
54
|
+
return undefined;
|
|
55
|
+
if (file.category !== record.category)
|
|
56
|
+
return undefined;
|
|
57
|
+
const max = Math.max(file.fileSize, record.fileSize);
|
|
58
|
+
const min = Math.min(file.fileSize, record.fileSize);
|
|
59
|
+
if (max === 0 || min / max < 0.6)
|
|
60
|
+
return undefined;
|
|
61
|
+
return {
|
|
62
|
+
sourceFile: file.relativePath,
|
|
63
|
+
targetFile: record.relativePath,
|
|
64
|
+
relationshipType: 'complementary',
|
|
65
|
+
confidence: 0.45,
|
|
66
|
+
detail: '同目录、同类型且文件大小相近',
|
|
67
|
+
userConfirmed: 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
parseVersion(filePath) {
|
|
71
|
+
const parsed = path.parse(filePath);
|
|
72
|
+
const match = parsed.name.match(/^(.*?)(?:[_-]?v)(\d+)$/iu);
|
|
73
|
+
if (!match?.[1] || !match[2])
|
|
74
|
+
return undefined;
|
|
75
|
+
return { base: path.join(parsed.dir, match[1]).toLowerCase(), version: Number(match[2]) };
|
|
76
|
+
}
|
|
77
|
+
parseLanguageSuffix(filePath) {
|
|
78
|
+
const parsed = path.parse(filePath);
|
|
79
|
+
const match = parsed.name.match(/^(.*?)[_-](cn|zh|en)$/iu);
|
|
80
|
+
if (!match?.[1] || !match[2])
|
|
81
|
+
return undefined;
|
|
82
|
+
return { base: path.join(parsed.dir, match[1]).toLowerCase(), lang: match[2].toLowerCase() };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface EmbeddingProvider {
|
|
2
|
+
readonly model: string;
|
|
3
|
+
readonly dimensions: number;
|
|
4
|
+
embedDocuments(texts: string[]): Promise<number[][]>;
|
|
5
|
+
embedQuery(text: string): Promise<number[]>;
|
|
6
|
+
}
|
|
7
|
+
export declare class HashEmbeddingProvider implements EmbeddingProvider {
|
|
8
|
+
readonly dimensions: number;
|
|
9
|
+
readonly model = "hash-embedding-local";
|
|
10
|
+
constructor(dimensions?: number);
|
|
11
|
+
embedDocuments(texts: string[]): Promise<number[][]>;
|
|
12
|
+
embedQuery(text: string): Promise<number[]>;
|
|
13
|
+
private embed;
|
|
14
|
+
private normalize;
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
export class HashEmbeddingProvider {
|
|
3
|
+
dimensions;
|
|
4
|
+
model = 'hash-embedding-local';
|
|
5
|
+
constructor(dimensions = 384) {
|
|
6
|
+
this.dimensions = dimensions;
|
|
7
|
+
}
|
|
8
|
+
async embedDocuments(texts) {
|
|
9
|
+
return texts.map(text => this.embed(text));
|
|
10
|
+
}
|
|
11
|
+
async embedQuery(text) {
|
|
12
|
+
return this.embed(text);
|
|
13
|
+
}
|
|
14
|
+
embed(text) {
|
|
15
|
+
const vector = Array.from({ length: this.dimensions }, () => 0);
|
|
16
|
+
const tokens = text.toLowerCase().normalize('NFKC').split(/\s+/u).filter(Boolean);
|
|
17
|
+
for (const token of tokens) {
|
|
18
|
+
const hash = crypto.createHash('sha256').update(token).digest();
|
|
19
|
+
const index = hash.readUInt32BE(0) % this.dimensions;
|
|
20
|
+
const sign = hash.readUInt8(4) % 2 === 0 ? 1 : -1;
|
|
21
|
+
vector[index] = (vector[index] ?? 0) + sign;
|
|
22
|
+
}
|
|
23
|
+
return this.normalize(vector);
|
|
24
|
+
}
|
|
25
|
+
normalize(vector) {
|
|
26
|
+
const norm = Math.sqrt(vector.reduce((sum, value) => sum + value * value, 0));
|
|
27
|
+
if (norm === 0)
|
|
28
|
+
return vector;
|
|
29
|
+
return vector.map(value => value / norm);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ExternalExtractorRegistry } from './external-extractor.js';
|
|
2
|
+
import type { ClassifiedFile } from '../types.js';
|
|
3
|
+
export interface ExtractionResult {
|
|
4
|
+
text: string;
|
|
5
|
+
metadata: Record<string, unknown>;
|
|
6
|
+
warnings: string[];
|
|
7
|
+
extractionTimeMs: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class ContentExtractor {
|
|
10
|
+
private readonly externalExtractors;
|
|
11
|
+
constructor(externalExtractors?: ExternalExtractorRegistry);
|
|
12
|
+
extract(file: ClassifiedFile): Promise<ExtractionResult>;
|
|
13
|
+
private tryExternalExtractor;
|
|
14
|
+
private extractCad;
|
|
15
|
+
private extractCadMesh;
|
|
16
|
+
private extractBinaryStrings;
|
|
17
|
+
private extractData;
|
|
18
|
+
private extractDiagram;
|
|
19
|
+
private parseDelimitedLine;
|
|
20
|
+
private extractDelimitedText;
|
|
21
|
+
private extractOfficeDocument;
|
|
22
|
+
private extractSpreadsheet;
|
|
23
|
+
private extractOfficeZip;
|
|
24
|
+
private extractArchive;
|
|
25
|
+
private extractRasterImage;
|
|
26
|
+
private validateRasterImage;
|
|
27
|
+
private extractPdf;
|
|
28
|
+
private extractPdfText;
|
|
29
|
+
private extractSvg;
|
|
30
|
+
private isTextReadable;
|
|
31
|
+
private looksTextFile;
|
|
32
|
+
private matchAll;
|
|
33
|
+
private stripXml;
|
|
34
|
+
private flattenJson;
|
|
35
|
+
private metadataOnlyText;
|
|
36
|
+
}
|