feat(context,kernel): compression pipeline stage 3 — replay-safe tier summarizer
ContextSummarizedEvent (registered) keyed on stable session sequence, not the ephemeral pack ordinal, so replay reuses the recorded summary artifact and never re-calls the LLM (invariants #6/#8). TierContextSummarizer mirrors JournalCompactionService for freeform context (tool outputs/docs/retrieved text) the journal path doesn't cover. ContextState + DefaultContextReducer fold the event; reducer replay-safety unit-tested.
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
package com.correx.core.context
|
||||
|
||||
import com.correx.core.context.state.ContextState
|
||||
import com.correx.core.events.events.ContextSummarizedEvent
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
|
||||
class DefaultContextReducer : ContextReducer {
|
||||
override fun reduce(state: ContextState, event: StoredEvent): ContextState = state
|
||||
override fun reduce(state: ContextState, event: StoredEvent): ContextState =
|
||||
when (val p = event.payload) {
|
||||
is ContextSummarizedEvent -> state.copy(
|
||||
summarizedThroughSequence = p.coversThroughSequence,
|
||||
summaryArtifactId = p.summaryArtifactId,
|
||||
summaryTier = p.tier,
|
||||
)
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.correx.core.context.state
|
||||
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
import com.correx.core.events.types.ContextPackId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -8,4 +9,10 @@ data class ContextState(
|
||||
val builtPackIds: List<ContextPackId> = emptyList(),
|
||||
val buildingInProgress: Boolean = false,
|
||||
val interrupted: Boolean = false,
|
||||
// Recursive-summarization coverage (pipeline stage 5). Freeform context up through this
|
||||
// stable event sequence is represented by [summaryArtifactId]; assembly drops the covered
|
||||
// raw entries and injects the summary instead. -1 = nothing summarized yet.
|
||||
val summarizedThroughSequence: Long = -1L,
|
||||
val summaryArtifactId: ArtifactId? = null,
|
||||
val summaryTier: Int = 0,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import com.correx.core.context.DefaultContextReducer
|
||||
import com.correx.core.context.state.ContextState
|
||||
import com.correx.core.events.events.ContextSummarizedEvent
|
||||
import com.correx.core.events.events.EventMetadata
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
import com.correx.core.events.types.EventId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlinx.datetime.Instant
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ContextSummaryReducerTest {
|
||||
|
||||
private val reducer = DefaultContextReducer()
|
||||
|
||||
private fun stored(payload: ContextSummarizedEvent, seq: Long) = StoredEvent(
|
||||
metadata = EventMetadata(
|
||||
eventId = EventId("e$seq"),
|
||||
sessionId = SessionId("s"),
|
||||
timestamp = Instant.fromEpochSeconds(0),
|
||||
schemaVersion = 1,
|
||||
causationId = null,
|
||||
correlationId = null,
|
||||
),
|
||||
sequence = seq,
|
||||
sessionSequence = seq,
|
||||
payload = payload,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `latest summary event wins and records stable coverage`() {
|
||||
// Replaying the same events must yield the same state — coverage is keyed on the stable
|
||||
// event sequence, so no live LLM call is needed to rebuild (invariant #8).
|
||||
var state = ContextState()
|
||||
state = reducer.reduce(state, stored(
|
||||
ContextSummarizedEvent(SessionId("s"), 10L, ArtifactId("a1"), tier = 2, entriesOmittedCount = 5), 1L))
|
||||
state = reducer.reduce(state, stored(
|
||||
ContextSummarizedEvent(SessionId("s"), 25L, ArtifactId("a2"), tier = 3, entriesOmittedCount = 8), 2L))
|
||||
|
||||
assertEquals(25L, state.summarizedThroughSequence)
|
||||
assertEquals(ArtifactId("a2"), state.summaryArtifactId)
|
||||
assertEquals(3, state.summaryTier)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user