Add configurable timeout for LLM provider and session summarization

Replaces hardcoded 120s timeout with a timeout field in agents.defaults config, allowing users running local/slow models to avoid request timeouts.
This commit is contained in:
John Sykes 2026-02-16 02:57:32 -05:00
parent 1d748fb742
commit 406c996763
4 changed files with 17 additions and 5 deletions

View file

@ -6,7 +6,8 @@
"model": "glm-4.7", "model": "glm-4.7",
"max_tokens": 8192, "max_tokens": 8192,
"temperature": 0.7, "temperature": 0.7,
"max_tool_iterations": 20 "max_tool_iterations": 20,
"timeout": 120
} }
}, },
"channels": { "channels": {

View file

@ -41,6 +41,7 @@ type AgentLoop struct {
contextBuilder *ContextBuilder contextBuilder *ContextBuilder
tools *tools.ToolRegistry tools *tools.ToolRegistry
running atomic.Bool running atomic.Bool
timeout int
summarizing sync.Map // Tracks which sessions are currently being summarized summarizing sync.Map // Tracks which sessions are currently being summarized
} }
@ -143,6 +144,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
maxIterations: cfg.Agents.Defaults.MaxToolIterations, maxIterations: cfg.Agents.Defaults.MaxToolIterations,
sessions: sessionsManager, sessions: sessionsManager,
state: stateManager, state: stateManager,
timeout: cfg.Agents.Defaults.Timeout,
contextBuilder: contextBuilder, contextBuilder: contextBuilder,
tools: toolsRegistry, tools: toolsRegistry,
summarizing: sync.Map{}, summarizing: sync.Map{},
@ -674,7 +676,11 @@ func formatToolsForLog(tools []providers.ToolDefinition) string {
// summarizeSession summarizes the conversation history for a session. // summarizeSession summarizes the conversation history for a session.
func (al *AgentLoop) summarizeSession(sessionKey string) { func (al *AgentLoop) summarizeSession(sessionKey string) {
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) summarizeTimeout := al.timeout
if summarizeTimeout <= 0 {
summarizeTimeout = 120
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(summarizeTimeout)*time.Second)
defer cancel() defer cancel()
history := al.sessions.GetHistory(sessionKey) history := al.sessions.GetHistory(sessionKey)

View file

@ -66,6 +66,7 @@ type AgentDefaults struct {
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" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"` Temperature float64 `json:"temperature" 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"`
Timeout int `json:"timeout" env:"PICOCLAW_AGENTS_DEFAULTS_TIMEOUT"`
} }
type ChannelsConfig struct { type ChannelsConfig struct {
@ -225,6 +226,7 @@ func DefaultConfig() *Config {
MaxTokens: 8192, MaxTokens: 8192,
Temperature: 0.7, Temperature: 0.7,
MaxToolIterations: 20, MaxToolIterations: 20,
Timeout: 120,
}, },
}, },
Channels: ChannelsConfig{ Channels: ChannelsConfig{

View file

@ -27,9 +27,12 @@ type HTTPProvider struct {
httpClient *http.Client httpClient *http.Client
} }
func NewHTTPProvider(apiKey, apiBase, proxy string) *HTTPProvider { func NewHTTPProvider(apiKey, apiBase, proxy string, timeout int) *HTTPProvider {
if timeout <= 0 {
timeout = 120
}
client := &http.Client{ client := &http.Client{
Timeout: 120 * time.Second, Timeout: time.Duration(timeout) * time.Second,
} }
if proxy != "" { if proxy != "" {
@ -429,5 +432,5 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
return nil, fmt.Errorf("no API base configured for provider (model: %s)", model) return nil, fmt.Errorf("no API base configured for provider (model: %s)", model)
} }
return NewHTTPProvider(apiKey, apiBase, proxy), nil return NewHTTPProvider(apiKey, apiBase, proxy, cfg.Agents.Defaults.Timeout), nil
} }