feat: L3 memory retrieval on the router read path (record-only)

When a user sends input, embed it, query L3 across all sessions, dedup
against in-session turns, and record the retrieval as an event so replay
is deterministic (invariant #9). Hits land in RouterState; context
injection follows in a later slice.

- Add L3MemoryRetrievedEvent + L3RetrievedHit (registered in eventModule)
- RouterState.lastRetrievedMemory + reducer case; RouterTurn carries turnId
- RouterConfig.retrievalK (default 5)
- Harden L3 write path: runCatching + visible logging, cancellation
  re-thrown; event append stays ahead of the L3 write
- Warn prominently when the non-durable in_memory L3 backend is selected
This commit is contained in:
2026-05-30 14:36:30 +04:00
parent e8372e0643
commit e6239515bd
10 changed files with 340 additions and 37 deletions
+1
View File
@@ -27,6 +27,7 @@ dependencies {
implementation "io.ktor:ktor-client-cio:$ktor_version"
implementation "io.ktor:ktor-client-content-negotiation:$ktor_version"
implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
implementation "org.slf4j:slf4j-api:2.0.16"
}
tasks.named("koverVerify").configure { enabled = false }
@@ -51,12 +51,15 @@ import com.correx.infrastructure.workflow.PromptLoader
import com.correx.infrastructure.workflow.TomlWorkflowLoader
import com.correx.infrastructure.workflow.WorkflowLoader
import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.sql.DriverManager
private val log = LoggerFactory.getLogger(InfrastructureModule::class.java)
@Suppress("TooManyFunctions")
object InfrastructureModule {
private val defaultDbPath: String =
@@ -199,7 +202,14 @@ object InfrastructureModule {
fun createL3MemoryStoreFromConfig(config: L3Config): L3MemoryStore {
return when (config.backend) {
"in_memory" -> createInMemoryL3MemoryStore()
"in_memory" -> {
log.warn(
"L3 backend is 'in_memory': non-durable, intended for tests/dev only — " +
"all router memory is lost on restart. " +
"Set [l3] backend = \"turbovec\" for persistence."
)
createInMemoryL3MemoryStore()
}
"turbovec" -> createTurboVecL3MemoryStore(config)
else -> throw IllegalArgumentException(
"Unknown L3 backend: '${config.backend}'. Supported: in_memory, turbovec"