@sylphx/flow 1.1.0 → 1.2.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/package.json +12 -2
  3. package/src/commands/hook-command.ts +10 -230
  4. package/src/composables/index.ts +0 -1
  5. package/src/config/servers.ts +35 -78
  6. package/src/core/interfaces.ts +0 -33
  7. package/src/domains/index.ts +0 -2
  8. package/src/index.ts +0 -4
  9. package/src/services/mcp-service.ts +0 -16
  10. package/src/targets/claude-code.ts +3 -9
  11. package/src/targets/functional/claude-code-logic.ts +4 -22
  12. package/src/targets/opencode.ts +0 -6
  13. package/src/types/mcp.types.ts +29 -38
  14. package/src/types/target.types.ts +0 -2
  15. package/src/types.ts +0 -1
  16. package/src/commands/codebase-command.ts +0 -168
  17. package/src/commands/knowledge-command.ts +0 -161
  18. package/src/composables/useTargetConfig.ts +0 -45
  19. package/src/core/formatting/bytes.test.ts +0 -115
  20. package/src/core/validation/limit.test.ts +0 -155
  21. package/src/core/validation/query.test.ts +0 -44
  22. package/src/domains/codebase/index.ts +0 -5
  23. package/src/domains/codebase/tools.ts +0 -139
  24. package/src/domains/knowledge/index.ts +0 -10
  25. package/src/domains/knowledge/resources.ts +0 -537
  26. package/src/domains/knowledge/tools.ts +0 -174
  27. package/src/services/search/base-indexer.ts +0 -156
  28. package/src/services/search/codebase-indexer-types.ts +0 -38
  29. package/src/services/search/codebase-indexer.ts +0 -647
  30. package/src/services/search/embeddings-provider.ts +0 -455
  31. package/src/services/search/embeddings.ts +0 -316
  32. package/src/services/search/functional-indexer.ts +0 -323
  33. package/src/services/search/index.ts +0 -27
  34. package/src/services/search/indexer.ts +0 -380
  35. package/src/services/search/knowledge-indexer.ts +0 -422
  36. package/src/services/search/semantic-search.ts +0 -244
  37. package/src/services/search/tfidf.ts +0 -559
  38. package/src/services/search/unified-search-service.ts +0 -888
  39. package/src/services/storage/cache-storage.ts +0 -487
  40. package/src/services/storage/drizzle-storage.ts +0 -581
  41. package/src/services/storage/index.ts +0 -15
  42. package/src/services/storage/lancedb-vector-storage.ts +0 -494
  43. package/src/services/storage/memory-storage.ts +0 -268
  44. package/src/services/storage/separated-storage.ts +0 -467
  45. package/src/services/storage/vector-storage.ts +0 -13
@@ -1,455 +0,0 @@
1
- /**
2
- * Embeddings Provider Service - 嵌入向量提供者服務
3
- * 統一管理所有embedding相關操作
4
- */
5
-
6
- import { type Result, tryCatchAsync } from '../../core/functional/result.js';
7
- import {
8
- EmbeddingInitError,
9
- EmbeddingNotInitializedError,
10
- type EmbeddingsErrorType,
11
- VectorDimensionError,
12
- } from '../../errors/embeddings-errors.js';
13
- import type { EmbeddingProvider } from '../../utils/embeddings.js';
14
- import { getDefaultEmbeddingProvider } from '../../utils/embeddings.js';
15
- import { chunk } from '../../utils/functional/array.js';
16
- import { createLogger } from '../../utils/debug-logger.js';
17
-
18
- const log = createLogger('search:embeddings');
19
-
20
- export interface EmbeddingConfig {
21
- provider?: 'openai' | 'local' | 'auto';
22
- model?: string;
23
- dimensions?: number;
24
- batchSize?: number;
25
- maxTokens?: number;
26
- }
27
-
28
- export interface EmbeddingResult {
29
- embeddings: number[][];
30
- tokensUsed: number;
31
- processingTime: number;
32
- }
33
-
34
- /**
35
- * Internal state for embeddings provider
36
- */
37
- interface EmbeddingsProviderState {
38
- readonly provider?: EmbeddingProvider;
39
- readonly isInitialized: boolean;
40
- }
41
-
42
- /**
43
- * Embeddings Provider Service Interface
44
- * 嵌入向量提供者服務
45
- */
46
- export interface EmbeddingsProviderService {
47
- readonly initialize: () => Promise<Result<void, EmbeddingsErrorType>>;
48
- readonly generateEmbeddings: (
49
- texts: string[]
50
- ) => Promise<Result<number[][], EmbeddingsErrorType>>;
51
- readonly generateEmbedding: (text: string) => Promise<Result<number[], EmbeddingsErrorType>>;
52
- readonly generateEmbeddingsWithMetrics: (
53
- texts: string[]
54
- ) => Promise<Result<EmbeddingResult, EmbeddingsErrorType>>;
55
- readonly isValidText: (text: string) => boolean;
56
- readonly preprocessText: (text: string) => string;
57
- readonly preprocessTexts: (texts: string[]) => string[];
58
- readonly cosineSimilarity: (
59
- vecA: number[],
60
- vecB: number[]
61
- ) => Result<number, VectorDimensionError>;
62
- readonly findMostSimilar: (
63
- queryVector: number[],
64
- candidateVectors: number[][]
65
- ) => Result<{ index: number; similarity: number }, VectorDimensionError>;
66
- readonly getConfig: () => EmbeddingConfig;
67
- readonly updateConfig: (newConfig: Partial<EmbeddingConfig>) => void;
68
- readonly getProviderInfo: () => Promise<
69
- Result<
70
- {
71
- provider: string;
72
- model: string;
73
- dimensions: number;
74
- available: boolean;
75
- },
76
- EmbeddingsErrorType
77
- >
78
- >;
79
- }
80
-
81
- /**
82
- * Create embeddings provider service (Factory Function)
83
- * 提供統一嘅embedding interface和配置管理
84
- */
85
- export const createEmbeddingsProviderService = (
86
- config: EmbeddingConfig = {}
87
- ): EmbeddingsProviderService => {
88
- // Immutable config with defaults
89
- let serviceConfig: EmbeddingConfig = {
90
- provider: 'auto',
91
- batchSize: 10,
92
- maxTokens: 8000,
93
- dimensions: 1536,
94
- ...config,
95
- };
96
-
97
- // Mutable state in closure (will be updated immutably)
98
- let state: EmbeddingsProviderState = {
99
- provider: undefined,
100
- isInitialized: false,
101
- };
102
-
103
- // Helper: Update state immutably
104
- const updateState = (updates: Partial<EmbeddingsProviderState>): void => {
105
- state = { ...state, ...updates };
106
- };
107
-
108
- /**
109
- * 初始化embedding provider
110
- */
111
- const initialize = async (): Promise<Result<void, EmbeddingsErrorType>> => {
112
- if (state.isInitialized) {
113
- return { _tag: 'Success', value: undefined };
114
- }
115
-
116
- return await tryCatchAsync(
117
- async () => {
118
- const provider = await getDefaultEmbeddingProvider();
119
- updateState({ provider, isInitialized: true });
120
- return undefined;
121
- },
122
- (error) => new EmbeddingInitError(error)
123
- );
124
- };
125
-
126
- /**
127
- * 確保已初始化
128
- */
129
- const ensureInitialized = async (): Promise<Result<void, EmbeddingsErrorType>> => {
130
- if (!state.isInitialized) {
131
- return await initialize();
132
- }
133
- return { _tag: 'Success', value: undefined };
134
- };
135
-
136
- /**
137
- * 生成嵌入向量
138
- */
139
- const generateEmbeddings = async (
140
- texts: string[]
141
- ): Promise<Result<number[][], EmbeddingsErrorType>> => {
142
- return await tryCatchAsync(
143
- async () => {
144
- const initResult = await ensureInitialized();
145
- if (initResult._tag === 'Failure') {
146
- throw initResult.error;
147
- }
148
-
149
- if (!state.provider) {
150
- throw new EmbeddingNotInitializedError();
151
- }
152
-
153
- // 檢查文本長度
154
- const filteredTexts = texts.filter((text) => text.trim().length > 0);
155
- if (filteredTexts.length === 0) {
156
- return [];
157
- }
158
-
159
- // FUNCTIONAL: 批量處理 - Use chunk and flatMap instead of for loop
160
- const batchSize = serviceConfig.batchSize || 10;
161
- const batches = chunk(batchSize)(filteredTexts);
162
-
163
- // Process batches and flatten results
164
- const batchResults = await Promise.all(
165
- batches.map(async (batch, index) => {
166
- try {
167
- return await state.provider?.generateEmbeddings(batch);
168
- } catch (error) {
169
- log(
170
- `Batch ${index * batchSize}-${index * batchSize + batchSize} failed:`,
171
- error instanceof Error ? error.message : String(error)
172
- );
173
- // 為失敗嘅batch添加零向量
174
- const zeroVector = new Array(serviceConfig.dimensions || 1536).fill(0);
175
- return Array(batch.length).fill(zeroVector);
176
- }
177
- })
178
- );
179
-
180
- return batchResults.flat();
181
- },
182
- (error) => {
183
- if (error instanceof EmbeddingInitError || error instanceof EmbeddingNotInitializedError) {
184
- return error;
185
- }
186
- return new EmbeddingInitError(error);
187
- }
188
- );
189
- };
190
-
191
- /**
192
- * 生成單個文本嘅嵌入向量
193
- */
194
- const generateEmbedding = async (
195
- text: string
196
- ): Promise<Result<number[], EmbeddingsErrorType>> => {
197
- return await tryCatchAsync(
198
- async () => {
199
- const result = await generateEmbeddings([text]);
200
- if (result._tag === 'Failure') {
201
- throw result.error;
202
- }
203
- return result.value[0] || [];
204
- },
205
- (error) => {
206
- if (error instanceof EmbeddingInitError || error instanceof EmbeddingNotInitializedError) {
207
- return error;
208
- }
209
- return new EmbeddingInitError(error);
210
- }
211
- );
212
- };
213
-
214
- /**
215
- * 批量生成嵌入向量(帶性能監控)
216
- */
217
- const generateEmbeddingsWithMetrics = async (
218
- texts: string[]
219
- ): Promise<Result<EmbeddingResult, EmbeddingsErrorType>> => {
220
- return await tryCatchAsync(
221
- async () => {
222
- const startTime = Date.now();
223
-
224
- const result = await generateEmbeddings(texts);
225
- if (result._tag === 'Failure') {
226
- throw result.error;
227
- }
228
-
229
- const processingTime = Date.now() - startTime;
230
- const tokensUsed = estimateTokens(texts);
231
-
232
- return {
233
- embeddings: result.value,
234
- tokensUsed,
235
- processingTime,
236
- };
237
- },
238
- (error) => {
239
- if (error instanceof EmbeddingInitError || error instanceof EmbeddingNotInitializedError) {
240
- return error;
241
- }
242
- return new EmbeddingInitError(error);
243
- }
244
- );
245
- };
246
-
247
- /**
248
- * 估算token數量
249
- */
250
- const estimateTokens = (texts: string[]): number => {
251
- // 簡單嘅token估算(約4 characters = 1 token)
252
- const totalChars = texts.reduce((sum, text) => sum + text.length, 0);
253
- return Math.ceil(totalChars / 4);
254
- };
255
-
256
- /**
257
- * 檢查文本是否適合生成embedding
258
- */
259
- const isValidText = (text: string): boolean => {
260
- if (!text || typeof text !== 'string') {
261
- return false;
262
- }
263
-
264
- const trimmed = text.trim();
265
- if (trimmed.length === 0) {
266
- return false;
267
- }
268
-
269
- // 檢查長度限制
270
- const maxTokens = serviceConfig.maxTokens || 8000;
271
- const estimatedTokens = Math.ceil(trimmed.length / 4);
272
-
273
- return estimatedTokens <= maxTokens;
274
- };
275
-
276
- /**
277
- * 預處理文本
278
- */
279
- const preprocessText = (text: string): string => {
280
- if (!text || typeof text !== 'string') {
281
- return '';
282
- }
283
-
284
- return (
285
- text
286
- .trim()
287
- // 移除多餘嘅空白字符
288
- .replace(/\s+/g, ' ')
289
- // 截斷過長嘅文本
290
- .slice(0, (serviceConfig.maxTokens || 8000) * 4)
291
- );
292
- };
293
-
294
- /**
295
- * 批量預處理文本
296
- */
297
- const preprocessTexts = (texts: string[]): string[] => {
298
- return texts.filter((text) => isValidText(text)).map((text) => preprocessText(text));
299
- };
300
-
301
- /**
302
- * 計算兩個向量嘅餘弦相似度
303
- */
304
- const cosineSimilarity = (
305
- vecA: number[],
306
- vecB: number[]
307
- ): Result<number, VectorDimensionError> => {
308
- if (vecA.length !== vecB.length) {
309
- return { _tag: 'Failure', error: new VectorDimensionError(vecA.length, vecB.length) };
310
- }
311
-
312
- // FUNCTIONAL: Use reduce instead of for loop
313
- const { dotProduct, normA, normB } = vecA.reduce(
314
- (acc, aVal, i) => {
315
- const bVal = vecB[i];
316
- return {
317
- dotProduct: acc.dotProduct + aVal * bVal,
318
- normA: acc.normA + aVal * aVal,
319
- normB: acc.normB + bVal * bVal,
320
- };
321
- },
322
- { dotProduct: 0, normA: 0, normB: 0 }
323
- );
324
-
325
- if (normA === 0 || normB === 0) {
326
- return { _tag: 'Success', value: 0 };
327
- }
328
-
329
- return { _tag: 'Success', value: dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)) };
330
- };
331
-
332
- /**
333
- * 找到最相似嘅向量
334
- */
335
- const findMostSimilar = (
336
- queryVector: number[],
337
- candidateVectors: number[][]
338
- ): Result<{ index: number; similarity: number }, VectorDimensionError> => {
339
- // FUNCTIONAL: Use reduce instead of for loop
340
- const result = candidateVectors.reduce<
341
- Result<{ index: number; similarity: number }, VectorDimensionError>
342
- >(
343
- (acc, candidateVector, index) => {
344
- // Short-circuit on error
345
- if (acc._tag === 'Failure') {
346
- return acc;
347
- }
348
-
349
- const similarityResult = cosineSimilarity(queryVector, candidateVector);
350
- if (similarityResult._tag === 'Failure') {
351
- return similarityResult;
352
- }
353
-
354
- const similarity = similarityResult.value;
355
- return similarity > acc.value.similarity
356
- ? { _tag: 'Success', value: { index, similarity } }
357
- : acc;
358
- },
359
- { _tag: 'Success', value: { index: -1, similarity: 0 } }
360
- );
361
-
362
- return result;
363
- };
364
-
365
- /**
366
- * 獲取配置信息
367
- */
368
- const getConfig = (): EmbeddingConfig => {
369
- return { ...serviceConfig };
370
- };
371
-
372
- /**
373
- * 更新配置
374
- */
375
- const updateConfig = (newConfig: Partial<EmbeddingConfig>): void => {
376
- serviceConfig = { ...serviceConfig, ...newConfig };
377
-
378
- // 如果provider相關配置改變,需要重新初始化
379
- if (newConfig.provider || newConfig.model) {
380
- updateState({ isInitialized: false });
381
- }
382
- };
383
-
384
- /**
385
- * 獲取provider信息
386
- */
387
- const getProviderInfo = async (): Promise<
388
- Result<
389
- {
390
- provider: string;
391
- model: string;
392
- dimensions: number;
393
- available: boolean;
394
- },
395
- EmbeddingsErrorType
396
- >
397
- > => {
398
- return await tryCatchAsync(
399
- async () => {
400
- const initResult = await ensureInitialized();
401
- if (initResult._tag === 'Failure') {
402
- // Return unavailable info instead of throwing
403
- return {
404
- provider: serviceConfig.provider || 'unknown',
405
- model: serviceConfig.model || 'unknown',
406
- dimensions: serviceConfig.dimensions || 1536,
407
- available: false,
408
- };
409
- }
410
-
411
- if (!state.provider) {
412
- return {
413
- provider: serviceConfig.provider || 'unknown',
414
- model: serviceConfig.model || 'unknown',
415
- dimensions: serviceConfig.dimensions || 1536,
416
- available: false,
417
- };
418
- }
419
-
420
- // 這裡需要根據實際provider interface實現
421
- return {
422
- provider: serviceConfig.provider || 'unknown',
423
- model: serviceConfig.model || 'unknown',
424
- dimensions: serviceConfig.dimensions || 1536,
425
- available: true,
426
- };
427
- },
428
- (error) => new EmbeddingInitError(error)
429
- );
430
- };
431
-
432
- // Return service interface
433
- return {
434
- initialize,
435
- generateEmbeddings,
436
- generateEmbedding,
437
- generateEmbeddingsWithMetrics,
438
- isValidText,
439
- preprocessText,
440
- preprocessTexts,
441
- cosineSimilarity,
442
- findMostSimilar,
443
- getConfig,
444
- updateConfig,
445
- getProviderInfo,
446
- };
447
- };
448
-
449
- // 預設實例 - Use factory function
450
- export const defaultEmbeddingsProvider = createEmbeddingsProviderService();
451
-
452
- // 工廠函數別名 - For backwards compatibility
453
- export function createEmbeddingsProvider(config?: EmbeddingConfig): EmbeddingsProviderService {
454
- return createEmbeddingsProviderService(config);
455
- }