|
1 | 1 | import type { MemoryConfig } from "../config/types.ts"; |
2 | 2 | import { type EmbeddingClient, textToSparseVector } from "./embeddings.ts"; |
3 | 3 | import type { QdrantClient } from "./qdrant-client.ts"; |
| 4 | +import { calculateEpisodeRecallScore } from "./ranking.ts"; |
4 | 5 | import type { Episode, QdrantSearchResult, RecallOptions } from "./types.ts"; |
5 | 6 |
|
6 | 7 | const COLLECTION_SCHEMA = { |
@@ -128,6 +129,7 @@ export class EpisodicStore { |
128 | 129 | for (const id of ids) { |
129 | 130 | try { |
130 | 131 | await this.qdrant.updatePayload(this.collectionName, id, { |
| 132 | + access_count: { $inc: 1 }, |
131 | 133 | last_accessed_at: new Date().toISOString(), |
132 | 134 | }); |
133 | 135 | } catch { |
@@ -165,34 +167,23 @@ export class EpisodicStore { |
165 | 167 | return { must }; |
166 | 168 | } |
167 | 169 |
|
168 | | - private applyStrategy(results: QdrantSearchResult[], strategy: string): QdrantSearchResult[] { |
169 | | - const now = Date.now(); |
170 | | - |
| 170 | + private applyStrategy(results: QdrantSearchResult[], strategy: RecallOptions["strategy"]): QdrantSearchResult[] { |
171 | 171 | return results |
172 | 172 | .map((r) => { |
173 | | - const startedAt = (r.payload.started_at as number) ?? 0; |
174 | | - const importance = (r.payload.importance as number) ?? 0.5; |
175 | | - const hoursSince = (now - startedAt) / (1000 * 60 * 60); |
176 | | - const recencyScore = Math.exp(-0.01 * hoursSince); |
177 | | - |
178 | | - let finalScore: number; |
179 | | - switch (strategy) { |
180 | | - case "similarity": |
181 | | - finalScore = r.score * 0.7 + importance * 0.2 + recencyScore * 0.1; |
182 | | - break; |
183 | | - case "temporal": |
184 | | - finalScore = recencyScore * 0.7 + r.score * 0.2 + importance * 0.1; |
185 | | - break; |
186 | | - case "metadata": |
187 | | - finalScore = r.score * 0.5 + recencyScore * 0.3 + importance * 0.2; |
188 | | - break; |
189 | | - default: |
190 | | - // recency-biased (default) |
191 | | - finalScore = r.score * 0.4 + recencyScore * 0.4 + importance * 0.2; |
192 | | - break; |
193 | | - } |
194 | | - |
195 | | - return { ...r, score: finalScore }; |
| 173 | + return { |
| 174 | + ...r, |
| 175 | + score: calculateEpisodeRecallScore( |
| 176 | + r.score, |
| 177 | + { |
| 178 | + importance: (r.payload.importance as number) ?? 0.5, |
| 179 | + accessCount: (r.payload.access_count as number) ?? 0, |
| 180 | + startedAt: (r.payload.started_at as number) ?? 0, |
| 181 | + lastAccessedAt: (r.payload.last_accessed_at as string | undefined) ?? undefined, |
| 182 | + decayRate: (r.payload.decay_rate as number) ?? 1, |
| 183 | + }, |
| 184 | + strategy, |
| 185 | + ), |
| 186 | + }; |
196 | 187 | }) |
197 | 188 | .sort((a, b) => b.score - a.score); |
198 | 189 | } |
|
0 commit comments