mindroom 0.0.0__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mindroom/__init__.py +3 -0
- mindroom/agent_prompts.py +963 -0
- mindroom/agents.py +248 -0
- mindroom/ai.py +421 -0
- mindroom/api/__init__.py +1 -0
- mindroom/api/credentials.py +137 -0
- mindroom/api/google_integration.py +355 -0
- mindroom/api/google_tools_helper.py +40 -0
- mindroom/api/homeassistant_integration.py +421 -0
- mindroom/api/integrations.py +189 -0
- mindroom/api/main.py +506 -0
- mindroom/api/matrix_operations.py +219 -0
- mindroom/api/tools.py +94 -0
- mindroom/background_tasks.py +87 -0
- mindroom/bot.py +2470 -0
- mindroom/cli.py +86 -0
- mindroom/commands.py +377 -0
- mindroom/config.py +343 -0
- mindroom/config_commands.py +324 -0
- mindroom/config_confirmation.py +411 -0
- mindroom/constants.py +52 -0
- mindroom/credentials.py +146 -0
- mindroom/credentials_sync.py +134 -0
- mindroom/custom_tools/__init__.py +8 -0
- mindroom/custom_tools/config_manager.py +765 -0
- mindroom/custom_tools/gmail.py +92 -0
- mindroom/custom_tools/google_calendar.py +92 -0
- mindroom/custom_tools/google_sheets.py +92 -0
- mindroom/custom_tools/homeassistant.py +341 -0
- mindroom/error_handling.py +35 -0
- mindroom/file_watcher.py +49 -0
- mindroom/interactive.py +313 -0
- mindroom/logging_config.py +207 -0
- mindroom/matrix/__init__.py +1 -0
- mindroom/matrix/client.py +782 -0
- mindroom/matrix/event_info.py +173 -0
- mindroom/matrix/identity.py +149 -0
- mindroom/matrix/large_messages.py +267 -0
- mindroom/matrix/mentions.py +141 -0
- mindroom/matrix/message_builder.py +94 -0
- mindroom/matrix/message_content.py +209 -0
- mindroom/matrix/presence.py +178 -0
- mindroom/matrix/rooms.py +311 -0
- mindroom/matrix/state.py +77 -0
- mindroom/matrix/typing.py +91 -0
- mindroom/matrix/users.py +217 -0
- mindroom/memory/__init__.py +21 -0
- mindroom/memory/config.py +137 -0
- mindroom/memory/functions.py +396 -0
- mindroom/py.typed +0 -0
- mindroom/response_tracker.py +128 -0
- mindroom/room_cleanup.py +139 -0
- mindroom/routing.py +107 -0
- mindroom/scheduling.py +758 -0
- mindroom/stop.py +207 -0
- mindroom/streaming.py +203 -0
- mindroom/teams.py +749 -0
- mindroom/thread_utils.py +318 -0
- mindroom/tools/__init__.py +520 -0
- mindroom/tools/agentql.py +64 -0
- mindroom/tools/airflow.py +57 -0
- mindroom/tools/apify.py +49 -0
- mindroom/tools/arxiv.py +64 -0
- mindroom/tools/aws_lambda.py +41 -0
- mindroom/tools/aws_ses.py +57 -0
- mindroom/tools/baidusearch.py +87 -0
- mindroom/tools/brightdata.py +116 -0
- mindroom/tools/browserbase.py +62 -0
- mindroom/tools/cal_com.py +98 -0
- mindroom/tools/calculator.py +112 -0
- mindroom/tools/cartesia.py +84 -0
- mindroom/tools/composio.py +166 -0
- mindroom/tools/config_manager.py +44 -0
- mindroom/tools/confluence.py +73 -0
- mindroom/tools/crawl4ai.py +101 -0
- mindroom/tools/csv.py +104 -0
- mindroom/tools/custom_api.py +106 -0
- mindroom/tools/dalle.py +85 -0
- mindroom/tools/daytona.py +180 -0
- mindroom/tools/discord.py +81 -0
- mindroom/tools/docker.py +73 -0
- mindroom/tools/duckdb.py +124 -0
- mindroom/tools/duckduckgo.py +99 -0
- mindroom/tools/e2b.py +121 -0
- mindroom/tools/eleven_labs.py +77 -0
- mindroom/tools/email.py +74 -0
- mindroom/tools/exa.py +246 -0
- mindroom/tools/fal.py +50 -0
- mindroom/tools/file.py +80 -0
- mindroom/tools/financial_datasets_api.py +112 -0
- mindroom/tools/firecrawl.py +124 -0
- mindroom/tools/gemini.py +85 -0
- mindroom/tools/giphy.py +49 -0
- mindroom/tools/github.py +376 -0
- mindroom/tools/gmail.py +102 -0
- mindroom/tools/google_calendar.py +55 -0
- mindroom/tools/google_maps.py +112 -0
- mindroom/tools/google_sheets.py +86 -0
- mindroom/tools/googlesearch.py +83 -0
- mindroom/tools/groq.py +77 -0
- mindroom/tools/hackernews.py +54 -0
- mindroom/tools/jina.py +108 -0
- mindroom/tools/jira.py +70 -0
- mindroom/tools/linear.py +103 -0
- mindroom/tools/linkup.py +65 -0
- mindroom/tools/lumalabs.py +71 -0
- mindroom/tools/mem0.py +82 -0
- mindroom/tools/modelslabs.py +85 -0
- mindroom/tools/moviepy_video_tools.py +62 -0
- mindroom/tools/newspaper4k.py +63 -0
- mindroom/tools/openai.py +143 -0
- mindroom/tools/openweather.py +89 -0
- mindroom/tools/oxylabs.py +54 -0
- mindroom/tools/pandas.py +35 -0
- mindroom/tools/pubmed.py +64 -0
- mindroom/tools/python.py +120 -0
- mindroom/tools/reddit.py +155 -0
- mindroom/tools/replicate.py +56 -0
- mindroom/tools/resend.py +55 -0
- mindroom/tools/scrapegraph.py +87 -0
- mindroom/tools/searxng.py +120 -0
- mindroom/tools/serpapi.py +55 -0
- mindroom/tools/serper.py +81 -0
- mindroom/tools/shell.py +46 -0
- mindroom/tools/slack.py +80 -0
- mindroom/tools/sleep.py +38 -0
- mindroom/tools/spider.py +62 -0
- mindroom/tools/sql.py +138 -0
- mindroom/tools/tavily.py +104 -0
- mindroom/tools/telegram.py +54 -0
- mindroom/tools/todoist.py +103 -0
- mindroom/tools/trello.py +121 -0
- mindroom/tools/twilio.py +97 -0
- mindroom/tools/web_browser_tools.py +37 -0
- mindroom/tools/webex.py +63 -0
- mindroom/tools/website.py +45 -0
- mindroom/tools/whatsapp.py +81 -0
- mindroom/tools/wikipedia.py +45 -0
- mindroom/tools/x.py +97 -0
- mindroom/tools/yfinance.py +121 -0
- mindroom/tools/youtube.py +81 -0
- mindroom/tools/zendesk.py +62 -0
- mindroom/tools/zep.py +107 -0
- mindroom/tools/zoom.py +62 -0
- mindroom/tools_metadata.json +7643 -0
- mindroom/tools_metadata.py +220 -0
- mindroom/topic_generator.py +153 -0
- mindroom/voice_handler.py +266 -0
- mindroom-0.1.0.dist-info/METADATA +425 -0
- mindroom-0.1.0.dist-info/RECORD +152 -0
- {mindroom-0.0.0.dist-info → mindroom-0.1.0.dist-info}/WHEEL +1 -2
- mindroom-0.1.0.dist-info/entry_points.txt +2 -0
- mindroom-0.0.0.dist-info/METADATA +0 -24
- mindroom-0.0.0.dist-info/RECORD +0 -4
- mindroom-0.0.0.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
"""Consolidated ConfigManager tool for building and managing MindRoom agents."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
from agno.tools import Toolkit
|
|
12
|
+
|
|
13
|
+
from mindroom.commands import get_command_help
|
|
14
|
+
from mindroom.config import AgentConfig, Config, TeamConfig
|
|
15
|
+
from mindroom.constants import DEFAULT_AGENTS_CONFIG
|
|
16
|
+
from mindroom.logging_config import get_logger
|
|
17
|
+
from mindroom.tools_metadata import TOOL_METADATA, TOOL_REGISTRY, ToolCategory, ToolStatus
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InfoType(str, Enum):
|
|
23
|
+
"""Types of information that can be retrieved."""
|
|
24
|
+
|
|
25
|
+
MINDROOM_DOCS = "mindroom_docs"
|
|
26
|
+
CONFIG_SCHEMA = "config_schema"
|
|
27
|
+
AVAILABLE_MODELS = "available_models"
|
|
28
|
+
AGENTS = "agents"
|
|
29
|
+
TEAMS = "teams"
|
|
30
|
+
AVAILABLE_TOOLS = "available_tools"
|
|
31
|
+
TOOL_DETAILS = "tool_details"
|
|
32
|
+
AGENT_CONFIG = "agent_config"
|
|
33
|
+
AGENT_TEMPLATE = "agent_template"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ConfigManagerTools(Toolkit):
|
|
37
|
+
"""Consolidated tools for managing MindRoom agent configurations.
|
|
38
|
+
|
|
39
|
+
This toolkit provides comprehensive agent building capabilities with a minimal
|
|
40
|
+
number of tools to reduce cognitive load on AI models.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, config_path: Path | None = None) -> None:
|
|
44
|
+
"""Initialize the ConfigManager toolkit.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
config_path: Optional path to configuration file
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
self.config_path = config_path or DEFAULT_AGENTS_CONFIG
|
|
51
|
+
self._mindroom_docs: str | None = None
|
|
52
|
+
self._help_text: str | None = None
|
|
53
|
+
|
|
54
|
+
# Register only the consolidated tools
|
|
55
|
+
super().__init__(
|
|
56
|
+
name="config_manager",
|
|
57
|
+
tools=[
|
|
58
|
+
self.get_info,
|
|
59
|
+
self.manage_agent,
|
|
60
|
+
self.manage_team,
|
|
61
|
+
],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def get_info( # noqa: C901, PLR0911, PLR0912
|
|
65
|
+
self,
|
|
66
|
+
info_type: str,
|
|
67
|
+
name: str | None = None,
|
|
68
|
+
) -> str:
|
|
69
|
+
"""Get various types of information about MindRoom, agents, tools, and configuration.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
info_type: Type of information to retrieve. Options:
|
|
73
|
+
- "mindroom_docs": MindRoom documentation and help
|
|
74
|
+
- "config_schema": Configuration schema for agents and teams
|
|
75
|
+
- "available_models": List of configured AI models
|
|
76
|
+
- "agents": List all configured agents
|
|
77
|
+
- "teams": List all configured teams
|
|
78
|
+
- "available_tools": List all available tools by category
|
|
79
|
+
- "tool_details": Get details about a specific tool (requires name)
|
|
80
|
+
- "agent_config": Get configuration for a specific agent (requires name)
|
|
81
|
+
- "agent_template": Generate template for agent type (requires name as type)
|
|
82
|
+
name: Optional name/identifier for specific queries (tool name, agent name, or template type)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Requested information as formatted string
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
if info_type == InfoType.MINDROOM_DOCS:
|
|
90
|
+
return self._get_mindroom_info()
|
|
91
|
+
if info_type == InfoType.CONFIG_SCHEMA:
|
|
92
|
+
return self._get_config_schema()
|
|
93
|
+
if info_type == InfoType.AVAILABLE_MODELS:
|
|
94
|
+
return self._get_available_models()
|
|
95
|
+
if info_type == InfoType.AGENTS:
|
|
96
|
+
return self._list_agents()
|
|
97
|
+
if info_type == InfoType.TEAMS:
|
|
98
|
+
return self._list_teams()
|
|
99
|
+
if info_type == InfoType.AVAILABLE_TOOLS:
|
|
100
|
+
return self._list_available_tools()
|
|
101
|
+
if info_type == InfoType.TOOL_DETAILS:
|
|
102
|
+
if not name:
|
|
103
|
+
return "Error: tool_details requires 'name' parameter with the tool name"
|
|
104
|
+
return self._get_tool_details(name)
|
|
105
|
+
if info_type == InfoType.AGENT_CONFIG:
|
|
106
|
+
if not name:
|
|
107
|
+
return "Error: agent_config requires 'name' parameter with the agent name"
|
|
108
|
+
return self._get_agent_config(name)
|
|
109
|
+
if info_type == InfoType.AGENT_TEMPLATE:
|
|
110
|
+
if not name:
|
|
111
|
+
return "Error: agent_template requires 'name' parameter with the template type (researcher, developer, social, communicator, analyst, productivity)"
|
|
112
|
+
return self._generate_agent_template(name)
|
|
113
|
+
return f"Error: Unknown info_type '{info_type}'. Valid options: {', '.join([t.value for t in InfoType])}"
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.exception(f"Failed to get info for type {info_type}")
|
|
116
|
+
return f"Error getting {info_type}: {e}"
|
|
117
|
+
|
|
118
|
+
def manage_agent(
|
|
119
|
+
self,
|
|
120
|
+
operation: Literal["create", "update", "validate"],
|
|
121
|
+
agent_name: str,
|
|
122
|
+
display_name: str | None = None,
|
|
123
|
+
role: str | None = None,
|
|
124
|
+
tools: list[str] | None = None,
|
|
125
|
+
instructions: list[str] | None = None,
|
|
126
|
+
model: str | None = None,
|
|
127
|
+
rooms: list[str] | None = None,
|
|
128
|
+
num_history_runs: int | None = None,
|
|
129
|
+
markdown: bool | None = None,
|
|
130
|
+
add_history_to_messages: bool | None = None,
|
|
131
|
+
) -> str:
|
|
132
|
+
"""Manage agent configurations - create, update, or validate agents.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
operation: Operation to perform - "create", "update", or "validate"
|
|
136
|
+
agent_name: Internal name for the agent (alphanumeric, lowercase)
|
|
137
|
+
display_name: Human-readable display name (required for create)
|
|
138
|
+
role: Description of the agent's purpose (required for create)
|
|
139
|
+
tools: List of tool names to enable for the agent
|
|
140
|
+
instructions: List of instructions for the agent
|
|
141
|
+
model: Model to use (default: "default")
|
|
142
|
+
rooms: List of room IDs or names to auto-join
|
|
143
|
+
num_history_runs: Number of history runs to include
|
|
144
|
+
markdown: Whether to use markdown formatting
|
|
145
|
+
add_history_to_messages: Whether to add history to messages
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Success message or error details
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
if operation == "create":
|
|
152
|
+
if not display_name:
|
|
153
|
+
return "Error: display_name is required for create operation"
|
|
154
|
+
if role is None:
|
|
155
|
+
role = ""
|
|
156
|
+
return self._create_agent_config(
|
|
157
|
+
agent_name=agent_name,
|
|
158
|
+
display_name=display_name,
|
|
159
|
+
role=role,
|
|
160
|
+
tools=tools or [],
|
|
161
|
+
instructions=instructions or [],
|
|
162
|
+
model=model or "default",
|
|
163
|
+
rooms=rooms or [],
|
|
164
|
+
num_history_runs=num_history_runs,
|
|
165
|
+
markdown=markdown,
|
|
166
|
+
add_history_to_messages=add_history_to_messages,
|
|
167
|
+
)
|
|
168
|
+
if operation == "update":
|
|
169
|
+
return self._update_agent_config(
|
|
170
|
+
agent_name=agent_name,
|
|
171
|
+
display_name=display_name,
|
|
172
|
+
role=role,
|
|
173
|
+
tools=tools,
|
|
174
|
+
instructions=instructions,
|
|
175
|
+
model=model,
|
|
176
|
+
rooms=rooms,
|
|
177
|
+
num_history_runs=num_history_runs,
|
|
178
|
+
markdown=markdown,
|
|
179
|
+
add_history_to_messages=add_history_to_messages,
|
|
180
|
+
)
|
|
181
|
+
if operation == "validate":
|
|
182
|
+
return self._validate_agent_config(agent_name)
|
|
183
|
+
return f"Error: Unknown operation '{operation}'. Valid options: create, update, validate"
|
|
184
|
+
|
|
185
|
+
def manage_team(
|
|
186
|
+
self,
|
|
187
|
+
team_name: str,
|
|
188
|
+
display_name: str,
|
|
189
|
+
role: str,
|
|
190
|
+
agents: list[str],
|
|
191
|
+
mode: str = "coordinate",
|
|
192
|
+
) -> str:
|
|
193
|
+
"""Create or manage team configurations.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
team_name: Internal name for the team
|
|
197
|
+
display_name: Human-readable display name
|
|
198
|
+
role: Description of the team's purpose
|
|
199
|
+
agents: List of agent names that compose this team
|
|
200
|
+
mode: Team mode - "coordinate" or "collaborate"
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Success message or error details
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
return self._create_team_config(team_name, display_name, role, agents, mode)
|
|
207
|
+
|
|
208
|
+
# ===== Internal helper methods (not exposed as tools) =====
|
|
209
|
+
|
|
210
|
+
def _load_mindroom_docs(self) -> str:
|
|
211
|
+
"""Load MindRoom documentation once and cache it."""
|
|
212
|
+
if self._mindroom_docs is None:
|
|
213
|
+
readme_path = Path(__file__).parent.parent.parent.parent / "README.md"
|
|
214
|
+
try:
|
|
215
|
+
with readme_path.open() as f:
|
|
216
|
+
self._mindroom_docs = f.read()
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logger.warning(f"Could not load README.md: {e}")
|
|
219
|
+
self._mindroom_docs = "README.md not available"
|
|
220
|
+
return self._mindroom_docs
|
|
221
|
+
|
|
222
|
+
def _load_help_text(self) -> str:
|
|
223
|
+
"""Load help text once and cache it."""
|
|
224
|
+
if self._help_text is None:
|
|
225
|
+
self._help_text = get_command_help()
|
|
226
|
+
return self._help_text
|
|
227
|
+
|
|
228
|
+
def _get_available_models(self) -> str:
|
|
229
|
+
"""Get the list of configured models from the current configuration."""
|
|
230
|
+
try:
|
|
231
|
+
config = Config.from_yaml(self.config_path)
|
|
232
|
+
|
|
233
|
+
output = ["# Available Models\n"]
|
|
234
|
+
|
|
235
|
+
if not config.models:
|
|
236
|
+
return "No models configured in the system."
|
|
237
|
+
|
|
238
|
+
output.append("These models are currently configured and can be used:\n")
|
|
239
|
+
|
|
240
|
+
for model_name, model_config in config.models.items():
|
|
241
|
+
provider = model_config.provider
|
|
242
|
+
model_id = model_config.id
|
|
243
|
+
|
|
244
|
+
output.append(f"## `{model_name}`")
|
|
245
|
+
output.append(f"- **Provider**: {provider}")
|
|
246
|
+
output.append(f"- **Model ID**: {model_id}")
|
|
247
|
+
|
|
248
|
+
if model_config.host:
|
|
249
|
+
output.append(f"- **Host**: {model_config.host}")
|
|
250
|
+
|
|
251
|
+
if model_name == "default":
|
|
252
|
+
output.append("- **Note**: This is typically the system default model")
|
|
253
|
+
|
|
254
|
+
output.append("")
|
|
255
|
+
|
|
256
|
+
if config.router and config.router.model:
|
|
257
|
+
output.append("## Router Configuration")
|
|
258
|
+
output.append(f"The router uses model: `{config.router.model}`")
|
|
259
|
+
output.append("")
|
|
260
|
+
|
|
261
|
+
return "\n".join(output)
|
|
262
|
+
except Exception as e:
|
|
263
|
+
return f"Error loading model configuration: {e}"
|
|
264
|
+
|
|
265
|
+
def _format_schema_field(self, field: str, info: dict, required_fields: list) -> list[str]:
|
|
266
|
+
"""Format a single schema field for display."""
|
|
267
|
+
lines = []
|
|
268
|
+
required = field in required_fields
|
|
269
|
+
field_type = info.get("type", "unknown")
|
|
270
|
+
description = info.get("description", "")
|
|
271
|
+
default = info.get("default")
|
|
272
|
+
|
|
273
|
+
if "enum" in info:
|
|
274
|
+
field_type = f"enum: {info['enum']}"
|
|
275
|
+
|
|
276
|
+
lines.append(f"{field}: # {field_type}")
|
|
277
|
+
if description:
|
|
278
|
+
lines.append(f" # {description}")
|
|
279
|
+
if required:
|
|
280
|
+
lines.append(" # REQUIRED")
|
|
281
|
+
elif default is not None:
|
|
282
|
+
lines.append(f" # Default: {default}")
|
|
283
|
+
lines.append("")
|
|
284
|
+
return lines
|
|
285
|
+
|
|
286
|
+
def _get_config_schema(self) -> str:
|
|
287
|
+
"""Get the JSON schema for MindRoom configuration."""
|
|
288
|
+
output = ["# MindRoom Configuration Schema\n"]
|
|
289
|
+
|
|
290
|
+
agent_schema = AgentConfig.model_json_schema()
|
|
291
|
+
team_schema = TeamConfig.model_json_schema()
|
|
292
|
+
|
|
293
|
+
output.append("## Agent Configuration Fields\n")
|
|
294
|
+
output.append("```yaml")
|
|
295
|
+
output.append("# Required fields:")
|
|
296
|
+
for field, info in agent_schema.get("properties", {}).items():
|
|
297
|
+
output.extend(self._format_schema_field(field, info, agent_schema.get("required", [])))
|
|
298
|
+
output.append("```\n")
|
|
299
|
+
|
|
300
|
+
output.append("## Team Configuration Fields\n")
|
|
301
|
+
output.append("```yaml")
|
|
302
|
+
for field, info in team_schema.get("properties", {}).items():
|
|
303
|
+
output.extend(self._format_schema_field(field, info, team_schema.get("required", [])))
|
|
304
|
+
output.append("```\n")
|
|
305
|
+
|
|
306
|
+
output.append("## Team Modes")
|
|
307
|
+
if "properties" in team_schema and "mode" in team_schema["properties"]:
|
|
308
|
+
mode_info = team_schema["properties"]["mode"]
|
|
309
|
+
if "enum" in mode_info:
|
|
310
|
+
output.extend(f"- `{mode}`: {mode.title()} mode" for mode in mode_info["enum"])
|
|
311
|
+
|
|
312
|
+
return "\n".join(output)
|
|
313
|
+
|
|
314
|
+
def _get_mindroom_info(self) -> str:
|
|
315
|
+
"""Get comprehensive information about MindRoom."""
|
|
316
|
+
docs = self._load_mindroom_docs()
|
|
317
|
+
help_text = self._load_help_text()
|
|
318
|
+
|
|
319
|
+
return f"""# MindRoom Documentation
|
|
320
|
+
|
|
321
|
+
## README Content:
|
|
322
|
+
{docs}
|
|
323
|
+
|
|
324
|
+
## Available Commands:
|
|
325
|
+
{help_text}
|
|
326
|
+
|
|
327
|
+
## Key Concepts:
|
|
328
|
+
- **Agents**: AI assistants with specific roles and tools
|
|
329
|
+
- **Teams**: Groups of agents that collaborate
|
|
330
|
+
- **Tools**: Integrations that give agents capabilities (80+ available)
|
|
331
|
+
- **Memory**: Persistent conversation memory across sessions
|
|
332
|
+
- **Threading**: Agents respond in threads for organized conversations
|
|
333
|
+
- **Routing**: Smart agent selection based on message content
|
|
334
|
+
- **Commands**: Special !commands for configuration and control
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
def _list_agents(self) -> str:
|
|
338
|
+
"""List all configured agents and their details."""
|
|
339
|
+
try:
|
|
340
|
+
config = Config.from_yaml(self.config_path)
|
|
341
|
+
agents_info = []
|
|
342
|
+
|
|
343
|
+
for name, agent in config.agents.items():
|
|
344
|
+
tools_str = ", ".join(agent.tools) if agent.tools else "No tools"
|
|
345
|
+
role_line = f" - Role: {agent.role[:100]}..." if len(agent.role) > 100 else f" - Role: {agent.role}"
|
|
346
|
+
agents_info.append(
|
|
347
|
+
f"**{name}** ({agent.display_name})\n"
|
|
348
|
+
f"{role_line}\n"
|
|
349
|
+
f" - Tools: {tools_str}\n"
|
|
350
|
+
f" - Model: {agent.model}\n",
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
if not agents_info:
|
|
354
|
+
return "No agents configured yet."
|
|
355
|
+
|
|
356
|
+
return "## Configured Agents:\n\n" + "\n".join(agents_info)
|
|
357
|
+
except Exception as e:
|
|
358
|
+
return f"Error loading agents: {e}"
|
|
359
|
+
|
|
360
|
+
def _list_teams(self) -> str:
|
|
361
|
+
"""List all configured teams and their composition."""
|
|
362
|
+
try:
|
|
363
|
+
config = Config.from_yaml(self.config_path)
|
|
364
|
+
teams_info = []
|
|
365
|
+
|
|
366
|
+
for name, team in config.teams.items():
|
|
367
|
+
agents_str = ", ".join(team.agents)
|
|
368
|
+
teams_info.append(
|
|
369
|
+
f"**{name}** ({team.display_name})\n"
|
|
370
|
+
f" - Role: {team.role}\n"
|
|
371
|
+
f" - Agents: {agents_str}\n"
|
|
372
|
+
f" - Mode: {team.mode}\n",
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
if not teams_info:
|
|
376
|
+
return "No teams configured yet."
|
|
377
|
+
|
|
378
|
+
return "## Configured Teams:\n\n" + "\n".join(teams_info)
|
|
379
|
+
except Exception as e:
|
|
380
|
+
return f"Error loading teams: {e}"
|
|
381
|
+
|
|
382
|
+
def _list_available_tools(self) -> str:
|
|
383
|
+
"""List all available tools that can be used by agents."""
|
|
384
|
+
tools_by_category: dict[str, list[tuple[str, str]]] = {}
|
|
385
|
+
|
|
386
|
+
for tool_name in sorted(TOOL_REGISTRY.keys()):
|
|
387
|
+
if tool_name in TOOL_METADATA:
|
|
388
|
+
metadata = TOOL_METADATA[tool_name]
|
|
389
|
+
category = metadata.category.value
|
|
390
|
+
description = metadata.description
|
|
391
|
+
|
|
392
|
+
if category not in tools_by_category:
|
|
393
|
+
tools_by_category[category] = []
|
|
394
|
+
tools_by_category[category].append((tool_name, description))
|
|
395
|
+
else:
|
|
396
|
+
if "uncategorized" not in tools_by_category:
|
|
397
|
+
tools_by_category["uncategorized"] = []
|
|
398
|
+
tools_by_category["uncategorized"].append((tool_name, "No description available"))
|
|
399
|
+
|
|
400
|
+
output = ["## Available Tools by Category:\n"]
|
|
401
|
+
for category in sorted(tools_by_category.keys()):
|
|
402
|
+
output.append(f"\n### {category.title()}:")
|
|
403
|
+
for tool_name, description in tools_by_category[category]:
|
|
404
|
+
output.append(f"- **{tool_name}**: {description}")
|
|
405
|
+
|
|
406
|
+
return "\n".join(output)
|
|
407
|
+
|
|
408
|
+
def _get_tool_details(self, tool_name: str) -> str:
|
|
409
|
+
"""Get detailed information about a specific tool."""
|
|
410
|
+
if tool_name not in TOOL_REGISTRY:
|
|
411
|
+
available = ", ".join(sorted(TOOL_REGISTRY.keys()))
|
|
412
|
+
return f"Unknown tool: {tool_name}\n\nAvailable tools: {available}"
|
|
413
|
+
|
|
414
|
+
output = [f"## Tool: {tool_name}\n"]
|
|
415
|
+
|
|
416
|
+
if tool_name in TOOL_METADATA:
|
|
417
|
+
metadata = TOOL_METADATA[tool_name]
|
|
418
|
+
output.append(f"**Display Name**: {metadata.display_name}")
|
|
419
|
+
output.append(f"**Description**: {metadata.description}")
|
|
420
|
+
output.append(f"**Category**: {metadata.category.value}")
|
|
421
|
+
output.append(f"**Status**: {metadata.status.value}")
|
|
422
|
+
output.append(f"**Setup Type**: {metadata.setup_type.value}")
|
|
423
|
+
|
|
424
|
+
if metadata.config_fields:
|
|
425
|
+
output.append("\n**Configuration Fields**:")
|
|
426
|
+
for field in metadata.config_fields:
|
|
427
|
+
required = "Required" if field.required else "Optional"
|
|
428
|
+
output.append(f"- **{field.name}** ({field.type}, {required}): {field.description}")
|
|
429
|
+
if field.default is not None:
|
|
430
|
+
output.append(f" Default: {field.default}")
|
|
431
|
+
|
|
432
|
+
if metadata.dependencies:
|
|
433
|
+
output.append(f"\n**Dependencies**: {', '.join(metadata.dependencies)}")
|
|
434
|
+
|
|
435
|
+
if metadata.docs_url:
|
|
436
|
+
output.append(f"\n**Documentation**: {metadata.docs_url}")
|
|
437
|
+
else:
|
|
438
|
+
output.append("No metadata available for this tool.")
|
|
439
|
+
|
|
440
|
+
return "\n".join(output)
|
|
441
|
+
|
|
442
|
+
def _create_agent_config(
|
|
443
|
+
self,
|
|
444
|
+
agent_name: str,
|
|
445
|
+
display_name: str,
|
|
446
|
+
role: str,
|
|
447
|
+
tools: list[str],
|
|
448
|
+
instructions: list[str],
|
|
449
|
+
model: str,
|
|
450
|
+
rooms: list[str],
|
|
451
|
+
num_history_runs: int | None,
|
|
452
|
+
markdown: bool | None,
|
|
453
|
+
add_history_to_messages: bool | None,
|
|
454
|
+
) -> str:
|
|
455
|
+
"""Create a new agent configuration."""
|
|
456
|
+
# Validate agent name
|
|
457
|
+
if not re.match(r"^[a-z0-9_]+$", agent_name):
|
|
458
|
+
return "Error: Agent name must be lowercase alphanumeric with underscores only"
|
|
459
|
+
|
|
460
|
+
# Validate tools
|
|
461
|
+
invalid_tools = [t for t in tools if t not in TOOL_REGISTRY]
|
|
462
|
+
if invalid_tools:
|
|
463
|
+
return f"Error: Unknown tools: {', '.join(invalid_tools)}\n\nUse get_info with info_type='available_tools' to see valid tools."
|
|
464
|
+
|
|
465
|
+
try:
|
|
466
|
+
config = Config.from_yaml(self.config_path)
|
|
467
|
+
|
|
468
|
+
if agent_name in config.agents:
|
|
469
|
+
return f"Error: Agent '{agent_name}' already exists. Use manage_agent with operation='update' to modify it."
|
|
470
|
+
|
|
471
|
+
# Create new agent config
|
|
472
|
+
new_agent = AgentConfig(
|
|
473
|
+
display_name=display_name,
|
|
474
|
+
role=role,
|
|
475
|
+
tools=tools,
|
|
476
|
+
instructions=instructions,
|
|
477
|
+
model=model,
|
|
478
|
+
rooms=rooms,
|
|
479
|
+
num_history_runs=num_history_runs,
|
|
480
|
+
markdown=markdown,
|
|
481
|
+
add_history_to_messages=add_history_to_messages,
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
# Add to config
|
|
485
|
+
config.agents[agent_name] = new_agent
|
|
486
|
+
|
|
487
|
+
# Save config
|
|
488
|
+
config.save_to_yaml(self.config_path)
|
|
489
|
+
|
|
490
|
+
# Build success message
|
|
491
|
+
tools_str = ", ".join(tools) if tools else "None"
|
|
492
|
+
rooms_str = ", ".join(rooms) if rooms else "None"
|
|
493
|
+
return ( # noqa: TRY300
|
|
494
|
+
f"✅ Successfully created agent '{agent_name}'!\n\n"
|
|
495
|
+
f"**Configuration:**\n"
|
|
496
|
+
f"- Display Name: {display_name}\n"
|
|
497
|
+
f"- Role: {role}\n"
|
|
498
|
+
f"- Tools: {tools_str}\n"
|
|
499
|
+
f"- Model: {model}\n"
|
|
500
|
+
f"- Rooms: {rooms_str}\n\n"
|
|
501
|
+
f"The agent is now available and can be mentioned with @{agent_name}"
|
|
502
|
+
)
|
|
503
|
+
except Exception as e:
|
|
504
|
+
logger.exception("Failed to create agent")
|
|
505
|
+
return f"Error creating agent: {e}"
|
|
506
|
+
|
|
507
|
+
def _update_agent_config(
|
|
508
|
+
self,
|
|
509
|
+
agent_name: str,
|
|
510
|
+
display_name: str | None,
|
|
511
|
+
role: str | None,
|
|
512
|
+
tools: list[str] | None,
|
|
513
|
+
instructions: list[str] | None,
|
|
514
|
+
model: str | None,
|
|
515
|
+
rooms: list[str] | None,
|
|
516
|
+
num_history_runs: int | None,
|
|
517
|
+
markdown: bool | None,
|
|
518
|
+
add_history_to_messages: bool | None,
|
|
519
|
+
) -> str:
|
|
520
|
+
"""Update an existing agent configuration."""
|
|
521
|
+
try:
|
|
522
|
+
config = Config.from_yaml(self.config_path)
|
|
523
|
+
|
|
524
|
+
if agent_name not in config.agents:
|
|
525
|
+
return f"Error: Agent '{agent_name}' not found. Use manage_agent with operation='create' to create it."
|
|
526
|
+
|
|
527
|
+
agent = config.agents[agent_name]
|
|
528
|
+
|
|
529
|
+
# Validate tools if provided
|
|
530
|
+
if tools is not None:
|
|
531
|
+
invalid_tools = [t for t in tools if t not in TOOL_REGISTRY]
|
|
532
|
+
if invalid_tools:
|
|
533
|
+
return f"Error: Unknown tools: {', '.join(invalid_tools)}"
|
|
534
|
+
|
|
535
|
+
# Map of field names to (new_value, display_formatter)
|
|
536
|
+
updates = {
|
|
537
|
+
"display_name": (display_name, lambda v: v),
|
|
538
|
+
"role": (role, lambda v: v),
|
|
539
|
+
"tools": (tools, lambda v: ", ".join(v) if v else "(empty)"),
|
|
540
|
+
"instructions": (instructions, lambda v: f"{len(v)} instructions" if v else "(empty)"),
|
|
541
|
+
"model": (model, lambda v: v),
|
|
542
|
+
"rooms": (rooms, lambda v: ", ".join(v) if v else "(empty)"),
|
|
543
|
+
"num_history_runs": (num_history_runs, lambda v: str(v)),
|
|
544
|
+
"markdown": (markdown, lambda v: str(v)),
|
|
545
|
+
"add_history_to_messages": (add_history_to_messages, lambda v: str(v)),
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
# Apply updates and track changes
|
|
549
|
+
changes = []
|
|
550
|
+
for field_name, (new_value, formatter) in updates.items():
|
|
551
|
+
if new_value is not None:
|
|
552
|
+
current_value = getattr(agent, field_name)
|
|
553
|
+
if new_value != current_value:
|
|
554
|
+
setattr(agent, field_name, new_value)
|
|
555
|
+
display_name = field_name.replace("_", " ").title()
|
|
556
|
+
changes.append(f"{display_name} -> {formatter(new_value)}")
|
|
557
|
+
|
|
558
|
+
if not changes:
|
|
559
|
+
return "No changes made. All provided values are the same as current configuration."
|
|
560
|
+
|
|
561
|
+
# Save config
|
|
562
|
+
config.save_to_yaml(self.config_path)
|
|
563
|
+
|
|
564
|
+
return f"✅ Successfully updated agent '{agent_name}'!\n\n**Changes:**\n" + "\n".join(
|
|
565
|
+
f"- {c}" for c in changes
|
|
566
|
+
)
|
|
567
|
+
except Exception as e:
|
|
568
|
+
logger.exception("Failed to update agent")
|
|
569
|
+
return f"Error updating agent: {e}"
|
|
570
|
+
|
|
571
|
+
def _create_team_config(
|
|
572
|
+
self,
|
|
573
|
+
team_name: str,
|
|
574
|
+
display_name: str,
|
|
575
|
+
role: str,
|
|
576
|
+
agents: list[str],
|
|
577
|
+
mode: str = "coordinate",
|
|
578
|
+
) -> str:
|
|
579
|
+
"""Create a new team configuration."""
|
|
580
|
+
if mode not in ["coordinate", "collaborate"]:
|
|
581
|
+
return "Error: Team mode must be 'coordinate' or 'collaborate'"
|
|
582
|
+
|
|
583
|
+
try:
|
|
584
|
+
config = Config.from_yaml(self.config_path)
|
|
585
|
+
|
|
586
|
+
if team_name in config.teams:
|
|
587
|
+
return f"Error: Team '{team_name}' already exists."
|
|
588
|
+
|
|
589
|
+
# Validate agents exist
|
|
590
|
+
invalid_agents = [a for a in agents if a not in config.agents]
|
|
591
|
+
if invalid_agents:
|
|
592
|
+
return f"Error: Unknown agents: {', '.join(invalid_agents)}"
|
|
593
|
+
|
|
594
|
+
# Create new team config
|
|
595
|
+
new_team = TeamConfig(
|
|
596
|
+
display_name=display_name,
|
|
597
|
+
role=role,
|
|
598
|
+
agents=agents,
|
|
599
|
+
mode=mode,
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
# Add to config
|
|
603
|
+
config.teams[team_name] = new_team
|
|
604
|
+
|
|
605
|
+
# Save config
|
|
606
|
+
config.save_to_yaml(self.config_path)
|
|
607
|
+
|
|
608
|
+
return (
|
|
609
|
+
f"✅ Successfully created team '{team_name}'!\n\n"
|
|
610
|
+
f"**Configuration:**\n"
|
|
611
|
+
f"- Display Name: {display_name}\n"
|
|
612
|
+
f"- Role: {role}\n"
|
|
613
|
+
f"- Agents: {', '.join(agents)}\n"
|
|
614
|
+
f"- Mode: {mode}\n\n"
|
|
615
|
+
f"The team can now be mentioned with @{team_name}"
|
|
616
|
+
)
|
|
617
|
+
except Exception as e:
|
|
618
|
+
logger.exception("Failed to create team")
|
|
619
|
+
return f"Error creating team: {e}"
|
|
620
|
+
|
|
621
|
+
def _validate_agent_config(self, agent_name: str) -> str: # noqa: C901, PLR0912
|
|
622
|
+
"""Validate an agent's configuration."""
|
|
623
|
+
try:
|
|
624
|
+
config = Config.from_yaml(self.config_path)
|
|
625
|
+
|
|
626
|
+
if agent_name not in config.agents:
|
|
627
|
+
return f"Error: Agent '{agent_name}' not found."
|
|
628
|
+
|
|
629
|
+
agent = config.agents[agent_name]
|
|
630
|
+
issues = []
|
|
631
|
+
warnings = []
|
|
632
|
+
|
|
633
|
+
# Check display name
|
|
634
|
+
if not agent.display_name:
|
|
635
|
+
issues.append("Missing display name")
|
|
636
|
+
|
|
637
|
+
# Check role
|
|
638
|
+
if not agent.role:
|
|
639
|
+
warnings.append("No role description provided")
|
|
640
|
+
elif len(agent.role) < 20:
|
|
641
|
+
warnings.append("Role description is very short")
|
|
642
|
+
|
|
643
|
+
# Check tools
|
|
644
|
+
if not agent.tools:
|
|
645
|
+
warnings.append("No tools configured")
|
|
646
|
+
else:
|
|
647
|
+
invalid_tools = [t for t in agent.tools if t not in TOOL_REGISTRY]
|
|
648
|
+
if invalid_tools:
|
|
649
|
+
issues.append(f"Invalid tools: {', '.join(invalid_tools)}")
|
|
650
|
+
|
|
651
|
+
# Check model
|
|
652
|
+
available_models = list(config.models.keys()) if config.models else []
|
|
653
|
+
if available_models and agent.model not in available_models:
|
|
654
|
+
warnings.append(f"Model '{agent.model}' not in configured models: {', '.join(available_models)}")
|
|
655
|
+
|
|
656
|
+
# Format results
|
|
657
|
+
output = [f"## Validation Results for '{agent_name}':\n"]
|
|
658
|
+
|
|
659
|
+
if not issues and not warnings:
|
|
660
|
+
output.append("✅ Configuration is valid!")
|
|
661
|
+
else:
|
|
662
|
+
if issues:
|
|
663
|
+
output.append("### ❌ Issues (must fix):")
|
|
664
|
+
output.extend(f"- {issue}" for issue in issues)
|
|
665
|
+
|
|
666
|
+
if warnings:
|
|
667
|
+
output.append("\n### ⚠️ Warnings (consider fixing):")
|
|
668
|
+
output.extend(f"- {warning}" for warning in warnings)
|
|
669
|
+
|
|
670
|
+
# Add summary
|
|
671
|
+
output.append("\n### Configuration Summary:")
|
|
672
|
+
output.append(f"- Display Name: {agent.display_name}")
|
|
673
|
+
output.append(f"- Role: {agent.role[:100]}..." if len(agent.role) > 100 else f"- Role: {agent.role}")
|
|
674
|
+
output.append(f"- Tools: {', '.join(agent.tools) if agent.tools else 'None'}")
|
|
675
|
+
output.append(f"- Model: {agent.model}")
|
|
676
|
+
|
|
677
|
+
return "\n".join(output)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
return f"Error validating agent: {e}"
|
|
680
|
+
|
|
681
|
+
def _get_agent_config(self, agent_name: str) -> str:
|
|
682
|
+
"""Get the full configuration for a specific agent."""
|
|
683
|
+
try:
|
|
684
|
+
config = Config.from_yaml(self.config_path)
|
|
685
|
+
|
|
686
|
+
if agent_name not in config.agents:
|
|
687
|
+
return f"Error: Agent '{agent_name}' not found."
|
|
688
|
+
|
|
689
|
+
agent = config.agents[agent_name]
|
|
690
|
+
agent_dict = agent.model_dump(exclude_none=True)
|
|
691
|
+
|
|
692
|
+
yaml_str = yaml.dump(agent_dict, default_flow_style=False, sort_keys=False)
|
|
693
|
+
except Exception as e:
|
|
694
|
+
return f"Error loading agent config: {e}"
|
|
695
|
+
else:
|
|
696
|
+
return f"## Configuration for '{agent_name}':\n\n```yaml\n{yaml_str}```"
|
|
697
|
+
|
|
698
|
+
def _generate_agent_template(self, agent_type: str) -> str:
|
|
699
|
+
"""Generate a template configuration for common agent types."""
|
|
700
|
+
# Map agent types to tool categories
|
|
701
|
+
type_to_category = {
|
|
702
|
+
"researcher": ToolCategory.RESEARCH,
|
|
703
|
+
"developer": ToolCategory.DEVELOPMENT,
|
|
704
|
+
"social": ToolCategory.SOCIAL,
|
|
705
|
+
"communicator": ToolCategory.COMMUNICATION,
|
|
706
|
+
"analyst": ToolCategory.INFORMATION,
|
|
707
|
+
"productivity": ToolCategory.PRODUCTIVITY,
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if agent_type not in type_to_category:
|
|
711
|
+
available = ", ".join(type_to_category.keys())
|
|
712
|
+
return f"Unknown template type: {agent_type}\n\nAvailable templates: {available}"
|
|
713
|
+
|
|
714
|
+
category = type_to_category[agent_type]
|
|
715
|
+
|
|
716
|
+
# Get tools from this category that are available
|
|
717
|
+
tools = [
|
|
718
|
+
name
|
|
719
|
+
for name, metadata in TOOL_METADATA.items()
|
|
720
|
+
if metadata.category == category and metadata.status == ToolStatus.AVAILABLE
|
|
721
|
+
][:5] # Limit to 5 tools
|
|
722
|
+
|
|
723
|
+
# Generate role based on category
|
|
724
|
+
role_descriptions = {
|
|
725
|
+
ToolCategory.RESEARCH: "Research specialist focused on finding and analyzing information",
|
|
726
|
+
ToolCategory.DEVELOPMENT: "Software development expert for coding and technical tasks",
|
|
727
|
+
ToolCategory.SOCIAL: "Social interaction specialist for community engagement",
|
|
728
|
+
ToolCategory.COMMUNICATION: "Communication expert for messaging and collaboration",
|
|
729
|
+
ToolCategory.INFORMATION: "Information analyst for data processing and insights",
|
|
730
|
+
ToolCategory.PRODUCTIVITY: "Productivity specialist for task and workflow management",
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
role = role_descriptions.get(category, f"Specialist in {category.value} tasks")
|
|
734
|
+
|
|
735
|
+
# Generic instructions
|
|
736
|
+
instructions = [
|
|
737
|
+
f"Focus on {category.value} tasks",
|
|
738
|
+
"Provide clear and actionable responses",
|
|
739
|
+
"Use available tools effectively",
|
|
740
|
+
]
|
|
741
|
+
|
|
742
|
+
return f"""## Template for '{agent_type}' agent:
|
|
743
|
+
|
|
744
|
+
```yaml
|
|
745
|
+
display_name: "{agent_type.title()} Agent"
|
|
746
|
+
role: "{role}"
|
|
747
|
+
tools: {yaml.dump(tools, default_flow_style=True).strip() if tools else "[]"}
|
|
748
|
+
instructions: {yaml.dump(instructions, default_flow_style=False).strip()}
|
|
749
|
+
model: "default"
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**Available tools in {category.value} category:**
|
|
753
|
+
{chr(10).join(f"- {name}: {metadata.description}" for name, metadata in TOOL_METADATA.items() if metadata.category == category)}
|
|
754
|
+
|
|
755
|
+
**To create this agent, use:**
|
|
756
|
+
```
|
|
757
|
+
manage_agent(
|
|
758
|
+
operation="create",
|
|
759
|
+
agent_name="{agent_type}_agent",
|
|
760
|
+
display_name="{agent_type.title()} Agent",
|
|
761
|
+
role="{role}",
|
|
762
|
+
tools={tools},
|
|
763
|
+
instructions={instructions},
|
|
764
|
+
)
|
|
765
|
+
```"""
|