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,283 +1,283 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feedback Store
|
|
3
|
-
* Stores and retrieves memory feedback for adaptive learning
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import type { CollectionManager } from '@/memory/chroma/collection-manager'
|
|
8
|
-
import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
|
|
9
|
-
import type { MemoryFeedback, RateMemoryInput } from '../types'
|
|
10
|
-
|
|
11
|
-
export class FeedbackStore {
|
|
12
|
-
private logger: Logger
|
|
13
|
-
private collections: CollectionManager
|
|
14
|
-
private embeddings: EmbeddingProvider
|
|
15
|
-
|
|
16
|
-
constructor(
|
|
17
|
-
logger: Logger,
|
|
18
|
-
collections: CollectionManager,
|
|
19
|
-
embeddings: EmbeddingProvider
|
|
20
|
-
) {
|
|
21
|
-
this.logger = logger.child({ component: 'feedback-store' })
|
|
22
|
-
this.collections = collections
|
|
23
|
-
this.embeddings = embeddings
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Store feedback for a memory retrieval
|
|
28
|
-
*/
|
|
29
|
-
async storeFeedback(input: RateMemoryInput): Promise<string> {
|
|
30
|
-
const feedbackId = `feedback_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
31
|
-
const queryId = `query_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
32
|
-
const timestamp = new Date().toISOString()
|
|
33
|
-
|
|
34
|
-
this.logger.debug({
|
|
35
|
-
feedbackId,
|
|
36
|
-
memoryId: input.memory_id,
|
|
37
|
-
rating: input.rating
|
|
38
|
-
}, 'Storing feedback')
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const collection = await this.collections.getFeedback()
|
|
42
|
-
|
|
43
|
-
// Generate embedding for the query to enable similarity search
|
|
44
|
-
const embedding = await this.embeddings.generate(input.query)
|
|
45
|
-
|
|
46
|
-
// Determine collection type from memory_id prefix
|
|
47
|
-
const collectionType = this.inferCollectionType(input.memory_id)
|
|
48
|
-
|
|
49
|
-
await collection.add({
|
|
50
|
-
ids: [feedbackId],
|
|
51
|
-
embeddings: [embedding],
|
|
52
|
-
documents: [input.query],
|
|
53
|
-
metadatas: [{
|
|
54
|
-
memory_id: input.memory_id,
|
|
55
|
-
query_id: queryId,
|
|
56
|
-
query: input.query,
|
|
57
|
-
rating: input.rating,
|
|
58
|
-
was_used: input.was_used ?? true,
|
|
59
|
-
feedback_text: input.feedback_text || '',
|
|
60
|
-
project: input.project_name || '',
|
|
61
|
-
created_at: timestamp,
|
|
62
|
-
collection_type: collectionType
|
|
63
|
-
}]
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
this.logger.info({
|
|
67
|
-
feedbackId,
|
|
68
|
-
memoryId: input.memory_id,
|
|
69
|
-
rating: input.rating
|
|
70
|
-
}, 'Feedback stored successfully')
|
|
71
|
-
|
|
72
|
-
return feedbackId
|
|
73
|
-
|
|
74
|
-
} catch (error) {
|
|
75
|
-
this.logger.error({ error, input }, 'Failed to store feedback')
|
|
76
|
-
throw error
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Get feedback for a specific memory
|
|
82
|
-
*/
|
|
83
|
-
async getFeedbackForMemory(memoryId: string): Promise<MemoryFeedback[]> {
|
|
84
|
-
try {
|
|
85
|
-
const collection = await this.collections.getFeedback()
|
|
86
|
-
|
|
87
|
-
const results = await collection.get({
|
|
88
|
-
where: { memory_id: { $eq: memoryId } },
|
|
89
|
-
include: ['documents', 'metadatas']
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
if (!results.ids.length) {
|
|
93
|
-
return []
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return results.ids.map((id, index) => ({
|
|
97
|
-
id,
|
|
98
|
-
memoryId: results.metadatas?.[index]?.memory_id as string || '',
|
|
99
|
-
queryId: results.metadatas?.[index]?.query_id as string || '',
|
|
100
|
-
query: results.documents?.[index] || '',
|
|
101
|
-
rating: results.metadatas?.[index]?.rating as 1 | 2 | 3 | 4 | 5,
|
|
102
|
-
wasUsed: results.metadatas?.[index]?.was_used as boolean ?? true,
|
|
103
|
-
feedbackText: results.metadatas?.[index]?.feedback_text as string | undefined,
|
|
104
|
-
project: results.metadatas?.[index]?.project as string | undefined,
|
|
105
|
-
timestamp: results.metadatas?.[index]?.created_at as string || '',
|
|
106
|
-
collectionType: results.metadatas?.[index]?.collection_type as 'decisions' | 'memories' | 'patterns' | 'corrections'
|
|
107
|
-
}))
|
|
108
|
-
|
|
109
|
-
} catch (error) {
|
|
110
|
-
this.logger.error({ error, memoryId }, 'Failed to get feedback for memory')
|
|
111
|
-
return []
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get all feedback for a project
|
|
117
|
-
*/
|
|
118
|
-
async getFeedbackForProject(project: string, limit: number = 100): Promise<MemoryFeedback[]> {
|
|
119
|
-
try {
|
|
120
|
-
const collection = await this.collections.getFeedback()
|
|
121
|
-
|
|
122
|
-
const results = await collection.get({
|
|
123
|
-
where: { project: { $eq: project } },
|
|
124
|
-
include: ['documents', 'metadatas'],
|
|
125
|
-
limit
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
if (!results.ids.length) {
|
|
129
|
-
return []
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return results.ids.map((id, index) => ({
|
|
133
|
-
id,
|
|
134
|
-
memoryId: results.metadatas?.[index]?.memory_id as string || '',
|
|
135
|
-
queryId: results.metadatas?.[index]?.query_id as string || '',
|
|
136
|
-
query: results.documents?.[index] || '',
|
|
137
|
-
rating: results.metadatas?.[index]?.rating as 1 | 2 | 3 | 4 | 5,
|
|
138
|
-
wasUsed: results.metadatas?.[index]?.was_used as boolean ?? true,
|
|
139
|
-
feedbackText: results.metadatas?.[index]?.feedback_text as string | undefined,
|
|
140
|
-
project: results.metadatas?.[index]?.project as string | undefined,
|
|
141
|
-
timestamp: results.metadatas?.[index]?.created_at as string || '',
|
|
142
|
-
collectionType: results.metadatas?.[index]?.collection_type as 'decisions' | 'memories' | 'patterns' | 'corrections'
|
|
143
|
-
}))
|
|
144
|
-
|
|
145
|
-
} catch (error) {
|
|
146
|
-
this.logger.error({ error, project }, 'Failed to get feedback for project')
|
|
147
|
-
return []
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Get recent feedback (for adaptive learning)
|
|
153
|
-
*/
|
|
154
|
-
async getRecentFeedback(limit: number = 100): Promise<MemoryFeedback[]> {
|
|
155
|
-
try {
|
|
156
|
-
const collection = await this.collections.getFeedback()
|
|
157
|
-
|
|
158
|
-
// Get all feedback - ChromaDB doesn't support ordering, so we'll sort in memory
|
|
159
|
-
const results = await collection.get({
|
|
160
|
-
include: ['documents', 'metadatas'],
|
|
161
|
-
limit: limit * 2 // Get more to allow for sorting
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
if (!results.ids.length) {
|
|
165
|
-
return []
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const feedback: MemoryFeedback[] = results.ids.map((id, index) => ({
|
|
169
|
-
id,
|
|
170
|
-
memoryId: results.metadatas?.[index]?.memory_id as string || '',
|
|
171
|
-
queryId: results.metadatas?.[index]?.query_id as string || '',
|
|
172
|
-
query: results.documents?.[index] || '',
|
|
173
|
-
rating: results.metadatas?.[index]?.rating as 1 | 2 | 3 | 4 | 5,
|
|
174
|
-
wasUsed: results.metadatas?.[index]?.was_used as boolean ?? true,
|
|
175
|
-
feedbackText: results.metadatas?.[index]?.feedback_text as string | undefined,
|
|
176
|
-
project: results.metadatas?.[index]?.project as string | undefined,
|
|
177
|
-
timestamp: results.metadatas?.[index]?.created_at as string || '',
|
|
178
|
-
collectionType: results.metadatas?.[index]?.collection_type as 'decisions' | 'memories' | 'patterns' | 'corrections'
|
|
179
|
-
}))
|
|
180
|
-
|
|
181
|
-
// Sort by timestamp descending and take limit
|
|
182
|
-
return feedback
|
|
183
|
-
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
|
|
184
|
-
.slice(0, limit)
|
|
185
|
-
|
|
186
|
-
} catch (error) {
|
|
187
|
-
this.logger.error({ error }, 'Failed to get recent feedback')
|
|
188
|
-
return []
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Get feedback statistics
|
|
194
|
-
*/
|
|
195
|
-
async getStats(): Promise<{
|
|
196
|
-
totalFeedback: number
|
|
197
|
-
avgRating: number
|
|
198
|
-
positiveRate: number
|
|
199
|
-
usageRate: number
|
|
200
|
-
byCollection: Record<string, { count: number; avgRating: number }>
|
|
201
|
-
}> {
|
|
202
|
-
try {
|
|
203
|
-
const collection = await this.collections.getFeedback()
|
|
204
|
-
const totalCount = await collection.count()
|
|
205
|
-
|
|
206
|
-
if (totalCount === 0) {
|
|
207
|
-
return {
|
|
208
|
-
totalFeedback: 0,
|
|
209
|
-
avgRating: 0,
|
|
210
|
-
positiveRate: 0,
|
|
211
|
-
usageRate: 0,
|
|
212
|
-
byCollection: {}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const results = await collection.get({
|
|
217
|
-
include: ['metadatas'],
|
|
218
|
-
limit: 1000 // Sample for stats
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
let totalRating = 0
|
|
222
|
-
let positiveCount = 0
|
|
223
|
-
let usedCount = 0
|
|
224
|
-
const byCollection: Record<string, { count: number; totalRating: number }> = {}
|
|
225
|
-
|
|
226
|
-
for (const metadata of results.metadatas || []) {
|
|
227
|
-
const rating = metadata?.rating as number
|
|
228
|
-
const wasUsed = metadata?.was_used as boolean
|
|
229
|
-
const collectionType = (metadata?.collection_type as string) || 'unknown'
|
|
230
|
-
|
|
231
|
-
totalRating += rating
|
|
232
|
-
if (rating >= 4) positiveCount++
|
|
233
|
-
if (wasUsed) usedCount++
|
|
234
|
-
|
|
235
|
-
if (!byCollection[collectionType]) {
|
|
236
|
-
byCollection[collectionType] = { count: 0, totalRating: 0 }
|
|
237
|
-
}
|
|
238
|
-
byCollection[collectionType].count++
|
|
239
|
-
byCollection[collectionType].totalRating += rating
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const count = results.ids.length
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
totalFeedback: totalCount,
|
|
246
|
-
avgRating: count > 0 ? totalRating / count : 0,
|
|
247
|
-
positiveRate: count > 0 ? positiveCount / count : 0,
|
|
248
|
-
usageRate: count > 0 ? usedCount / count : 0,
|
|
249
|
-
byCollection: Object.fromEntries(
|
|
250
|
-
Object.entries(byCollection).map(([key, value]) => [
|
|
251
|
-
key,
|
|
252
|
-
{
|
|
253
|
-
count: value.count,
|
|
254
|
-
avgRating: value.count > 0 ? value.totalRating / value.count : 0
|
|
255
|
-
}
|
|
256
|
-
])
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
} catch (error) {
|
|
261
|
-
this.logger.error({ error }, 'Failed to get feedback stats')
|
|
262
|
-
return {
|
|
263
|
-
totalFeedback: 0,
|
|
264
|
-
avgRating: 0,
|
|
265
|
-
positiveRate: 0,
|
|
266
|
-
usageRate: 0,
|
|
267
|
-
byCollection: {}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Infer collection type from memory ID prefix
|
|
274
|
-
*/
|
|
275
|
-
private inferCollectionType(memoryId: string): 'decisions' | 'memories' | 'patterns' | 'corrections' {
|
|
276
|
-
if (memoryId.startsWith('dec_')) return 'decisions'
|
|
277
|
-
if (memoryId.startsWith('mem_')) return 'memories'
|
|
278
|
-
if (memoryId.startsWith('pat_')) return 'patterns'
|
|
279
|
-
if (memoryId.startsWith('cor_')) return 'corrections'
|
|
280
|
-
// Default to decisions if unknown
|
|
281
|
-
return 'decisions'
|
|
282
|
-
}
|
|
283
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Feedback Store
|
|
3
|
+
* Stores and retrieves memory feedback for adaptive learning
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { CollectionManager } from '@/memory/chroma/collection-manager'
|
|
8
|
+
import type { EmbeddingProvider } from '@/memory/chroma/embeddings'
|
|
9
|
+
import type { MemoryFeedback, RateMemoryInput } from '../types'
|
|
10
|
+
|
|
11
|
+
export class FeedbackStore {
|
|
12
|
+
private logger: Logger
|
|
13
|
+
private collections: CollectionManager
|
|
14
|
+
private embeddings: EmbeddingProvider
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
logger: Logger,
|
|
18
|
+
collections: CollectionManager,
|
|
19
|
+
embeddings: EmbeddingProvider
|
|
20
|
+
) {
|
|
21
|
+
this.logger = logger.child({ component: 'feedback-store' })
|
|
22
|
+
this.collections = collections
|
|
23
|
+
this.embeddings = embeddings
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Store feedback for a memory retrieval
|
|
28
|
+
*/
|
|
29
|
+
async storeFeedback(input: RateMemoryInput): Promise<string> {
|
|
30
|
+
const feedbackId = `feedback_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
31
|
+
const queryId = `query_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
32
|
+
const timestamp = new Date().toISOString()
|
|
33
|
+
|
|
34
|
+
this.logger.debug({
|
|
35
|
+
feedbackId,
|
|
36
|
+
memoryId: input.memory_id,
|
|
37
|
+
rating: input.rating
|
|
38
|
+
}, 'Storing feedback')
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const collection = await this.collections.getFeedback()
|
|
42
|
+
|
|
43
|
+
// Generate embedding for the query to enable similarity search
|
|
44
|
+
const embedding = await this.embeddings.generate(input.query)
|
|
45
|
+
|
|
46
|
+
// Determine collection type from memory_id prefix
|
|
47
|
+
const collectionType = this.inferCollectionType(input.memory_id)
|
|
48
|
+
|
|
49
|
+
await collection.add({
|
|
50
|
+
ids: [feedbackId],
|
|
51
|
+
embeddings: [embedding],
|
|
52
|
+
documents: [input.query],
|
|
53
|
+
metadatas: [{
|
|
54
|
+
memory_id: input.memory_id,
|
|
55
|
+
query_id: queryId,
|
|
56
|
+
query: input.query,
|
|
57
|
+
rating: input.rating,
|
|
58
|
+
was_used: input.was_used ?? true,
|
|
59
|
+
feedback_text: input.feedback_text || '',
|
|
60
|
+
project: input.project_name || '',
|
|
61
|
+
created_at: timestamp,
|
|
62
|
+
collection_type: collectionType
|
|
63
|
+
}]
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
this.logger.info({
|
|
67
|
+
feedbackId,
|
|
68
|
+
memoryId: input.memory_id,
|
|
69
|
+
rating: input.rating
|
|
70
|
+
}, 'Feedback stored successfully')
|
|
71
|
+
|
|
72
|
+
return feedbackId
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
this.logger.error({ error, input }, 'Failed to store feedback')
|
|
76
|
+
throw error
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get feedback for a specific memory
|
|
82
|
+
*/
|
|
83
|
+
async getFeedbackForMemory(memoryId: string): Promise<MemoryFeedback[]> {
|
|
84
|
+
try {
|
|
85
|
+
const collection = await this.collections.getFeedback()
|
|
86
|
+
|
|
87
|
+
const results = await collection.get({
|
|
88
|
+
where: { memory_id: { $eq: memoryId } },
|
|
89
|
+
include: ['documents', 'metadatas']
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (!results.ids.length) {
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return results.ids.map((id, index) => ({
|
|
97
|
+
id,
|
|
98
|
+
memoryId: results.metadatas?.[index]?.memory_id as string || '',
|
|
99
|
+
queryId: results.metadatas?.[index]?.query_id as string || '',
|
|
100
|
+
query: results.documents?.[index] || '',
|
|
101
|
+
rating: results.metadatas?.[index]?.rating as 1 | 2 | 3 | 4 | 5,
|
|
102
|
+
wasUsed: results.metadatas?.[index]?.was_used as boolean ?? true,
|
|
103
|
+
feedbackText: results.metadatas?.[index]?.feedback_text as string | undefined,
|
|
104
|
+
project: results.metadatas?.[index]?.project as string | undefined,
|
|
105
|
+
timestamp: results.metadatas?.[index]?.created_at as string || '',
|
|
106
|
+
collectionType: results.metadatas?.[index]?.collection_type as 'decisions' | 'memories' | 'patterns' | 'corrections'
|
|
107
|
+
}))
|
|
108
|
+
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this.logger.error({ error, memoryId }, 'Failed to get feedback for memory')
|
|
111
|
+
return []
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get all feedback for a project
|
|
117
|
+
*/
|
|
118
|
+
async getFeedbackForProject(project: string, limit: number = 100): Promise<MemoryFeedback[]> {
|
|
119
|
+
try {
|
|
120
|
+
const collection = await this.collections.getFeedback()
|
|
121
|
+
|
|
122
|
+
const results = await collection.get({
|
|
123
|
+
where: { project: { $eq: project } },
|
|
124
|
+
include: ['documents', 'metadatas'],
|
|
125
|
+
limit
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
if (!results.ids.length) {
|
|
129
|
+
return []
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return results.ids.map((id, index) => ({
|
|
133
|
+
id,
|
|
134
|
+
memoryId: results.metadatas?.[index]?.memory_id as string || '',
|
|
135
|
+
queryId: results.metadatas?.[index]?.query_id as string || '',
|
|
136
|
+
query: results.documents?.[index] || '',
|
|
137
|
+
rating: results.metadatas?.[index]?.rating as 1 | 2 | 3 | 4 | 5,
|
|
138
|
+
wasUsed: results.metadatas?.[index]?.was_used as boolean ?? true,
|
|
139
|
+
feedbackText: results.metadatas?.[index]?.feedback_text as string | undefined,
|
|
140
|
+
project: results.metadatas?.[index]?.project as string | undefined,
|
|
141
|
+
timestamp: results.metadatas?.[index]?.created_at as string || '',
|
|
142
|
+
collectionType: results.metadatas?.[index]?.collection_type as 'decisions' | 'memories' | 'patterns' | 'corrections'
|
|
143
|
+
}))
|
|
144
|
+
|
|
145
|
+
} catch (error) {
|
|
146
|
+
this.logger.error({ error, project }, 'Failed to get feedback for project')
|
|
147
|
+
return []
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get recent feedback (for adaptive learning)
|
|
153
|
+
*/
|
|
154
|
+
async getRecentFeedback(limit: number = 100): Promise<MemoryFeedback[]> {
|
|
155
|
+
try {
|
|
156
|
+
const collection = await this.collections.getFeedback()
|
|
157
|
+
|
|
158
|
+
// Get all feedback - ChromaDB doesn't support ordering, so we'll sort in memory
|
|
159
|
+
const results = await collection.get({
|
|
160
|
+
include: ['documents', 'metadatas'],
|
|
161
|
+
limit: limit * 2 // Get more to allow for sorting
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
if (!results.ids.length) {
|
|
165
|
+
return []
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const feedback: MemoryFeedback[] = results.ids.map((id, index) => ({
|
|
169
|
+
id,
|
|
170
|
+
memoryId: results.metadatas?.[index]?.memory_id as string || '',
|
|
171
|
+
queryId: results.metadatas?.[index]?.query_id as string || '',
|
|
172
|
+
query: results.documents?.[index] || '',
|
|
173
|
+
rating: results.metadatas?.[index]?.rating as 1 | 2 | 3 | 4 | 5,
|
|
174
|
+
wasUsed: results.metadatas?.[index]?.was_used as boolean ?? true,
|
|
175
|
+
feedbackText: results.metadatas?.[index]?.feedback_text as string | undefined,
|
|
176
|
+
project: results.metadatas?.[index]?.project as string | undefined,
|
|
177
|
+
timestamp: results.metadatas?.[index]?.created_at as string || '',
|
|
178
|
+
collectionType: results.metadatas?.[index]?.collection_type as 'decisions' | 'memories' | 'patterns' | 'corrections'
|
|
179
|
+
}))
|
|
180
|
+
|
|
181
|
+
// Sort by timestamp descending and take limit
|
|
182
|
+
return feedback
|
|
183
|
+
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
|
|
184
|
+
.slice(0, limit)
|
|
185
|
+
|
|
186
|
+
} catch (error) {
|
|
187
|
+
this.logger.error({ error }, 'Failed to get recent feedback')
|
|
188
|
+
return []
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get feedback statistics
|
|
194
|
+
*/
|
|
195
|
+
async getStats(): Promise<{
|
|
196
|
+
totalFeedback: number
|
|
197
|
+
avgRating: number
|
|
198
|
+
positiveRate: number
|
|
199
|
+
usageRate: number
|
|
200
|
+
byCollection: Record<string, { count: number; avgRating: number }>
|
|
201
|
+
}> {
|
|
202
|
+
try {
|
|
203
|
+
const collection = await this.collections.getFeedback()
|
|
204
|
+
const totalCount = await collection.count()
|
|
205
|
+
|
|
206
|
+
if (totalCount === 0) {
|
|
207
|
+
return {
|
|
208
|
+
totalFeedback: 0,
|
|
209
|
+
avgRating: 0,
|
|
210
|
+
positiveRate: 0,
|
|
211
|
+
usageRate: 0,
|
|
212
|
+
byCollection: {}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const results = await collection.get({
|
|
217
|
+
include: ['metadatas'],
|
|
218
|
+
limit: 1000 // Sample for stats
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
let totalRating = 0
|
|
222
|
+
let positiveCount = 0
|
|
223
|
+
let usedCount = 0
|
|
224
|
+
const byCollection: Record<string, { count: number; totalRating: number }> = {}
|
|
225
|
+
|
|
226
|
+
for (const metadata of results.metadatas || []) {
|
|
227
|
+
const rating = metadata?.rating as number
|
|
228
|
+
const wasUsed = metadata?.was_used as boolean
|
|
229
|
+
const collectionType = (metadata?.collection_type as string) || 'unknown'
|
|
230
|
+
|
|
231
|
+
totalRating += rating
|
|
232
|
+
if (rating >= 4) positiveCount++
|
|
233
|
+
if (wasUsed) usedCount++
|
|
234
|
+
|
|
235
|
+
if (!byCollection[collectionType]) {
|
|
236
|
+
byCollection[collectionType] = { count: 0, totalRating: 0 }
|
|
237
|
+
}
|
|
238
|
+
byCollection[collectionType].count++
|
|
239
|
+
byCollection[collectionType].totalRating += rating
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const count = results.ids.length
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
totalFeedback: totalCount,
|
|
246
|
+
avgRating: count > 0 ? totalRating / count : 0,
|
|
247
|
+
positiveRate: count > 0 ? positiveCount / count : 0,
|
|
248
|
+
usageRate: count > 0 ? usedCount / count : 0,
|
|
249
|
+
byCollection: Object.fromEntries(
|
|
250
|
+
Object.entries(byCollection).map(([key, value]) => [
|
|
251
|
+
key,
|
|
252
|
+
{
|
|
253
|
+
count: value.count,
|
|
254
|
+
avgRating: value.count > 0 ? value.totalRating / value.count : 0
|
|
255
|
+
}
|
|
256
|
+
])
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
} catch (error) {
|
|
261
|
+
this.logger.error({ error }, 'Failed to get feedback stats')
|
|
262
|
+
return {
|
|
263
|
+
totalFeedback: 0,
|
|
264
|
+
avgRating: 0,
|
|
265
|
+
positiveRate: 0,
|
|
266
|
+
usageRate: 0,
|
|
267
|
+
byCollection: {}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Infer collection type from memory ID prefix
|
|
274
|
+
*/
|
|
275
|
+
private inferCollectionType(memoryId: string): 'decisions' | 'memories' | 'patterns' | 'corrections' {
|
|
276
|
+
if (memoryId.startsWith('dec_')) return 'decisions'
|
|
277
|
+
if (memoryId.startsWith('mem_')) return 'memories'
|
|
278
|
+
if (memoryId.startsWith('pat_')) return 'patterns'
|
|
279
|
+
if (memoryId.startsWith('cor_')) return 'corrections'
|
|
280
|
+
// Default to decisions if unknown
|
|
281
|
+
return 'decisions'
|
|
282
|
+
}
|
|
283
|
+
}
|