claude-brain 0.30.2 → 0.30.3
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/README.md +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/scripts/postinstall.mjs +531 -531
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
|
@@ -1,207 +1,213 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic Cache
|
|
3
|
-
* LRU cache with semantic similarity matching for query deduplication
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { LRUCache } from 'lru-cache'
|
|
7
|
-
import type { Logger } from 'pino'
|
|
8
|
-
import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
|
|
9
|
-
|
|
10
|
-
export interface CachedResult {
|
|
11
|
-
query: string
|
|
12
|
-
result:
|
|
13
|
-
embedding?: number[]
|
|
14
|
-
timestamp: number
|
|
15
|
-
hitCount: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface CacheStats {
|
|
19
|
-
size: number
|
|
20
|
-
maxSize: number
|
|
21
|
-
hits: number
|
|
22
|
-
misses: number
|
|
23
|
-
hitRate: number
|
|
24
|
-
semanticHits: number
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class SemanticCache {
|
|
28
|
-
private logger: Logger
|
|
29
|
-
private cache: LRUCache<string, CachedResult>
|
|
30
|
-
private embeddings?: EmbeddingProvider
|
|
31
|
-
private similarityThreshold: number
|
|
32
|
-
private stats = { hits: 0, misses: 0, semanticHits: 0 }
|
|
33
|
-
private embeddingIndex:
|
|
34
|
-
|
|
35
|
-
constructor(
|
|
36
|
-
logger: Logger,
|
|
37
|
-
options: {
|
|
38
|
-
maxSize?: number
|
|
39
|
-
ttlMs?: number
|
|
40
|
-
similarityThreshold?: number
|
|
41
|
-
embeddings?: EmbeddingProvider
|
|
42
|
-
} = {}
|
|
43
|
-
) {
|
|
44
|
-
this.logger = logger.child({ component: 'semantic-cache' })
|
|
45
|
-
this.similarityThreshold = options.similarityThreshold || 0.85
|
|
46
|
-
this.embeddings = options.embeddings
|
|
47
|
-
|
|
48
|
-
this.cache = new LRUCache<string, CachedResult>({
|
|
49
|
-
max: options.maxSize || 500,
|
|
50
|
-
ttl: options.ttlMs || 30 * 60 * 1000, // 30 minutes default
|
|
51
|
-
dispose: (_value, key) => {
|
|
52
|
-
// Remove from embedding index
|
|
53
|
-
this.embeddingIndex
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Get cached result for a query
|
|
60
|
-
* Checks exact match first, then semantic similarity
|
|
61
|
-
*/
|
|
62
|
-
async get(query: string): Promise<any | undefined> {
|
|
63
|
-
// Exact match
|
|
64
|
-
const normalizedKey = this.normalizeKey(query)
|
|
65
|
-
const exact = this.cache.get(normalizedKey)
|
|
66
|
-
|
|
67
|
-
if (exact) {
|
|
68
|
-
exact.hitCount++
|
|
69
|
-
this.stats.hits++
|
|
70
|
-
return exact.result
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Semantic match (if embeddings available)
|
|
74
|
-
if (this.embeddings && this.embeddingIndex.
|
|
75
|
-
try {
|
|
76
|
-
const queryEmbedding = await this.embeddings.generate(query)
|
|
77
|
-
const match = this.findSemanticMatch(queryEmbedding)
|
|
78
|
-
|
|
79
|
-
if (match) {
|
|
80
|
-
const cached = this.cache.get(match.key)
|
|
81
|
-
if (cached) {
|
|
82
|
-
cached.hitCount++
|
|
83
|
-
this.stats.hits++
|
|
84
|
-
this.stats.semanticHits++
|
|
85
|
-
return cached.result
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
this.logger.debug({ error }, 'Semantic cache lookup failed')
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
this.stats.misses++
|
|
94
|
-
return undefined
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Store a result in the cache
|
|
99
|
-
*/
|
|
100
|
-
async set(query: string, result:
|
|
101
|
-
const normalizedKey = this.normalizeKey(query)
|
|
102
|
-
|
|
103
|
-
let embedding: number[] | undefined
|
|
104
|
-
|
|
105
|
-
if (this.embeddings) {
|
|
106
|
-
try {
|
|
107
|
-
embedding = await this.embeddings.generate(query)
|
|
108
|
-
this.embeddingIndex.
|
|
109
|
-
} catch (error) {
|
|
110
|
-
this.logger.debug({ error }, 'Failed to generate embedding for cache')
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
this.cache.set(normalizedKey, {
|
|
115
|
-
query,
|
|
116
|
-
result,
|
|
117
|
-
embedding,
|
|
118
|
-
timestamp: Date.now(),
|
|
119
|
-
hitCount: 0
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Invalidate cache entries matching a pattern
|
|
125
|
-
*/
|
|
126
|
-
invalidate(pattern: string): number {
|
|
127
|
-
const lowerPattern = pattern.toLowerCase()
|
|
128
|
-
let count = 0
|
|
129
|
-
|
|
130
|
-
for (const [key] of this.cache.entries()) {
|
|
131
|
-
if (key.includes(lowerPattern)) {
|
|
132
|
-
this.cache.delete(key)
|
|
133
|
-
count++
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return count
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Invalidate all entries for a project
|
|
142
|
-
*/
|
|
143
|
-
invalidateProject(project: string): number {
|
|
144
|
-
return this.invalidate(project)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Clear the entire cache
|
|
149
|
-
*/
|
|
150
|
-
clear(): void {
|
|
151
|
-
this.cache.clear()
|
|
152
|
-
this.embeddingIndex
|
|
153
|
-
this.stats = { hits: 0, misses: 0, semanticHits: 0 }
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Get cache statistics
|
|
158
|
-
*/
|
|
159
|
-
getStats(): CacheStats {
|
|
160
|
-
const total = this.stats.hits + this.stats.misses
|
|
161
|
-
return {
|
|
162
|
-
size: this.cache.size,
|
|
163
|
-
maxSize: this.cache.max,
|
|
164
|
-
hits: this.stats.hits,
|
|
165
|
-
misses: this.stats.misses,
|
|
166
|
-
hitRate: total > 0 ? this.stats.hits / total : 0,
|
|
167
|
-
semanticHits: this.stats.semanticHits
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
private normalizeKey(query: string): string {
|
|
172
|
-
return query.toLowerCase().replace(/\s+/g, ' ').trim()
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private findSemanticMatch(queryEmbedding: number[]): { key: string; similarity: number } | null {
|
|
176
|
-
let bestMatch: { key: string; similarity: number } | null = null
|
|
177
|
-
|
|
178
|
-
for (const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Cache
|
|
3
|
+
* LRU cache with semantic similarity matching for query deduplication
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LRUCache } from 'lru-cache'
|
|
7
|
+
import type { Logger } from 'pino'
|
|
8
|
+
import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
|
|
9
|
+
|
|
10
|
+
export interface CachedResult {
|
|
11
|
+
query: string
|
|
12
|
+
result: unknown
|
|
13
|
+
embedding?: number[]
|
|
14
|
+
timestamp: number
|
|
15
|
+
hitCount: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CacheStats {
|
|
19
|
+
size: number
|
|
20
|
+
maxSize: number
|
|
21
|
+
hits: number
|
|
22
|
+
misses: number
|
|
23
|
+
hitRate: number
|
|
24
|
+
semanticHits: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class SemanticCache {
|
|
28
|
+
private logger: Logger
|
|
29
|
+
private cache: LRUCache<string, CachedResult>
|
|
30
|
+
private embeddings?: EmbeddingProvider
|
|
31
|
+
private similarityThreshold: number
|
|
32
|
+
private stats = { hits: 0, misses: 0, semanticHits: 0 }
|
|
33
|
+
private embeddingIndex: Map<string, number[]> = new Map()
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
logger: Logger,
|
|
37
|
+
options: {
|
|
38
|
+
maxSize?: number
|
|
39
|
+
ttlMs?: number
|
|
40
|
+
similarityThreshold?: number
|
|
41
|
+
embeddings?: EmbeddingProvider
|
|
42
|
+
} = {}
|
|
43
|
+
) {
|
|
44
|
+
this.logger = logger.child({ component: 'semantic-cache' })
|
|
45
|
+
this.similarityThreshold = options.similarityThreshold || 0.85
|
|
46
|
+
this.embeddings = options.embeddings
|
|
47
|
+
|
|
48
|
+
this.cache = new LRUCache<string, CachedResult>({
|
|
49
|
+
max: options.maxSize || 500,
|
|
50
|
+
ttl: options.ttlMs || 30 * 60 * 1000, // 30 minutes default
|
|
51
|
+
dispose: (_value, key) => {
|
|
52
|
+
// Remove from embedding index
|
|
53
|
+
this.embeddingIndex.delete(key)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get cached result for a query
|
|
60
|
+
* Checks exact match first, then semantic similarity
|
|
61
|
+
*/
|
|
62
|
+
async get(query: string): Promise<any | undefined> {
|
|
63
|
+
// Exact match
|
|
64
|
+
const normalizedKey = this.normalizeKey(query)
|
|
65
|
+
const exact = this.cache.get(normalizedKey)
|
|
66
|
+
|
|
67
|
+
if (exact) {
|
|
68
|
+
exact.hitCount++
|
|
69
|
+
this.stats.hits++
|
|
70
|
+
return exact.result
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Semantic match (if embeddings available)
|
|
74
|
+
if (this.embeddings && this.embeddingIndex.size > 0) {
|
|
75
|
+
try {
|
|
76
|
+
const queryEmbedding = await this.embeddings.generate(query)
|
|
77
|
+
const match = this.findSemanticMatch(queryEmbedding)
|
|
78
|
+
|
|
79
|
+
if (match) {
|
|
80
|
+
const cached = this.cache.get(match.key)
|
|
81
|
+
if (cached) {
|
|
82
|
+
cached.hitCount++
|
|
83
|
+
this.stats.hits++
|
|
84
|
+
this.stats.semanticHits++
|
|
85
|
+
return cached.result
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.logger.debug({ error }, 'Semantic cache lookup failed')
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.stats.misses++
|
|
94
|
+
return undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Store a result in the cache
|
|
99
|
+
*/
|
|
100
|
+
async set(query: string, result: unknown): Promise<void> {
|
|
101
|
+
const normalizedKey = this.normalizeKey(query)
|
|
102
|
+
|
|
103
|
+
let embedding: number[] | undefined
|
|
104
|
+
|
|
105
|
+
if (this.embeddings) {
|
|
106
|
+
try {
|
|
107
|
+
embedding = await this.embeddings.generate(query)
|
|
108
|
+
this.embeddingIndex.set(normalizedKey, embedding)
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this.logger.debug({ error }, 'Failed to generate embedding for cache')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.cache.set(normalizedKey, {
|
|
115
|
+
query,
|
|
116
|
+
result,
|
|
117
|
+
embedding,
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
hitCount: 0
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Invalidate cache entries matching a pattern
|
|
125
|
+
*/
|
|
126
|
+
invalidate(pattern: string): number {
|
|
127
|
+
const lowerPattern = pattern.toLowerCase()
|
|
128
|
+
let count = 0
|
|
129
|
+
|
|
130
|
+
for (const [key] of this.cache.entries()) {
|
|
131
|
+
if (key.includes(lowerPattern)) {
|
|
132
|
+
this.cache.delete(key)
|
|
133
|
+
count++
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return count
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Invalidate all entries for a project
|
|
142
|
+
*/
|
|
143
|
+
invalidateProject(project: string): number {
|
|
144
|
+
return this.invalidate(project)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Clear the entire cache
|
|
149
|
+
*/
|
|
150
|
+
clear(): void {
|
|
151
|
+
this.cache.clear()
|
|
152
|
+
this.embeddingIndex.clear()
|
|
153
|
+
this.stats = { hits: 0, misses: 0, semanticHits: 0 }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get cache statistics
|
|
158
|
+
*/
|
|
159
|
+
getStats(): CacheStats {
|
|
160
|
+
const total = this.stats.hits + this.stats.misses
|
|
161
|
+
return {
|
|
162
|
+
size: this.cache.size,
|
|
163
|
+
maxSize: this.cache.max,
|
|
164
|
+
hits: this.stats.hits,
|
|
165
|
+
misses: this.stats.misses,
|
|
166
|
+
hitRate: total > 0 ? this.stats.hits / total : 0,
|
|
167
|
+
semanticHits: this.stats.semanticHits
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private normalizeKey(query: string): string {
|
|
172
|
+
return query.toLowerCase().replace(/\s+/g, ' ').trim()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private findSemanticMatch(queryEmbedding: number[]): { key: string; similarity: number } | null {
|
|
176
|
+
let bestMatch: { key: string; similarity: number } | null = null
|
|
177
|
+
|
|
178
|
+
for (const [key, embedding] of this.embeddingIndex) {
|
|
179
|
+
// Skip entries no longer in LRU cache (expired by TTL)
|
|
180
|
+
if (!this.cache.has(key)) {
|
|
181
|
+
this.embeddingIndex.delete(key)
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const similarity = this.cosineSimilarity(queryEmbedding, embedding)
|
|
186
|
+
|
|
187
|
+
if (similarity >= this.similarityThreshold) {
|
|
188
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
189
|
+
bestMatch = { key, similarity }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return bestMatch
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private cosineSimilarity(a: number[], b: number[]): number {
|
|
198
|
+
if (a.length !== b.length) return 0
|
|
199
|
+
|
|
200
|
+
let dotProduct = 0
|
|
201
|
+
let normA = 0
|
|
202
|
+
let normB = 0
|
|
203
|
+
|
|
204
|
+
for (let i = 0; i < a.length; i++) {
|
|
205
|
+
dotProduct += a[i]! * b[i]!
|
|
206
|
+
normA += a[i]! * a[i]!
|
|
207
|
+
normB += b[i]! * b[i]!
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB)
|
|
211
|
+
return denominator > 0 ? dotProduct / denominator : 0
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Predictive Intelligence Module
|
|
3
|
-
* Phase 15.3 - Recommendations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { Recommender } from './recommender'
|
|
7
|
-
export type { Recommendation, RecommendationResult } from './recommender'
|
|
1
|
+
/**
|
|
2
|
+
* Predictive Intelligence Module
|
|
3
|
+
* Phase 15.3 - Recommendations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { Recommender } from './recommender'
|
|
7
|
+
export type { Recommendation, RecommendationResult } from './recommender'
|