plan

package
v0.6.0 Latest Latest
Warning

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

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

Documentation

Overview

Package plan manages token budget windows and compaction. Compact and Calibrated adapt provider messages to bounded context windows. Summarize turns the messages a compaction drops into one validated, bounded summary document, through one bounded provider.Completer call. A summary failure is a caller-visible error; no structural fallback exists. The loop wiring lives in agentloop.

Layering: context holds three public packages. plan consumes ref and provider; agentloop wires plan above it. budget states byte caps beside it. The durable session contract and its store live in contextstate (github.com/MiviaLabs/mivia-ai-sdk/contextstate).

See docs/history/context/plan.md.

Index

Constants

View Source
const (
	// DefaultSmoothingFactor is the EWMA weight Observe applies when
	// Calibrate receives a non-positive alpha.
	DefaultSmoothingFactor = 0.3
	// MinCorrectionFactor is the floor Observe clamps the correction
	// factor to.
	MinCorrectionFactor = 0.5
	// MaxCorrectionFactor is the ceiling Observe clamps the correction
	// factor to.
	MaxCorrectionFactor = 2.0
)

EWMA smoothing bounds for Calibrated.

View Source
const (
	// DefaultTriggerPercent compacts at this percent of Window.Budget().
	DefaultTriggerPercent = 100
	// DefaultTargetPercent compacts down to this percent of Budget().
	DefaultTargetPercent = 10
	// DefaultRecentTail is the message-count bound of the tail fill.
	DefaultRecentTail = 8
	// MaxRecentTail is the highest tail bound a caller may set.
	MaxRecentTail = 64
	// CompactionAlgorithm names the idempotency-key fingerprint scheme.
	CompactionAlgorithm = "context-compact-v1"
)

Compaction thresholds and bounds.

View Source
const MaxExcerptTotalBytes = 16 * 1024

MaxExcerptTotalBytes bounds the whole source-excerpt section of one summarize prompt.

View Source
const MaxFieldBytes = 2 * 1024

MaxFieldBytes bounds every individual Summary text field and every list item.

View Source
const MaxItems = 32

MaxItems bounds every Summary list.

View Source
const SummaryMessageName = "context-summary"

SummaryMessageName is the provider.Message.Name of the injected summary message. Compaction preserves it through PreserveNames.

View Source
const SummaryPreamble = "This message restates the conversation that compaction removed."

SummaryPreamble is the framing line SummaryMessage places before Render output; Render itself carries no preamble.

View Source
const SummaryTimeout = 20 * time.Second

SummaryTimeout bounds one summarize call.

Variables

View Source
var (
	// ErrNoMessages is Compact's error for an empty message list.
	ErrNoMessages = errors.New("plan: no messages to compact")
	// ErrEstimateFailed is Compact's error when the token estimator
	// fails.
	ErrEstimateFailed = errors.New("plan: token estimate failed")
	// ErrRetentionOverflow is Compact's error when the retention set
	// alone exceeds the window budget.
	ErrRetentionOverflow = errors.New("plan: retention set alone exceeds the window")
	// ErrNoObjective is Compact's error when no user message exists to
	// retain as the objective.
	ErrNoObjective = errors.New("plan: no user message to retain as objective")
)

Sentinel errors for Compact; test with errors.Is.

View Source
var (
	// ErrNoMessagesToSummarize is Summarize's error for an empty message list.
	ErrNoMessagesToSummarize = errors.New("plan: no messages to summarize")
	// ErrInvalidReply is Summarize's error when the reply fails
	// strict parsing or Summary.Validate.
	ErrInvalidReply = errors.New("plan: reply failed strict parsing or validation")
	// ErrCallFailed is Summarize's error when the Completer call
	// itself fails.
	ErrCallFailed = errors.New("plan: summary call failed")
	// ErrSummarySkipped is the sentinel a summarize adapter returns
	// to decline summary injection; the concrete Summarizer never
	// returns it. An adapter may wrap it with a reason, for example
	// fmt.Errorf("%w: %s", ErrSummarySkipped, reason); agentloop's
	// summarizeDropped matches the skip through errors.Is, so a
	// wrapped sentinel still takes the skip path and the wrapping
	// error's own message carries the reason to the caller.
	ErrSummarySkipped = errors.New("plan: summary skipped")
)

Sentinel errors; test with errors.Is.

View Source
var (
	// ErrMaxTokensNotPositive is Validate's error when MaxTokens <= 0.
	// Kept separate from ErrInvalidOptions: agentloop/agentloop_test
	// asserts on this sentinel by name, so it stays a distinct value.
	ErrMaxTokensNotPositive = errors.New("plan: max tokens must be positive")
	// ErrInvalidOptions is the shared sentinel for every other
	// construction-time argument error in this package. Wrap it with
	// fmt.Errorf("%w: %s", ErrInvalidOptions, "<field>: <rule>") and
	// test with errors.Is plus a substring check on the field name.
	ErrInvalidOptions = errors.New("plan: invalid options")
)

Sentinel errors for Window.Validate; test with errors.Is.

Functions

func SummaryMessage

func SummaryMessage(s Summary) provider.Message

SummaryMessage renders s as one RoleUser message named SummaryMessageName, whose Content is SummaryPreamble, one newline, then s.Render().

func TokenEstimate

func TokenEstimate(n int) int

TokenEstimate prices n bytes at n/4 tokens, minimum one for non-zero input, zero for zero input.

Types

type Calibrated

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

Calibrated wraps a provider.TokenEstimator with an exponentially weighted moving average, corrected after each completed turn through Observe. A *Calibrated implements provider.TokenEstimator. Safe for concurrent use: one mutex guards factor, the only mutable field.

func Calibrate

func Calibrate(est provider.TokenEstimator, alpha float64) *Calibrated

Calibrate wraps est with an EWMA correction factor. alpha is the EWMA smoothing weight in (0, 1]; a value outside that range, including zero or negative, falls back to DefaultSmoothingFactor. A nil est is a caller error caught at the first EstimateTokens call, not at construction, matching the wrapped interface's own contract.

func (*Calibrated) EstimateTokens

func (c *Calibrated) EstimateTokens(req provider.Request) (int, error)

EstimateTokens calls the wrapped estimator, then scales the result by the current EWMA correction factor, itself always within [MinCorrectionFactor, MaxCorrectionFactor].

func (*Calibrated) Observe

func (c *Calibrated) Observe(estimated, actual int)

Observe records one completed turn: estimated is the value EstimateTokens returned to the caller for that turn - the already-corrected figure, not a raw pre-scale count - and actual is the real provider.Usage.TotalTokens for the same turn. Observe corrects factor multiplicatively against estimated, so the fixed point is unchanged from before this fix: a corrected estimate that tracks actual. The result is clamped to [MinCorrectionFactor, MaxCorrectionFactor]. A non-positive estimated or a non-positive actual is a no-op.

type CompactResult

type CompactResult struct {
	Kept          []provider.Message
	Dropped       []provider.Message
	BeforeTokens  int
	AfterTokens   int
	TriggerTokens int
	TargetTokens  int
	Compacted     bool
	Key           string
}

CompactResult is Compact's output.

func Compact

Compact applies the trigger check and the retention policy. An invalid window fails Window.Validate before any estimate. A request at or above the trigger compacts; below it passes through with Compacted false. The retention set is mandatory; the tail fill is optional and stops at the first unit that breaks contiguity, the message-count bound, or the target. Kept preserves the original relative order. The Key is deterministic per input.

type Compaction

type Compaction struct {
	TriggerPercent int
	TargetPercent  int
	TargetTokens   int
	RecentTail     int
	PreserveNames  []string
}

Compaction configures compaction thresholds and retention. The zero value means the defaults, never "disabled": TriggerPercent zero means DefaultTriggerPercent, TargetPercent zero means DefaultTargetPercent, RecentTail zero means DefaultRecentTail.

func (Compaction) Validate

func (c Compaction) Validate() error

Validate rejects percents outside [0, 100], a negative TargetTokens or RecentTail, a RecentTail over MaxRecentTail, an empty but present PreserveNames entry, and duplicate PreserveNames entries. When TargetTokens is zero, a TargetPercent at or above the resolved TriggerPercent is rejected; when TargetTokens is positive, that comparison is skipped and Window.Validate instead rejects a TargetTokens at or above Budget().

type Summarizer

type Summarizer struct {
	MaxTokens *int
	// contains filtered or unexported fields
}

Summarizer adapts one provider.Completer to summary generation. MaxTokens caps the summarize call's provider.Request.MaxTokens. Nil means the Completer's own default, the same behavior as before this field existed. Set it before the first Summarize call. Summarize reads MaxTokens exactly once, on the first call, and freezes that snapshot for every later call on this Summarizer; a write to MaxTokens after the first Summarize call has no effect. Summarize makes no promise about concurrent calls on the same Summarizer; use one Summarizer from one goroutine at a time.

func NewSummarizer

func NewSummarizer(c provider.Completer) (*Summarizer, error)

NewSummarizer binds one Completer. A nil Completer wraps ErrInvalidOptions.

func (*Summarizer) Summarize

func (s *Summarizer) Summarize(ctx context.Context, msgs []provider.Message) (Summary, error)

Summarize makes one bounded Completer call over msgs and returns the validated Summary. Never retries. Any failure is caller-visible. Summarize reads s.MaxTokens exactly once, on the first call, and freezes it for every later call; see the Summarizer doc comment.

type Summary

type Summary struct {
	Objective       string   `json:"objective"`
	State           string   `json:"state"`
	Decisions       []string `json:"decisions,omitempty"`
	Evidence        []string `json:"evidence,omitempty"`
	ChangedSurfaces []string `json:"changed_surfaces,omitempty"`
	OpenWork        []string `json:"open_work,omitempty"`
	Risks           []string `json:"risks,omitempty"`
}

Summary is one validated summary document. Data only: no tool, policy, or credential fields. The json tags pin the host durable schema keys; version and source_range stay with the caller.

func (Summary) Render

func (s Summary) Render() string

Render returns the deterministic text form of s: one labeled line or bullet per field, in field order.

func (Summary) Validate

func (s Summary) Validate() error

Validate enforces every bound this package claims: valid UTF-8, no control characters, non-empty Objective and State, MaxFieldBytes per field and per item, at most MaxItems per list, no duplicate items, and no blank item (empty or whitespace-only).

type Window

type Window struct {
	MaxTokens  int
	Reserve    int
	Compaction Compaction
}

Window is the token budget for one planned request. MaxTokens is the model's context window; Reserve is the headroom Plan never spends, held back for the model's own reply. Compaction carries the compaction thresholds and retention rules; its zero value means the defaults, never "disabled".

func (Window) Budget

func (w Window) Budget() int

Budget returns MaxTokens - Reserve, the tokens Plan may spend on Request.Messages.

func (Window) CompactTarget

func (w Window) CompactTarget() int

CompactTarget returns the target in tokens: TargetTokens when positive, else Budget times TargetPercent, floored.

func (Window) CompactTrigger

func (w Window) CompactTrigger() int

CompactTrigger returns the trigger in tokens: Budget times TriggerPercent, floored.

func (Window) Validate

func (w Window) Validate() error

Validate rejects a non-positive MaxTokens, a negative Reserve, a Reserve at or above MaxTokens, an invalid Compaction, and a positive Compaction.TargetTokens at or above Budget().

Jump to

Keyboard shortcuts

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