Documentation
¶
Overview ¶
Package ctxmgr provides context management strategies for long-running loops. It keeps model context within token limits through replayable compaction plans.
Design: strategies produce pure Plans (no side effects). The runner records the plan in a CompactionApplied event BEFORE the ModelCall, so replay re-applies the recorded plan rather than re-running the strategy — determinism guaranteed even if strategy code changes between runs.
Index ¶
- func Apply(msgs []model.Message, plan Plan) []model.Message
- func ApplyPlanJSON(planJSON []byte, msgs []model.Message) []model.Message
- type ArtifactOffload
- type Ledger
- type OffloadSpec
- type Plan
- type Range
- type RuntimePlan
- type SlidingWindow
- type Strategy
- type StrategyAdapter
- type SummarizeOlder
- type SummarySpec
- type TurnCount
- type TurnSource
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply transforms a message slice according to a recorded Plan. Drop ranges are applied in reverse order to preserve indices. Summary messages are injected after the specified index. Offload messages are replaced with an artifact-reference placeholder. The raw event log is NOT modified; only the in-memory view is transformed.
Types ¶
type ArtifactOffload ¶
type ArtifactOffload struct {
// Threshold is the minimum byte size of a tool-result content that triggers offload.
Threshold int
}
ArtifactOffload is a Strategy that offloads large tool results to an ArtifactStore. Messages whose tool-result content exceeds Threshold bytes are replaced with an artifact reference placeholder (`artifact://<key>`).
The actual Put call to ArtifactStore is performed by the runner (Task 5); here we only identify which messages should be offloaded and produce OffloadSpec entries.
type Ledger ¶
type Ledger struct {
// TotalTokens is the cumulative token count across all turns.
TotalTokens int
// BySource maps TurnSource → total tokens from that source.
BySource map[TurnSource]int
// Turns records the per-turn counts in chronological order.
Turns []TurnCount
}
Ledger tracks token totals per turn and per source, derived by folding events. It is used by strategies to decide whether and how to compact.
func (*Ledger) Record ¶
func (l *Ledger) Record(source TurnSource, tokens int)
Record adds a turn count to the ledger.
type OffloadSpec ¶
type OffloadSpec struct {
// MessageIndex is the message index to offload.
MessageIndex int `json:"message_index"`
// ArtifactKey is the key under which the content was stored (set after Put).
ArtifactKey string `json:"artifact_key,omitempty"`
// Threshold is the byte size that triggered offload (informational).
Threshold int `json:"threshold,omitempty"`
}
OffloadSpec describes one tool-result message to offload to an ArtifactStore.
type Plan ¶
type Plan struct {
// Drop contains ranges of message indices to remove.
Drop []Range `json:"drop,omitempty"`
// Summaries contains summary messages to inject.
Summaries []SummarySpec `json:"summaries,omitempty"`
// Offloads contains messages to offload to an ArtifactStore.
Offloads []OffloadSpec `json:"offloads,omitempty"`
}
Plan describes a set of message-history transformations to apply before a ModelCall. It is serialised verbatim into the CompactionApplied event for deterministic replay.
func (Plan) MarshalJSON ¶
MarshalJSON marshals a Plan to JSON.
func (*Plan) UnmarshalJSON ¶
UnmarshalJSON unmarshals a Plan from JSON.
type Range ¶
type Range struct {
// From is the first message index to drop (inclusive).
From int `json:"from"`
// To is the last message index to drop (exclusive).
To int `json:"to"`
}
Range describes a contiguous slice [From, To) of message indices to drop.
type RuntimePlan ¶
type RuntimePlan struct {
Strategy string `json:"strategy"`
Drop []Range `json:"drop,omitempty"`
Summaries []SummarySpec `json:"summaries,omitempty"`
Offloads []OffloadSpec `json:"offloads,omitempty"`
}
RuntimePlan is a compaction plan in the runtime wire format. It mirrors runtime.CompactionPlan but lives in core/ctxmgr to avoid the import-cycle (core/* must not import runtime). The runner marshals a RuntimePlan into CompactionApplied.PlanJSON.
type SlidingWindow ¶
type SlidingWindow struct {
// KeepLast is the number of most-recent non-system messages to retain.
KeepLast int
// PinSystem, if true, keeps all system messages regardless of position.
PinSystem bool
}
SlidingWindow is a Strategy that keeps the last KeepLast messages. If PinSystem is true, system messages are always kept regardless of position.
Plan: drop messages [1, len-KeepLast) (0-based), preserving system messages when PinSystem is set.
type Strategy ¶
type Strategy interface {
// Plan examines the ledger and message list and produces a compaction plan.
// Returns (plan, true) if compaction is needed; (Plan{}, false) if not.
// Plan must be pure: no side effects; no I/O.
Plan(ledger *Ledger, msgs []model.Message, limit int) (Plan, bool)
}
Strategy decides whether and how to compact a message slice.
type StrategyAdapter ¶
type StrategyAdapter struct {
// contains filtered or unexported fields
}
StrategyAdapter wraps a Strategy to implement the runtime.ContextManager interface. It converts between ctxmgr.Plan domain types and JSON-serialised RuntimePlan.
StrategyAdapter satisfies runtime.ContextManager:
- Plan(*Ledger, []model.Message) ([]byte, bool)
- ApplyPlanJSON([]byte, []model.Message) []model.Message
- StrategyName() string
func NewStrategyAdapter ¶
func NewStrategyAdapter(name string, limit int, s Strategy) *StrategyAdapter
NewStrategyAdapter creates an adapter for strategy s. name is recorded as CompactionApplied.Strategy. limit is the token limit passed to the strategy's Plan method.
func (*StrategyAdapter) ApplyPlanJSON ¶
ApplyPlanJSON applies a previously recorded plan (from CompactionApplied.PlanJSON) to a message slice. Used during replay — the plan is fixed; strategy code is bypassed.
func (*StrategyAdapter) Plan ¶
Plan calls the underlying strategy and returns a JSON-encoded RuntimePlan if compaction is needed.
func (*StrategyAdapter) StrategyName ¶
func (a *StrategyAdapter) StrategyName() string
StrategyName returns the name of the wrapped strategy.
type SummarizeOlder ¶
type SummarizeOlder struct {
// Provider is the model provider name to use for summarization.
Provider string
// Model is the model identifier to use for summarization.
Model string
// ChunkTurns is how many messages to group per summary call.
ChunkTurns int
// Budget is the resource budget for each summary model call.
Budget budget.Budget
// KeepRecent is the number of recent messages to always preserve (not summarized).
KeepRecent int
}
SummarizeOlder is a Strategy that chunks older messages and produces summary specs. The actual summary generation (ModelCall action) is handled by the runner in Task 5; here we only produce the Plan with SummarySpec entries that describe which chunks need summarizing.
SummarizeOlder triggers when the approximate token count exceeds limit. It groups non-recent messages into ChunkTurns-sized chunks. For each chunk, a SummarySpec is produced with placeholder content (the runner replaces it with actual model output and records the ActionRequested event).
type SummarySpec ¶
type SummarySpec struct {
// InsertAfter is the message index after which to insert the summary.
InsertAfter int `json:"insert_after"`
// Content is the summary text.
Content string `json:"content"`
}
SummarySpec describes one summary message to inject into the context.
type TurnCount ¶
type TurnCount struct {
Source TurnSource `json:"source"`
Tokens int `json:"tokens"`
}
TurnCount tracks per-source token totals for a single conversation turn.
type TurnSource ¶
type TurnSource string
TurnSource identifies which component produced tokens in a turn.
const ( // SourceModel tokens were produced by the AI model (output tokens). SourceModel TurnSource = "model" // SourceTool tokens came from a tool result. SourceTool TurnSource = "tool" // SourceHuman tokens came from a human message. SourceHuman TurnSource = "human" // SourceSystem tokens came from a system prompt. SourceSystem TurnSource = "system" )