@nahisaho/shikigami-mcp-server 1.7.0 → 1.10.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 (114) hide show
  1. package/README.md +29 -0
  2. package/dist/cache/__tests__/global.test.d.ts +6 -0
  3. package/dist/cache/__tests__/global.test.d.ts.map +1 -0
  4. package/dist/cache/__tests__/global.test.js +269 -0
  5. package/dist/cache/__tests__/global.test.js.map +1 -0
  6. package/dist/cache/__tests__/manager.test.d.ts +6 -0
  7. package/dist/cache/__tests__/manager.test.d.ts.map +1 -0
  8. package/dist/cache/__tests__/manager.test.js +286 -0
  9. package/dist/cache/__tests__/manager.test.js.map +1 -0
  10. package/dist/cache/__tests__/semantic.test.d.ts +6 -0
  11. package/dist/cache/__tests__/semantic.test.d.ts.map +1 -0
  12. package/dist/cache/__tests__/semantic.test.js +271 -0
  13. package/dist/cache/__tests__/semantic.test.js.map +1 -0
  14. package/dist/cache/__tests__/store.test.d.ts +6 -0
  15. package/dist/cache/__tests__/store.test.d.ts.map +1 -0
  16. package/dist/cache/__tests__/store.test.js +289 -0
  17. package/dist/cache/__tests__/store.test.js.map +1 -0
  18. package/dist/cache/global.d.ts +140 -0
  19. package/dist/cache/global.d.ts.map +1 -0
  20. package/dist/cache/global.js +260 -0
  21. package/dist/cache/global.js.map +1 -0
  22. package/dist/cache/index.d.ts +10 -0
  23. package/dist/cache/index.d.ts.map +1 -0
  24. package/dist/cache/index.js +10 -0
  25. package/dist/cache/index.js.map +1 -0
  26. package/dist/cache/manager.d.ts +146 -0
  27. package/dist/cache/manager.d.ts.map +1 -0
  28. package/dist/cache/manager.js +229 -0
  29. package/dist/cache/manager.js.map +1 -0
  30. package/dist/cache/semantic.d.ts +164 -0
  31. package/dist/cache/semantic.d.ts.map +1 -0
  32. package/dist/cache/semantic.js +241 -0
  33. package/dist/cache/semantic.js.map +1 -0
  34. package/dist/cache/store.d.ts +98 -0
  35. package/dist/cache/store.d.ts.map +1 -0
  36. package/dist/cache/store.js +469 -0
  37. package/dist/cache/store.js.map +1 -0
  38. package/dist/cache/types.d.ts +171 -0
  39. package/dist/cache/types.d.ts.map +1 -0
  40. package/dist/cache/types.js +8 -0
  41. package/dist/cache/types.js.map +1 -0
  42. package/dist/config/types.d.ts +67 -0
  43. package/dist/config/types.d.ts.map +1 -1
  44. package/dist/config/types.js +30 -0
  45. package/dist/config/types.js.map +1 -1
  46. package/dist/tools/__tests__/multilingual-search.test.d.ts +7 -0
  47. package/dist/tools/__tests__/multilingual-search.test.d.ts.map +1 -0
  48. package/dist/tools/__tests__/multilingual-search.test.js +71 -0
  49. package/dist/tools/__tests__/multilingual-search.test.js.map +1 -0
  50. package/dist/tools/search/recovery/__tests__/logger.test.d.ts +8 -0
  51. package/dist/tools/search/recovery/__tests__/logger.test.d.ts.map +1 -0
  52. package/dist/tools/search/recovery/__tests__/logger.test.js +249 -0
  53. package/dist/tools/search/recovery/__tests__/logger.test.js.map +1 -0
  54. package/dist/tools/search/recovery/__tests__/manager-logger.test.d.ts +8 -0
  55. package/dist/tools/search/recovery/__tests__/manager-logger.test.d.ts.map +1 -0
  56. package/dist/tools/search/recovery/__tests__/manager-logger.test.js +158 -0
  57. package/dist/tools/search/recovery/__tests__/manager-logger.test.js.map +1 -0
  58. package/dist/tools/search/recovery/index.d.ts +31 -2
  59. package/dist/tools/search/recovery/index.d.ts.map +1 -1
  60. package/dist/tools/search/recovery/index.js +51 -7
  61. package/dist/tools/search/recovery/index.js.map +1 -1
  62. package/dist/tools/search/recovery/logger.d.ts +149 -0
  63. package/dist/tools/search/recovery/logger.d.ts.map +1 -0
  64. package/dist/tools/search/recovery/logger.js +218 -0
  65. package/dist/tools/search/recovery/logger.js.map +1 -0
  66. package/dist/tools/search.d.ts +48 -0
  67. package/dist/tools/search.d.ts.map +1 -1
  68. package/dist/tools/search.js +152 -0
  69. package/dist/tools/search.js.map +1 -1
  70. package/dist/tools/visit/recovery/__tests__/index.test.d.ts +10 -0
  71. package/dist/tools/visit/recovery/__tests__/index.test.d.ts.map +1 -0
  72. package/dist/tools/visit/recovery/__tests__/index.test.js +239 -0
  73. package/dist/tools/visit/recovery/__tests__/index.test.js.map +1 -0
  74. package/dist/tools/visit/recovery/__tests__/wayback.test.d.ts +8 -0
  75. package/dist/tools/visit/recovery/__tests__/wayback.test.d.ts.map +1 -0
  76. package/dist/tools/visit/recovery/__tests__/wayback.test.js +271 -0
  77. package/dist/tools/visit/recovery/__tests__/wayback.test.js.map +1 -0
  78. package/dist/tools/visit/recovery/index.d.ts +126 -0
  79. package/dist/tools/visit/recovery/index.d.ts.map +1 -0
  80. package/dist/tools/visit/recovery/index.js +203 -0
  81. package/dist/tools/visit/recovery/index.js.map +1 -0
  82. package/dist/tools/visit/recovery/wayback.d.ts +101 -0
  83. package/dist/tools/visit/recovery/wayback.d.ts.map +1 -0
  84. package/dist/tools/visit/recovery/wayback.js +140 -0
  85. package/dist/tools/visit/recovery/wayback.js.map +1 -0
  86. package/dist/tools/visit.d.ts +33 -0
  87. package/dist/tools/visit.d.ts.map +1 -1
  88. package/dist/tools/visit.js +127 -1
  89. package/dist/tools/visit.js.map +1 -1
  90. package/package.json +7 -3
  91. package/shikigami.config.example.yaml +9 -0
  92. package/src/cache/__tests__/global.test.ts +340 -0
  93. package/src/cache/__tests__/manager.test.ts +353 -0
  94. package/src/cache/__tests__/semantic.test.ts +331 -0
  95. package/src/cache/__tests__/store.test.ts +369 -0
  96. package/src/cache/global.ts +351 -0
  97. package/src/cache/index.ts +10 -0
  98. package/src/cache/manager.ts +325 -0
  99. package/src/cache/semantic.ts +368 -0
  100. package/src/cache/store.ts +555 -0
  101. package/src/cache/types.ts +189 -0
  102. package/src/config/types.ts +108 -0
  103. package/src/tools/__tests__/multilingual-search.test.ts +88 -0
  104. package/src/tools/search/recovery/__tests__/logger.test.ts +334 -0
  105. package/src/tools/search/recovery/__tests__/manager-logger.test.ts +199 -0
  106. package/src/tools/search/recovery/index.ts +67 -9
  107. package/src/tools/search/recovery/logger.ts +351 -0
  108. package/src/tools/search.ts +212 -0
  109. package/src/tools/visit/recovery/__tests__/index.test.ts +297 -0
  110. package/src/tools/visit/recovery/__tests__/wayback.test.ts +344 -0
  111. package/src/tools/visit/recovery/index.ts +312 -0
  112. package/src/tools/visit/recovery/wayback.ts +210 -0
  113. package/src/tools/visit.ts +159 -2
  114. package/vitest.config.ts +22 -0
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Semantic Cache Matcher
3
+ * v1.0.0 - REQ-CACHE-001-02
4
+ *
5
+ * 意味的に類似したクエリのキャッシュマッチングを行う
6
+ * 埋め込みベクトルとコサイン類似度を使用
7
+ */
8
+
9
+ import { CacheEntry, CacheSource, CacheQueryOptions } from './types.js';
10
+ import { FileCacheStore } from './store.js';
11
+
12
+ /**
13
+ * 埋め込みベクトル
14
+ */
15
+ export type EmbeddingVector = number[];
16
+
17
+ /**
18
+ * クエリ埋め込みエントリ
19
+ */
20
+ export interface QueryEmbedding {
21
+ /** 元のクエリ文字列 */
22
+ query: string;
23
+ /** 埋め込みベクトル */
24
+ embedding: EmbeddingVector;
25
+ /** キャッシュキー */
26
+ cacheKey: string;
27
+ /** 作成日時 */
28
+ createdAt: string;
29
+ /** ソース種別 */
30
+ source: CacheSource;
31
+ }
32
+
33
+ /**
34
+ * 類似クエリ候補
35
+ */
36
+ export interface SimilarQueryCandidate {
37
+ /** 類似したクエリのキャッシュキー */
38
+ cacheKey: string;
39
+ /** 元のクエリ */
40
+ originalQuery: string;
41
+ /** 類似度スコア (0-1) */
42
+ similarity: number;
43
+ /** 作成日時 */
44
+ createdAt: string;
45
+ }
46
+
47
+ /**
48
+ * SemanticCacheMatcher設定
49
+ */
50
+ export interface SemanticCacheConfig {
51
+ /** 類似度の閾値 (0-1, デフォルト: 0.90) */
52
+ similarityThreshold: number;
53
+ /** 最大類似クエリ数 */
54
+ maxCandidates: number;
55
+ /** 埋め込みキャッシュの有効化 */
56
+ enabled: boolean;
57
+ /** 埋め込みモデル名 */
58
+ embeddingModel: string;
59
+ /** 埋め込み次元数 */
60
+ embeddingDimension: number;
61
+ }
62
+
63
+ /**
64
+ * デフォルト設定
65
+ */
66
+ export const DEFAULT_SEMANTIC_CONFIG: SemanticCacheConfig = {
67
+ similarityThreshold: 0.90,
68
+ maxCandidates: 5,
69
+ enabled: true,
70
+ embeddingModel: 'default',
71
+ embeddingDimension: 1536,
72
+ };
73
+
74
+ /**
75
+ * 埋め込みサービスインターフェース
76
+ */
77
+ export interface IEmbeddingService {
78
+ /** テキストから埋め込みベクトルを生成 */
79
+ embed(text: string): Promise<EmbeddingVector>;
80
+ /** 複数テキストの埋め込みベクトルをバッチ生成 */
81
+ embedBatch?(texts: string[]): Promise<EmbeddingVector[]>;
82
+ /** モデル名を取得 */
83
+ getModelName(): string;
84
+ /** 次元数を取得 */
85
+ getDimension(): number;
86
+ }
87
+
88
+ /**
89
+ * シンプルな埋め込みサービス(テスト/フォールバック用)
90
+ * 実運用では外部APIを使用
91
+ */
92
+ export class SimpleEmbeddingService implements IEmbeddingService {
93
+ private dimension: number;
94
+ private modelName: string;
95
+
96
+ constructor(dimension = 64, modelName = 'simple-hash') {
97
+ this.dimension = dimension;
98
+ this.modelName = modelName;
99
+ }
100
+
101
+ /**
102
+ * 簡易埋め込み生成(ハッシュベース)
103
+ * 実運用では OpenAI, Anthropic 等の API を使用
104
+ */
105
+ async embed(text: string): Promise<EmbeddingVector> {
106
+ const normalized = text.toLowerCase().trim();
107
+ const vector: number[] = new Array(this.dimension).fill(0);
108
+
109
+ // シンプルなハッシュベースの埋め込み
110
+ for (let i = 0; i < normalized.length; i++) {
111
+ const charCode = normalized.charCodeAt(i);
112
+ const idx = (charCode * (i + 1)) % this.dimension;
113
+ vector[idx] += charCode / 127; // normalize to ~1
114
+ }
115
+
116
+ // 正規化
117
+ const magnitude = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0)) || 1;
118
+ return vector.map((v) => v / magnitude);
119
+ }
120
+
121
+ async embedBatch(texts: string[]): Promise<EmbeddingVector[]> {
122
+ return Promise.all(texts.map((t) => this.embed(t)));
123
+ }
124
+
125
+ getModelName(): string {
126
+ return this.modelName;
127
+ }
128
+
129
+ getDimension(): number {
130
+ return this.dimension;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * コサイン類似度を計算
136
+ */
137
+ export function cosineSimilarity(a: EmbeddingVector, b: EmbeddingVector): number {
138
+ if (a.length !== b.length) {
139
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
140
+ }
141
+
142
+ let dotProduct = 0;
143
+ let magnitudeA = 0;
144
+ let magnitudeB = 0;
145
+
146
+ for (let i = 0; i < a.length; i++) {
147
+ dotProduct += a[i] * b[i];
148
+ magnitudeA += a[i] * a[i];
149
+ magnitudeB += b[i] * b[i];
150
+ }
151
+
152
+ magnitudeA = Math.sqrt(magnitudeA);
153
+ magnitudeB = Math.sqrt(magnitudeB);
154
+
155
+ if (magnitudeA === 0 || magnitudeB === 0) {
156
+ return 0;
157
+ }
158
+
159
+ return dotProduct / (magnitudeA * magnitudeB);
160
+ }
161
+
162
+ /**
163
+ * SemanticCacheMatcher
164
+ * 意味的に類似したクエリのキャッシュマッチングを行う
165
+ */
166
+ export class SemanticCacheMatcher {
167
+ private config: SemanticCacheConfig;
168
+ private embeddingService: IEmbeddingService;
169
+ private cacheStore: FileCacheStore;
170
+
171
+ /** 埋め込みインデックス(メモリ上) */
172
+ private embeddingIndex: Map<string, QueryEmbedding> = new Map();
173
+
174
+ constructor(
175
+ cacheStore: FileCacheStore,
176
+ config: Partial<SemanticCacheConfig> = {},
177
+ embeddingService?: IEmbeddingService
178
+ ) {
179
+ this.config = { ...DEFAULT_SEMANTIC_CONFIG, ...config };
180
+ this.cacheStore = cacheStore;
181
+ this.embeddingService =
182
+ embeddingService || new SimpleEmbeddingService(this.config.embeddingDimension);
183
+ }
184
+
185
+ /**
186
+ * クエリの埋め込みを生成し、インデックスに追加
187
+ */
188
+ async indexQuery(query: string, cacheKey: string, source: CacheSource): Promise<void> {
189
+ if (!this.config.enabled) return;
190
+
191
+ const embedding = await this.embeddingService.embed(query);
192
+
193
+ const entry: QueryEmbedding = {
194
+ query,
195
+ embedding,
196
+ cacheKey,
197
+ source,
198
+ createdAt: new Date().toISOString(),
199
+ };
200
+
201
+ this.embeddingIndex.set(cacheKey, entry);
202
+
203
+ // 埋め込みもキャッシュストアに保存(永続化)
204
+ await this.cacheStore.set(`embedding:${cacheKey}`, entry, {
205
+ source: 'embedding',
206
+ ttlSeconds: 3600 * 24, // 24時間
207
+ });
208
+ }
209
+
210
+ /**
211
+ * 類似クエリを検索
212
+ */
213
+ async findSimilar(query: string, source?: CacheSource): Promise<SimilarQueryCandidate[]> {
214
+ if (!this.config.enabled) return [];
215
+
216
+ const queryEmbedding = await this.embeddingService.embed(query);
217
+ const candidates: SimilarQueryCandidate[] = [];
218
+
219
+ // メモリインデックスから検索
220
+ for (const [_key, entry] of this.embeddingIndex) {
221
+ // ソースフィルタ
222
+ if (source && entry.source !== source) continue;
223
+
224
+ const similarity = cosineSimilarity(queryEmbedding, entry.embedding);
225
+
226
+ if (similarity >= this.config.similarityThreshold) {
227
+ candidates.push({
228
+ cacheKey: entry.cacheKey,
229
+ originalQuery: entry.query,
230
+ similarity,
231
+ createdAt: entry.createdAt,
232
+ });
233
+ }
234
+ }
235
+
236
+ // 類似度でソートして上位を返す
237
+ return candidates
238
+ .sort((a, b) => b.similarity - a.similarity)
239
+ .slice(0, this.config.maxCandidates);
240
+ }
241
+
242
+ /**
243
+ * 最も類似したキャッシュエントリを取得
244
+ */
245
+ async getBestMatch<T = unknown>(
246
+ query: string,
247
+ source?: CacheSource
248
+ ): Promise<{ entry: CacheEntry<T>; similarity: number } | null> {
249
+ const candidates = await this.findSimilar(query, source);
250
+
251
+ if (candidates.length === 0) {
252
+ return null;
253
+ }
254
+
255
+ const best = candidates[0];
256
+ const result = await this.cacheStore.get<T>(best.cacheKey);
257
+
258
+ if (result.hit && result.value !== undefined) {
259
+ return {
260
+ entry: {
261
+ key: best.cacheKey,
262
+ value: result.value,
263
+ meta: result.meta!,
264
+ },
265
+ similarity: best.similarity,
266
+ };
267
+ }
268
+
269
+ return null;
270
+ }
271
+
272
+ /**
273
+ * 埋め込みインデックスからエントリを削除
274
+ */
275
+ removeFromIndex(cacheKey: string): void {
276
+ this.embeddingIndex.delete(cacheKey);
277
+ }
278
+
279
+ /**
280
+ * インデックスをクリア
281
+ */
282
+ clearIndex(): void {
283
+ this.embeddingIndex.clear();
284
+ }
285
+
286
+ /**
287
+ * 永続化されたインデックスをロード
288
+ */
289
+ async loadIndex(): Promise<number> {
290
+ const entries = await this.cacheStore.query({
291
+ source: 'embedding',
292
+ limit: 10000,
293
+ });
294
+
295
+ let loadedCount = 0;
296
+
297
+ for (const entry of entries) {
298
+ const queryEmbedding = entry.value as QueryEmbedding;
299
+ if (queryEmbedding.cacheKey && queryEmbedding.embedding) {
300
+ this.embeddingIndex.set(queryEmbedding.cacheKey, queryEmbedding);
301
+ loadedCount++;
302
+ }
303
+ }
304
+
305
+ return loadedCount;
306
+ }
307
+
308
+ /**
309
+ * インデックスサイズを取得
310
+ */
311
+ getIndexSize(): number {
312
+ return this.embeddingIndex.size;
313
+ }
314
+
315
+ /**
316
+ * 設定を取得
317
+ */
318
+ getConfig(): SemanticCacheConfig {
319
+ return { ...this.config };
320
+ }
321
+
322
+ /**
323
+ * 類似度閾値を更新
324
+ */
325
+ setSimilarityThreshold(threshold: number): void {
326
+ if (threshold < 0 || threshold > 1) {
327
+ throw new Error('Threshold must be between 0 and 1');
328
+ }
329
+ this.config.similarityThreshold = threshold;
330
+ }
331
+
332
+ /**
333
+ * 有効/無効を切り替え
334
+ */
335
+ setEnabled(enabled: boolean): void {
336
+ this.config.enabled = enabled;
337
+ }
338
+
339
+ /**
340
+ * 統計情報を取得
341
+ */
342
+ getStats(): {
343
+ indexSize: number;
344
+ threshold: number;
345
+ enabled: boolean;
346
+ model: string;
347
+ dimension: number;
348
+ } {
349
+ return {
350
+ indexSize: this.embeddingIndex.size,
351
+ threshold: this.config.similarityThreshold,
352
+ enabled: this.config.enabled,
353
+ model: this.embeddingService.getModelName(),
354
+ dimension: this.embeddingService.getDimension(),
355
+ };
356
+ }
357
+ }
358
+
359
+ /**
360
+ * デフォルトインスタンス作成関数
361
+ */
362
+ export function createSemanticCacheMatcher(
363
+ cacheStore: FileCacheStore,
364
+ config?: Partial<SemanticCacheConfig>,
365
+ embeddingService?: IEmbeddingService
366
+ ): SemanticCacheMatcher {
367
+ return new SemanticCacheMatcher(cacheStore, config, embeddingService);
368
+ }