mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
7a7e205cc8
Address remaining review feedback: 1) Add HistoryTokens field to ContextUsage/ContextStats, showing history-only token count in /context and frontend UI alongside SummarizeAtTokens so users can see the actual summarization trigger comparison. 2) Remove .codebuddy/github-contribute/ state files accidentally included in the PR.
92 lines
3.0 KiB
Go
92 lines
3.0 KiB
Go
package agent
|
|
|
|
import (
|
|
"github.com/sipeed/picoclaw/pkg/bus"
|
|
)
|
|
|
|
// computeContextUsage estimates current context window consumption for the
|
|
// given agent and session. Includes history, system prompt (with dynamic context,
|
|
// summary, and skills — mirroring BuildMessages composition), and tool definitions.
|
|
// The output reserve (MaxTokens) is not counted as "used" but reduces the
|
|
// effective budget, matching isOverContextBudget's compression trigger:
|
|
//
|
|
// compress when: history + system + tools + maxTokens > contextWindow
|
|
// equivalent to: history + system + tools > contextWindow - maxTokens
|
|
//
|
|
// Returns nil when the agent or session is unavailable.
|
|
func computeContextUsage(agent *AgentInstance, sessionKey string) *bus.ContextUsage {
|
|
if agent == nil || agent.Sessions == nil {
|
|
return nil
|
|
}
|
|
contextWindow := agent.ContextWindow
|
|
if contextWindow <= 0 {
|
|
return nil
|
|
}
|
|
|
|
// History tokens
|
|
history := agent.Sessions.GetHistory(sessionKey)
|
|
historyTokens := 0
|
|
for _, m := range history {
|
|
historyTokens += EstimateMessageTokens(m)
|
|
}
|
|
|
|
// System message tokens: uses EstimateSystemTokens which mirrors
|
|
// the full system message composition in BuildMessages (static prompt,
|
|
// dynamic context, active skills, summary with wrapping prefix).
|
|
systemTokens := 0
|
|
if agent.ContextBuilder != nil {
|
|
summary := agent.Sessions.GetSummary(sessionKey)
|
|
// Pass nil for active skills: skills are only injected when the user
|
|
// explicitly activates them via /use, which is rare. Using nil matches
|
|
// the common case and avoids over-counting all installed skills.
|
|
systemTokens = agent.ContextBuilder.EstimateSystemTokens(summary, nil)
|
|
}
|
|
|
|
// Tool definition tokens
|
|
toolTokens := 0
|
|
if agent.Tools != nil {
|
|
toolTokens = EstimateToolDefsTokens(agent.Tools.ToProviderDefs())
|
|
}
|
|
|
|
// Used = history + system (includes summary) + tools
|
|
usedTokens := historyTokens + systemTokens + toolTokens
|
|
|
|
// Effective budget = contextWindow minus output reserve (maxTokens)
|
|
effectiveWindow := contextWindow - agent.MaxTokens
|
|
if effectiveWindow < 0 {
|
|
effectiveWindow = contextWindow
|
|
}
|
|
|
|
// compressAt = effectiveWindow: aligns with isOverContextBudget's
|
|
// proactive trigger (msgTokens + toolTokens + maxTokens > contextWindow).
|
|
compressAt := effectiveWindow
|
|
|
|
// summarizeAt = soft summarization trigger: matches maybeSummarize's
|
|
// threshold (contextWindow * SummarizeTokenPercent / 100).
|
|
//
|
|
// The engine compares this against history-message tokens ONLY (not
|
|
// UsedTokens). HistoryTokens is exposed alongside UsedTokens so the
|
|
// UI can show both values and avoid user confusion.
|
|
summarizeAt := contextWindow * agent.SummarizeTokenPercent / 100
|
|
if summarizeAt <= 0 {
|
|
summarizeAt = compressAt
|
|
}
|
|
|
|
usedPercent := 0
|
|
if compressAt > 0 {
|
|
usedPercent = usedTokens * 100 / compressAt
|
|
}
|
|
if usedPercent > 100 {
|
|
usedPercent = 100
|
|
}
|
|
|
|
return &bus.ContextUsage{
|
|
UsedTokens: usedTokens,
|
|
TotalTokens: contextWindow,
|
|
HistoryTokens: historyTokens,
|
|
CompressAtTokens: compressAt,
|
|
SummarizeAtTokens: summarizeAt,
|
|
UsedPercent: usedPercent,
|
|
}
|
|
}
|