ctxmgr

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Apply

func Apply(msgs []model.Message, plan Plan) []model.Message

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.

func ApplyPlanJSON

func ApplyPlanJSON(planJSON []byte, msgs []model.Message) []model.Message

ApplyPlanJSON is a standalone helper that applies a recorded RuntimePlan to msgs. Use this when you have the JSON but not the original StrategyAdapter.

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.

func (ArtifactOffload) Plan

func (a ArtifactOffload) Plan(ledger *Ledger, msgs []model.Message, limit int) (Plan, bool)

Plan scans msgs for tool-result messages exceeding Threshold bytes. Returns (plan, true) when at least one message should be offloaded.

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 NewLedger

func NewLedger() *Ledger

NewLedger creates an empty Ledger.

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

func (p Plan) MarshalJSON() ([]byte, error)

MarshalJSON marshals a Plan to JSON.

func (*Plan) UnmarshalJSON

func (p *Plan) UnmarshalJSON(data []byte) error

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.

func (SlidingWindow) Plan

func (w SlidingWindow) Plan(ledger *Ledger, msgs []model.Message, limit int) (Plan, bool)

Plan decides whether dropping older messages is needed. Returns (plan, true) when len(msgs) exceeds KeepLast (accounting for pinned system msgs).

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

func (a *StrategyAdapter) ApplyPlanJSON(planJSON []byte, msgs []model.Message) []model.Message

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

func (a *StrategyAdapter) Plan(ledger *Ledger, msgs []model.Message) ([]byte, bool)

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).

func (SummarizeOlder) Plan

func (s SummarizeOlder) Plan(ledger *Ledger, msgs []model.Message, limit int) (Plan, bool)

Plan decides whether and how to summarize older messages. Returns (plan, true) when the estimated token count exceeds limit and there are enough messages to summarize.

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"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL