compaction

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	RecoverySourceLocal      = "local"
	RecoverySourceTranscript = "transcript"

	RecoveryWarningCorruptSegment = "corrupt_segment"
)
View Source
const (
	MetaWorldState    = "scitrera_world_state"
	MetaContextBudget = "scitrera_context_budget"
	// MetaUsage carries the finalized assistant message's per-turn token
	// accounting ({model, prompt_tokens, completion_tokens, total_tokens, calls}),
	// summed across the turn's provider calls. Present only when a provider
	// reported usage; consumed by trace export.
	MetaUsage = "scitrera_usage"
)

Variables

View Source
var ErrTranscriptPathRequired = errors.New("compaction: transcript path required")

Functions

func CompactionCount

func CompactionCount(ctx context.Context) int

CompactionCount returns the current value of the ctx compaction-event counter (0 when absent). Lets the turn loop detect a compaction event by comparing the count across a context Build, without threading the counter pointer around.

func ContextBudgetFrom

func ContextBudgetFrom(ctx context.Context) (int, bool)

ContextBudgetFrom returns the per-call token budget carried on ctx, if any.

func EstimateTokens

func EstimateTokens(messages []protocol.ChatMessage) int

EstimateTokens returns a rough token estimate for a message slice.

func NoteCompaction

func NoteCompaction(ctx context.Context)

NoteCompaction bumps the ctx compaction-event counter (if present) by one. Called by the assembler when a Reduce actually dropped messages — counts EVENTS (per compacting provider call), not messages.

func Reduce

func Reduce(messages []protocol.ChatMessage, cfg Config) ([]protocol.ChatMessage, error)

func TurnNumberFrom

func TurnNumberFrom(ctx context.Context) (int, bool)

TurnNumberFrom returns the current turn number carried on ctx, if any.

func WithCompactionCounter

func WithCompactionCounter(ctx context.Context) (context.Context, *int)

WithCompactionCounter returns ctx carrying a fresh per-turn compaction-event counter plus its pointer. The assembler bumps it (NoteCompaction) each time a context Build drops messages; the turn's world-state sink reads it to persist a running per-thread total (WorldState.Compactions).

func WithContextBudget

func WithContextBudget(ctx context.Context, tokens int) context.Context

WithContextBudget carries a per-call history token budget on ctx: the effective context ceiling for the model the runner is ABOUT to call. The runner sets it before each provider call (from the current/escalated model's window), so the assembler compacts history to fit the model actually in play — a turn that escalates a large-context orchestrator to a smaller-context vision model then trims to the vision model's window instead of overflowing it. A budget <= 0, or none set, leaves the assembler's static MaxContextTokens in force.

func WithTurnNumber

func WithTurnNumber(ctx context.Context, n int) context.Context

WithTurnNumber carries the current turn's number (the advanced WorldState.Turn for the in-flight turn) on ctx. The runner sets it once per turn so consumers — notably the context assembler computing invoked-skill age — measure age against "now", not against the last turn already stamped in history.

Types

type ClassicCompactor

type ClassicCompactor struct{}

ClassicCompactor is the historical drop-oldest/trim behavior, exposed through the Compactor seam. It is a thin wrapper over ReduceWithReport so existing callers and tests keep the exact same semantics.

func (ClassicCompactor) Compact

func (ClassicCompactor) Compact(_ context.Context, messages []protocol.ChatMessage, cfg Config) (Report, error)

type Compactor

type Compactor interface {
	Compact(ctx context.Context, messages []protocol.ChatMessage, cfg Config) (Report, error)
}

Compactor turns a full history into a budget-fitting Report. Implementations differ in HOW they shed tokens (drop-oldest, evict-parts, summarize); the context assembler is agnostic and calls whichever one it is handed.

func CompactorFor

func CompactorFor(mode string, sink EvictionSink, summarizer Summarizer) Compactor

CompactorFor selects a compactor by mode name. "" / "evict" / "composite" -> the eviction-first default; "classic" -> plain drop-oldest; "summarize" -> summary-then-classic (needs a Summarizer). An unknown mode falls back to the default.

func DefaultCompactor

func DefaultCompactor(sink EvictionSink) Compactor

DefaultCompactor is the standard eviction-first composite: evict oversized parts (offloaded to sink), then drop-oldest as a last resort if still over budget. A nil sink makes the eviction stage a passthrough.

type CompositeCompactor

type CompositeCompactor struct {
	Primary  Compactor
	Fallback Compactor
}

CompositeCompactor runs Primary, then falls back to Fallback only when the primary's output still overflows cfg.MaxTokens. A nil Primary or Fallback is treated as ClassicCompactor.

func (CompositeCompactor) Compact

func (c CompositeCompactor) Compact(ctx context.Context, messages []protocol.ChatMessage, cfg Config) (Report, error)

type Config

type Config struct {
	MaxMessages      int
	MaxTextPartBytes int
	// MaxTokens, when > 0, drops the oldest messages until the estimated token
	// count fits the budget (applied after the message-count/byte reductions).
	MaxTokens int
	// WorldState carries deterministic local state that must survive compaction.
	WorldState WorldState
}

type ContextBudget

type ContextBudget struct {
	MaxTokens         int `json:"max_tokens,omitempty"`
	EstimatedTokens   int `json:"estimated_tokens"`
	RemainingTokens   int `json:"remaining_tokens"`
	MessageCount      int `json:"message_count"`
	CompactedMessages int `json:"compacted_messages,omitempty"`
}

func BudgetFor

func BudgetFor(messages []protocol.ChatMessage, maxTokens int, compactedMessages int) ContextBudget

type EvictingCompactor

type EvictingCompactor struct {
	Sink                  EvictionSink
	ToolResultEvictTokens int
	KeepRecent            int
	PreviewHeadLines      int
	PreviewTailLines      int
}

EvictingCompactor shrinks large parts in older messages instead of dropping whole messages: each oversized tool-result or text part is offloaded to the Sink and replaced inline with a head/tail preview plus a read-me-back ref. It never touches the last KeepRecent messages, and it never evicts image parts. With a nil Sink it is a safe passthrough (delegates to ClassicCompactor).

func (EvictingCompactor) Compact

func (c EvictingCompactor) Compact(ctx context.Context, messages []protocol.ChatMessage, cfg Config) (Report, error)

type EvictionSink

type EvictionSink interface {
	Put(ctx context.Context, key string, content []byte) (ref string, err error)
}

EvictionSink offloads a large content blob to durable storage, returning a ref (a path/handle) the model can later re-read via read_file. The concrete sink is injected so this package stays free of any localtools/store import.

type FileMetadata

type FileMetadata struct {
	Path     string `json:"path"`
	Kind     string `json:"kind,omitempty"` // write | edit
	Digest   string `json:"digest,omitempty"`
	Bytes    int64  `json:"bytes,omitempty"`
	LastTurn int    `json:"last_turn,omitempty"`
}

type LocalTranscriptStore

type LocalTranscriptStore struct {
	// contains filtered or unexported fields
}

func NewLocalTranscriptStore

func NewLocalTranscriptStore(path string) *LocalTranscriptStore

func (*LocalTranscriptStore) AppendSegment

func (s *LocalTranscriptStore) AppendSegment(ctx context.Context, segment TranscriptSegment) error

func (*LocalTranscriptStore) Recover

func (s *LocalTranscriptStore) Recover(ctx context.Context, localHistory []protocol.ChatMessage) (RecoveryResult, error)

type PlanRef

type PlanRef struct {
	ID       string `json:"id"`
	Path     string `json:"path,omitempty"`
	Status   string `json:"status,omitempty"`
	LastTurn int    `json:"last_turn,omitempty"`
}

type RecoveryResult

type RecoveryResult struct {
	Source     string                 `json:"source"`
	Compacted  bool                   `json:"compacted"`
	Messages   []protocol.ChatMessage `json:"messages"`
	WorldState WorldState             `json:"world_state,omitempty"`
	Budget     ContextBudget          `json:"budget,omitempty"`
	Warnings   []RecoveryWarning      `json:"warnings,omitempty"`
}

type RecoveryWarning

type RecoveryWarning struct {
	Kind    string `json:"kind"`
	Segment int    `json:"segment,omitempty"`
	Detail  string `json:"detail,omitempty"`
}

type Report

type Report struct {
	Messages   []protocol.ChatMessage
	WorldState WorldState
	Budget     ContextBudget
}

func ReduceWithReport

func ReduceWithReport(messages []protocol.ChatMessage, cfg Config) (Report, error)

type SkillRef

type SkillRef struct {
	Name string `json:"name"`
	// Source is the skill's origin (e.g. path or catalog).
	Source string `json:"source,omitempty"`
	// LastTurn is the WorldState.Turn at which the skill was last invoked (via
	// load_skill). Age = current WorldState.Turn - LastTurn.
	LastTurn int `json:"last_turn,omitempty"`
}

type SubagentHandle

type SubagentHandle struct {
	// ID is the child sub-agent's thread_id — the re-addressable handle returned
	// from spawn_subagent (pass it back as the `thread` arg to resume that child).
	// It is the dedup/merge key in MergeWorldState.
	ID       string `json:"id"`
	Name     string `json:"name,omitempty"`
	Task     string `json:"task,omitempty"`
	Status   string `json:"status,omitempty"`
	Summary  string `json:"summary,omitempty"`
	LastTurn int    `json:"last_turn,omitempty"`
}

type Summarizer

type Summarizer interface {
	Summarize(ctx context.Context, messages []protocol.ChatMessage) (summary string, err error)
}

Summarizer condenses a run of older messages into a single natural-language summary. Injected so this package pulls in no provider/LLM dependency.

type SummarizingCompactor

type SummarizingCompactor struct {
	Summarizer Summarizer
	Sink       EvictionSink
	KeepRecent int
}

SummarizingCompactor folds the older-than-keep-window messages into a single schema-versioned system summary when over budget, optionally offloading the full dropped run via Sink. OPT-IN only: a nil Summarizer degrades to classic behavior, so this is never silently selected as a default.

func (SummarizingCompactor) Compact

func (c SummarizingCompactor) Compact(ctx context.Context, messages []protocol.ChatMessage, cfg Config) (Report, error)

type TaskState

type TaskState struct {
	ID        string   `json:"id"`
	Title     string   `json:"title,omitempty"`
	Status    string   `json:"status,omitempty"`
	BlockedBy []string `json:"blocked_by,omitempty"`
	LastTurn  int      `json:"last_turn,omitempty"`
}

type TodoState

type TodoState struct {
	ID       string `json:"id"`
	Content  string `json:"content,omitempty"`
	Status   string `json:"status,omitempty"`
	LastTurn int    `json:"last_turn,omitempty"`
}

type TranscriptSegment

type TranscriptSegment struct {
	ID         string                 `json:"id"`
	Compacted  bool                   `json:"compacted,omitempty"`
	Messages   []protocol.ChatMessage `json:"messages"`
	WorldState WorldState             `json:"world_state,omitempty"`
	Budget     ContextBudget          `json:"budget,omitempty"`
}

type WorldState

type WorldState struct {
	// Turn is a monotonic per-thread turn counter that survives compaction (it
	// rides the WorldState meta). Used to age InvokedSkills: a skill's staleness
	// is Turn - SkillRef.LastTurn.
	Turn int `json:"turn,omitempty"`
	// Compactions is a monotonic per-thread count of context-compaction events (each
	// provider call whose context Build dropped messages). Survives compaction via the
	// WorldState meta. Record-keeping today (no behavioral effect); a future signal
	// source — e.g. a hint that older detail was compacted out and should be searched
	// in history/memory rather than assumed gone.
	Compactions     int              `json:"compactions,omitempty"`
	PlanRefs        []PlanRef        `json:"plan_refs,omitempty"`
	Tasks           []TaskState      `json:"tasks,omitempty"`
	Todos           []TodoState      `json:"todos,omitempty"`
	InvokedSkills   []SkillRef       `json:"invoked_skills,omitempty"`
	RecentFiles     []FileMetadata   `json:"recent_files,omitempty"`
	DynamicTools    []string         `json:"dynamic_tools,omitempty"`
	ActiveSubagents []SubagentHandle `json:"active_subagents,omitempty"`
	Extra           json.RawMessage  `json:"extra,omitempty"`
}

func ExtractWorldState

func ExtractWorldState(messages []protocol.ChatMessage) WorldState

func MergeWorldState

func MergeWorldState(states ...WorldState) WorldState

func (WorldState) ActiveInvokedSkills

func (w WorldState) ActiveInvokedSkills(ttl int) []SkillRef

ActiveInvokedSkills returns the invoked skills whose age is <= ttl (ttl <= 0 means no limit), ordered most-recently-invoked first. Used to surface the still-relevant loaded skills to the model without an unbounded, ever-growing list on long threads.

func (WorldState) ActiveRecentFiles

func (w WorldState) ActiveRecentFiles(ttl int) []FileMetadata

ActiveRecentFiles returns files written/edited within ttl turns (ttl <= 0 = no limit), most-recent first.

func (WorldState) ActiveSubagentHandles

func (w WorldState) ActiveSubagentHandles(ttl int) []SubagentHandle

ActiveSubagentHandles returns the sub-agents spawned this session whose age is <= ttl (ttl <= 0 = no limit), most-recently-touched first. Surfaces the live specialists (name + thread_id handle + status + summary) so the orchestrator can route a follow-up back to one instead of re-delegating.

func (WorldState) ActiveTodos

func (w WorldState) ActiveTodos(ttl int) []TodoState

ActiveTodos returns todos touched within ttl turns (ttl <= 0 = no limit), most-recently-updated first.

func (WorldState) SkillAge

func (w WorldState) SkillAge(s SkillRef) int

SkillAge returns turns since a skill was last invoked (Turn - LastTurn).

Jump to

Keyboard shortcuts

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