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",
"restrict_to_workspace": true,
"model": "gpt4",
"context_window": 8192,
"max_tokens": 8192,
"temperature": 0.7,
"summary_max_tokens": 1024,
"summary_temperature": 0.3,
"max_tool_iterations": 20
}
},

View file

@ -15,22 +15,24 @@ import (
// AgentInstance represents a fully configured agent with its own workspace,
// session manager, context builder, and tool registry.
type AgentInstance struct {
ID string
Name string
Model string
Fallbacks []string
Workspace string
MaxIterations int
MaxTokens int
Temperature float64
ContextWindow int
Provider providers.LLMProvider
Sessions *session.SessionManager
ContextBuilder *ContextBuilder
Tools *tools.ToolRegistry
Subagents *config.SubagentsConfig
SkillsFilter []string
Candidates []providers.FallbackCandidate
ID string
Name string
Model string
Fallbacks []string
Workspace string
MaxIterations int
ContextWindow int // Token limit used for summarization threshold and message-size guard (not the LLM response limit).
MaxTokens int // Max tokens allowed in a single LLM response (passed to provider Chat).
Temperature float64
SummaryMaxTokens int
SummaryTemperature float64
Provider providers.LLMProvider
Sessions *session.SessionManager
ContextBuilder *ContextBuilder
Tools *tools.ToolRegistry
Subagents *config.SubagentsConfig
SkillsFilter []string
Candidates []providers.FallbackCandidate
}
// NewAgentInstance creates an agent instance from config.
@ -78,11 +80,26 @@ func NewAgentInstance(
maxIter = 20
}
contextWindow := defaults.ContextWindow
if contextWindow <= 0 {
contextWindow = 8192
}
maxTokens := defaults.MaxTokens
if maxTokens == 0 {
if maxTokens <= 0 {
maxTokens = 8192
}
sumMaxTok := defaults.SummaryMaxTokens
if sumMaxTok <= 0 {
sumMaxTok = 1024
}
sumTemp := defaults.SummaryTemperature
if sumTemp <= 0 {
sumTemp = 0.3
}
temperature := 0.7
if defaults.Temperature != nil {
temperature = *defaults.Temperature
@ -96,22 +113,24 @@ func NewAgentInstance(
candidates := providers.ResolveCandidates(modelCfg, defaults.Provider)
return &AgentInstance{
ID: agentID,
Name: agentName,
Model: model,
Fallbacks: fallbacks,
Workspace: workspace,
MaxIterations: maxIter,
MaxTokens: maxTokens,
Temperature: temperature,
ContextWindow: maxTokens,
Provider: provider,
Sessions: sessionsManager,
ContextBuilder: contextBuilder,
Tools: toolsRegistry,
Subagents: subagents,
SkillsFilter: skillsFilter,
Candidates: candidates,
ID: agentID,
Name: agentName,
Model: model,
Fallbacks: fallbacks,
Workspace: workspace,
MaxIterations: maxIter,
ContextWindow: contextWindow,
MaxTokens: maxTokens,
Temperature: temperature,
SummaryMaxTokens: sumMaxTok,
SummaryTemperature: sumTemp,
Provider: provider,
Sessions: sessionsManager,
ContextBuilder: contextBuilder,
Tools: toolsRegistry,
Subagents: subagents,
SkillsFilter: skillsFilter,
Candidates: candidates,
}
}

View file

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

View file

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

View file

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

View file

@ -178,8 +178,11 @@ type AgentDefaults struct {
ModelFallbacks []string `json:"model_fallbacks,omitempty"`
ImageModel string `json:"image_model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_IMAGE_MODEL"`
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"`
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"`
}

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
func TestDefaultConfig_MaxTokens(t *testing.T) {
cfg := DefaultConfig()
@ -337,6 +346,9 @@ func TestConfig_Complete(t *testing.T) {
if cfg.Agents.Defaults.Temperature != nil {
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 {
t.Error("MaxTokens should not be zero")
}

View file

@ -14,7 +14,10 @@ func DefaultConfig() *Config {
RestrictToWorkspace: true,
Provider: "",
Model: "glm-4.7",
ContextWindow: 8192,
MaxTokens: 8192,
SummaryMaxTokens: 1024,
SummaryTemperature: 0.3,
Temperature: nil, // nil means use provider default
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 {
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 {
cfg.Agents.Defaults.MaxTokens = int(v)
}
if v, ok := getFloat(defaults, "temperature"); ok {
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 {
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" {
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 {
t.Errorf("MaxTokens = %d, want %d", cfg.Agents.Defaults.MaxTokens, 4096)
}