feat: rehydrate L3 metadata from event log on startup
TurboVec persists vectors via the sidecar but kept entry metadata (sessionId/turnId/text) only in an in-memory map, so restarting with backend=turbovec silently dropped every query hit (the id→metadata lookup missed). Rebuild that map from the ChatTurnEvent log at startup. - RehydratableL3MemoryStore: capability interface for stores whose vectors persist independently of the JVM. TurboVec implements it; InMemory deliberately does not (its vectors are volatile too). - L3MetadataRehydrator replays ChatTurnEvents into the metadata map. No embedder: the query path never reads entry.vector, so vectors stay in the sidecar and metadata comes from the event log (the source of truth, invariant #1). Short-circuits for non-rehydratable backends before scanning the log. - Wired into Main before the server accepts queries; no sidecar spawn. Backfill of never-embedded history (needs the embedder) is a separate follow-up. The in_memory non-durability warning already exists.
This commit is contained in:
+9
-3
@@ -2,8 +2,8 @@ package com.correx.infrastructure.router.turbovec
|
||||
|
||||
import com.correx.core.router.l3.L3Hit
|
||||
import com.correx.core.router.l3.L3MemoryEntry
|
||||
import com.correx.core.router.l3.L3MemoryStore
|
||||
import com.correx.core.router.l3.L3Query
|
||||
import com.correx.core.router.l3.RehydratableL3MemoryStore
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withTimeout
|
||||
@@ -15,14 +15,20 @@ import kotlin.io.path.createDirectories
|
||||
* Adapter to TurboVec sidecar process for cross-session vector-based memory.
|
||||
* Maintains in-memory metadata (sessionId, turnId, text, timestamp) since turbovec
|
||||
* stores only vectors and IDs.
|
||||
*
|
||||
* The metadata map is volatile, but the sidecar vectors persist (via `persistPath`);
|
||||
* on restart [rehydrateMetadata] rebuilds the map from the `ChatTurnEvent` log so
|
||||
* queries resolve their hits again (invariant #1: the event log is authoritative).
|
||||
*/
|
||||
class TurboVecL3MemoryStore(private val config: TurboVecSidecarConfig) : L3MemoryStore {
|
||||
class TurboVecL3MemoryStore(private val config: TurboVecSidecarConfig) : RehydratableL3MemoryStore {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val mutex = Mutex()
|
||||
private var process: Process? = null
|
||||
private val metadata = java.util.concurrent.ConcurrentHashMap<String, L3MemoryEntry>()
|
||||
|
||||
// SCAFFOLDING: metadata persistence to be added with ChatTurnEvent
|
||||
override suspend fun rehydrateMetadata(entries: List<L3MemoryEntry>) {
|
||||
entries.forEach { metadata.putIfAbsent(it.id, it) }
|
||||
}
|
||||
|
||||
override suspend fun store(entry: L3MemoryEntry) {
|
||||
val request = SidecarRequest(
|
||||
|
||||
Reference in New Issue
Block a user