@reftrixmcp/ml 0.1.7

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 (97) hide show
  1. package/LICENSE +661 -0
  2. package/dist/dinov2/index.d.ts +7 -0
  3. package/dist/dinov2/index.d.ts.map +1 -0
  4. package/dist/dinov2/index.js +7 -0
  5. package/dist/dinov2/index.js.map +1 -0
  6. package/dist/dinov2/service.d.ts +106 -0
  7. package/dist/dinov2/service.d.ts.map +1 -0
  8. package/dist/dinov2/service.js +446 -0
  9. package/dist/dinov2/service.js.map +1 -0
  10. package/dist/dinov2/worker-thread-types.d.ts +77 -0
  11. package/dist/dinov2/worker-thread-types.d.ts.map +1 -0
  12. package/dist/dinov2/worker-thread-types.js +4 -0
  13. package/dist/dinov2/worker-thread-types.js.map +1 -0
  14. package/dist/dinov2/worker-thread.d.ts +2 -0
  15. package/dist/dinov2/worker-thread.d.ts.map +1 -0
  16. package/dist/dinov2/worker-thread.js +231 -0
  17. package/dist/dinov2/worker-thread.js.map +1 -0
  18. package/dist/embeddings/index.d.ts +12 -0
  19. package/dist/embeddings/index.d.ts.map +1 -0
  20. package/dist/embeddings/index.js +13 -0
  21. package/dist/embeddings/index.js.map +1 -0
  22. package/dist/embeddings/multimodal-embedding.service.d.ts +400 -0
  23. package/dist/embeddings/multimodal-embedding.service.d.ts.map +1 -0
  24. package/dist/embeddings/multimodal-embedding.service.js +650 -0
  25. package/dist/embeddings/multimodal-embedding.service.js.map +1 -0
  26. package/dist/embeddings/service.d.ts +261 -0
  27. package/dist/embeddings/service.d.ts.map +1 -0
  28. package/dist/embeddings/service.js +1093 -0
  29. package/dist/embeddings/service.js.map +1 -0
  30. package/dist/embeddings/style-embedding.service.d.ts +117 -0
  31. package/dist/embeddings/style-embedding.service.d.ts.map +1 -0
  32. package/dist/embeddings/style-embedding.service.js +157 -0
  33. package/dist/embeddings/style-embedding.service.js.map +1 -0
  34. package/dist/embeddings/types.d.ts +83 -0
  35. package/dist/embeddings/types.d.ts.map +1 -0
  36. package/dist/embeddings/types.js +4 -0
  37. package/dist/embeddings/types.js.map +1 -0
  38. package/dist/embeddings/vision-embedding.service.d.ts +70 -0
  39. package/dist/embeddings/vision-embedding.service.d.ts.map +1 -0
  40. package/dist/embeddings/vision-embedding.service.js +221 -0
  41. package/dist/embeddings/vision-embedding.service.js.map +1 -0
  42. package/dist/embeddings/vision-embedding.types.d.ts +102 -0
  43. package/dist/embeddings/vision-embedding.types.d.ts.map +1 -0
  44. package/dist/embeddings/vision-embedding.types.js +4 -0
  45. package/dist/embeddings/vision-embedding.types.js.map +1 -0
  46. package/dist/embeddings/worker-thread-types.d.ts +152 -0
  47. package/dist/embeddings/worker-thread-types.d.ts.map +1 -0
  48. package/dist/embeddings/worker-thread-types.js +4 -0
  49. package/dist/embeddings/worker-thread-types.js.map +1 -0
  50. package/dist/embeddings/worker-thread.d.ts +4 -0
  51. package/dist/embeddings/worker-thread.d.ts.map +1 -0
  52. package/dist/embeddings/worker-thread.js +441 -0
  53. package/dist/embeddings/worker-thread.js.map +1 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +19 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/search/fulltext-helpers.d.ts +30 -0
  59. package/dist/search/fulltext-helpers.d.ts.map +1 -0
  60. package/dist/search/fulltext-helpers.js +40 -0
  61. package/dist/search/fulltext-helpers.js.map +1 -0
  62. package/dist/search/hybrid.d.ts +40 -0
  63. package/dist/search/hybrid.d.ts.map +1 -0
  64. package/dist/search/hybrid.js +61 -0
  65. package/dist/search/hybrid.js.map +1 -0
  66. package/dist/search/index.d.ts +12 -0
  67. package/dist/search/index.d.ts.map +1 -0
  68. package/dist/search/index.js +11 -0
  69. package/dist/search/index.js.map +1 -0
  70. package/dist/search/rrf.d.ts +69 -0
  71. package/dist/search/rrf.d.ts.map +1 -0
  72. package/dist/search/rrf.js +108 -0
  73. package/dist/search/rrf.js.map +1 -0
  74. package/dist/search/types.d.ts +130 -0
  75. package/dist/search/types.d.ts.map +1 -0
  76. package/dist/search/types.js +4 -0
  77. package/dist/search/types.js.map +1 -0
  78. package/package.json +65 -0
  79. package/src/dinov2/index.ts +21 -0
  80. package/src/dinov2/service.ts +539 -0
  81. package/src/dinov2/worker-thread-types.ts +103 -0
  82. package/src/dinov2/worker-thread.ts +306 -0
  83. package/src/embeddings/index.ts +74 -0
  84. package/src/embeddings/multimodal-embedding.service.ts +992 -0
  85. package/src/embeddings/service.ts +1287 -0
  86. package/src/embeddings/style-embedding.service.ts +201 -0
  87. package/src/embeddings/types.ts +95 -0
  88. package/src/embeddings/vision-embedding.service.ts +273 -0
  89. package/src/embeddings/vision-embedding.types.ts +117 -0
  90. package/src/embeddings/worker-thread-types.ts +194 -0
  91. package/src/embeddings/worker-thread.ts +529 -0
  92. package/src/index.ts +70 -0
  93. package/src/search/fulltext-helpers.ts +45 -0
  94. package/src/search/hybrid.ts +107 -0
  95. package/src/search/index.ts +25 -0
  96. package/src/search/rrf.ts +154 -0
  97. package/src/search/types.ts +141 -0
@@ -0,0 +1,650 @@
1
+ // SPDX-FileCopyrightText: 2026 TKMD and Reftrix Contributors
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+ /**
4
+ * Multimodal Embedding Service
5
+ *
6
+ * VisionAnalyzerの特徴抽出結果とテキストEmbeddingを組み合わせて
7
+ * マルチモーダルEmbeddingを生成します。
8
+ *
9
+ * text_embedding + vision_embedding の統合サービス
10
+ * - searchMode: 'text_only' | 'vision_only' | 'combined'
11
+ * - Graceful Degradation: visionFeaturesがない場合はtext_onlyにフォールバック
12
+ * - L2正規化: すべてのEmbeddingは正規化される
13
+ *
14
+ * @module embeddings/multimodal-embedding.service
15
+ */
16
+ import { z } from "zod";
17
+ import pLimit from "p-limit";
18
+ import { visionFeaturesToText } from "./vision-embedding.service.js";
19
+ /**
20
+ * デフォルト設定
21
+ */
22
+ export const DEFAULT_MULTIMODAL_CONFIG = {
23
+ textWeight: 0.6,
24
+ visionWeight: 0.4,
25
+ embeddingDimension: 768,
26
+ searchMode: "combined",
27
+ normalizeOutput: true,
28
+ };
29
+ /**
30
+ * 設定スキーマ
31
+ */
32
+ export const multimodalEmbeddingConfigSchema = z.object({
33
+ textWeight: z.number().min(0).max(1).optional(),
34
+ visionWeight: z.number().min(0).max(1).optional(),
35
+ embeddingDimension: z.number().int().positive().optional(),
36
+ searchMode: z.enum(["text_only", "vision_only", "combined"]).optional(),
37
+ normalizeOutput: z.boolean().optional(),
38
+ });
39
+ /**
40
+ * 入力スキーマ
41
+ */
42
+ export const multimodalEmbeddingInputSchema = z.object({
43
+ textRepresentation: z.string().min(1),
44
+ visionFeatureText: z.string().optional(),
45
+ originalText: z.string().optional(),
46
+ });
47
+ /**
48
+ * 結果スキーマ
49
+ */
50
+ export const multimodalEmbeddingResultSchema = z.object({
51
+ combinedEmbedding: z.array(z.number()).length(768),
52
+ textEmbedding: z.array(z.number()).length(768),
53
+ visionEmbedding: z.array(z.number()).length(768),
54
+ weights: z.object({
55
+ text: z.number().min(0).max(1),
56
+ vision: z.number().min(0).max(1),
57
+ }),
58
+ processingTimeMs: z.number().min(0),
59
+ });
60
+ // =============================================================================
61
+ // MultimodalEmbeddingService
62
+ // =============================================================================
63
+ /**
64
+ * マルチモーダルEmbeddingサービス
65
+ *
66
+ * VisionAnalyzerの特徴抽出結果とテキストEmbeddingを組み合わせて
67
+ * 統合Embeddingを生成します。
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const service = new MultimodalEmbeddingService(embeddingService);
72
+ *
73
+ * const result = await service.generateMultimodalEmbedding({
74
+ * textRepresentation: "Layout: two-column grid...",
75
+ * visionFeatureText: "Colors: blue, white. Whitespace: generous.",
76
+ * originalText: "Hero section design",
77
+ * });
78
+ *
79
+ * console.log(result.combinedEmbedding.length); // 768
80
+ * ```
81
+ */
82
+ export class MultimodalEmbeddingService {
83
+ config;
84
+ embeddingService;
85
+ constructor(embeddingService, config = {}) {
86
+ // 設定をマージ
87
+ this.config = { ...DEFAULT_MULTIMODAL_CONFIG, ...config };
88
+ // 重みの合計が1になるように正規化
89
+ this.normalizeWeights();
90
+ this.embeddingService = embeddingService;
91
+ if (process.env.NODE_ENV === "development") {
92
+ // eslint-disable-next-line no-console
93
+ console.log("[ML] MultimodalEmbeddingService created with config:", {
94
+ textWeight: this.config.textWeight,
95
+ visionWeight: this.config.visionWeight,
96
+ embeddingDimension: this.config.embeddingDimension,
97
+ searchMode: this.config.searchMode,
98
+ normalizeOutput: this.config.normalizeOutput,
99
+ });
100
+ }
101
+ }
102
+ /**
103
+ * 重みを正規化(合計が1になるように)
104
+ */
105
+ normalizeWeights() {
106
+ const totalWeight = this.config.textWeight + this.config.visionWeight;
107
+ if (totalWeight !== 1 && totalWeight > 0) {
108
+ this.config.textWeight = this.config.textWeight / totalWeight;
109
+ this.config.visionWeight = this.config.visionWeight / totalWeight;
110
+ }
111
+ }
112
+ /**
113
+ * マルチモーダルEmbeddingを生成
114
+ *
115
+ * @param input - 入力データ
116
+ * @returns マルチモーダルEmbedding結果
117
+ */
118
+ async generateMultimodalEmbedding(input) {
119
+ const startTime = Date.now();
120
+ // 入力バリデーション
121
+ multimodalEmbeddingInputSchema.parse(input);
122
+ if (process.env.NODE_ENV === "development") {
123
+ // eslint-disable-next-line no-console
124
+ console.log("[ML] Generating multimodal embedding for:", {
125
+ textRepLength: input.textRepresentation.length,
126
+ hasVisionFeatureText: !!input.visionFeatureText,
127
+ hasOriginalText: !!input.originalText,
128
+ });
129
+ }
130
+ // テキスト表現からEmbeddingを生成
131
+ const textForEmbedding = this.buildTextForEmbedding(input);
132
+ const textEmbedding = await this.embeddingService.generateEmbedding(textForEmbedding, "passage");
133
+ // Vision特徴テキストからEmbeddingを生成
134
+ const visionText = this.buildVisionText(input);
135
+ const visionEmbedding = await this.embeddingService.generateEmbedding(visionText, "passage");
136
+ // 重み付き結合
137
+ const combinedEmbedding = this.combineEmbeddings(textEmbedding, visionEmbedding);
138
+ const processingTimeMs = Date.now() - startTime;
139
+ if (process.env.NODE_ENV === "development") {
140
+ // eslint-disable-next-line no-console
141
+ console.log("[ML] Multimodal embedding generated in", processingTimeMs, "ms");
142
+ }
143
+ return {
144
+ combinedEmbedding,
145
+ textEmbedding,
146
+ visionEmbedding,
147
+ weights: {
148
+ text: this.config.textWeight,
149
+ vision: this.config.visionWeight,
150
+ },
151
+ processingTimeMs,
152
+ };
153
+ }
154
+ /**
155
+ * テキストEmbedding用の文字列を構築
156
+ */
157
+ buildTextForEmbedding(input) {
158
+ const parts = [];
159
+ if (input.originalText) {
160
+ parts.push(input.originalText);
161
+ }
162
+ parts.push(input.textRepresentation);
163
+ return parts.join(" ");
164
+ }
165
+ /**
166
+ * Vision特徴テキストを構築
167
+ */
168
+ buildVisionText(input) {
169
+ if (input.visionFeatureText) {
170
+ return input.visionFeatureText;
171
+ }
172
+ // visionFeatureTextがない場合はtextRepresentationを使用
173
+ return input.textRepresentation;
174
+ }
175
+ /**
176
+ * 2つのEmbeddingを重み付き結合
177
+ */
178
+ combineEmbeddings(textEmbedding, visionEmbedding) {
179
+ if (textEmbedding.length !== visionEmbedding.length) {
180
+ throw new Error(`Embedding dimensions mismatch: text=${textEmbedding.length}, vision=${visionEmbedding.length}`);
181
+ }
182
+ // 片方の重みが1(もう一方が0)の場合は、そのままそのEmbeddingを返す
183
+ if (this.config.textWeight === 1) {
184
+ return [...textEmbedding];
185
+ }
186
+ if (this.config.visionWeight === 1) {
187
+ return [...visionEmbedding];
188
+ }
189
+ const combined = new Array(textEmbedding.length);
190
+ for (let i = 0; i < textEmbedding.length; i++) {
191
+ const textVal = textEmbedding[i] ?? 0;
192
+ const visionVal = visionEmbedding[i] ?? 0;
193
+ combined[i] = textVal * this.config.textWeight + visionVal * this.config.visionWeight;
194
+ }
195
+ // L2正規化
196
+ return this.normalizeVector(combined);
197
+ }
198
+ /**
199
+ * ベクトルをL2正規化
200
+ */
201
+ normalizeVector(vector) {
202
+ const norm = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
203
+ if (norm === 0)
204
+ return vector;
205
+ return vector.map((val) => val / norm);
206
+ }
207
+ /**
208
+ * 現在の設定を取得
209
+ */
210
+ getConfig() {
211
+ return { ...this.config };
212
+ }
213
+ // ===========================================================================
214
+ // 新しいメソッド
215
+ // ===========================================================================
216
+ /**
217
+ * マルチモーダルEmbeddingを生成
218
+ *
219
+ * searchModeに応じて適切なEmbeddingを生成します。
220
+ * - text_only: テキストEmbeddingのみ
221
+ * - vision_only: VisionEmbeddingのみ(visionFeatures必須)
222
+ * - combined: テキスト + Vision の重み付き結合
223
+ *
224
+ * Graceful Degradation:
225
+ * - visionFeaturesがない場合、combinedモードはtext_onlyにフォールバック
226
+ *
227
+ * @param content - テキストコンテンツ
228
+ * @param visionFeatures - VisionFeatures(オプション)
229
+ * @param config - オーバーライド設定(オプション)
230
+ * @returns MultimodalEmbeddingResultV2
231
+ */
232
+ async createMultimodalEmbedding(content, visionFeatures, config) {
233
+ const startTime = Date.now();
234
+ // 設定をマージ(インスタンス設定 < 呼び出し時設定)
235
+ const mergedConfig = this.mergeConfig(config);
236
+ // 検索モードを決定(Graceful Degradation対応)
237
+ let effectiveSearchMode = mergedConfig.searchMode;
238
+ // Graceful Degradation: visionFeaturesがない場合、combinedはtext_onlyにフォールバック
239
+ if (effectiveSearchMode === "combined" && !visionFeatures) {
240
+ effectiveSearchMode = "text_only";
241
+ }
242
+ // vision_onlyモードでvisionFeaturesがない場合はエラー
243
+ if (effectiveSearchMode === "vision_only" && !visionFeatures) {
244
+ throw new Error("visionFeatures is required for vision_only mode");
245
+ }
246
+ if (process.env.NODE_ENV === "development") {
247
+ // eslint-disable-next-line no-console
248
+ console.log("[ML] createMultimodalEmbedding:", {
249
+ contentLength: content.length,
250
+ hasVisionFeatures: !!visionFeatures,
251
+ requestedMode: mergedConfig.searchMode,
252
+ effectiveMode: effectiveSearchMode,
253
+ });
254
+ }
255
+ // Embedding生成
256
+ let textEmbedding = null;
257
+ let visionEmbedding = null;
258
+ let combinedEmbedding = null;
259
+ // text_only または combined: テキストEmbeddingを生成
260
+ if (effectiveSearchMode === "text_only" || effectiveSearchMode === "combined") {
261
+ textEmbedding = await this.embeddingService.generateEmbedding(content, "passage");
262
+ if (mergedConfig.normalizeOutput) {
263
+ textEmbedding = this.normalizeVector(textEmbedding);
264
+ }
265
+ }
266
+ // vision_only または combined: VisionEmbeddingを生成
267
+ if ((effectiveSearchMode === "vision_only" || effectiveSearchMode === "combined") &&
268
+ visionFeatures) {
269
+ const visionText = visionFeaturesToText(visionFeatures);
270
+ visionEmbedding = await this.embeddingService.generateEmbedding(visionText, "passage");
271
+ if (mergedConfig.normalizeOutput) {
272
+ visionEmbedding = this.normalizeVector(visionEmbedding);
273
+ }
274
+ }
275
+ // combinedEmbeddingを計算
276
+ if (effectiveSearchMode === "text_only" && textEmbedding) {
277
+ // text_only: combinedはtextEmbeddingと同等
278
+ combinedEmbedding = [...textEmbedding];
279
+ }
280
+ else if (effectiveSearchMode === "vision_only" && visionEmbedding) {
281
+ // vision_only: combinedはvisionEmbeddingと同等
282
+ combinedEmbedding = [...visionEmbedding];
283
+ }
284
+ else if (effectiveSearchMode === "combined" && textEmbedding && visionEmbedding) {
285
+ // combined: 重み付き結合
286
+ combinedEmbedding = this.combineEmbeddingsWithConfig(textEmbedding, visionEmbedding, mergedConfig);
287
+ }
288
+ const processingTimeMs = Date.now() - startTime;
289
+ if (process.env.NODE_ENV === "development") {
290
+ // eslint-disable-next-line no-console
291
+ console.log("[ML] createMultimodalEmbedding completed in", processingTimeMs, "ms");
292
+ }
293
+ return {
294
+ textEmbedding,
295
+ visionEmbedding,
296
+ combinedEmbedding,
297
+ searchMode: effectiveSearchMode,
298
+ weights: {
299
+ text: mergedConfig.textWeight,
300
+ vision: mergedConfig.visionWeight,
301
+ },
302
+ metadata: {
303
+ textRepresentation: content,
304
+ ...(visionFeatures !== undefined && { visionFeatures }),
305
+ processingTimeMs,
306
+ },
307
+ };
308
+ }
309
+ /**
310
+ * バッチでマルチモーダルEmbeddingを生成
311
+ *
312
+ * @param items - バッチ処理入力アイテムの配列
313
+ * @param config - オーバーライド設定(オプション)
314
+ * @returns MultimodalBatchResult
315
+ */
316
+ async createBatchMultimodalEmbeddings(items, config) {
317
+ const startTime = Date.now();
318
+ if (items.length === 0) {
319
+ return {
320
+ results: [],
321
+ stats: {
322
+ total: 0,
323
+ success: 0,
324
+ failed: 0,
325
+ totalProcessingTimeMs: 0,
326
+ },
327
+ };
328
+ }
329
+ if (process.env.NODE_ENV === "development") {
330
+ // eslint-disable-next-line no-console
331
+ console.log("[ML] createBatchMultimodalEmbeddings:", {
332
+ count: items.length,
333
+ });
334
+ }
335
+ const results = [];
336
+ let successCount = 0;
337
+ let failedCount = 0;
338
+ // 各アイテムを処理(partial failure対応)
339
+ for (const item of items) {
340
+ try {
341
+ const result = await this.createMultimodalEmbedding(item.content, item.visionFeatures, config);
342
+ results.push(result);
343
+ successCount++;
344
+ }
345
+ catch (error) {
346
+ // partial failure: エラーを記録し、処理を続行
347
+ failedCount++;
348
+ if (process.env.NODE_ENV === "development") {
349
+ console.warn("[ML] Batch item failed:", error);
350
+ }
351
+ }
352
+ }
353
+ const totalProcessingTimeMs = Date.now() - startTime;
354
+ if (process.env.NODE_ENV === "development") {
355
+ // eslint-disable-next-line no-console
356
+ console.log("[ML] createBatchMultimodalEmbeddings completed:", {
357
+ total: items.length,
358
+ success: successCount,
359
+ failed: failedCount,
360
+ timeMs: totalProcessingTimeMs,
361
+ });
362
+ }
363
+ return {
364
+ results,
365
+ stats: {
366
+ total: items.length,
367
+ success: successCount,
368
+ failed: failedCount,
369
+ totalProcessingTimeMs,
370
+ },
371
+ };
372
+ }
373
+ /**
374
+ * 設定をマージ
375
+ */
376
+ mergeConfig(override) {
377
+ if (!override) {
378
+ return { ...this.config };
379
+ }
380
+ const merged = { ...this.config, ...override };
381
+ // 重みの正規化
382
+ const totalWeight = merged.textWeight + merged.visionWeight;
383
+ if (totalWeight !== 1 && totalWeight > 0) {
384
+ merged.textWeight = merged.textWeight / totalWeight;
385
+ merged.visionWeight = merged.visionWeight / totalWeight;
386
+ }
387
+ return merged;
388
+ }
389
+ /**
390
+ * 設定を使用して2つのEmbeddingを重み付き結合
391
+ */
392
+ combineEmbeddingsWithConfig(textEmbedding, visionEmbedding, config) {
393
+ if (textEmbedding.length !== visionEmbedding.length) {
394
+ throw new Error(`Embedding dimensions mismatch: text=${textEmbedding.length}, vision=${visionEmbedding.length}`);
395
+ }
396
+ const combined = new Array(textEmbedding.length);
397
+ for (let i = 0; i < textEmbedding.length; i++) {
398
+ const textVal = textEmbedding[i] ?? 0;
399
+ const visionVal = visionEmbedding[i] ?? 0;
400
+ combined[i] = textVal * config.textWeight + visionVal * config.visionWeight;
401
+ }
402
+ // L2正規化(normalizeOutput=trueの場合)
403
+ if (config.normalizeOutput) {
404
+ return this.normalizeVector(combined);
405
+ }
406
+ return combined;
407
+ }
408
+ // ===========================================================================
409
+ // バッチ処理最適化(並列化 + メトリクス)
410
+ // ===========================================================================
411
+ /** キャッシュ(コンテンツハッシュ -> 結果) */
412
+ embeddingCache = new Map();
413
+ /** 最大キャッシュサイズ */
414
+ maxCacheSize = 5000;
415
+ /**
416
+ * キャッシュキーを生成
417
+ */
418
+ generateCacheKey(content, visionFeatures, searchMode) {
419
+ const visionPart = visionFeatures
420
+ ? JSON.stringify({
421
+ r: visionFeatures.rhythm,
422
+ w: visionFeatures.whitespaceRatio,
423
+ d: visionFeatures.density,
424
+ g: visionFeatures.gravity,
425
+ t: visionFeatures.theme,
426
+ m: visionFeatures.mood,
427
+ b: visionFeatures.brandTone,
428
+ })
429
+ : "null";
430
+ return `${searchMode ?? this.config.searchMode}:${content}:${visionPart}`;
431
+ }
432
+ /**
433
+ * キャッシュから取得(LRU更新)
434
+ */
435
+ getCacheEntry(key) {
436
+ const value = this.embeddingCache.get(key);
437
+ if (value !== undefined) {
438
+ // LRU: 最近使用したエントリを末尾に移動
439
+ this.embeddingCache.delete(key);
440
+ this.embeddingCache.set(key, value);
441
+ }
442
+ return value;
443
+ }
444
+ /**
445
+ * キャッシュに保存(LRU eviction)
446
+ */
447
+ setCacheEntry(key, value) {
448
+ // キャッシュがいっぱいの場合、最も古いエントリを削除
449
+ while (this.embeddingCache.size >= this.maxCacheSize) {
450
+ const oldestKey = this.embeddingCache.keys().next().value;
451
+ if (oldestKey !== undefined) {
452
+ this.embeddingCache.delete(oldestKey);
453
+ }
454
+ else {
455
+ break;
456
+ }
457
+ }
458
+ this.embeddingCache.set(key, value);
459
+ }
460
+ /**
461
+ * 最適化バッチでマルチモーダルEmbeddingを生成
462
+ *
463
+ * p-limitを使用した並列処理により、高スループットを実現します。
464
+ *
465
+ * 特徴:
466
+ * - 並列処理(デフォルト同時実行数: 5)
467
+ * - キャッシュによる重複処理の回避
468
+ * - 個別アイテムタイムアウト
469
+ * - バッチ全体タイムアウト
470
+ * - 進捗コールバック
471
+ * - Graceful Degradation(部分失敗許容)
472
+ * - 詳細なパフォーマンスメトリクス
473
+ *
474
+ * @param items - バッチ処理入力アイテムの配列
475
+ * @param options - 最適化オプション
476
+ * @returns OptimizedBatchResult
477
+ */
478
+ async createOptimizedBatchMultimodalEmbeddings(items, options = {}) {
479
+ const startTime = Date.now();
480
+ // デフォルトオプション
481
+ const concurrency = options.concurrency ?? 5;
482
+ const itemTimeoutMs = options.itemTimeoutMs ?? 30000;
483
+ const totalTimeoutMs = options.totalTimeoutMs ?? 300000; // 5分
484
+ const onProgress = options.onProgress;
485
+ const embeddingConfig = options.embeddingConfig;
486
+ // 空の入力の場合
487
+ if (items.length === 0) {
488
+ return {
489
+ results: [],
490
+ metrics: {
491
+ totalItems: 0,
492
+ successCount: 0,
493
+ failedCount: 0,
494
+ cacheHitCount: 0,
495
+ cacheHitRate: 0,
496
+ avgProcessingTimeMs: 0,
497
+ totalProcessingTimeMs: 0,
498
+ throughputPerMinute: 0,
499
+ },
500
+ errors: [],
501
+ };
502
+ }
503
+ if (process.env.NODE_ENV === "development") {
504
+ // eslint-disable-next-line no-console
505
+ console.log("[ML] createOptimizedBatchMultimodalEmbeddings:", {
506
+ count: items.length,
507
+ concurrency,
508
+ itemTimeoutMs,
509
+ totalTimeoutMs,
510
+ });
511
+ }
512
+ // p-limitでの並列制御
513
+ const limit = pLimit(concurrency);
514
+ // 結果とメトリクス追跡
515
+ const results = [];
516
+ const errors = [];
517
+ let cacheHitCount = 0;
518
+ let completedCount = 0;
519
+ const processingTimes = [];
520
+ // タイムアウトフラグ
521
+ let isTimedOut = false;
522
+ // 全体タイムアウト設定
523
+ const totalTimeoutPromise = new Promise((resolve) => {
524
+ setTimeout(() => {
525
+ isTimedOut = true;
526
+ resolve("timeout");
527
+ }, totalTimeoutMs);
528
+ });
529
+ // 各アイテムの処理関数
530
+ const processItem = async (item, index) => {
531
+ // 全体タイムアウトチェック
532
+ if (isTimedOut) {
533
+ errors.push({ index, error: "Batch timeout exceeded" });
534
+ return;
535
+ }
536
+ const itemStartTime = Date.now();
537
+ // キャッシュキーを生成
538
+ const cacheKey = this.generateCacheKey(item.content, item.visionFeatures, embeddingConfig?.searchMode);
539
+ // キャッシュチェック
540
+ const cached = this.getCacheEntry(cacheKey);
541
+ if (cached) {
542
+ cacheHitCount++;
543
+ results.push({ index, result: cached });
544
+ completedCount++;
545
+ // 進捗コールバック
546
+ if (onProgress) {
547
+ onProgress({
548
+ completed: completedCount,
549
+ total: items.length,
550
+ progress: completedCount / items.length,
551
+ currentIndex: index,
552
+ });
553
+ }
554
+ processingTimes.push(Date.now() - itemStartTime);
555
+ return;
556
+ }
557
+ // タイムアウト付きでEmbedding生成
558
+ try {
559
+ const result = await Promise.race([
560
+ this.createMultimodalEmbedding(item.content, item.visionFeatures, embeddingConfig),
561
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Item timeout")), itemTimeoutMs)),
562
+ ]);
563
+ // キャッシュに保存
564
+ this.setCacheEntry(cacheKey, result);
565
+ results.push({ index, result });
566
+ }
567
+ catch (error) {
568
+ const errorMessage = error instanceof Error ? error.message : String(error);
569
+ errors.push({ index, error: errorMessage });
570
+ }
571
+ completedCount++;
572
+ processingTimes.push(Date.now() - itemStartTime);
573
+ // 進捗コールバック
574
+ if (onProgress) {
575
+ onProgress({
576
+ completed: completedCount,
577
+ total: items.length,
578
+ progress: completedCount / items.length,
579
+ currentIndex: index,
580
+ });
581
+ }
582
+ };
583
+ // 並列処理を実行
584
+ const processingPromise = Promise.all(items.map((item, index) => limit(() => processItem(item, index))));
585
+ // 全体タイムアウトとの競合
586
+ await Promise.race([processingPromise, totalTimeoutPromise]);
587
+ // 残りの未処理アイテムをエラーとして追加
588
+ if (isTimedOut) {
589
+ for (let i = 0; i < items.length; i++) {
590
+ const hasResult = results.some((r) => r.index === i);
591
+ const hasError = errors.some((e) => e.index === i);
592
+ if (!hasResult && !hasError) {
593
+ errors.push({ index: i, error: "Batch timeout exceeded" });
594
+ }
595
+ }
596
+ }
597
+ // 結果をインデックス順にソート
598
+ results.sort((a, b) => a.index - b.index);
599
+ errors.sort((a, b) => a.index - b.index);
600
+ const totalProcessingTimeMs = Date.now() - startTime;
601
+ // メトリクス計算
602
+ const successCount = results.length;
603
+ const failedCount = errors.length;
604
+ const avgProcessingTimeMs = processingTimes.length > 0
605
+ ? processingTimes.reduce((sum, t) => sum + t, 0) / processingTimes.length
606
+ : 0;
607
+ const throughputPerMinute = totalProcessingTimeMs > 0 ? (successCount / totalProcessingTimeMs) * 60000 : 0;
608
+ const metrics = {
609
+ totalItems: items.length,
610
+ successCount,
611
+ failedCount,
612
+ cacheHitCount,
613
+ cacheHitRate: items.length > 0 ? cacheHitCount / items.length : 0,
614
+ avgProcessingTimeMs,
615
+ totalProcessingTimeMs,
616
+ throughputPerMinute,
617
+ };
618
+ if (process.env.NODE_ENV === "development") {
619
+ // eslint-disable-next-line no-console
620
+ console.log("[ML] createOptimizedBatchMultimodalEmbeddings completed:", {
621
+ ...metrics,
622
+ });
623
+ }
624
+ return {
625
+ results: results.map((r) => r.result),
626
+ metrics,
627
+ errors,
628
+ };
629
+ }
630
+ /**
631
+ * Embeddingキャッシュをクリア
632
+ */
633
+ clearEmbeddingCache() {
634
+ this.embeddingCache.clear();
635
+ if (process.env.NODE_ENV === "development") {
636
+ // eslint-disable-next-line no-console
637
+ console.log("[ML] Embedding cache cleared");
638
+ }
639
+ }
640
+ /**
641
+ * キャッシュ統計を取得
642
+ */
643
+ getEmbeddingCacheStats() {
644
+ return {
645
+ size: this.embeddingCache.size,
646
+ maxSize: this.maxCacheSize,
647
+ };
648
+ }
649
+ }
650
+ //# sourceMappingURL=multimodal-embedding.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multimodal-embedding.service.js","sourceRoot":"","sources":["../../src/embeddings/multimodal-embedding.service.ts"],"names":[],"mappings":"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"}