from dataclasses import dataclass, field
@dataclass
class AgentProfile:
"""
Defines a complete agent configuration.
A profile ties together a system prompt, an LLM backend, a model,
and the set of tools the agent is allowed to use.
"""
id: str
name: str
description: str
system_prompt: str
enabled_tools: list[str] # tool names; resolved by ToolRegistry at runtime
llm_backend: str = "ollama" # backend key, e.g. "ollama", "openai"
# Ordered list of preferred models; first available wins at runtime.
# Accepts a plain string for backward compatibility (auto-wrapped in a list).
model: list[str] = field(default_factory=lambda: ["gemma4:31b-cloud"])
def __post_init__(self) -> None:
if isinstance(self.model, str):
self.model = [self.model]
max_iterations: int = 10
temperature: float = 0.7
planning_enabled: bool = False # if True, run a planning LLM call before the main loop
# Profile discoverability — used for system prompt injection and list_profiles tool.
# short_description: 1-line summary shown in every system prompt to all profiles.
# full_description: structured dict with keys: specialization, when_to_use, key_tools.
short_description: str = ""
full_description: dict = field(default_factory=dict)
# ── Thinking mechanics ────────────────────────────────────────────────────
# Each flag can be set per-profile in config.json to tune the balance
# between reasoning depth and response latency.
# Extended reasoning: passes think=True to the LLM on every main-loop call.
# Disable for latency-sensitive profiles (e.g. smart_home).
think_enabled: bool = True
# Inject remaining iteration count at the end of every LLM context so the
# model knows when to wrap up instead of hitting the limit blindly.
iteration_budget_enabled: bool = True
# Run reflect's 3 advisors (Critic/Pragmatist/Detailer) inside planning
# phase 1 to catch blind spots before execution starts. Adds ~3 LLM calls.
planning_reflect_enabled: bool = False
# Inject a goal-reminder system message every N iterations to prevent drift
# on long tasks. N = goal_anchoring_interval.
goal_anchoring_enabled: bool = True
goal_anchoring_interval: int = 5
# Detect when the model is looping without todo progress for
# anti_stall_threshold iterations and inject a hard warning.
anti_stall_enabled: bool = True
anti_stall_threshold: int = 8
# After the model marks a todo step as done, run a lightweight LLM check:
# "did the result actually satisfy the step goal?" Adds ~1 LLM call per step.
step_validation_enabled: bool = False
# When a todo step is marked as failed, trigger a lightweight re-planning
# pass to adjust the remaining steps. Depends on step_validation.
adaptive_replan_enabled: bool = False
# Sub-agent configuration
# subagent_tools: tool names available to sub-agents spawned from this profile.
# If empty, falls back to enabled_tools minus dangerous/irrelevant ones.
# subagent_planning_enabled: if True, sub-agents run the planning phase before their tool loop.
# subagent_system_prompt: injected as an additional system message for sub-agents,
# after the profile's main system_prompt. Loaded from subagent_system_prompt.txt if present.
subagent_tools: list[str] = field(default_factory=list)
subagent_planning_enabled: bool = False
subagent_system_prompt: str = ""