feat(config): add context_window, summary_max_tokens, summary_temperature

This commit is contained in:
easyzoom 2026-02-19 10:51:40 +08:00
parent 96fda2e090
commit f6df9991e5
10 changed files with 104 additions and 37 deletions

View file

@ -4,8 +4,11 @@
"workspace": "~/.picoclaw/workspace", "workspace": "~/.picoclaw/workspace",
"restrict_to_workspace": true, "restrict_to_workspace": true,
"model": "gpt4", "model": "gpt4",
"context_window": 8192,
"max_tokens": 8192, "max_tokens": 8192,
"temperature": 0.7, "temperature": 0.7,
"summary_max_tokens": 1024,
"summary_temperature": 0.3,
"max_tool_iterations": 20 "max_tool_iterations": 20
} }
}, },

View file

@ -15,22 +15,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 ContextWindow int // Token limit used for summarization threshold and message-size guard (not the LLM response limit).
Temperature float64 MaxTokens int // Max tokens allowed in a single LLM response (passed to provider Chat).
ContextWindow int Temperature float64
Provider providers.LLMProvider SummaryMaxTokens int
Sessions *session.SessionManager SummaryTemperature float64
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.
@ -78,11 +80,26 @@ func NewAgentInstance(
maxIter = 20 maxIter = 20
} }
contextWindow := defaults.ContextWindow
if contextWindow <= 0 {
contextWindow = 8192
}
maxTokens := defaults.MaxTokens maxTokens := defaults.MaxTokens
if maxTokens == 0 { if maxTokens <= 0 {
maxTokens = 8192 maxTokens = 8192
} }
sumMaxTok := defaults.SummaryMaxTokens
if sumMaxTok <= 0 {
sumMaxTok = 1024
}
sumTemp := defaults.SummaryTemperature
if sumTemp <= 0 {
sumTemp = 0.3
}
temperature := 0.7 temperature := 0.7
if defaults.Temperature != nil { if defaults.Temperature != nil {
temperature = *defaults.Temperature temperature = *defaults.Temperature
@ -96,22 +113,24 @@ func NewAgentInstance(
candidates := providers.ResolveCandidates(modelCfg, defaults.Provider) candidates := providers.ResolveCandidates(modelCfg, defaults.Provider)
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, ContextWindow: contextWindow,
Temperature: temperature, MaxTokens: maxTokens,
ContextWindow: maxTokens, Temperature: temperature,
Provider: provider, SummaryMaxTokens: sumMaxTok,
Sessions: sessionsManager, SummaryTemperature: sumTemp,
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

@ -977,8 +977,8 @@ func (al *AgentLoop) summarizeSession(agent *AgentInstance, sessionKey string) {
nil, nil,
agent.Model, agent.Model,
map[string]any{ map[string]any{
"max_tokens": 1024, "max_tokens": agent.SummaryMaxTokens,
"temperature": 0.3, "temperature": agent.SummaryTemperature,
}, },
) )
if err == nil { if err == nil {
@ -1027,8 +1027,8 @@ func (al *AgentLoop) summarizeBatch(
nil, nil,
agent.Model, agent.Model,
map[string]any{ map[string]any{
"max_tokens": 1024, "max_tokens": agent.SummaryMaxTokens,
"temperature": 0.3, "temperature": agent.SummaryTemperature,
}, },
) )
if err != nil { if err != nil {

View file

@ -28,6 +28,7 @@ func TestRecordLastChannel(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -73,6 +74,7 @@ func TestRecordLastChatID(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -118,6 +120,7 @@ func TestNewAgentLoop_StateInitialized(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -154,6 +157,7 @@ func TestToolRegistry_ToolRegistration(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -200,6 +204,7 @@ func TestToolContext_Updates(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -231,6 +236,7 @@ func TestToolRegistry_GetDefinitions(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -275,6 +281,7 @@ func TestAgentLoop_GetStartupInfo(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -322,6 +329,7 @@ func TestAgentLoop_Stop(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -450,6 +458,7 @@ func TestToolResult_SilentToolDoesNotSendUserMessage(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -492,6 +501,7 @@ func TestToolResult_UserFacingToolDoesSendMessage(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },
@ -563,6 +573,7 @@ func TestAgentLoop_ContextExhaustionRetry(t *testing.T) {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: tmpDir, Workspace: tmpDir,
Model: "test-model", Model: "test-model",
ContextWindow: 4096,
MaxTokens: 4096, MaxTokens: 4096,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },

View file

@ -30,6 +30,7 @@ func testCfg(agents []config.AgentConfig) *config.Config {
Defaults: config.AgentDefaults{ Defaults: config.AgentDefaults{
Workspace: "/tmp/picoclaw-test-registry", Workspace: "/tmp/picoclaw-test-registry",
Model: "gpt-4", Model: "gpt-4",
ContextWindow: 8192,
MaxTokens: 8192, MaxTokens: 8192,
MaxToolIterations: 10, MaxToolIterations: 10,
}, },

View file

@ -178,8 +178,11 @@ type AgentDefaults struct {
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"`
ContextWindow int `json:"context_window" env:"PICOCLAW_AGENTS_DEFAULTS_CONTEXT_WINDOW"`
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"`
SummaryMaxTokens int `json:"summary_max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_SUMMARY_MAX_TOKENS"`
SummaryTemperature float64 `json:"summary_temperature" env:"PICOCLAW_AGENTS_DEFAULTS_SUMMARY_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"`
} }

View file

@ -215,6 +215,15 @@ func TestDefaultConfig_Model(t *testing.T) {
} }
} }
// TestDefaultConfig_ContextWindow verifies context_window has default value
func TestDefaultConfig_ContextWindow(t *testing.T) {
cfg := DefaultConfig()
if cfg.Agents.Defaults.ContextWindow == 0 {
t.Error("ContextWindow should not be zero")
}
}
// TestDefaultConfig_MaxTokens verifies max tokens has default value // TestDefaultConfig_MaxTokens verifies max tokens has default value
func TestDefaultConfig_MaxTokens(t *testing.T) { func TestDefaultConfig_MaxTokens(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
@ -337,6 +346,9 @@ func TestConfig_Complete(t *testing.T) {
if cfg.Agents.Defaults.Temperature != nil { if cfg.Agents.Defaults.Temperature != nil {
t.Error("Temperature should be nil when not provided") t.Error("Temperature should be nil when not provided")
} }
if cfg.Agents.Defaults.ContextWindow == 0 {
t.Error("ContextWindow should not be zero")
}
if cfg.Agents.Defaults.MaxTokens == 0 { if cfg.Agents.Defaults.MaxTokens == 0 {
t.Error("MaxTokens should not be zero") t.Error("MaxTokens should not be zero")
} }

View file

@ -14,7 +14,10 @@ func DefaultConfig() *Config {
RestrictToWorkspace: true, RestrictToWorkspace: true,
Provider: "", Provider: "",
Model: "glm-4.7", Model: "glm-4.7",
ContextWindow: 8192,
MaxTokens: 8192, MaxTokens: 8192,
SummaryMaxTokens: 1024,
SummaryTemperature: 0.3,
Temperature: nil, // nil means use provider default Temperature: nil, // nil means use provider default
MaxToolIterations: 20, MaxToolIterations: 20,
}, },

View file

@ -75,12 +75,24 @@ func ConvertConfig(data map[string]any) (*config.Config, []string, error) {
if v, ok := getString(defaults, "model"); ok { if v, ok := getString(defaults, "model"); ok {
cfg.Agents.Defaults.Model = v cfg.Agents.Defaults.Model = v
} }
if v, ok := getFloat(defaults, "context_window"); ok {
cfg.Agents.Defaults.ContextWindow = int(v)
} else if v, ok := getFloat(defaults, "max_tokens"); ok {
// Backward compat: old configs used max_tokens for both; set context_window from it.
cfg.Agents.Defaults.ContextWindow = int(v)
}
if v, ok := getFloat(defaults, "max_tokens"); ok { if v, ok := getFloat(defaults, "max_tokens"); ok {
cfg.Agents.Defaults.MaxTokens = int(v) cfg.Agents.Defaults.MaxTokens = int(v)
} }
if v, ok := getFloat(defaults, "temperature"); ok { if v, ok := getFloat(defaults, "temperature"); ok {
cfg.Agents.Defaults.Temperature = &v cfg.Agents.Defaults.Temperature = &v
} }
if v, ok := getFloat(defaults, "summary_max_tokens"); ok {
cfg.Agents.Defaults.SummaryMaxTokens = int(v)
}
if v, ok := getFloat(defaults, "summary_temperature"); ok {
cfg.Agents.Defaults.SummaryTemperature = v
}
if v, ok := getFloat(defaults, "max_tool_iterations"); ok { if v, ok := getFloat(defaults, "max_tool_iterations"); ok {
cfg.Agents.Defaults.MaxToolIterations = int(v) cfg.Agents.Defaults.MaxToolIterations = int(v)
} }

View file

@ -272,6 +272,9 @@ func TestConvertConfig(t *testing.T) {
if cfg.Agents.Defaults.Model != "claude-3-opus" { if cfg.Agents.Defaults.Model != "claude-3-opus" {
t.Errorf("Model = %q, want %q", cfg.Agents.Defaults.Model, "claude-3-opus") t.Errorf("Model = %q, want %q", cfg.Agents.Defaults.Model, "claude-3-opus")
} }
if cfg.Agents.Defaults.ContextWindow != 4096 {
t.Errorf("ContextWindow = %d, want 4096 (backward compat from max_tokens)", cfg.Agents.Defaults.ContextWindow)
}
if cfg.Agents.Defaults.MaxTokens != 4096 { if cfg.Agents.Defaults.MaxTokens != 4096 {
t.Errorf("MaxTokens = %d, want %d", cfg.Agents.Defaults.MaxTokens, 4096) t.Errorf("MaxTokens = %d, want %d", cfg.Agents.Defaults.MaxTokens, 4096)
} }