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,188 +1,189 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-Encoder Reranker
|
|
3
|
-
* Uses a cross-encoder model to rerank search results for improved relevance
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import { loadRerankerModel, isModelLoaded, unloadModel } from './model'
|
|
8
|
-
import type { HybridSearchResult } from '../types'
|
|
9
|
-
|
|
10
|
-
export interface RerankerConfig {
|
|
11
|
-
/** Model to use for reranking */
|
|
12
|
-
model: string
|
|
13
|
-
/** Number of top results to rerank */
|
|
14
|
-
topK: number
|
|
15
|
-
/** Batch size for inference */
|
|
16
|
-
batchSize?: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class Reranker {
|
|
20
|
-
private logger: Logger
|
|
21
|
-
private config: RerankerConfig
|
|
22
|
-
private model:
|
|
23
|
-
|
|
24
|
-
constructor(logger: Logger, config: Partial<RerankerConfig> = {}) {
|
|
25
|
-
this.logger = logger.child({ component: 'reranker' })
|
|
26
|
-
this.config = {
|
|
27
|
-
model: config.model || 'cross-encoder/ms-marco-MiniLM-L-6-v2',
|
|
28
|
-
topK: config.topK || 10,
|
|
29
|
-
batchSize: config.batchSize || 8
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Initialize the reranker (loads model)
|
|
35
|
-
*/
|
|
36
|
-
async initialize(): Promise<void> {
|
|
37
|
-
if (this.model) {
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.logger.info('Initializing reranker')
|
|
42
|
-
const { model } = await loadRerankerModel(this.logger, this.config.model)
|
|
43
|
-
this.model = model
|
|
44
|
-
this.logger.info('Reranker initialized')
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Rerank search results using cross-encoder
|
|
49
|
-
*
|
|
50
|
-
* @param query - The search query
|
|
51
|
-
* @param results - Results to rerank
|
|
52
|
-
* @returns Reranked results with updated scores
|
|
53
|
-
*/
|
|
54
|
-
async rerank(
|
|
55
|
-
query: string,
|
|
56
|
-
results: HybridSearchResult[]
|
|
57
|
-
): Promise<HybridSearchResult[]> {
|
|
58
|
-
if (!this.model) {
|
|
59
|
-
await this.initialize()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (results.length === 0) {
|
|
63
|
-
return []
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Only rerank top K results
|
|
67
|
-
const toRerank = results.slice(0, this.config.topK)
|
|
68
|
-
const remainder = results.slice(this.config.topK)
|
|
69
|
-
|
|
70
|
-
this.logger.debug({
|
|
71
|
-
query: query.slice(0, 50),
|
|
72
|
-
resultsCount: toRerank.length
|
|
73
|
-
}, 'Reranking results')
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
// Create query-document pairs for cross-encoder
|
|
77
|
-
const pairs = toRerank.map(result => ({
|
|
78
|
-
text: query,
|
|
79
|
-
text_pair: result.content
|
|
80
|
-
}))
|
|
81
|
-
|
|
82
|
-
// Process in batches
|
|
83
|
-
const scores: number[] = []
|
|
84
|
-
for (let i = 0; i < pairs.length; i += this.config.batchSize!) {
|
|
85
|
-
const batch = pairs.slice(i, i + this.config.batchSize!)
|
|
86
|
-
|
|
87
|
-
// Run inference
|
|
88
|
-
const batchScores = await Promise.all(
|
|
89
|
-
batch.map(async (pair) => {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (scores.length ===
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Encoder Reranker
|
|
3
|
+
* Uses a cross-encoder model to rerank search results for improved relevance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import { loadRerankerModel, isModelLoaded, unloadModel } from './model'
|
|
8
|
+
import type { HybridSearchResult } from '../types'
|
|
9
|
+
|
|
10
|
+
export interface RerankerConfig {
|
|
11
|
+
/** Model to use for reranking */
|
|
12
|
+
model: string
|
|
13
|
+
/** Number of top results to rerank */
|
|
14
|
+
topK: number
|
|
15
|
+
/** Batch size for inference */
|
|
16
|
+
batchSize?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class Reranker {
|
|
20
|
+
private logger: Logger
|
|
21
|
+
private config: RerankerConfig
|
|
22
|
+
private model: unknown = null
|
|
23
|
+
|
|
24
|
+
constructor(logger: Logger, config: Partial<RerankerConfig> = {}) {
|
|
25
|
+
this.logger = logger.child({ component: 'reranker' })
|
|
26
|
+
this.config = {
|
|
27
|
+
model: config.model || 'cross-encoder/ms-marco-MiniLM-L-6-v2',
|
|
28
|
+
topK: config.topK || 10,
|
|
29
|
+
batchSize: config.batchSize || 8
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the reranker (loads model)
|
|
35
|
+
*/
|
|
36
|
+
async initialize(): Promise<void> {
|
|
37
|
+
if (this.model) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.logger.info('Initializing reranker')
|
|
42
|
+
const { model } = await loadRerankerModel(this.logger, this.config.model)
|
|
43
|
+
this.model = model
|
|
44
|
+
this.logger.info('Reranker initialized')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Rerank search results using cross-encoder
|
|
49
|
+
*
|
|
50
|
+
* @param query - The search query
|
|
51
|
+
* @param results - Results to rerank
|
|
52
|
+
* @returns Reranked results with updated scores
|
|
53
|
+
*/
|
|
54
|
+
async rerank(
|
|
55
|
+
query: string,
|
|
56
|
+
results: HybridSearchResult[]
|
|
57
|
+
): Promise<HybridSearchResult[]> {
|
|
58
|
+
if (!this.model) {
|
|
59
|
+
await this.initialize()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (results.length === 0) {
|
|
63
|
+
return []
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Only rerank top K results
|
|
67
|
+
const toRerank = results.slice(0, this.config.topK)
|
|
68
|
+
const remainder = results.slice(this.config.topK)
|
|
69
|
+
|
|
70
|
+
this.logger.debug({
|
|
71
|
+
query: query.slice(0, 50),
|
|
72
|
+
resultsCount: toRerank.length
|
|
73
|
+
}, 'Reranking results')
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
// Create query-document pairs for cross-encoder
|
|
77
|
+
const pairs = toRerank.map(result => ({
|
|
78
|
+
text: query,
|
|
79
|
+
text_pair: result.content
|
|
80
|
+
}))
|
|
81
|
+
|
|
82
|
+
// Process in batches
|
|
83
|
+
const scores: number[] = []
|
|
84
|
+
for (let i = 0; i < pairs.length; i += this.config.batchSize!) {
|
|
85
|
+
const batch = pairs.slice(i, i + this.config.batchSize!)
|
|
86
|
+
|
|
87
|
+
// Run inference
|
|
88
|
+
const batchScores = await Promise.all(
|
|
89
|
+
batch.map(async (pair) => {
|
|
90
|
+
const modelFn = this.model as (input: string) => Promise<unknown>
|
|
91
|
+
const output = await modelFn(`${pair.text} [SEP] ${pair.text_pair}`)
|
|
92
|
+
// Cross-encoder outputs score indicating relevance
|
|
93
|
+
// Higher score = more relevant
|
|
94
|
+
return this.extractScore(output)
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
scores.push(...batchScores)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Normalize scores to 0-1 range
|
|
102
|
+
const normalizedScores = this.normalizeScores(scores)
|
|
103
|
+
|
|
104
|
+
// Update results with reranked scores
|
|
105
|
+
const rerankedResults = toRerank.map((result, index) => ({
|
|
106
|
+
...result,
|
|
107
|
+
scores: {
|
|
108
|
+
...result.scores,
|
|
109
|
+
reranked: normalizedScores[index],
|
|
110
|
+
final: normalizedScores[index] // Reranked score becomes final
|
|
111
|
+
}
|
|
112
|
+
}))
|
|
113
|
+
|
|
114
|
+
// Sort by reranked score
|
|
115
|
+
rerankedResults.sort((a, b) => (b.scores.final ?? 0) - (a.scores.final ?? 0))
|
|
116
|
+
|
|
117
|
+
// Append non-reranked results
|
|
118
|
+
return [...rerankedResults, ...remainder] as HybridSearchResult[]
|
|
119
|
+
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.logger.error({ error }, 'Reranking failed, returning original order')
|
|
122
|
+
return results
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extract score from model output
|
|
128
|
+
*/
|
|
129
|
+
private extractScore(output: unknown): number {
|
|
130
|
+
// Handle different output formats from transformers.js
|
|
131
|
+
if (Array.isArray(output) && output.length > 0) {
|
|
132
|
+
// Classification output format
|
|
133
|
+
if (typeof output[0] === 'object' && output[0] !== null && 'score' in output[0]) {
|
|
134
|
+
return (output[0] as { score: number }).score
|
|
135
|
+
}
|
|
136
|
+
// Raw logits format
|
|
137
|
+
if (typeof output[0] === 'number') {
|
|
138
|
+
return output[0]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Single score
|
|
142
|
+
if (typeof output === 'number') {
|
|
143
|
+
return output
|
|
144
|
+
}
|
|
145
|
+
// Object with score
|
|
146
|
+
if (typeof output === 'object' && output !== null && 'score' in output) {
|
|
147
|
+
return (output as { score: number }).score
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.logger.warn({ output }, 'Unexpected reranker output format')
|
|
151
|
+
return 0
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Normalize scores to 0-1 range using min-max normalization
|
|
156
|
+
*/
|
|
157
|
+
private normalizeScores(scores: number[]): number[] {
|
|
158
|
+
if (scores.length === 0) return []
|
|
159
|
+
if (scores.length === 1) return [1.0]
|
|
160
|
+
|
|
161
|
+
const min = Math.min(...scores)
|
|
162
|
+
const max = Math.max(...scores)
|
|
163
|
+
const range = max - min
|
|
164
|
+
|
|
165
|
+
if (range === 0) {
|
|
166
|
+
return scores.map(() => 0.5)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return scores.map(score => (score - min) / range)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Check if reranker is ready
|
|
174
|
+
*/
|
|
175
|
+
isReady(): boolean {
|
|
176
|
+
return isModelLoaded()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Cleanup model from memory
|
|
181
|
+
*/
|
|
182
|
+
cleanup(): void {
|
|
183
|
+
unloadModel()
|
|
184
|
+
this.model = null
|
|
185
|
+
this.logger.info('Reranker model unloaded')
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { loadRerankerModel, isModelLoaded, unloadModel } from './model'
|
|
@@ -1,95 +1,99 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reranker Model Loader
|
|
3
|
-
* Handles loading and caching of the cross-encoder model
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
|
|
8
|
-
// Dynamic import for transformers to avoid bundling issues
|
|
9
|
-
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
logger.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Reranker Model Loader
|
|
3
|
+
* Handles loading and caching of the cross-encoder model
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
|
|
8
|
+
// Dynamic import for transformers to avoid bundling issues
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic optional dependency
|
|
10
|
+
let pipeline: unknown
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic optional dependency
|
|
12
|
+
let AutoTokenizer: unknown
|
|
13
|
+
|
|
14
|
+
interface RerankerModel {
|
|
15
|
+
tokenizer: unknown
|
|
16
|
+
model: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let modelInstance: RerankerModel | null = null
|
|
20
|
+
let loadingPromise: Promise<RerankerModel> | null = null
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Load the cross-encoder model
|
|
24
|
+
* Uses singleton pattern to avoid loading multiple times
|
|
25
|
+
*/
|
|
26
|
+
export async function loadRerankerModel(
|
|
27
|
+
logger: Logger,
|
|
28
|
+
modelName: string = 'cross-encoder/ms-marco-MiniLM-L-6-v2'
|
|
29
|
+
): Promise<RerankerModel> {
|
|
30
|
+
// Return existing instance if loaded
|
|
31
|
+
if (modelInstance) {
|
|
32
|
+
return modelInstance
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Wait for in-progress loading
|
|
36
|
+
if (loadingPromise) {
|
|
37
|
+
return loadingPromise
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Start loading
|
|
41
|
+
loadingPromise = (async () => {
|
|
42
|
+
logger.info({ modelName }, 'Loading cross-encoder reranker model')
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Dynamic import to avoid issues if transformers.js not available
|
|
46
|
+
const transformers = await import('@xenova/transformers')
|
|
47
|
+
pipeline = transformers.pipeline
|
|
48
|
+
AutoTokenizer = transformers.AutoTokenizer
|
|
49
|
+
|
|
50
|
+
// Load text-classification pipeline for cross-encoder
|
|
51
|
+
// Cross-encoders score query-document pairs
|
|
52
|
+
const pipelineFn = pipeline as (...args: unknown[]) => Promise<unknown>
|
|
53
|
+
const classifier = await pipelineFn('text-classification', modelName, {
|
|
54
|
+
quantized: true // Use quantized model for faster inference
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const AutoTokenizerClass = AutoTokenizer as { from_pretrained: (...args: unknown[]) => Promise<unknown> }
|
|
58
|
+
const tokenizer = await AutoTokenizerClass.from_pretrained(modelName)
|
|
59
|
+
|
|
60
|
+
modelInstance = {
|
|
61
|
+
tokenizer,
|
|
62
|
+
model: classifier
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
logger.info({ modelName }, 'Cross-encoder model loaded successfully')
|
|
66
|
+
return modelInstance
|
|
67
|
+
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.error({ error, modelName }, 'Failed to load cross-encoder model')
|
|
70
|
+
loadingPromise = null
|
|
71
|
+
throw error
|
|
72
|
+
}
|
|
73
|
+
})()
|
|
74
|
+
|
|
75
|
+
return loadingPromise
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if model is loaded
|
|
80
|
+
*/
|
|
81
|
+
export function isModelLoaded(): boolean {
|
|
82
|
+
return modelInstance !== null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Unload model to free memory
|
|
87
|
+
*/
|
|
88
|
+
export function unloadModel(): void {
|
|
89
|
+
modelInstance = null
|
|
90
|
+
loadingPromise = null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get model memory usage estimate
|
|
95
|
+
*/
|
|
96
|
+
export function getModelMemoryEstimate(): number {
|
|
97
|
+
// ms-marco-MiniLM-L-6-v2 quantized is roughly 22MB
|
|
98
|
+
return isModelLoaded() ? 22 * 1024 * 1024 : 0
|
|
99
|
+
}
|