Spaces:
Sleeping
Sleeping
File size: 6,376 Bytes
1397957 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
"""
Agent module - defines agent configurations and system prompts.
"""
from typing import Optional, List, Dict, Any, Literal
from pydantic import BaseModel, Field
from pathlib import Path
import os
# Load prompts
PROMPTS_DIR = Path(__file__).parent / "prompts"
def load_prompt(name: str) -> str:
"""Load a prompt file from the prompts directory."""
prompt_path = PROMPTS_DIR / f"{name}.txt"
if prompt_path.exists():
return prompt_path.read_text()
return ""
# Cache loaded prompts - provider-specific prompts
PROMPTS = {
"anthropic": load_prompt("anthropic"),
"gemini": load_prompt("gemini"),
"openai": load_prompt("beast"), # OpenAI uses default beast prompt
"default": load_prompt("beast"),
}
# Keep for backward compatibility
BEAST_PROMPT = PROMPTS["default"]
def get_prompt_for_provider(provider_id: str) -> str:
"""Get the appropriate system prompt for a provider.
Args:
provider_id: The provider identifier (e.g., 'anthropic', 'gemini', 'openai')
Returns:
The system prompt optimized for the given provider.
"""
return PROMPTS.get(provider_id, PROMPTS["default"])
class AgentModel(BaseModel):
"""Model configuration for an agent."""
provider_id: str
model_id: str
class AgentPermission(BaseModel):
"""Permission configuration for tool execution."""
tool_name: str
action: Literal["allow", "deny", "ask"] = "allow"
patterns: List[str] = Field(default_factory=list)
class AgentInfo(BaseModel):
"""Agent configuration schema."""
id: str
name: str
description: Optional[str] = None
mode: Literal["primary", "subagent", "all"] = "primary"
hidden: bool = False
native: bool = True
# Model settings
model: Optional[AgentModel] = None
temperature: Optional[float] = None
top_p: Optional[float] = None
max_tokens: Optional[int] = None
# Prompt
prompt: Optional[str] = None
# Behavior
tools: List[str] = Field(default_factory=list, description="Allowed tools, empty = all")
permissions: List[AgentPermission] = Field(default_factory=list)
# Agentic loop settings
auto_continue: bool = True
max_steps: int = 50
pause_on_question: bool = True
# Extra options
options: Dict[str, Any] = Field(default_factory=dict)
# Default agents
DEFAULT_AGENTS: Dict[str, AgentInfo] = {
"build": AgentInfo(
id="build",
name="build",
description="Default agent with full capabilities. Continues working until task is complete.",
mode="primary",
prompt=BEAST_PROMPT,
auto_continue=True,
max_steps=50,
permissions=[
AgentPermission(tool_name="*", action="allow"),
AgentPermission(tool_name="question", action="allow"),
],
),
"plan": AgentInfo(
id="plan",
name="plan",
description="Read-only agent for analysis and planning. Does not modify files.",
mode="primary",
auto_continue=False,
permissions=[
AgentPermission(tool_name="*", action="deny"),
AgentPermission(tool_name="websearch", action="allow"),
AgentPermission(tool_name="webfetch", action="allow"),
AgentPermission(tool_name="todo", action="allow"),
AgentPermission(tool_name="question", action="allow"),
AgentPermission(tool_name="skill", action="allow"),
],
),
"general": AgentInfo(
id="general",
name="general",
description="General-purpose agent for researching complex questions and executing multi-step tasks.",
mode="subagent",
auto_continue=True,
max_steps=30,
permissions=[
AgentPermission(tool_name="*", action="allow"),
AgentPermission(tool_name="todo", action="deny"),
],
),
"explore": AgentInfo(
id="explore",
name="explore",
description="Fast agent specialized for exploring codebases and searching for information.",
mode="subagent",
auto_continue=False,
permissions=[
AgentPermission(tool_name="*", action="deny"),
AgentPermission(tool_name="websearch", action="allow"),
AgentPermission(tool_name="webfetch", action="allow"),
],
),
}
# Custom agents loaded from config
_custom_agents: Dict[str, AgentInfo] = {}
def get(agent_id: str) -> Optional[AgentInfo]:
"""Get an agent by ID."""
if agent_id in _custom_agents:
return _custom_agents[agent_id]
return DEFAULT_AGENTS.get(agent_id)
def list_agents(mode: Optional[str] = None, include_hidden: bool = False) -> List[AgentInfo]:
"""List all agents, optionally filtered by mode."""
all_agents = {**DEFAULT_AGENTS, **_custom_agents}
agents = []
for agent in all_agents.values():
if agent.hidden and not include_hidden:
continue
if mode and agent.mode != mode:
continue
agents.append(agent)
# Sort by name, with 'build' first
agents.sort(key=lambda a: (a.name != "build", a.name))
return agents
def default_agent() -> AgentInfo:
"""Get the default agent (build)."""
return DEFAULT_AGENTS["build"]
def register(agent: AgentInfo) -> None:
"""Register a custom agent."""
_custom_agents[agent.id] = agent
def unregister(agent_id: str) -> bool:
"""Unregister a custom agent."""
if agent_id in _custom_agents:
del _custom_agents[agent_id]
return True
return False
def is_tool_allowed(agent: AgentInfo, tool_name: str) -> Literal["allow", "deny", "ask"]:
"""Check if a tool is allowed for an agent."""
result: Literal["allow", "deny", "ask"] = "allow"
for perm in agent.permissions:
if perm.tool_name == "*" or perm.tool_name == tool_name:
result = perm.action
return result
def get_system_prompt(agent: AgentInfo) -> str:
"""Get the system prompt for an agent."""
parts = []
# Add beast mode prompt for agents with auto_continue
if agent.auto_continue and agent.prompt:
parts.append(agent.prompt)
# Add agent description
if agent.description:
parts.append(f"You are the '{agent.name}' agent: {agent.description}")
return "\n\n".join(parts)
|