@huyooo/ai-search 0.2.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.
@@ -0,0 +1,576 @@
1
+ import { ToolPlugin, Tool } from '@huyooo/ai-chat-core';
2
+
3
+ /**
4
+ * 文件类型(与 file-explorer 兼容)
5
+ */
6
+ declare const FileType: {
7
+ readonly FOLDER: "folder";
8
+ readonly FILE: "file";
9
+ readonly IMAGE: "image";
10
+ readonly VIDEO: "video";
11
+ readonly MUSIC: "music";
12
+ readonly DOCUMENT: "document";
13
+ readonly CODE: "code";
14
+ readonly TEXT: "text";
15
+ readonly PDF: "pdf";
16
+ readonly ARCHIVE: "archive";
17
+ readonly APPLICATION: "application";
18
+ readonly UNKNOWN: "unknown";
19
+ };
20
+ type FileType = (typeof FileType)[keyof typeof FileType];
21
+ /**
22
+ * 搜索结果(兼容 file-explorer 的 FileItem)
23
+ */
24
+ interface SearchResult {
25
+ /** 唯一标识(文件路径) */
26
+ id: string;
27
+ /** 文件名 */
28
+ name: string;
29
+ /** 文件类型 */
30
+ type: FileType;
31
+ /** 格式化后的文件大小 */
32
+ size?: string;
33
+ /** 格式化后的修改日期 */
34
+ dateModified?: string;
35
+ /** 文件 URL */
36
+ url?: string;
37
+ /** 缩略图 URL(文档类型通常没有) */
38
+ thumbnailUrl?: string;
39
+ /** 相关度分数 0-1 */
40
+ score: number;
41
+ /** 匹配文本摘要 */
42
+ snippet?: string;
43
+ /** 匹配类型 */
44
+ matchType: 'semantic' | 'keyword' | 'hybrid';
45
+ /** 匹配的块索引(分块后可用) */
46
+ chunkIndex?: number;
47
+ }
48
+ /**
49
+ * 索引项(内部使用)
50
+ */
51
+ interface IndexedDocument {
52
+ /** 唯一 ID */
53
+ id: string;
54
+ /** 文件路径 */
55
+ path: string;
56
+ /** 文件名 */
57
+ name: string;
58
+ /** 文件类型 */
59
+ fileType: FileType;
60
+ /** 文件扩展名 */
61
+ extension: string;
62
+ /** 文档标题(从内容提取) */
63
+ title?: string;
64
+ /** 文档内容(用于全文搜索) */
65
+ content: string;
66
+ /** 文件大小(字节) */
67
+ fileSize: number;
68
+ /** 创建时间 */
69
+ createdAt: Date;
70
+ /** 修改时间 */
71
+ modifiedAt: Date;
72
+ /** 索引时间 */
73
+ indexedAt: Date;
74
+ /** 内容哈希(用于检测变更) */
75
+ contentHash: string;
76
+ /** 分块数量(用于清理旧数据) */
77
+ chunkCount?: number;
78
+ }
79
+ /**
80
+ * 搜索配置
81
+ */
82
+ interface SearchConfig {
83
+ /** 数据存储目录 */
84
+ dataDir: string;
85
+ /** 索引目录列表 */
86
+ indexDirectories?: string[];
87
+ /** 排除目录 */
88
+ excludeDirs?: string[];
89
+ /** 支持的扩展名 */
90
+ extensions?: string[];
91
+ /** 最大文件大小(字节) */
92
+ maxFileSize?: number;
93
+ /** Embedding 模型(默认 doubao-embedding-vision-250615) */
94
+ embeddingModel?: string;
95
+ /** 豆包 API Key(ARK_API_KEY),也可通过环境变量设置 */
96
+ arkApiKey?: string;
97
+ /** 向量维度(默认 1024) */
98
+ embeddingDimension?: number;
99
+ /** 并行索引并发数(默认 5) */
100
+ indexConcurrency?: number;
101
+ /** 是否启用并行索引(默认 true) */
102
+ enableParallelIndexing?: boolean;
103
+ }
104
+ /**
105
+ * 搜索选项
106
+ */
107
+ interface SearchOptions {
108
+ /** 返回数量限制 */
109
+ limit?: number;
110
+ /** 文件类型过滤 */
111
+ fileTypes?: FileType[];
112
+ /** 时间范围过滤 */
113
+ dateRange?: {
114
+ start?: Date;
115
+ end?: Date;
116
+ };
117
+ /** 目录过滤 */
118
+ directories?: string[];
119
+ /** 搜索模式 */
120
+ mode?: 'semantic' | 'keyword' | 'hybrid';
121
+ /** 文件大小过滤 */
122
+ sizeRange?: {
123
+ min?: number;
124
+ max?: number;
125
+ };
126
+ /** 文件名模式过滤(支持通配符 * 和 ?) */
127
+ fileNamePattern?: string;
128
+ /** 文档标题包含 */
129
+ titleContains?: string;
130
+ /** 组合条件模式 */
131
+ combineMode?: 'AND' | 'OR';
132
+ }
133
+ /**
134
+ * 索引进度回调
135
+ */
136
+ interface IndexProgress {
137
+ /** 当前已索引数量 */
138
+ indexed: number;
139
+ /** 总文件数量 */
140
+ total: number;
141
+ /** 当前正在处理的文件 */
142
+ currentFile?: string;
143
+ /** 阶段 */
144
+ stage: 'scanning' | 'parsing' | 'embedding' | 'storing' | 'done' | 'cancelled';
145
+ }
146
+ /**
147
+ * 索引统计
148
+ */
149
+ interface IndexStats {
150
+ /** 总文档数 */
151
+ totalDocuments: number;
152
+ /** 按类型统计 */
153
+ byType: Record<FileType, number>;
154
+ /** 索引目录 */
155
+ directories: string[];
156
+ /** 最后更新时间 */
157
+ lastUpdated?: Date;
158
+ /** 索引大小(字节) */
159
+ indexSize: number;
160
+ }
161
+ /**
162
+ * 文件监听选项
163
+ */
164
+ interface WatchOptions {
165
+ /** 是否忽略初始扫描(只监听后续变化) */
166
+ ignoreInitial?: boolean;
167
+ /** 防抖延迟(毫秒),避免频繁触发 */
168
+ debounce?: number;
169
+ /** 监听事件回调 */
170
+ onEvent?: (event: WatchEvent) => void;
171
+ }
172
+ /**
173
+ * 文件监听事件
174
+ */
175
+ interface WatchEvent {
176
+ /** 事件类型 */
177
+ type: 'add' | 'change' | 'unlink' | 'unlinkDir';
178
+ /** 文件路径 */
179
+ path: string;
180
+ /** 时间戳 */
181
+ timestamp: Date;
182
+ }
183
+ /**
184
+ * 批量操作结果
185
+ */
186
+ interface BatchOperationResult {
187
+ /** 成功数量 */
188
+ success: number;
189
+ /** 失败数量 */
190
+ failed: number;
191
+ /** 失败的文件路径和错误信息 */
192
+ errors: Array<{
193
+ path: string;
194
+ error: string;
195
+ }>;
196
+ }
197
+ /**
198
+ * 索引导出信息
199
+ */
200
+ interface ExportInfo {
201
+ /** 导出路径 */
202
+ exportPath: string;
203
+ /** 导出时间 */
204
+ timestamp: Date;
205
+ /** 包含的组件 */
206
+ components: {
207
+ meta: boolean;
208
+ vectors: boolean;
209
+ fulltext: boolean;
210
+ };
211
+ /** 统计信息 */
212
+ stats: IndexStats;
213
+ }
214
+ /**
215
+ * 备份信息
216
+ */
217
+ interface BackupInfo {
218
+ /** 备份路径 */
219
+ path: string;
220
+ /** 备份时间 */
221
+ timestamp: Date;
222
+ /** 备份大小(字节) */
223
+ size: number;
224
+ }
225
+ /**
226
+ * 索引错误信息
227
+ */
228
+ interface IndexError {
229
+ /** 文件路径 */
230
+ filePath: string;
231
+ /** 错误信息 */
232
+ error: string;
233
+ /** 重试次数 */
234
+ retryCount: number;
235
+ /** 时间戳 */
236
+ timestamp: Date;
237
+ }
238
+ /**
239
+ * 健康检查结果
240
+ */
241
+ interface HealthCheckResult {
242
+ /** 是否健康 */
243
+ healthy: boolean;
244
+ /** 总文档数 */
245
+ totalDocuments: number;
246
+ /** 无效索引数(文件不存在) */
247
+ invalidIndexes: number;
248
+ /** 过期索引数(文件已修改) */
249
+ staleIndexes: number;
250
+ /** 错误数 */
251
+ errorCount: number;
252
+ /** 索引完整性 */
253
+ integrity: {
254
+ meta: boolean;
255
+ vectors: boolean;
256
+ fulltext: boolean;
257
+ };
258
+ }
259
+ /**
260
+ * 默认配置
261
+ * 注意:详细的排除规则和文件类型规则现在由 rules.ts 中的规则管理器统一管理
262
+ * 这里的配置主要用于向后兼容和简单配置场景
263
+ */
264
+ declare const DEFAULT_CONFIG: Partial<SearchConfig>;
265
+
266
+ /**
267
+ * 文本分块模块
268
+ * 递归语义分割,保持上下文连贯性
269
+ */
270
+ interface ChunkOptions {
271
+ /** 目标块大小(字符数) */
272
+ chunkSize?: number;
273
+ /** 重叠大小(字符数) */
274
+ overlap?: number;
275
+ /** 最小块大小(小于此值会合并到上一块) */
276
+ minChunkSize?: number;
277
+ }
278
+ interface TextChunk {
279
+ /** 块内容 */
280
+ content: string;
281
+ /** 块索引(从 0 开始) */
282
+ index: number;
283
+ /** 在原文中的起始位置 */
284
+ startOffset: number;
285
+ /** 在原文中的结束位置 */
286
+ endOffset: number;
287
+ }
288
+ /**
289
+ * 递归文本分块
290
+ *
291
+ * 算法:
292
+ * 1. 尝试用当前分隔符切分文本
293
+ * 2. 如果块太大,用下一个分隔符继续切分
294
+ * 3. 合并小块,添加重叠
295
+ */
296
+ declare function splitText(text: string, options?: ChunkOptions): TextChunk[];
297
+ /**
298
+ * 获取分块统计信息
299
+ */
300
+ declare function getChunkStats(chunks: TextChunk[]): {
301
+ count: number;
302
+ avgSize: number;
303
+ minSize: number;
304
+ maxSize: number;
305
+ totalSize: number;
306
+ };
307
+
308
+ /**
309
+ * 索引流水线 - 支持文本分块
310
+ *
311
+ * 设计原则:
312
+ * 1. 一个文件 → 多个 chunk → 多个向量
313
+ * 2. 并发执行文件处理,每完成一个文件进度 +1
314
+ * 3. 分块后每个 chunk 单独向量化,提高检索精度
315
+ */
316
+
317
+ /** 流水线配置 */
318
+ interface PipelineConfig {
319
+ /** 并发数(同时处理的文件数) */
320
+ concurrency: number;
321
+ /** 分块配置(小于 chunkSize 的文档自然只有 1 个 chunk) */
322
+ chunk?: ChunkOptions;
323
+ }
324
+ /** 兼容旧接口 */
325
+ interface EmbeddedDoc {
326
+ filePath: string;
327
+ content: string;
328
+ title: string;
329
+ contentHash: string;
330
+ vector: number[];
331
+ /** 新增:块信息 */
332
+ chunkIndex?: number;
333
+ totalChunks?: number;
334
+ }
335
+ /** 流水线统计 */
336
+ interface PipelineStats {
337
+ totalFiles: number;
338
+ completed: number;
339
+ stored: number;
340
+ skipped: number;
341
+ failed: number;
342
+ totalTime: number;
343
+ }
344
+ /** 存储回调 */
345
+ type StoreCallback = (doc: EmbeddedDoc) => Promise<{
346
+ stored: boolean;
347
+ skipped: boolean;
348
+ }>;
349
+ /** 跳过检查回调(基于 mtime) */
350
+ type SkipCheckCallback = (filePath: string, mtime: Date) => boolean;
351
+ /**
352
+ * 简化版索引流水线
353
+ */
354
+ declare class IndexingPipeline {
355
+ private config;
356
+ private onProgress?;
357
+ private storeCallback?;
358
+ private skipCheck?;
359
+ private cancelled;
360
+ private stats;
361
+ constructor(config?: Partial<PipelineConfig>);
362
+ /** 取消正在执行的任务 */
363
+ cancel(): void;
364
+ /** 检查是否已取消 */
365
+ isCancelled(): boolean;
366
+ /**
367
+ * 处理单个文件(完整流程:解析 → 分块 → 嵌入 → 存储)
368
+ */
369
+ private processFile;
370
+ /**
371
+ * 执行流水线
372
+ */
373
+ run(files: string[], storeCallback: StoreCallback, onProgress?: (progress: IndexProgress) => void, skipCheck?: SkipCheckCallback): Promise<PipelineStats>;
374
+ /** 报告进度 */
375
+ private reportProgress;
376
+ }
377
+ /**
378
+ * 创建索引流水线
379
+ */
380
+ declare function createIndexingPipeline(config?: Partial<PipelineConfig>): IndexingPipeline;
381
+
382
+ declare class DocumentSearch {
383
+ private config;
384
+ private vectorStore;
385
+ private fullTextIndex;
386
+ private metaStore;
387
+ private initialized;
388
+ private directoryWatcher;
389
+ private indexErrors;
390
+ private maxRetries;
391
+ /** 索引锁:防止同一文件被并发索引 */
392
+ private indexingLocks;
393
+ /** 当前运行的流水线(用于取消) */
394
+ private currentPipeline;
395
+ constructor(config: SearchConfig);
396
+ /**
397
+ * 初始化搜索引擎
398
+ */
399
+ init(): Promise<void>;
400
+ /**
401
+ * 索引单个文件(带重试机制和并发安全)
402
+ */
403
+ indexFile(filePath: string, retryCount?: number): Promise<void>;
404
+ /**
405
+ * 索引目录(流水线架构)
406
+ * 三阶段并行处理:解析 → 嵌入 → 存储
407
+ * 智能增量索引:只处理新增、修改的文件,跳过未变化的文件
408
+ */
409
+ indexDirectory(directory: string, onProgress?: (progress: IndexProgress) => void, pipelineConfig?: Partial<PipelineConfig>): Promise<void>;
410
+ /**
411
+ * 取消正在进行的索引任务
412
+ * @returns 是否成功取消(如果没有运行中的任务则返回 false)
413
+ */
414
+ cancelIndexing(): boolean;
415
+ /**
416
+ * 检查是否正在索引
417
+ */
418
+ isIndexing(): boolean;
419
+ /**
420
+ * 索引默认目录
421
+ */
422
+ indexDefaultDirectories(onProgress?: (progress: IndexProgress) => void): Promise<void>;
423
+ /**
424
+ * 搜索文档
425
+ */
426
+ search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
427
+ /**
428
+ * 检查文档是否匹配所有过滤条件
429
+ */
430
+ private matchesFilters;
431
+ /**
432
+ * 将通配符模式转换为正则表达式
433
+ * * 匹配任意字符
434
+ * ? 匹配单个字符
435
+ */
436
+ private wildcardToRegex;
437
+ /**
438
+ * 标准化向量搜索分数
439
+ * 将距离转换为 0-1 的相似度分数
440
+ */
441
+ private normalizeVectorScores;
442
+ /**
443
+ * 删除文件索引
444
+ */
445
+ removeFile(filePath: string): Promise<void>;
446
+ /**
447
+ * 获取统计信息
448
+ */
449
+ getStats(): IndexStats;
450
+ /**
451
+ * 清空所有索引
452
+ */
453
+ clear(): Promise<void>;
454
+ /**
455
+ * 保存索引
456
+ */
457
+ save(): Promise<void>;
458
+ /**
459
+ * 批量索引文件(根据配置自动选择串行或并行)
460
+ */
461
+ indexFiles(filePaths: string[], onProgress?: (progress: IndexProgress) => void): Promise<BatchOperationResult>;
462
+ /**
463
+ * 串行索引文件(原实现)
464
+ */
465
+ private indexFilesSerial;
466
+ /**
467
+ * 并行索引文件(控制并发数)
468
+ */
469
+ indexFilesParallel(filePaths: string[], onProgress?: (progress: IndexProgress) => void, concurrency?: number): Promise<BatchOperationResult>;
470
+ /**
471
+ * 批量删除文件索引
472
+ */
473
+ removeFiles(filePaths: string[]): Promise<BatchOperationResult>;
474
+ /**
475
+ * 批量更新文件索引(重新索引)
476
+ */
477
+ updateFiles(filePaths: string[], onProgress?: (progress: IndexProgress) => void): Promise<BatchOperationResult>;
478
+ /** 监听目录变化并自动更新索引 */
479
+ watchDirectory(directory: string, options?: WatchOptions): void;
480
+ /** 停止监听目录 */
481
+ unwatchDirectory(directory: string): void;
482
+ /** 停止所有监听 */
483
+ unwatchAll(): void;
484
+ /** 获取正在监听的目录列表 */
485
+ getWatchedDirectories(): string[];
486
+ /** 获取维护依赖 */
487
+ private getMaintenanceDeps;
488
+ /** 清理无效索引(文件已删除但索引还在) */
489
+ cleanup(): Promise<{
490
+ removed: number;
491
+ updated: number;
492
+ }>;
493
+ /** 优化索引(压缩、碎片整理) */
494
+ optimize(): Promise<void>;
495
+ /** 健康检查 */
496
+ healthCheck(): Promise<HealthCheckResult>;
497
+ /** 获取索引错误列表 */
498
+ getIndexErrors(): IndexError[];
499
+ /** 重试失败的索引 */
500
+ retryFailedIndexes(): Promise<BatchOperationResult>;
501
+ /** 确保已初始化 */
502
+ private ensureInitialized;
503
+ /** 获取备份依赖 */
504
+ private getBackupDeps;
505
+ /** 导出索引数据 */
506
+ exportIndex(outputPath: string): Promise<ExportInfo>;
507
+ /** 导入索引数据 */
508
+ importIndex(inputPath: string): Promise<void>;
509
+ /** 列出备份 */
510
+ listBackups(backupDir: string): Promise<BackupInfo[]>;
511
+ /** 销毁资源,释放所有占用的资源 */
512
+ destroy(): Promise<void>;
513
+ }
514
+
515
+ /**
516
+ * AI Agent 工具定义
517
+ *
518
+ * Vite 插件风格 API
519
+ */
520
+
521
+ /** 工作空间状态 */
522
+ interface WorkspaceState {
523
+ /** 当前工作空间目录 */
524
+ directory: string | null;
525
+ /** 是否已索引 */
526
+ indexed: boolean;
527
+ /** 索引文件数量 */
528
+ filesIndexed: number;
529
+ }
530
+ /** 搜索插件配置 */
531
+ interface SearchPluginOptions {
532
+ /** 数据存储目录 */
533
+ dataDir: string;
534
+ /** 初始工作空间(可选,设置后自动索引) */
535
+ workspace?: string;
536
+ /** 豆包 API Key(用于向量化,也可通过环境变量 ARK_API_KEY 设置) */
537
+ arkApiKey?: string;
538
+ /** 向量维度(默认 1024) */
539
+ embeddingDimension?: number;
540
+ }
541
+ /** 搜索插件实例(扩展 ToolPlugin,兼容 Vite 插件风格) */
542
+ interface SearchPluginInstance extends ToolPlugin {
543
+ /** AI Agent 工具数组 */
544
+ tools: Tool[];
545
+ /** 设置/切换工作空间 */
546
+ setWorkspace: (directory: string) => Promise<void>;
547
+ /** 获取工作空间状态 */
548
+ getWorkspaceState: () => WorkspaceState;
549
+ /** 底层 DocumentSearch 实例(高级用法) */
550
+ search: DocumentSearch;
551
+ }
552
+ /**
553
+ * 搜索插件(Vite 风格)
554
+ *
555
+ * @example
556
+ * ```typescript
557
+ * import { searchPlugin } from '@huyooo/ai-search';
558
+ *
559
+ * // 像 Vite 插件一样使用
560
+ * await createElectronBridge({
561
+ * tools: [
562
+ * getCwdTool,
563
+ * searchPlugin({ dataDir, workspace }), // 直接传入
564
+ * ],
565
+ * });
566
+ *
567
+ * // 运行时控制(通过 getSearchPlugin 获取实例)
568
+ * import { getSearchPlugin } from '@huyooo/ai-search';
569
+ * await getSearchPlugin()?.setWorkspace('/new/path');
570
+ * ```
571
+ */
572
+ declare function searchPlugin(options: SearchPluginOptions): Promise<SearchPluginInstance>;
573
+ /** 获取搜索插件实例(用于运行时控制) */
574
+ declare function getSearchPlugin(): SearchPluginInstance | null;
575
+
576
+ export { type BatchOperationResult as B, type ChunkOptions as C, DocumentSearch as D, type ExportInfo as E, FileType as F, type HealthCheckResult as H, type IndexProgress as I, type PipelineConfig as P, type SearchConfig as S, type TextChunk as T, type WatchOptions as W, type IndexedDocument as a, type IndexStats as b, type SearchOptions as c, type SearchResult as d, type WatchEvent as e, type BackupInfo as f, type IndexError as g, DEFAULT_CONFIG as h, createIndexingPipeline as i, IndexingPipeline as j, type PipelineStats as k, type SkipCheckCallback as l, getChunkStats as m, searchPlugin as n, getSearchPlugin as o, type SearchPluginOptions as p, type SearchPluginInstance as q, splitText as s };