Help us shape the future of Go! Take the 2026 Go Developer Survey

ctxmgr

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package ctxmgr provides automatic context window management for Celeste CLI. The package name is ctxmgr (not context) to avoid collision with the stdlib context package. Import path: github.com/whykusanagi/celeste-cli/v2/cmd/celeste/context

Index

Constants

View Source
const (
	// DefaultMaxToolResultBytes is the maximum size in bytes for a single tool
	// result before it gets capped and spilled to disk: 128 KiB.
	DefaultMaxToolResultBytes = 128 * 1024

	// SpillKeepAge is how long a session's spilled tool results are kept
	// after its last spill.
	SpillKeepAge = 30 * 24 * time.Hour
)
View Source
const LocalDefaultLimit = 8192

LocalDefaultLimit is the fallback window for local endpoints (Ollama, llama.cpp, MLX) with no context_limit set: their window is whatever the server was started with, and small defaults are common (#201).

Variables

View Source
var ModelLimits = map[string]int{

	"gpt-4.1":       1050000,
	"gpt-4.1-mini":  400000,
	"gpt-4.1-nano":  400000,
	"gpt-5.3-codex": 1050000,
	"gpt-5.4":       1050000,
	"gpt-5.4-mini":  400000,
	"gpt-5.4-nano":  400000,
	"gpt-5.4-pro":   1050000,
	"o3":            1050000,
	"o4-mini":       400000,

	"claude-fable-5-1":  1000000,
	"claude-fable-5":    1000000,
	"claude-opus-5-5":   1000000,
	"claude-opus-5":     1000000,
	"claude-opus-4-8":   1000000,
	"claude-opus-4-7":   1000000,
	"claude-opus-4-6":   1000000,
	"claude-sonnet-5-5": 1000000,
	"claude-sonnet-5":   1000000,
	"claude-sonnet-4-6": 1000000,
	"claude-haiku-4-5":  200000,

	"claude-haiku-4-5-20251001":  200000,
	"claude-sonnet-4-5":          200000,
	"claude-sonnet-4-5-20250929": 200000,
	"claude-opus-4-5":            200000,
	"claude-opus-4-5-20251101":   200000,
	"claude-opus-4-1":            200000,
	"claude-opus-4-1-20250805":   200000,

	"grok-build-0.1":              256000,
	"grok-4-1-fast":               2000000,
	"grok-4-1-fast-reasoning":     2000000,
	"grok-4-1-fast-non-reasoning": 2000000,

	"grok-4.3":                     1000000,
	"grok-4.20-0309-reasoning":     1000000,
	"grok-4.20-0309-non-reasoning": 1000000,
	"grok-4.20-multi-agent-0309":   1000000,
	"grok-code-fast-1":             2000000,

	"fugu":                1000000,
	"fugu-ultra":          1000000,
	"fugu-ultra-20260615": 1000000,

	"gemini-flash-latest": 1048576,
	"gemini-2.0-flash":    1048576,

	"openai/gpt-4.1-nano": 1047576,

	"venice-uncensored":                    32000,
	"venice-uncensored-role-play":          128000,
	"deepseek-v3.2":                        160000,
	"qwen3-coder-480b-a35b-instruct":       256000,
	"qwen3-coder-480b-a35b-instruct-turbo": 256000,
	"qwen3-235b-a22b-thinking-2507":        128000,
	"kimi-k2-5":                            256000,
	"zai-org-glm-4.7":                      198000,
	"mistral-small-3-2-24b-instruct":       256000,
	"llama-3.3-70b":                        128000,
	"minimax-m25":                          198000,

	"default": 128000,
}

ModelLimits maps model names to their context window sizes in tokens. Migrated from config/tokens.go.

Functions

func CapToolResult

func CapToolResult(result string, maxBytes int, sessionID, toolCallID, baseDir string) (capped string, wasCapped bool, err error)

CapToolResult checks whether a tool result exceeds maxBytes. If it does, the full result is written to disk at:

{baseDir}/{sessionID}/{toolCallID}.txt

and SnipToolResult's head and tail of it are returned, around a marker that names the file.

If baseDir is empty, ToolResultsBaseDir() is used.

Returns:

  • capped: the (possibly truncated) result string to send to the model
  • wasCapped: true if the result was truncated
  • err: any I/O error from writing the spill file

func EstimateTokens

func EstimateTokens(text string) int

EstimateTokens approximates token count from text length. Uses the rough heuristic of 4 characters per token.

func FormatTokenCount

func FormatTokenCount(tokens int) string

FormatTokenCount formats a token count with K/M suffix for display.

func GetModelLimit

func GetModelLimit(model string) int

GetModelLimit returns the token limit for a model name. Falls back to the "default" entry if the model is not found.

func LoadSpilled

func LoadSpilled(baseDir, id string) (string, error)

LoadSpilled returns the full tool result CapToolResult spilled under baseDir ("" is ToolResultsBaseDir) for a recall id <sessionID>/<toolCallID>, the id its notice names.

func LookupModelLimit

func LookupModelLimit(model string) (int, bool)

LookupModelLimit returns the token limit and whether the model was actually found. Callers that validate a user's configured window need the second value: for an unrecognised model the returned limit is a conservative fallback, not knowledge, and treating it as an upper bound would reject a correct setting. Local models are never in the table — their window is whatever the server was started with.

func PruneToolResults

func PruneToolResults(baseDir, keep string, now time.Time) error

PruneToolResults deletes spilled tool results under baseDir ("" is ToolResultsBaseDir): only spill session directories (spillSessionDir), never the compaction store beside them, whose bodies expire one by one after SpillKeepAge instead (expireCompactStore). Every session directory last changed more than SpillKeepAge ago, then the oldest others idle for spillActiveAge while all of them together are over maxTotalSpillBytes. keep (the session running now) always survives, and so does any session that spilled within spillActiveAge, as another celeste process may still be using it. A missing baseDir is nothing to prune (Aikido 806869375).

func SnipToolResult

func SnipToolResult(result string, maxBytes int, note string) string

SnipToolResult cuts result to at most maxBytes in memory, keeping its head and tail around a "[...snipped N bytes...]" marker, without the spill file CapToolResult writes (2.0 F3). note, if not empty, goes into the marker: callers say there why the middle cannot be recalled and what to do instead. The loop uses it when the spill file cannot be written; sessions and checkpoints use it on tool results loaded from disk. Results at or under maxBytes come back unchanged. Cuts fall on UTF-8 character boundaries. The result is never over maxBytes: a cap too small for the marker gets a plain cut of the head.

func ToolResultsBaseDir

func ToolResultsBaseDir() (string, error)

ToolResultsBaseDir returns the base directory for spilled tool results. Default: ~/.celeste/tool-results

Types

type TokenBudget

type TokenBudget struct {

	// Capacity
	ModelLimit int // Total context window for the model

	// Fixed allocations (set once at session start, updated rarely)
	SystemPromptTokens   int // Tokens consumed by system prompt
	ToolDefinitionTokens int // Tokens consumed by tool/function schemas

	// Dynamic tracking
	HistoryTokens    int // Tokens consumed by conversation history (all messages)
	LastPromptTokens int // Prompt tokens from last API response
	LastCompTokens   int // Completion tokens from last API response

	// Counters
	TurnCount    int // Number of user-assistant turn pairs completed
	CompactCount int // Number of times compaction has been triggered
	// contains filtered or unexported fields
}

TokenBudget tracks token usage across all components of a conversation. It provides fine-grained tracking beyond a simple current/max counter, separating system prompt, tool definitions, conversation history, and per-turn usage to enable intelligent compaction decisions.

func NewTokenBudget

func NewTokenBudget(modelLimit, systemPromptTokens, toolDefTokens int) *TokenBudget

NewTokenBudget creates a TokenBudget for the given model. systemPromptTokens and toolDefTokens represent the fixed token overhead that is sent with every request.

func (*TokenBudget) AddTurn

func (tb *TokenBudget) AddTurn(promptTokens, completionTokens int)

AddTurn records token usage from an API response and increments the turn counter. promptTokens and completionTokens come from the API's usage field. If the API does not return usage, callers should pass estimates.

func (*TokenBudget) Available

func (tb *TokenBudget) Available() int

Available returns tokens remaining before hitting the model limit. This is the space available for the next prompt + completion.

func (*TokenBudget) GetUsagePercent

func (tb *TokenBudget) GetUsagePercent() float64

GetUsagePercent returns usage as a fraction from 0.0 to 1.0.

func (*TokenBudget) GetWarningLevel

func (tb *TokenBudget) GetWarningLevel() string

GetWarningLevel returns a severity string based on current usage. Returns "ok", "warn" (75%), "caution" (85%), or "critical" (95%).

func (*TokenBudget) RecordCompaction

func (tb *TokenBudget) RecordCompaction(historyTokens int)

RecordCompaction records that compaction replaced the history, which now costs about historyTokens. The last API-reported prompt count described the old history, so it is cleared until the next response.

func (*TokenBudget) SetHistoryTokens

func (tb *TokenBudget) SetHistoryTokens(tokens int)

SetHistoryTokens directly sets the history token count. Use this when recalculating after compaction or when API usage data is unavailable.

func (*TokenBudget) ShouldCompactProactive

func (tb *TokenBudget) ShouldCompactProactive(interval int) bool

ShouldCompactProactive returns true when the turn count has reached a multiple of the given interval AND usage is above 50%. This catches slow-growing sessions before they hit the reactive threshold.

func (*TokenBudget) ShouldCompactReactive

func (tb *TokenBudget) ShouldCompactReactive() bool

ShouldCompactReactive returns true when usage has crossed the reactive compaction threshold (80% of model limit). This is checked after every API response.

func (*TokenBudget) Summary

func (tb *TokenBudget) Summary() string

Summary returns a human-readable one-line summary of the budget state.

func (*TokenBudget) TotalUsed

func (tb *TokenBudget) TotalUsed() int

TotalUsed returns the total tokens currently consumed across all components.

Jump to

Keyboard shortcuts

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