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,221 +1,221 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adaptive Learning
|
|
3
|
-
* Learns optimal retrieval thresholds from feedback
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import type { MemoryFeedback, AdaptiveThresholds } from '../types'
|
|
8
|
-
import type { FeedbackStore } from './store'
|
|
9
|
-
import { calculateAllMetrics, calculateAverageRating, calculatePositiveRate } from './metrics'
|
|
10
|
-
|
|
11
|
-
/** Default thresholds before any learning */
|
|
12
|
-
const DEFAULT_THRESHOLDS: AdaptiveThresholds = {
|
|
13
|
-
denseMinSimilarity: 0.3,
|
|
14
|
-
denseWeight: 0.7,
|
|
15
|
-
sparseWeight: 0.3,
|
|
16
|
-
rrfK: 60,
|
|
17
|
-
feedbackCount: 0,
|
|
18
|
-
lastUpdated: new Date().toISOString()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Thresholds bounds to prevent extreme values */
|
|
22
|
-
const BOUNDS = {
|
|
23
|
-
denseMinSimilarity: { min: 0.1, max: 0.9 },
|
|
24
|
-
denseWeight: { min: 0.3, max: 0.9 },
|
|
25
|
-
sparseWeight: { min: 0.1, max: 0.7 },
|
|
26
|
-
rrfK: { min: 20, max: 100 }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AdaptiveLearnerConfig {
|
|
30
|
-
/** Minimum feedback count before adaptation */
|
|
31
|
-
minFeedbackForAdaptation: number
|
|
32
|
-
/** Learning rate for threshold adjustments */
|
|
33
|
-
learningRate: number
|
|
34
|
-
/** Target positive feedback rate */
|
|
35
|
-
targetPositiveRate: number
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const DEFAULT_CONFIG: AdaptiveLearnerConfig = {
|
|
39
|
-
minFeedbackForAdaptation: 10,
|
|
40
|
-
learningRate: 0.1,
|
|
41
|
-
targetPositiveRate: 0.7
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class AdaptiveLearner {
|
|
45
|
-
private logger: Logger
|
|
46
|
-
private feedbackStore: FeedbackStore
|
|
47
|
-
private config: AdaptiveLearnerConfig
|
|
48
|
-
private currentThresholds: AdaptiveThresholds
|
|
49
|
-
|
|
50
|
-
constructor(
|
|
51
|
-
logger: Logger,
|
|
52
|
-
feedbackStore: FeedbackStore,
|
|
53
|
-
config: Partial<AdaptiveLearnerConfig> = {}
|
|
54
|
-
) {
|
|
55
|
-
this.logger = logger.child({ component: 'adaptive-learner' })
|
|
56
|
-
this.feedbackStore = feedbackStore
|
|
57
|
-
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
58
|
-
this.currentThresholds = { ...DEFAULT_THRESHOLDS }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get current adaptive thresholds
|
|
63
|
-
*/
|
|
64
|
-
getThresholds(): AdaptiveThresholds {
|
|
65
|
-
return { ...this.currentThresholds }
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Update thresholds based on feedback
|
|
70
|
-
*/
|
|
71
|
-
async updateThresholds(): Promise<AdaptiveThresholds> {
|
|
72
|
-
const feedback = await this.feedbackStore.getRecentFeedback(1000)
|
|
73
|
-
|
|
74
|
-
if (feedback.length < this.config.minFeedbackForAdaptation) {
|
|
75
|
-
this.logger.debug({
|
|
76
|
-
feedbackCount: feedback.length,
|
|
77
|
-
required: this.config.minFeedbackForAdaptation
|
|
78
|
-
}, 'Not enough feedback for adaptation')
|
|
79
|
-
return this.currentThresholds
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
this.logger.info({ feedbackCount: feedback.length }, 'Adapting thresholds')
|
|
83
|
-
|
|
84
|
-
const metrics = calculateAllMetrics(feedback)
|
|
85
|
-
const positiveRate = calculatePositiveRate(feedback)
|
|
86
|
-
const avgRating = calculateAverageRating(feedback)
|
|
87
|
-
|
|
88
|
-
// Analyze feedback by provenance
|
|
89
|
-
const denseOnly = feedback.filter(f => this.inferProvenance(f) === 'dense')
|
|
90
|
-
const sparseOnly = feedback.filter(f => this.inferProvenance(f) === 'sparse')
|
|
91
|
-
const both = feedback.filter(f => this.inferProvenance(f) === 'both')
|
|
92
|
-
|
|
93
|
-
const densePositiveRate = denseOnly.length > 0 ? calculatePositiveRate(denseOnly) : 0.5
|
|
94
|
-
const sparsePositiveRate = sparseOnly.length > 0 ? calculatePositiveRate(sparseOnly) : 0.5
|
|
95
|
-
// Adjust dense/sparse weights based on performance
|
|
96
|
-
if (denseOnly.length >= 5 && sparseOnly.length >= 5) {
|
|
97
|
-
// If dense performs better, increase its weight
|
|
98
|
-
if (densePositiveRate > sparsePositiveRate + 0.1) {
|
|
99
|
-
this.currentThresholds.denseWeight = this.adjustValue(
|
|
100
|
-
this.currentThresholds.denseWeight,
|
|
101
|
-
this.config.learningRate,
|
|
102
|
-
'increase',
|
|
103
|
-
BOUNDS.denseWeight
|
|
104
|
-
)
|
|
105
|
-
this.currentThresholds.sparseWeight = this.adjustValue(
|
|
106
|
-
this.currentThresholds.sparseWeight,
|
|
107
|
-
this.config.learningRate,
|
|
108
|
-
'decrease',
|
|
109
|
-
BOUNDS.sparseWeight
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
// If sparse performs better, increase its weight
|
|
113
|
-
else if (sparsePositiveRate > densePositiveRate + 0.1) {
|
|
114
|
-
this.currentThresholds.sparseWeight = this.adjustValue(
|
|
115
|
-
this.currentThresholds.sparseWeight,
|
|
116
|
-
this.config.learningRate,
|
|
117
|
-
'increase',
|
|
118
|
-
BOUNDS.sparseWeight
|
|
119
|
-
)
|
|
120
|
-
this.currentThresholds.denseWeight = this.adjustValue(
|
|
121
|
-
this.currentThresholds.denseWeight,
|
|
122
|
-
this.config.learningRate,
|
|
123
|
-
'decrease',
|
|
124
|
-
BOUNDS.denseWeight
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Adjust minimum similarity based on positive rate
|
|
130
|
-
if (positiveRate < this.config.targetPositiveRate - 0.1) {
|
|
131
|
-
// Too many negative results - lower threshold to get more results
|
|
132
|
-
this.currentThresholds.denseMinSimilarity = this.adjustValue(
|
|
133
|
-
this.currentThresholds.denseMinSimilarity,
|
|
134
|
-
this.config.learningRate,
|
|
135
|
-
'decrease',
|
|
136
|
-
BOUNDS.denseMinSimilarity
|
|
137
|
-
)
|
|
138
|
-
} else if (positiveRate > this.config.targetPositiveRate + 0.1) {
|
|
139
|
-
// Results are good - can raise threshold to be more selective
|
|
140
|
-
this.currentThresholds.denseMinSimilarity = this.adjustValue(
|
|
141
|
-
this.currentThresholds.denseMinSimilarity,
|
|
142
|
-
this.config.learningRate / 2, // More conservative increase
|
|
143
|
-
'increase',
|
|
144
|
-
BOUNDS.denseMinSimilarity
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Adjust RRF K based on metrics
|
|
149
|
-
// Higher K reduces effect of rank, lower K emphasizes top ranks
|
|
150
|
-
if (metrics.mrr < 0.5 && both.length > 0) {
|
|
151
|
-
// If MRR is low and we have combined results, try lower K
|
|
152
|
-
this.currentThresholds.rrfK = this.adjustValue(
|
|
153
|
-
this.currentThresholds.rrfK,
|
|
154
|
-
5, // Adjust by fixed amount
|
|
155
|
-
'decrease',
|
|
156
|
-
BOUNDS.rrfK
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
this.currentThresholds.feedbackCount = feedback.length
|
|
161
|
-
this.currentThresholds.lastUpdated = new Date().toISOString()
|
|
162
|
-
|
|
163
|
-
this.logger.info({
|
|
164
|
-
thresholds: this.currentThresholds,
|
|
165
|
-
metrics,
|
|
166
|
-
positiveRate,
|
|
167
|
-
avgRating
|
|
168
|
-
}, 'Thresholds adapted')
|
|
169
|
-
|
|
170
|
-
return this.currentThresholds
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Adjust a value with bounds checking
|
|
175
|
-
*/
|
|
176
|
-
private adjustValue(
|
|
177
|
-
current: number,
|
|
178
|
-
amount: number,
|
|
179
|
-
direction: 'increase' | 'decrease',
|
|
180
|
-
bounds: { min: number; max: number }
|
|
181
|
-
): number {
|
|
182
|
-
const delta = direction === 'increase' ? amount : -amount
|
|
183
|
-
const newValue = current + delta
|
|
184
|
-
return Math.max(bounds.min, Math.min(bounds.max, newValue))
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Infer provenance from feedback (heuristic)
|
|
189
|
-
* In a real implementation, this would be stored in feedback
|
|
190
|
-
*/
|
|
191
|
-
private inferProvenance(_feedback: MemoryFeedback): 'dense' | 'sparse' | 'both' {
|
|
192
|
-
// This is a heuristic - in production, provenance should be stored with feedback
|
|
193
|
-
// For now, assume most results are from combined search
|
|
194
|
-
return 'both'
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Get adaptation status
|
|
199
|
-
*/
|
|
200
|
-
getStatus(): {
|
|
201
|
-
feedbackCount: number
|
|
202
|
-
canAdapt: boolean
|
|
203
|
-
currentThresholds: AdaptiveThresholds
|
|
204
|
-
lastUpdated: string
|
|
205
|
-
} {
|
|
206
|
-
return {
|
|
207
|
-
feedbackCount: this.currentThresholds.feedbackCount,
|
|
208
|
-
canAdapt: this.currentThresholds.feedbackCount >= this.config.minFeedbackForAdaptation,
|
|
209
|
-
currentThresholds: this.currentThresholds,
|
|
210
|
-
lastUpdated: this.currentThresholds.lastUpdated
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Reset thresholds to defaults
|
|
216
|
-
*/
|
|
217
|
-
reset(): void {
|
|
218
|
-
this.currentThresholds = { ...DEFAULT_THRESHOLDS }
|
|
219
|
-
this.logger.info('Thresholds reset to defaults')
|
|
220
|
-
}
|
|
221
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Learning
|
|
3
|
+
* Learns optimal retrieval thresholds from feedback
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { MemoryFeedback, AdaptiveThresholds } from '../types'
|
|
8
|
+
import type { FeedbackStore } from './store'
|
|
9
|
+
import { calculateAllMetrics, calculateAverageRating, calculatePositiveRate } from './metrics'
|
|
10
|
+
|
|
11
|
+
/** Default thresholds before any learning */
|
|
12
|
+
const DEFAULT_THRESHOLDS: AdaptiveThresholds = {
|
|
13
|
+
denseMinSimilarity: 0.3,
|
|
14
|
+
denseWeight: 0.7,
|
|
15
|
+
sparseWeight: 0.3,
|
|
16
|
+
rrfK: 60,
|
|
17
|
+
feedbackCount: 0,
|
|
18
|
+
lastUpdated: new Date().toISOString()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Thresholds bounds to prevent extreme values */
|
|
22
|
+
const BOUNDS = {
|
|
23
|
+
denseMinSimilarity: { min: 0.1, max: 0.9 },
|
|
24
|
+
denseWeight: { min: 0.3, max: 0.9 },
|
|
25
|
+
sparseWeight: { min: 0.1, max: 0.7 },
|
|
26
|
+
rrfK: { min: 20, max: 100 }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AdaptiveLearnerConfig {
|
|
30
|
+
/** Minimum feedback count before adaptation */
|
|
31
|
+
minFeedbackForAdaptation: number
|
|
32
|
+
/** Learning rate for threshold adjustments */
|
|
33
|
+
learningRate: number
|
|
34
|
+
/** Target positive feedback rate */
|
|
35
|
+
targetPositiveRate: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const DEFAULT_CONFIG: AdaptiveLearnerConfig = {
|
|
39
|
+
minFeedbackForAdaptation: 10,
|
|
40
|
+
learningRate: 0.1,
|
|
41
|
+
targetPositiveRate: 0.7
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class AdaptiveLearner {
|
|
45
|
+
private logger: Logger
|
|
46
|
+
private feedbackStore: FeedbackStore
|
|
47
|
+
private config: AdaptiveLearnerConfig
|
|
48
|
+
private currentThresholds: AdaptiveThresholds
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
logger: Logger,
|
|
52
|
+
feedbackStore: FeedbackStore,
|
|
53
|
+
config: Partial<AdaptiveLearnerConfig> = {}
|
|
54
|
+
) {
|
|
55
|
+
this.logger = logger.child({ component: 'adaptive-learner' })
|
|
56
|
+
this.feedbackStore = feedbackStore
|
|
57
|
+
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
58
|
+
this.currentThresholds = { ...DEFAULT_THRESHOLDS }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get current adaptive thresholds
|
|
63
|
+
*/
|
|
64
|
+
getThresholds(): AdaptiveThresholds {
|
|
65
|
+
return { ...this.currentThresholds }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Update thresholds based on feedback
|
|
70
|
+
*/
|
|
71
|
+
async updateThresholds(): Promise<AdaptiveThresholds> {
|
|
72
|
+
const feedback = await this.feedbackStore.getRecentFeedback(1000)
|
|
73
|
+
|
|
74
|
+
if (feedback.length < this.config.minFeedbackForAdaptation) {
|
|
75
|
+
this.logger.debug({
|
|
76
|
+
feedbackCount: feedback.length,
|
|
77
|
+
required: this.config.minFeedbackForAdaptation
|
|
78
|
+
}, 'Not enough feedback for adaptation')
|
|
79
|
+
return this.currentThresholds
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.logger.info({ feedbackCount: feedback.length }, 'Adapting thresholds')
|
|
83
|
+
|
|
84
|
+
const metrics = calculateAllMetrics(feedback)
|
|
85
|
+
const positiveRate = calculatePositiveRate(feedback)
|
|
86
|
+
const avgRating = calculateAverageRating(feedback)
|
|
87
|
+
|
|
88
|
+
// Analyze feedback by provenance
|
|
89
|
+
const denseOnly = feedback.filter(f => this.inferProvenance(f) === 'dense')
|
|
90
|
+
const sparseOnly = feedback.filter(f => this.inferProvenance(f) === 'sparse')
|
|
91
|
+
const both = feedback.filter(f => this.inferProvenance(f) === 'both')
|
|
92
|
+
|
|
93
|
+
const densePositiveRate = denseOnly.length > 0 ? calculatePositiveRate(denseOnly) : 0.5
|
|
94
|
+
const sparsePositiveRate = sparseOnly.length > 0 ? calculatePositiveRate(sparseOnly) : 0.5
|
|
95
|
+
// Adjust dense/sparse weights based on performance
|
|
96
|
+
if (denseOnly.length >= 5 && sparseOnly.length >= 5) {
|
|
97
|
+
// If dense performs better, increase its weight
|
|
98
|
+
if (densePositiveRate > sparsePositiveRate + 0.1) {
|
|
99
|
+
this.currentThresholds.denseWeight = this.adjustValue(
|
|
100
|
+
this.currentThresholds.denseWeight,
|
|
101
|
+
this.config.learningRate,
|
|
102
|
+
'increase',
|
|
103
|
+
BOUNDS.denseWeight
|
|
104
|
+
)
|
|
105
|
+
this.currentThresholds.sparseWeight = this.adjustValue(
|
|
106
|
+
this.currentThresholds.sparseWeight,
|
|
107
|
+
this.config.learningRate,
|
|
108
|
+
'decrease',
|
|
109
|
+
BOUNDS.sparseWeight
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
// If sparse performs better, increase its weight
|
|
113
|
+
else if (sparsePositiveRate > densePositiveRate + 0.1) {
|
|
114
|
+
this.currentThresholds.sparseWeight = this.adjustValue(
|
|
115
|
+
this.currentThresholds.sparseWeight,
|
|
116
|
+
this.config.learningRate,
|
|
117
|
+
'increase',
|
|
118
|
+
BOUNDS.sparseWeight
|
|
119
|
+
)
|
|
120
|
+
this.currentThresholds.denseWeight = this.adjustValue(
|
|
121
|
+
this.currentThresholds.denseWeight,
|
|
122
|
+
this.config.learningRate,
|
|
123
|
+
'decrease',
|
|
124
|
+
BOUNDS.denseWeight
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Adjust minimum similarity based on positive rate
|
|
130
|
+
if (positiveRate < this.config.targetPositiveRate - 0.1) {
|
|
131
|
+
// Too many negative results - lower threshold to get more results
|
|
132
|
+
this.currentThresholds.denseMinSimilarity = this.adjustValue(
|
|
133
|
+
this.currentThresholds.denseMinSimilarity,
|
|
134
|
+
this.config.learningRate,
|
|
135
|
+
'decrease',
|
|
136
|
+
BOUNDS.denseMinSimilarity
|
|
137
|
+
)
|
|
138
|
+
} else if (positiveRate > this.config.targetPositiveRate + 0.1) {
|
|
139
|
+
// Results are good - can raise threshold to be more selective
|
|
140
|
+
this.currentThresholds.denseMinSimilarity = this.adjustValue(
|
|
141
|
+
this.currentThresholds.denseMinSimilarity,
|
|
142
|
+
this.config.learningRate / 2, // More conservative increase
|
|
143
|
+
'increase',
|
|
144
|
+
BOUNDS.denseMinSimilarity
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Adjust RRF K based on metrics
|
|
149
|
+
// Higher K reduces effect of rank, lower K emphasizes top ranks
|
|
150
|
+
if (metrics.mrr < 0.5 && both.length > 0) {
|
|
151
|
+
// If MRR is low and we have combined results, try lower K
|
|
152
|
+
this.currentThresholds.rrfK = this.adjustValue(
|
|
153
|
+
this.currentThresholds.rrfK,
|
|
154
|
+
5, // Adjust by fixed amount
|
|
155
|
+
'decrease',
|
|
156
|
+
BOUNDS.rrfK
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.currentThresholds.feedbackCount = feedback.length
|
|
161
|
+
this.currentThresholds.lastUpdated = new Date().toISOString()
|
|
162
|
+
|
|
163
|
+
this.logger.info({
|
|
164
|
+
thresholds: this.currentThresholds,
|
|
165
|
+
metrics,
|
|
166
|
+
positiveRate,
|
|
167
|
+
avgRating
|
|
168
|
+
}, 'Thresholds adapted')
|
|
169
|
+
|
|
170
|
+
return this.currentThresholds
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Adjust a value with bounds checking
|
|
175
|
+
*/
|
|
176
|
+
private adjustValue(
|
|
177
|
+
current: number,
|
|
178
|
+
amount: number,
|
|
179
|
+
direction: 'increase' | 'decrease',
|
|
180
|
+
bounds: { min: number; max: number }
|
|
181
|
+
): number {
|
|
182
|
+
const delta = direction === 'increase' ? amount : -amount
|
|
183
|
+
const newValue = current + delta
|
|
184
|
+
return Math.max(bounds.min, Math.min(bounds.max, newValue))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Infer provenance from feedback (heuristic)
|
|
189
|
+
* In a real implementation, this would be stored in feedback
|
|
190
|
+
*/
|
|
191
|
+
private inferProvenance(_feedback: MemoryFeedback): 'dense' | 'sparse' | 'both' {
|
|
192
|
+
// This is a heuristic - in production, provenance should be stored with feedback
|
|
193
|
+
// For now, assume most results are from combined search
|
|
194
|
+
return 'both'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get adaptation status
|
|
199
|
+
*/
|
|
200
|
+
getStatus(): {
|
|
201
|
+
feedbackCount: number
|
|
202
|
+
canAdapt: boolean
|
|
203
|
+
currentThresholds: AdaptiveThresholds
|
|
204
|
+
lastUpdated: string
|
|
205
|
+
} {
|
|
206
|
+
return {
|
|
207
|
+
feedbackCount: this.currentThresholds.feedbackCount,
|
|
208
|
+
canAdapt: this.currentThresholds.feedbackCount >= this.config.minFeedbackForAdaptation,
|
|
209
|
+
currentThresholds: this.currentThresholds,
|
|
210
|
+
lastUpdated: this.currentThresholds.lastUpdated
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Reset thresholds to defaults
|
|
216
|
+
*/
|
|
217
|
+
reset(): void {
|
|
218
|
+
this.currentThresholds = { ...DEFAULT_THRESHOLDS }
|
|
219
|
+
this.logger.info('Thresholds reset to defaults')
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feedback Module Exports
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { FeedbackStore } from './store'
|
|
6
|
-
export {
|
|
7
|
-
FeedbackMetrics,
|
|
8
|
-
calculateMRR,
|
|
9
|
-
calculatePrecisionAtK,
|
|
10
|
-
calculateNDCG,
|
|
11
|
-
calculateAllMetrics,
|
|
12
|
-
calculateAverageRating,
|
|
13
|
-
calculatePositiveRate,
|
|
14
|
-
calculateUsageRate
|
|
15
|
-
} from './metrics'
|
|
16
|
-
export { AdaptiveLearner, type AdaptiveLearnerConfig } from './adaptive'
|
|
1
|
+
/**
|
|
2
|
+
* Feedback Module Exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { FeedbackStore } from './store'
|
|
6
|
+
export {
|
|
7
|
+
FeedbackMetrics,
|
|
8
|
+
calculateMRR,
|
|
9
|
+
calculatePrecisionAtK,
|
|
10
|
+
calculateNDCG,
|
|
11
|
+
calculateAllMetrics,
|
|
12
|
+
calculateAverageRating,
|
|
13
|
+
calculatePositiveRate,
|
|
14
|
+
calculateUsageRate
|
|
15
|
+
} from './metrics'
|
|
16
|
+
export { AdaptiveLearner, type AdaptiveLearnerConfig } from './adaptive'
|