@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
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 使用 CJS require 解析 npm 包为绝对路径。
|
|
3
|
+
* 多层回退:CJS require.resolve → .pnpm 遍历。
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolvePackage(specifier: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* 解析并加载一个 npm 包。
|
|
8
|
+
* 使用 CJS require() 而非 ESM import(),确保在 Next.js standalone /
|
|
9
|
+
* 打包 Server 上下文中也能正确加载(CJS 走 Module._resolveFilename
|
|
10
|
+
* monkey-patch,不受 ESM 自定义 loader 影响)。
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveAndImport<T = unknown>(specifier: string): Promise<T>;
|
|
13
|
+
/**
|
|
14
|
+
* 获取 node_modules 根目录路径(包含 .pnpm 的那个)。
|
|
15
|
+
* 用于设置子进程 NODE_PATH,确保 OCR 等子进程能解析依赖。
|
|
16
|
+
*/
|
|
17
|
+
export declare function getNodeModulesRoot(): string | null;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模块解析工具。
|
|
3
|
+
*
|
|
4
|
+
* Next.js standalone / 打包 Server 中,ESM 动态 import(绝对路径) 被自定义
|
|
5
|
+
* loader 拦截后可能失败。但 CJS require() 走 Module._resolveFilename
|
|
6
|
+
* monkey-patch,能正确找到 pnpm store 和 vendor 目录中的包。
|
|
7
|
+
*
|
|
8
|
+
* 此模块统一使用 CJS require 加载依赖,确保在所有上下文中可靠工作。
|
|
9
|
+
*/
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
/** 从 knowledge 包自身目录解析的 CJS require 函数 */
|
|
15
|
+
const localRequire = createRequire(import.meta.url);
|
|
16
|
+
/** knowledge 包所在的实际目录 */
|
|
17
|
+
const knowledgeDir = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
/**
|
|
19
|
+
* 在 .pnpm 目录中查找包的实际路径(处理 standalone 缺少顶层 symlink 的情况)。
|
|
20
|
+
*/
|
|
21
|
+
function findInPnpm(packageName) {
|
|
22
|
+
let dir = knowledgeDir;
|
|
23
|
+
for (let i = 0; i < 10; i++) {
|
|
24
|
+
const nodeModules = path.join(dir, 'node_modules');
|
|
25
|
+
const pnpmDir = path.join(nodeModules, '.pnpm');
|
|
26
|
+
if (fs.existsSync(pnpmDir)) {
|
|
27
|
+
const pkgParts = packageName.split('/');
|
|
28
|
+
const flatName = pkgParts.length > 1
|
|
29
|
+
? `${pkgParts[0]}+${pkgParts.slice(1).join('/')}`
|
|
30
|
+
: packageName;
|
|
31
|
+
try {
|
|
32
|
+
for (const entry of fs.readdirSync(pnpmDir)) {
|
|
33
|
+
if (entry.startsWith(flatName + '@')) {
|
|
34
|
+
const pkgDir = path.join(pnpmDir, entry, 'node_modules', packageName);
|
|
35
|
+
if (fs.existsSync(pkgDir))
|
|
36
|
+
return pkgDir;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch { /* continue upward */ }
|
|
41
|
+
}
|
|
42
|
+
const parent = path.dirname(dir);
|
|
43
|
+
if (parent === dir)
|
|
44
|
+
break;
|
|
45
|
+
dir = parent;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 使用 CJS require 解析 npm 包为绝对路径。
|
|
51
|
+
* 多层回退:CJS require.resolve → .pnpm 遍历。
|
|
52
|
+
*/
|
|
53
|
+
export function resolvePackage(specifier) {
|
|
54
|
+
try {
|
|
55
|
+
return localRequire.resolve(specifier);
|
|
56
|
+
}
|
|
57
|
+
catch { /* fall through */ }
|
|
58
|
+
const parts = specifier.split('/');
|
|
59
|
+
let packageName;
|
|
60
|
+
if (parts[0]?.startsWith('@')) {
|
|
61
|
+
packageName = `${parts[0]}/${parts[1]}`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
packageName = parts[0] ?? specifier;
|
|
65
|
+
}
|
|
66
|
+
const pnpmRoot = findInPnpm(packageName);
|
|
67
|
+
if (pnpmRoot) {
|
|
68
|
+
const subPath = packageName === specifier ? '' : specifier.slice(packageName.length + 1);
|
|
69
|
+
const fullPath = subPath ? path.join(pnpmRoot, subPath) : pnpmRoot;
|
|
70
|
+
if (fs.existsSync(fullPath))
|
|
71
|
+
return fullPath;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`Cannot resolve package: ${specifier}`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 解析并加载一个 npm 包。
|
|
77
|
+
* 使用 CJS require() 而非 ESM import(),确保在 Next.js standalone /
|
|
78
|
+
* 打包 Server 上下文中也能正确加载(CJS 走 Module._resolveFilename
|
|
79
|
+
* monkey-patch,不受 ESM 自定义 loader 影响)。
|
|
80
|
+
*/
|
|
81
|
+
export async function resolveAndImport(specifier) {
|
|
82
|
+
const resolvedPath = resolvePackage(specifier);
|
|
83
|
+
// CJS require 能穿透 pnpm store 和 Next.js bundle,兼容性最好
|
|
84
|
+
try {
|
|
85
|
+
return localRequire(resolvedPath);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// 某些包可能是纯 ESM(如 pdfjs-dist v5),回退到 import()
|
|
89
|
+
return import(resolvedPath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 获取 node_modules 根目录路径(包含 .pnpm 的那个)。
|
|
94
|
+
* 用于设置子进程 NODE_PATH,确保 OCR 等子进程能解析依赖。
|
|
95
|
+
*/
|
|
96
|
+
export function getNodeModulesRoot() {
|
|
97
|
+
let dir = path.dirname(knowledgeDir);
|
|
98
|
+
for (let i = 0; i < 10; i++) {
|
|
99
|
+
// 开发环境: node_modules/.pnpm
|
|
100
|
+
const nm = path.join(dir, 'node_modules');
|
|
101
|
+
if (fs.existsSync(path.join(nm, '.pnpm')))
|
|
102
|
+
return nm;
|
|
103
|
+
// 打包环境: vendor/.pnpm(bundle-server 把 node_modules 改名 vendor)
|
|
104
|
+
const vendor = path.join(dir, 'vendor');
|
|
105
|
+
if (fs.existsSync(path.join(vendor, '.pnpm')))
|
|
106
|
+
return vendor;
|
|
107
|
+
const parent = path.dirname(dir);
|
|
108
|
+
if (parent === dir)
|
|
109
|
+
break;
|
|
110
|
+
dir = parent;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export * from './types.js';
|
|
2
|
-
export * from './constants.js';
|
|
3
1
|
export { TextChunker, type ChunkConfig, type TextChunk } from './chunking/text-chunker.js';
|
|
4
2
|
export { FileClassifier } from './classification/classifier.js';
|
|
5
3
|
export { DedupEngine, type MinHashSignature, type SimilarityMatch } from './dedup/dedup-engine.js';
|
|
@@ -10,7 +8,7 @@ export { CommandExternalExtractor, ExternalExtractorRegistry, type CommandExtern
|
|
|
10
8
|
export { ChangeTracker } from './core/change-tracker.js';
|
|
11
9
|
export { KnowledgeFileScanner, type DiskFileStat } from './core/file-scanner.js';
|
|
12
10
|
export { IndexStateStore, type ChunkSearchResult, type FileHashRecord, type FileRelationship, type StoredChunk } from './core/index-state-store.js';
|
|
13
|
-
export { KnowledgeBaseManager, type KnowledgeBaseManagerOptions } from './core/knowledge-base-manager.js';
|
|
11
|
+
export { KnowledgeBaseManager, type KnowledgeBaseManagerOptions, type KnowledgeIndexProgress } from './core/knowledge-base-manager.js';
|
|
14
12
|
export { MultiProjectManager } from './core/multi-project-manager.js';
|
|
15
13
|
export { computeProjectId } from './core/project-id.js';
|
|
16
14
|
export { ensureProjectCustomizeFile, getProjectConfigPath, getProjectKbPath, ProjectConfigManager } from './core/project-config.js';
|
|
@@ -20,4 +18,4 @@ export { CollectionManager, globalCollectionName, projectCollectionName } from '
|
|
|
20
18
|
export type { CollectionClient, VectorCollectionInfo, VectorDocument, VectorSearchQuery, VectorSearchResult, VectorStoreInterface } from './vector/types.js';
|
|
21
19
|
export { VectorIndexer, type VectorIndexResult } from './vector/vector-indexer.js';
|
|
22
20
|
export { FederationSearch, type FederatedQuery, type FederatedResult, type FederatedSearchItem, type SearchFilters, type SearchScope } from './search/federation-search.js';
|
|
23
|
-
export {
|
|
21
|
+
export type { LLMChatMessage, LLMChatOptions, LLMChatResponse, LLMSearchProvider } from './llm/llm-search-provider.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Types and constants from types.ts and constants.ts are not exported
|
|
2
|
+
// as they are only used internally within the knowledge package.
|
|
3
3
|
export { TextChunker } from './chunking/text-chunker.js';
|
|
4
4
|
export { FileClassifier } from './classification/classifier.js';
|
|
5
5
|
export { DedupEngine } from './dedup/dedup-engine.js';
|
|
@@ -19,4 +19,3 @@ export { ChromaHttpClient, ChromaVectorStore } from './vector/chroma-store.js';
|
|
|
19
19
|
export { CollectionManager, globalCollectionName, projectCollectionName } from './vector/collection-manager.js';
|
|
20
20
|
export { VectorIndexer } from './vector/vector-indexer.js';
|
|
21
21
|
export { FederationSearch } from './search/federation-search.js';
|
|
22
|
-
export { startKnowledgeDashboard } from './server/dashboard-server.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 轻量级 LLM 搜索 Provider 接口。
|
|
3
|
+
*
|
|
4
|
+
* 定义在 knowledge 包内部,避免直接依赖 @customize-agent/llm。
|
|
5
|
+
* CLI 层的 ILLMProvider 在结构上兼容此接口,可直接传入。
|
|
6
|
+
*/
|
|
7
|
+
export interface LLMChatMessage {
|
|
8
|
+
role: 'system' | 'user' | 'assistant';
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
export interface LLMChatOptions {
|
|
12
|
+
temperature?: number;
|
|
13
|
+
maxTokens?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface LLMChatResponse {
|
|
16
|
+
content: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* LLM 搜索 Provider —— 用于查询扩展和语义重排序。
|
|
20
|
+
*/
|
|
21
|
+
export interface LLMSearchProvider {
|
|
22
|
+
chat(messages: LLMChatMessage[], options?: LLMChatOptions): Promise<LLMChatResponse>;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -8,16 +8,45 @@ export interface FederatedSearchItem {
|
|
|
8
8
|
collection: string;
|
|
9
9
|
score: number;
|
|
10
10
|
contentHash?: string;
|
|
11
|
+
chunkIndex?: number;
|
|
12
|
+
parentId?: string;
|
|
13
|
+
source?: 'keyword' | 'vector' | 'hybrid';
|
|
14
|
+
sectionTitle?: string;
|
|
15
|
+
rowRange?: string;
|
|
16
|
+
chunkKind?: string;
|
|
17
|
+
scoreDetails?: {
|
|
18
|
+
keywordScore?: number;
|
|
19
|
+
bm25Score?: number;
|
|
20
|
+
vectorScore?: number;
|
|
21
|
+
hybridScore?: number;
|
|
22
|
+
exactPhraseBoost?: number;
|
|
23
|
+
rerankBoost?: number;
|
|
24
|
+
llmRelevanceScore?: number;
|
|
25
|
+
};
|
|
26
|
+
facets?: Record<string, string | number | string[]>;
|
|
11
27
|
}
|
|
12
28
|
export interface FederatedResult {
|
|
13
29
|
results: FederatedSearchItem[];
|
|
14
30
|
scopesSearched: Array<'project' | 'global'>;
|
|
15
31
|
queryTimeMs: number;
|
|
32
|
+
debug?: {
|
|
33
|
+
originalQuery?: string;
|
|
34
|
+
rewrittenQueries?: string[];
|
|
35
|
+
weights?: Record<string, number>;
|
|
36
|
+
recallCounts?: Record<string, number>;
|
|
37
|
+
reranker?: string;
|
|
38
|
+
};
|
|
16
39
|
}
|
|
17
40
|
export interface SearchFilters {
|
|
18
41
|
category?: string;
|
|
19
42
|
filePath?: string;
|
|
20
43
|
}
|
|
44
|
+
export interface RetrievalWeights {
|
|
45
|
+
keyword?: number;
|
|
46
|
+
vector?: number;
|
|
47
|
+
rewrite?: number;
|
|
48
|
+
hybridBonus?: number;
|
|
49
|
+
}
|
|
21
50
|
export interface FederatedQuery {
|
|
22
51
|
query: string;
|
|
23
52
|
queryEmbedding: number[];
|
|
@@ -79,6 +79,13 @@ export class FederationSearch {
|
|
|
79
79
|
collection: result.collection,
|
|
80
80
|
score: result.score,
|
|
81
81
|
contentHash: typeof result.document.metadata.content_hash === 'string' ? result.document.metadata.content_hash : undefined,
|
|
82
|
+
chunkIndex: typeof result.document.metadata.chunk_index === 'number' ? result.document.metadata.chunk_index : undefined,
|
|
83
|
+
parentId: typeof result.document.metadata.parent_id === 'string' ? result.document.metadata.parent_id : undefined,
|
|
84
|
+
source: 'vector',
|
|
85
|
+
sectionTitle: typeof result.document.metadata.section_title === 'string' ? result.document.metadata.section_title : undefined,
|
|
86
|
+
rowRange: typeof result.document.metadata.row_range === 'string' ? result.document.metadata.row_range : undefined,
|
|
87
|
+
chunkKind: typeof result.document.metadata.chunk_kind === 'string' ? result.document.metadata.chunk_kind : undefined,
|
|
88
|
+
scoreDetails: { vectorScore: result.score },
|
|
82
89
|
};
|
|
83
90
|
}
|
|
84
91
|
resolveScopes(scope) {
|
|
@@ -91,7 +98,7 @@ export class FederationSearch {
|
|
|
91
98
|
crossScopeDedup(results) {
|
|
92
99
|
const byKey = new Map();
|
|
93
100
|
for (const result of results) {
|
|
94
|
-
const key = result.contentHash ?? result.filePath
|
|
101
|
+
const key = result.contentHash ?? `${result.filePath}#${result.parentId ?? result.chunkIndex ?? result.id}`;
|
|
95
102
|
const existing = byKey.get(key);
|
|
96
103
|
if (!existing || (existing.scope === 'global' && result.scope === 'project')) {
|
|
97
104
|
byKey.set(key, result);
|
|
@@ -14,6 +14,7 @@ export declare class ChromaHttpClient implements CollectionClient {
|
|
|
14
14
|
readonly baseUrl: string;
|
|
15
15
|
readonly tenant: string;
|
|
16
16
|
readonly database: string;
|
|
17
|
+
private readonly collectionIds;
|
|
17
18
|
constructor(options?: ChromaClientOptions);
|
|
18
19
|
heartbeat(): Promise<boolean>;
|
|
19
20
|
getOrCreateCollection(name: string, metadata?: Record<string, unknown>): Promise<VectorCollectionInfo>;
|
|
@@ -22,6 +23,7 @@ export declare class ChromaHttpClient implements CollectionClient {
|
|
|
22
23
|
upsert(collectionName: string, documents: VectorDocument[]): Promise<void>;
|
|
23
24
|
deleteWhere(collectionName: string, where: Record<string, string | number | boolean>): Promise<void>;
|
|
24
25
|
query(collectionName: string, query: VectorSearchQuery): Promise<ChromaQueryResponse>;
|
|
26
|
+
private getCollectionId;
|
|
25
27
|
private collectionsPath;
|
|
26
28
|
private request;
|
|
27
29
|
private toCollectionInfo;
|
|
@@ -2,14 +2,15 @@ export class ChromaHttpClient {
|
|
|
2
2
|
baseUrl;
|
|
3
3
|
tenant;
|
|
4
4
|
database;
|
|
5
|
+
collectionIds = new Map();
|
|
5
6
|
constructor(options = {}) {
|
|
6
|
-
this.baseUrl = options.baseUrl ?? 'http://localhost:
|
|
7
|
+
this.baseUrl = options.baseUrl ?? process.env.CHROMA_URL ?? process.env.CHROMA_BASE_URL ?? 'http://localhost:17322';
|
|
7
8
|
this.tenant = options.tenant ?? 'default_tenant';
|
|
8
9
|
this.database = options.database ?? 'default_database';
|
|
9
10
|
}
|
|
10
11
|
async heartbeat() {
|
|
11
12
|
try {
|
|
12
|
-
await this.request('/api/
|
|
13
|
+
await this.request('/api/v2/heartbeat');
|
|
13
14
|
return true;
|
|
14
15
|
}
|
|
15
16
|
catch {
|
|
@@ -17,24 +18,33 @@ export class ChromaHttpClient {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
async getOrCreateCollection(name, metadata = {}) {
|
|
21
|
+
const body = { name, get_or_create: true };
|
|
22
|
+
if (Object.keys(metadata).length > 0)
|
|
23
|
+
body.metadata = metadata;
|
|
20
24
|
const response = await this.request(this.collectionsPath(), {
|
|
21
25
|
method: 'POST',
|
|
22
|
-
body: JSON.stringify(
|
|
23
|
-
});
|
|
26
|
+
body: JSON.stringify(body),
|
|
27
|
+
}, 10000);
|
|
28
|
+
if (response.id)
|
|
29
|
+
this.collectionIds.set(name, response.id);
|
|
24
30
|
return this.toCollectionInfo(response);
|
|
25
31
|
}
|
|
26
32
|
async listCollections() {
|
|
27
|
-
const response = await this.request(this.collectionsPath());
|
|
33
|
+
const response = await this.request(this.collectionsPath(), {}, 10000);
|
|
34
|
+
for (const collection of response)
|
|
35
|
+
if (collection.id)
|
|
36
|
+
this.collectionIds.set(collection.name, collection.id);
|
|
28
37
|
return response.map(collection => this.toCollectionInfo(collection));
|
|
29
38
|
}
|
|
30
39
|
async deleteCollection(name) {
|
|
31
40
|
await this.request(`${this.collectionsPath()}/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
|
41
|
+
this.collectionIds.delete(name);
|
|
32
42
|
}
|
|
33
43
|
async upsert(collectionName, documents) {
|
|
34
44
|
if (documents.length === 0)
|
|
35
45
|
return;
|
|
36
|
-
await this.
|
|
37
|
-
await this.request(`${this.collectionsPath()}/${encodeURIComponent(
|
|
46
|
+
const collectionId = await this.getCollectionId(collectionName);
|
|
47
|
+
await this.request(`${this.collectionsPath()}/${encodeURIComponent(collectionId)}/upsert`, {
|
|
38
48
|
method: 'POST',
|
|
39
49
|
body: JSON.stringify({
|
|
40
50
|
ids: documents.map(document => document.id),
|
|
@@ -42,16 +52,18 @@ export class ChromaHttpClient {
|
|
|
42
52
|
documents: documents.map(document => document.content),
|
|
43
53
|
metadatas: documents.map(document => document.metadata),
|
|
44
54
|
}),
|
|
45
|
-
});
|
|
55
|
+
}, 30000);
|
|
46
56
|
}
|
|
47
57
|
async deleteWhere(collectionName, where) {
|
|
48
|
-
await this.
|
|
58
|
+
const collectionId = await this.getCollectionId(collectionName);
|
|
59
|
+
await this.request(`${this.collectionsPath()}/${encodeURIComponent(collectionId)}/delete`, {
|
|
49
60
|
method: 'POST',
|
|
50
61
|
body: JSON.stringify({ where }),
|
|
51
|
-
});
|
|
62
|
+
}, 10000);
|
|
52
63
|
}
|
|
53
64
|
async query(collectionName, query) {
|
|
54
|
-
|
|
65
|
+
const collectionId = await this.getCollectionId(collectionName);
|
|
66
|
+
return this.request(`${this.collectionsPath()}/${encodeURIComponent(collectionId)}/query`, {
|
|
55
67
|
method: 'POST',
|
|
56
68
|
body: JSON.stringify({
|
|
57
69
|
query_embeddings: [query.queryEmbedding],
|
|
@@ -59,19 +71,38 @@ export class ChromaHttpClient {
|
|
|
59
71
|
where: query.where,
|
|
60
72
|
include: ['documents', 'metadatas', 'distances'],
|
|
61
73
|
}),
|
|
62
|
-
});
|
|
74
|
+
}, 10000);
|
|
75
|
+
}
|
|
76
|
+
async getCollectionId(name) {
|
|
77
|
+
const cached = this.collectionIds.get(name);
|
|
78
|
+
if (cached)
|
|
79
|
+
return cached;
|
|
80
|
+
const collection = await this.getOrCreateCollection(name);
|
|
81
|
+
if (!collection.id)
|
|
82
|
+
throw new Error(`ChromaDB collection has no id: ${name}`);
|
|
83
|
+
this.collectionIds.set(name, collection.id);
|
|
84
|
+
return collection.id;
|
|
63
85
|
}
|
|
64
86
|
collectionsPath() {
|
|
65
|
-
return `/api/
|
|
66
|
-
}
|
|
67
|
-
async request(path, init = {}) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
return `/api/v2/tenants/${encodeURIComponent(this.tenant)}/databases/${encodeURIComponent(this.database)}/collections`;
|
|
88
|
+
}
|
|
89
|
+
async request(path, init = {}, timeoutMs = 3000) {
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
92
|
+
let response;
|
|
93
|
+
try {
|
|
94
|
+
response = await fetch(`${this.baseUrl}${path}`, {
|
|
95
|
+
...init,
|
|
96
|
+
signal: controller.signal,
|
|
97
|
+
headers: {
|
|
98
|
+
'content-type': 'application/json',
|
|
99
|
+
...(init.headers ?? {}),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
}
|
|
75
106
|
if (!response.ok) {
|
|
76
107
|
throw new Error(`ChromaDB request failed: ${response.status} ${response.statusText}`);
|
|
77
108
|
}
|
|
@@ -44,6 +44,7 @@ export class VectorIndexer {
|
|
|
44
44
|
return grouped;
|
|
45
45
|
}
|
|
46
46
|
toVectorDocument(chunk, embedding) {
|
|
47
|
+
const chunkMetadata = this.parseMetadata(chunk.metadataJson);
|
|
47
48
|
return {
|
|
48
49
|
id: chunk.id,
|
|
49
50
|
content: chunk.content,
|
|
@@ -55,7 +56,29 @@ export class VectorIndexer {
|
|
|
55
56
|
format: chunk.format,
|
|
56
57
|
token_count: chunk.tokenCount,
|
|
57
58
|
section_title: chunk.sectionTitle ?? null,
|
|
59
|
+
parent_id: this.metadataString(chunkMetadata.parentId),
|
|
60
|
+
parent_index: this.metadataNumber(chunkMetadata.parentIndex),
|
|
61
|
+
child_index: this.metadataNumber(chunkMetadata.childIndex),
|
|
62
|
+
chunk_kind: this.metadataString(chunkMetadata.chunkKind),
|
|
63
|
+
row_range: this.metadataString(chunkMetadata.rowRange),
|
|
64
|
+
split_strategy: this.metadataString(chunkMetadata.splitStrategy),
|
|
58
65
|
},
|
|
59
66
|
};
|
|
60
67
|
}
|
|
68
|
+
parseMetadata(metadataJson) {
|
|
69
|
+
if (!metadataJson)
|
|
70
|
+
return {};
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(metadataJson);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
metadataString(value) {
|
|
79
|
+
return typeof value === 'string' ? value : null;
|
|
80
|
+
}
|
|
81
|
+
metadataNumber(value) {
|
|
82
|
+
return typeof value === 'number' ? value : null;
|
|
83
|
+
}
|
|
61
84
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@customize-agent/knowledge",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Local knowledge base infrastructure for customize-agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
"license": "MIT",
|
|
9
15
|
"author": "Pan-jijian",
|
|
10
16
|
"repository": {
|
|
@@ -28,9 +34,11 @@
|
|
|
28
34
|
"better-sqlite3": "^12.10.0",
|
|
29
35
|
"fast-glob": "^3.3.3",
|
|
30
36
|
"jszip": "^3.10.1",
|
|
31
|
-
"
|
|
37
|
+
"@napi-rs/canvas": "^0.1.82",
|
|
38
|
+
"mammoth": "^1.12.0",
|
|
32
39
|
"pdf-parse": "^2.4.5",
|
|
33
|
-
"
|
|
40
|
+
"pdfjs-dist": "^5.4.394",
|
|
41
|
+
"tesseract.js": "^7.0.0",
|
|
34
42
|
"xlsx": "^0.18.5"
|
|
35
43
|
},
|
|
36
44
|
"devDependencies": {
|