feat: make summarization message threshold and token percent configurable (#854)

This commit is contained in:
Rahul Bansal 2026-03-01 22:12:32 +05:30
parent 3b76824665
commit 7e9b78bef5
5 changed files with 82 additions and 52 deletions

View file

@ -16,22 +16,24 @@ import (
// AgentInstance represents a fully configured agent with its own workspace, // AgentInstance represents a fully configured agent with its own workspace,
// session manager, context builder, and tool registry. // session manager, context builder, and tool registry.
type AgentInstance struct { type AgentInstance struct {
ID string ID string
Name string Name string
Model string Model string
Fallbacks []string Fallbacks []string
Workspace string Workspace string
MaxIterations int MaxIterations int
MaxTokens int MaxTokens int
Temperature float64 Temperature float64
ContextWindow int ContextWindow int
Provider providers.LLMProvider SummarizeMessageThreshold int
Sessions *session.SessionManager SummarizeTokenPercent int
ContextBuilder *ContextBuilder Provider providers.LLMProvider
Tools *tools.ToolRegistry Sessions *session.SessionManager
Subagents *config.SubagentsConfig ContextBuilder *ContextBuilder
SkillsFilter []string Tools *tools.ToolRegistry
Candidates []providers.FallbackCandidate Subagents *config.SubagentsConfig
SkillsFilter []string
Candidates []providers.FallbackCandidate
} }
// NewAgentInstance creates an agent instance from config. // NewAgentInstance creates an agent instance from config.
@ -93,6 +95,16 @@ func NewAgentInstance(
temperature = *defaults.Temperature temperature = *defaults.Temperature
} }
summarizeMessageThreshold := defaults.SummarizeMessageThreshold
if summarizeMessageThreshold == 0 {
summarizeMessageThreshold = 20
}
summarizeTokenPercent := defaults.SummarizeTokenPercent
if summarizeTokenPercent == 0 {
summarizeTokenPercent = 75
}
// Resolve fallback candidates // Resolve fallback candidates
modelCfg := providers.ModelConfig{ modelCfg := providers.ModelConfig{
Primary: model, Primary: model,
@ -141,22 +153,24 @@ func NewAgentInstance(
candidates := providers.ResolveCandidatesWithLookup(modelCfg, defaults.Provider, resolveFromModelList) candidates := providers.ResolveCandidatesWithLookup(modelCfg, defaults.Provider, resolveFromModelList)
return &AgentInstance{ return &AgentInstance{
ID: agentID, ID: agentID,
Name: agentName, Name: agentName,
Model: model, Model: model,
Fallbacks: fallbacks, Fallbacks: fallbacks,
Workspace: workspace, Workspace: workspace,
MaxIterations: maxIter, MaxIterations: maxIter,
MaxTokens: maxTokens, MaxTokens: maxTokens,
Temperature: temperature, Temperature: temperature,
ContextWindow: maxTokens, ContextWindow: maxTokens,
Provider: provider, SummarizeMessageThreshold: summarizeMessageThreshold,
Sessions: sessionsManager, SummarizeTokenPercent: summarizeTokenPercent,
ContextBuilder: contextBuilder, Provider: provider,
Tools: toolsRegistry, Sessions: sessionsManager,
Subagents: subagents, ContextBuilder: contextBuilder,
SkillsFilter: skillsFilter, Tools: toolsRegistry,
Candidates: candidates, Subagents: subagents,
SkillsFilter: skillsFilter,
Candidates: candidates,
} }
} }

View file

@ -958,9 +958,9 @@ func (al *AgentLoop) updateToolContexts(agent *AgentInstance, channel, chatID st
func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey, channel, chatID string) { func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey, channel, chatID string) {
newHistory := agent.Sessions.GetHistory(sessionKey) newHistory := agent.Sessions.GetHistory(sessionKey)
tokenEstimate := al.estimateTokens(newHistory) tokenEstimate := al.estimateTokens(newHistory)
threshold := agent.ContextWindow * 75 / 100 threshold := agent.ContextWindow * agent.SummarizeTokenPercent / 100
if len(newHistory) > 20 || tokenEstimate > threshold { if len(newHistory) > agent.SummarizeMessageThreshold || tokenEstimate > threshold {
summarizeKey := agent.ID + ":" + sessionKey summarizeKey := agent.ID + ":" + sessionKey
if _, loading := al.summarizing.LoadOrStore(summarizeKey, true); !loading { if _, loading := al.summarizing.LoadOrStore(summarizeKey, true); !loading {
go func() { go func() {

View file

@ -168,17 +168,19 @@ type SessionConfig struct {
} }
type AgentDefaults struct { type AgentDefaults struct {
Workspace string `json:"workspace" env:"PICOCLAW_AGENTS_DEFAULTS_WORKSPACE"` Workspace string `json:"workspace" env:"PICOCLAW_AGENTS_DEFAULTS_WORKSPACE"`
RestrictToWorkspace bool `json:"restrict_to_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_RESTRICT_TO_WORKSPACE"` RestrictToWorkspace bool `json:"restrict_to_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_RESTRICT_TO_WORKSPACE"`
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"` Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL_NAME"` ModelName string `json:"model_name,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL_NAME"`
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"` // Deprecated: use model_name instead Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"` // Deprecated: use model_name instead
ModelFallbacks []string `json:"model_fallbacks,omitempty"` ModelFallbacks []string `json:"model_fallbacks,omitempty"`
ImageModel string `json:"image_model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_IMAGE_MODEL"` ImageModel string `json:"image_model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_IMAGE_MODEL"`
ImageModelFallbacks []string `json:"image_model_fallbacks,omitempty"` ImageModelFallbacks []string `json:"image_model_fallbacks,omitempty"`
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"` MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
Temperature *float64 `json:"temperature,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"` Temperature *float64 `json:"temperature,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"`
MaxToolIterations int `json:"max_tool_iterations" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"` MaxToolIterations int `json:"max_tool_iterations" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"`
SummarizeMessageThreshold int `json:"summarize_message_threshold" env:"PICOCLAW_AGENTS_DEFAULTS_SUMMARIZE_MESSAGE_THRESHOLD"`
SummarizeTokenPercent int `json:"summarize_token_percent" env:"PICOCLAW_AGENTS_DEFAULTS_SUMMARIZE_TOKEN_PERCENT"`
} }
// GetModelName returns the effective model name for the agent defaults. // GetModelName returns the effective model name for the agent defaults.

View file

@ -435,6 +435,18 @@ func TestLoadConfig_WebToolsProxy(t *testing.T) {
} }
// TestDefaultConfig_DMScope verifies the default dm_scope value // TestDefaultConfig_DMScope verifies the default dm_scope value
// TestDefaultConfig_SummarizationThresholds verifies summarization defaults
func TestDefaultConfig_SummarizationThresholds(t *testing.T) {
cfg := DefaultConfig()
if cfg.Agents.Defaults.SummarizeMessageThreshold != 20 {
t.Errorf("SummarizeMessageThreshold = %d, want 20", cfg.Agents.Defaults.SummarizeMessageThreshold)
}
if cfg.Agents.Defaults.SummarizeTokenPercent != 75 {
t.Errorf("SummarizeTokenPercent = %d, want 75", cfg.Agents.Defaults.SummarizeTokenPercent)
}
}
func TestDefaultConfig_DMScope(t *testing.T) { func TestDefaultConfig_DMScope(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()

View file

@ -10,13 +10,15 @@ func DefaultConfig() *Config {
return &Config{ return &Config{
Agents: AgentsConfig{ Agents: AgentsConfig{
Defaults: AgentDefaults{ Defaults: AgentDefaults{
Workspace: "~/.picoclaw/workspace", Workspace: "~/.picoclaw/workspace",
RestrictToWorkspace: true, RestrictToWorkspace: true,
Provider: "", Provider: "",
Model: "", Model: "",
MaxTokens: 32768, MaxTokens: 32768,
Temperature: nil, // nil means use provider default Temperature: nil, // nil means use provider default
MaxToolIterations: 50, MaxToolIterations: 50,
SummarizeMessageThreshold: 20,
SummarizeTokenPercent: 75,
}, },
}, },
Bindings: []AgentBinding{}, Bindings: []AgentBinding{},