feat(context,infra): compression pipeline stages 4-5 — token pruning + relevance + ToMe

- build() is now suspend: pipeline runs all stages in fixed order, each gated by level
- TOKEN_PRUNE (level 3): TokenPruner interface + LLMLingua-2 sidecar (sidecars/llmlingua)
  + HttpTokenPruner adapter (fails open if sidecar down); prunes freeform, preserves
  protected spans, skips tier-0 turns when TIER_SPLIT on
- TOME_MERGE (level 8): ToMeMerger collapses near-duplicate freeform turns (Jaccard)
- Stage 5 selection: RelevanceScorer + EmbeddingRelevanceScorer (cosine over Embedder);
  query-conditioned reorder so least-relevant freeform drops first under budget
- [orchestration] compression_level + token_pruner_url config, wired in Main
- suspend ripple fixed across builder callers/stubs
This commit is contained in:
2026-07-01 14:29:56 +04:00
parent e0c222392c
commit 047e2a4070
25 changed files with 509 additions and 65 deletions
@@ -81,11 +81,11 @@ class CompressionPipelineStagesTest {
}
@Test
fun `builder pins a fact sheet at level 7 and omits it below`() {
fun `builder pins a fact sheet at level 7 and omits it below`() = kotlinx.coroutines.runBlocking {
val entries = listOf(entry("chat", ContextLayer.L1, EntryRole.USER, "deploy to 10.0.0.1"))
val compressor = com.correx.core.context.compression.DefaultContextCompressor()
fun buildAt(level: Int) = com.correx.core.context.builder.DefaultContextPackBuilder(
suspend fun buildAt(level: Int) = com.correx.core.context.builder.DefaultContextPackBuilder(
compressor, CompressionPolicy(level)
).build(
com.correx.core.events.types.ContextPackId("p"),
@@ -98,6 +98,43 @@ class CompressionPipelineStagesTest {
val atSeven = buildAt(7).layers.values.flatten()
assertTrue(atSeven.any { it.sourceType == "factSheet" && it.content.contains("10.0.0.1") })
assertFalse(buildAt(6).layers.values.flatten().any { it.sourceType == "factSheet" })
Unit
}
@Test
fun `tome merges near-duplicate freeform turns keeping the newest`() {
val merger = com.correx.core.context.compression.ToMeMerger(threshold = 0.8)
val a = entry("chat", ContextLayer.L2, EntryRole.USER, "the deploy failed on host web").copy(ordinal = 0)
val b = entry("chat", ContextLayer.L2, EntryRole.USER, "the deploy failed on host web again").copy(ordinal = 1)
val c = entry("chat", ContextLayer.L2, EntryRole.USER, "totally unrelated content here").copy(ordinal = 2)
val out = merger.merge(listOf(a, b, c))
// a and b are near-duplicates -> only the newest (b) survives; c is distinct
assertEquals(listOf(1, 2), out.map { it.ordinal })
}
@Test
fun `token pruning at level 3 rewrites freeform preserving protected spans`() = kotlinx.coroutines.runBlocking {
val pruner = object : com.correx.core.context.compression.TokenPruner {
override suspend fun prune(content: String, protectedSpans: List<String>, targetRatio: Double): String =
"PRUNED " + protectedSpans.joinToString(" ")
}
val builder = com.correx.core.context.builder.DefaultContextPackBuilder(
com.correx.core.context.compression.DefaultContextCompressor(),
CompressionPolicy(3),
tokenPruner = pruner,
)
val entries = listOf(entry("chat", ContextLayer.L1, EntryRole.USER, "please redeploy 10.0.0.1 now"))
val pack = builder.build(
com.correx.core.events.types.ContextPackId("p"),
com.correx.core.events.types.SessionId("s"),
com.correx.core.events.types.StageId("st"),
entries,
com.correx.core.context.model.TokenBudget(limit = 4000),
)
val content = pack.layers.values.flatten().first { it.sourceType == "chat" }.content
assertTrue(content.startsWith("PRUNED"), content)
assertTrue(content.contains("10.0.0.1"), content)
Unit
}
@Test
@@ -20,7 +20,7 @@ class DecisionJournalPinningTest {
private val packId = ContextPackId("pack-1")
@Test
fun `decisionJournal entry survives an under-budget build`() {
fun `decisionJournal entry survives an under-budget build`() = kotlinx.coroutines.runBlocking {
val entries = listOf(
ContextEntry(
id = ContextEntryId("journal-1"),
@@ -37,5 +37,6 @@ class DecisionJournalPinningTest {
allRetained.any { it.sourceType == "decisionJournal" },
"decisionJournal entry must be retained even when budget is exceeded"
)
Unit
}
}