Files
xtrm-agent/xtrm_agent/config.py
Kaloyan Danchev 872ed24f0c Add performance features: caching, cost tracking, retry, compaction, classification, scrubbing
Inspired by zeroclaw's lightweight patterns for slow hardware:
- Response cache (SQLite + SHA-256 keyed) to skip redundant LLM calls
- History compaction — LLM-summarize old messages when history exceeds 50
- Query classifier routes simple/research queries to cheaper models
- Credential scrubbing removes secrets from tool output before sending to LLM
- Cost tracker with daily/monthly budget enforcement (SQLite)
- Resilient provider with retry + exponential backoff + fallback provider
- Approval engine gains session "always allow" and audit log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:20:52 +02:00

126 lines
3.5 KiB
Python

"""Configuration system — YAML config + Pydantic validation."""
from __future__ import annotations
from pathlib import Path
from typing import Any
import yaml
from pydantic import BaseModel, Field
class ProviderConfig(BaseModel):
"""Single LLM provider configuration."""
provider: str = "anthropic"
model: str = "claude-sonnet-4-5-20250929"
max_tokens: int = 8192
temperature: float = 0.3
api_key_env: str = ""
class LLMConfig(BaseModel):
"""LLM providers section."""
providers: dict[str, ProviderConfig] = Field(default_factory=dict)
class CLIChannelConfig(BaseModel):
enabled: bool = True
default_agent: str = "coder"
class DiscordChannelConfig(BaseModel):
enabled: bool = False
token_env: str = "DISCORD_BOT_TOKEN"
default_agent: str = "coder"
allowed_users: list[str] = Field(default_factory=list)
class ChannelsConfig(BaseModel):
cli: CLIChannelConfig = Field(default_factory=CLIChannelConfig)
discord: DiscordChannelConfig = Field(default_factory=DiscordChannelConfig)
class ToolsConfig(BaseModel):
workspace: str = "./data"
auto_approve: list[str] = Field(
default_factory=lambda: ["read_file", "list_dir", "web_fetch", "delegate"]
)
require_approval: list[str] = Field(
default_factory=lambda: ["bash", "write_file", "edit_file"]
)
class MCPServerConfig(BaseModel):
"""Single MCP server configuration."""
command: str = ""
args: list[str] = Field(default_factory=list)
env: dict[str, str] = Field(default_factory=dict)
url: str = ""
class PerformanceConfig(BaseModel):
"""Performance tuning — caching, cost tracking, model routing."""
cache_ttl: int = 3600
daily_budget_usd: float = 0.0
monthly_budget_usd: float = 0.0
fallback_model: str = ""
model_routing: dict[str, str] = Field(default_factory=dict)
class OrchestratorConfig(BaseModel):
max_concurrent: int = 5
delegation_timeout: int = 120
class AgentFileConfig(BaseModel):
"""Parsed from agent markdown frontmatter."""
name: str = ""
provider: str = "anthropic"
model: str = ""
temperature: float = 0.3
max_iterations: int = 30
tools: list[str] = Field(default_factory=list)
instructions: str = ""
class Config(BaseModel):
"""Top-level application config."""
llm: LLMConfig = Field(default_factory=LLMConfig)
channels: ChannelsConfig = Field(default_factory=ChannelsConfig)
tools: ToolsConfig = Field(default_factory=ToolsConfig)
mcp_servers: dict[str, MCPServerConfig] = Field(default_factory=dict)
agents: dict[str, str] = Field(default_factory=dict)
orchestrator: OrchestratorConfig = Field(default_factory=OrchestratorConfig)
performance: PerformanceConfig = Field(default_factory=PerformanceConfig)
def load_config(path: str | Path = "config.yaml") -> Config:
"""Load and validate config from YAML file."""
p = Path(path)
if not p.exists():
return Config()
raw = yaml.safe_load(p.read_text()) or {}
return Config.model_validate(raw)
def parse_agent_file(path: str | Path) -> AgentFileConfig:
"""Parse a markdown agent definition with YAML frontmatter."""
text = Path(path).read_text()
if not text.startswith("---"):
return AgentFileConfig(instructions=text)
parts = text.split("---", 2)
if len(parts) < 3:
return AgentFileConfig(instructions=text)
frontmatter = yaml.safe_load(parts[1]) or {}
body = parts[2].strip()
frontmatter["instructions"] = body
return AgentFileConfig.model_validate(frontmatter)