agentpool 2.1.9__py3-none-any.whl → 2.2.3__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.
- acp/__init__.py +13 -0
- acp/bridge/README.md +15 -2
- acp/bridge/__init__.py +3 -2
- acp/bridge/__main__.py +60 -19
- acp/bridge/ws_server.py +173 -0
- acp/bridge/ws_server_cli.py +89 -0
- acp/notifications.py +2 -1
- acp/stdio.py +39 -9
- acp/transports.py +362 -2
- acp/utils.py +15 -2
- agentpool/__init__.py +4 -1
- agentpool/agents/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +203 -88
- agentpool/agents/acp_agent/acp_converters.py +46 -21
- agentpool/agents/acp_agent/client_handler.py +157 -3
- agentpool/agents/acp_agent/session_state.py +4 -1
- agentpool/agents/agent.py +314 -107
- agentpool/agents/agui_agent/__init__.py +0 -2
- agentpool/agents/agui_agent/agui_agent.py +90 -21
- agentpool/agents/agui_agent/agui_converters.py +0 -131
- agentpool/agents/base_agent.py +163 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +626 -179
- agentpool/agents/claude_code_agent/converters.py +71 -3
- agentpool/agents/claude_code_agent/history.py +474 -0
- agentpool/agents/context.py +40 -0
- agentpool/agents/events/__init__.py +2 -0
- agentpool/agents/events/builtin_handlers.py +2 -1
- agentpool/agents/events/event_emitter.py +29 -2
- agentpool/agents/events/events.py +20 -0
- agentpool/agents/modes.py +54 -0
- agentpool/agents/tool_call_accumulator.py +213 -0
- agentpool/common_types.py +21 -0
- agentpool/config_resources/__init__.py +38 -1
- agentpool/config_resources/claude_code_agent.yml +3 -0
- agentpool/delegation/pool.py +37 -29
- agentpool/delegation/team.py +1 -0
- agentpool/delegation/teamrun.py +1 -0
- agentpool/diagnostics/__init__.py +53 -0
- agentpool/diagnostics/lsp_manager.py +1593 -0
- agentpool/diagnostics/lsp_proxy.py +41 -0
- agentpool/diagnostics/lsp_proxy_script.py +229 -0
- agentpool/diagnostics/models.py +398 -0
- agentpool/mcp_server/__init__.py +0 -2
- agentpool/mcp_server/client.py +12 -3
- agentpool/mcp_server/manager.py +25 -31
- agentpool/mcp_server/registries/official_registry_client.py +25 -0
- agentpool/mcp_server/tool_bridge.py +78 -66
- agentpool/messaging/__init__.py +0 -2
- agentpool/messaging/compaction.py +72 -197
- agentpool/messaging/message_history.py +12 -0
- agentpool/messaging/messages.py +52 -9
- agentpool/messaging/processing.py +3 -1
- agentpool/models/acp_agents/base.py +0 -22
- agentpool/models/acp_agents/mcp_capable.py +8 -148
- agentpool/models/acp_agents/non_mcp.py +129 -72
- agentpool/models/agents.py +35 -13
- agentpool/models/claude_code_agents.py +33 -2
- agentpool/models/manifest.py +43 -0
- agentpool/repomap.py +1 -1
- agentpool/resource_providers/__init__.py +9 -1
- agentpool/resource_providers/aggregating.py +52 -3
- agentpool/resource_providers/base.py +57 -1
- agentpool/resource_providers/mcp_provider.py +23 -0
- agentpool/resource_providers/plan_provider.py +130 -41
- agentpool/resource_providers/pool.py +2 -0
- agentpool/resource_providers/static.py +2 -0
- agentpool/sessions/__init__.py +2 -1
- agentpool/sessions/manager.py +31 -2
- agentpool/sessions/models.py +50 -0
- agentpool/skills/registry.py +13 -8
- agentpool/storage/manager.py +217 -1
- agentpool/testing.py +537 -19
- agentpool/utils/file_watcher.py +269 -0
- agentpool/utils/identifiers.py +121 -0
- agentpool/utils/pydantic_ai_helpers.py +46 -0
- agentpool/utils/streams.py +690 -1
- agentpool/utils/subprocess_utils.py +155 -0
- agentpool/utils/token_breakdown.py +461 -0
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/METADATA +27 -7
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/RECORD +170 -112
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +4 -0
- agentpool_cli/serve_acp.py +41 -20
- agentpool_cli/serve_agui.py +87 -0
- agentpool_cli/serve_opencode.py +119 -0
- agentpool_commands/__init__.py +30 -0
- agentpool_commands/agents.py +74 -1
- agentpool_commands/history.py +62 -0
- agentpool_commands/mcp.py +176 -0
- agentpool_commands/models.py +56 -3
- agentpool_commands/tools.py +57 -0
- agentpool_commands/utils.py +51 -0
- agentpool_config/builtin_tools.py +77 -22
- agentpool_config/commands.py +24 -1
- agentpool_config/compaction.py +258 -0
- agentpool_config/mcp_server.py +131 -1
- agentpool_config/storage.py +46 -1
- agentpool_config/tools.py +7 -1
- agentpool_config/toolsets.py +92 -148
- agentpool_server/acp_server/acp_agent.py +134 -150
- agentpool_server/acp_server/commands/acp_commands.py +216 -51
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +10 -10
- agentpool_server/acp_server/server.py +23 -79
- agentpool_server/acp_server/session.py +181 -19
- agentpool_server/opencode_server/.rules +95 -0
- agentpool_server/opencode_server/ENDPOINTS.md +362 -0
- agentpool_server/opencode_server/__init__.py +27 -0
- agentpool_server/opencode_server/command_validation.py +172 -0
- agentpool_server/opencode_server/converters.py +869 -0
- agentpool_server/opencode_server/dependencies.py +24 -0
- agentpool_server/opencode_server/input_provider.py +269 -0
- agentpool_server/opencode_server/models/__init__.py +228 -0
- agentpool_server/opencode_server/models/agent.py +53 -0
- agentpool_server/opencode_server/models/app.py +60 -0
- agentpool_server/opencode_server/models/base.py +26 -0
- agentpool_server/opencode_server/models/common.py +23 -0
- agentpool_server/opencode_server/models/config.py +37 -0
- agentpool_server/opencode_server/models/events.py +647 -0
- agentpool_server/opencode_server/models/file.py +88 -0
- agentpool_server/opencode_server/models/mcp.py +25 -0
- agentpool_server/opencode_server/models/message.py +162 -0
- agentpool_server/opencode_server/models/parts.py +190 -0
- agentpool_server/opencode_server/models/provider.py +81 -0
- agentpool_server/opencode_server/models/pty.py +43 -0
- agentpool_server/opencode_server/models/session.py +99 -0
- agentpool_server/opencode_server/routes/__init__.py +25 -0
- agentpool_server/opencode_server/routes/agent_routes.py +442 -0
- agentpool_server/opencode_server/routes/app_routes.py +139 -0
- agentpool_server/opencode_server/routes/config_routes.py +241 -0
- agentpool_server/opencode_server/routes/file_routes.py +392 -0
- agentpool_server/opencode_server/routes/global_routes.py +94 -0
- agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
- agentpool_server/opencode_server/routes/message_routes.py +705 -0
- agentpool_server/opencode_server/routes/pty_routes.py +299 -0
- agentpool_server/opencode_server/routes/session_routes.py +1205 -0
- agentpool_server/opencode_server/routes/tui_routes.py +139 -0
- agentpool_server/opencode_server/server.py +430 -0
- agentpool_server/opencode_server/state.py +121 -0
- agentpool_server/opencode_server/time_utils.py +8 -0
- agentpool_storage/__init__.py +16 -0
- agentpool_storage/base.py +103 -0
- agentpool_storage/claude_provider.py +907 -0
- agentpool_storage/file_provider.py +129 -0
- agentpool_storage/memory_provider.py +61 -0
- agentpool_storage/models.py +3 -0
- agentpool_storage/opencode_provider.py +730 -0
- agentpool_storage/project_store.py +325 -0
- agentpool_storage/session_store.py +6 -0
- agentpool_storage/sql_provider/__init__.py +4 -2
- agentpool_storage/sql_provider/models.py +48 -0
- agentpool_storage/sql_provider/sql_provider.py +134 -1
- agentpool_storage/sql_provider/utils.py +10 -1
- agentpool_storage/text_log_provider.py +1 -0
- agentpool_toolsets/builtin/__init__.py +0 -8
- agentpool_toolsets/builtin/code.py +95 -56
- agentpool_toolsets/builtin/debug.py +16 -21
- agentpool_toolsets/builtin/execution_environment.py +99 -103
- agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
- agentpool_toolsets/builtin/skills.py +86 -4
- agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
- agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
- agentpool_toolsets/fsspec_toolset/grep.py +74 -2
- agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +159 -38
- agentpool_toolsets/mcp_discovery/__init__.py +5 -0
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +454 -0
- agentpool_toolsets/mcp_run_toolset.py +84 -6
- agentpool_toolsets/builtin/agent_management.py +0 -239
- agentpool_toolsets/builtin/history.py +0 -36
- agentpool_toolsets/builtin/integration.py +0 -85
- agentpool_toolsets/builtin/tool_management.py +0 -90
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/entry_points.txt +0 -0
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""MCP server management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import HttpUrl
|
|
8
|
+
from slashed import CommandContext, CommandError # noqa: TC002
|
|
9
|
+
|
|
10
|
+
from agentpool_commands.base import NodeCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from agentpool.agents.context import AgentContext
|
|
15
|
+
from agentpool.messaging import MessageNode
|
|
16
|
+
from agentpool_config.mcp_server import MCPServerConfig
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AddMCPServerCommand(NodeCommand):
|
|
20
|
+
"""Add a local MCP server via stdio transport.
|
|
21
|
+
|
|
22
|
+
Registers a new MCP server that runs as a subprocess.
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--args "arg1|arg2" Pipe-separated command arguments
|
|
26
|
+
--env "KEY=val|KEY2=val" Pipe-separated environment variables
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
/add-mcp-server myserver npx --args "mcp-server-filesystem|/tmp"
|
|
30
|
+
/add-mcp-server graphql npx --args "mcp-graphql" --env "ENDPOINT=https://api.example.com/graphql"
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
name = "add-mcp-server"
|
|
34
|
+
category = "integrations"
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
38
|
+
from agentpool import Agent
|
|
39
|
+
|
|
40
|
+
return isinstance(node, Agent)
|
|
41
|
+
|
|
42
|
+
async def execute_command(
|
|
43
|
+
self,
|
|
44
|
+
ctx: CommandContext[AgentContext],
|
|
45
|
+
name: str,
|
|
46
|
+
command: str,
|
|
47
|
+
*,
|
|
48
|
+
args: str | None = None,
|
|
49
|
+
env: str | None = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Add a local MCP server.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
ctx: Command context
|
|
55
|
+
name: Unique name for the MCP server
|
|
56
|
+
command: Command to execute for the server
|
|
57
|
+
args: Pipe-separated command arguments
|
|
58
|
+
env: Pipe-separated environment variables (KEY=value format)
|
|
59
|
+
"""
|
|
60
|
+
from agentpool_config.mcp_server import StdioMCPServerConfig
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
# Parse arguments
|
|
64
|
+
arg_list = [a.strip() for a in args.split("|")] if args else []
|
|
65
|
+
# Parse environment variables
|
|
66
|
+
env_dict: dict[str, str] = {}
|
|
67
|
+
if env:
|
|
68
|
+
for pair in env.split("|"):
|
|
69
|
+
if "=" in pair:
|
|
70
|
+
key, value = pair.split("=", 1)
|
|
71
|
+
env_dict[key.strip()] = value.strip()
|
|
72
|
+
|
|
73
|
+
config = StdioMCPServerConfig(name=name, command=command, args=arg_list, env=env_dict)
|
|
74
|
+
await ctx.context.agent.mcp.setup_server(config, add_to_config=True)
|
|
75
|
+
await ctx.print(f"**Added MCP server** `{name}` with command: `{command}`")
|
|
76
|
+
|
|
77
|
+
except Exception as e:
|
|
78
|
+
msg = f"Failed to add MCP server: {e}"
|
|
79
|
+
raise CommandError(msg) from e
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AddRemoteMCPServerCommand(NodeCommand):
|
|
83
|
+
"""Add a remote MCP server via HTTP transport.
|
|
84
|
+
|
|
85
|
+
Connects to an MCP server running on a remote URL.
|
|
86
|
+
|
|
87
|
+
Options:
|
|
88
|
+
--transport sse|streamable-http Transport type (default: streamable-http)
|
|
89
|
+
|
|
90
|
+
Examples:
|
|
91
|
+
/add-remote-mcp-server myapi https://api.example.com/mcp
|
|
92
|
+
/add-remote-mcp-server legacy https://old.api.com/mcp --transport sse
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
name = "add-remote-mcp-server"
|
|
96
|
+
category = "integrations"
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
100
|
+
from agentpool import Agent
|
|
101
|
+
|
|
102
|
+
return isinstance(node, Agent)
|
|
103
|
+
|
|
104
|
+
async def execute_command(
|
|
105
|
+
self,
|
|
106
|
+
ctx: CommandContext[AgentContext],
|
|
107
|
+
name: str,
|
|
108
|
+
url: str,
|
|
109
|
+
*,
|
|
110
|
+
transport: Literal["sse", "streamable-http"] = "streamable-http",
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Add a remote MCP server.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
ctx: Command context
|
|
116
|
+
name: Unique name for the MCP server
|
|
117
|
+
url: Server URL endpoint
|
|
118
|
+
transport: HTTP transport type (sse or streamable-http)
|
|
119
|
+
"""
|
|
120
|
+
from agentpool_config.mcp_server import SSEMCPServerConfig, StreamableHTTPMCPServerConfig
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
config: MCPServerConfig
|
|
124
|
+
if transport == "sse":
|
|
125
|
+
config = SSEMCPServerConfig(name=name, url=HttpUrl(url))
|
|
126
|
+
else:
|
|
127
|
+
config = StreamableHTTPMCPServerConfig(name=name, url=HttpUrl(url))
|
|
128
|
+
|
|
129
|
+
await ctx.context.agent.mcp.setup_server(config, add_to_config=True)
|
|
130
|
+
await ctx.print(f"**Added remote MCP server** `{name}` at `{url}` using {transport}")
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
msg = f"Failed to add remote MCP server: {e}"
|
|
134
|
+
raise CommandError(msg) from e
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ListMCPServersCommand(NodeCommand):
|
|
138
|
+
"""List all configured MCP servers.
|
|
139
|
+
|
|
140
|
+
Shows server name, type, and connection status.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
name = "list-mcp-servers"
|
|
144
|
+
category = "integrations"
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
148
|
+
from agentpool import Agent
|
|
149
|
+
|
|
150
|
+
return isinstance(node, Agent)
|
|
151
|
+
|
|
152
|
+
async def execute_command(self, ctx: CommandContext[AgentContext]) -> None:
|
|
153
|
+
"""List all MCP servers.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
ctx: Command context
|
|
157
|
+
"""
|
|
158
|
+
from agentpool_commands.markdown_utils import format_table
|
|
159
|
+
|
|
160
|
+
servers = ctx.context.agent.mcp.providers
|
|
161
|
+
if not servers:
|
|
162
|
+
await ctx.print("No MCP servers configured.")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
rows = []
|
|
166
|
+
for server in servers:
|
|
167
|
+
server_type = type(server.server).__name__.replace("MCPServerConfig", "")
|
|
168
|
+
rows.append({
|
|
169
|
+
"Name": server.name,
|
|
170
|
+
"Type": server_type,
|
|
171
|
+
"Status": "connected" if server.client.connected else "disconnected",
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
headers = ["Name", "Type", "Status"]
|
|
175
|
+
table = format_table(headers, rows)
|
|
176
|
+
await ctx.print(f"## MCP Servers\n\n{table}")
|
agentpool_commands/models.py
CHANGED
|
@@ -16,6 +16,59 @@ if TYPE_CHECKING:
|
|
|
16
16
|
from agentpool.messaging import MessageNode
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
class ListModelsCommand(NodeCommand):
|
|
20
|
+
"""List available models for the current agent.
|
|
21
|
+
|
|
22
|
+
Displays all models that can be used with the current agent,
|
|
23
|
+
formatted as a markdown table with model ID, name, and description.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
/list-models
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
name = "list-models"
|
|
30
|
+
category = "model"
|
|
31
|
+
|
|
32
|
+
async def execute_command(
|
|
33
|
+
self,
|
|
34
|
+
ctx: CommandContext[AgentContext],
|
|
35
|
+
) -> None:
|
|
36
|
+
"""List available models.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
ctx: Command context
|
|
40
|
+
"""
|
|
41
|
+
agent = ctx.context.agent
|
|
42
|
+
models = await agent.get_available_models()
|
|
43
|
+
|
|
44
|
+
if not models:
|
|
45
|
+
await ctx.print("No models available for this agent.")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
# Build markdown table
|
|
49
|
+
lines = [
|
|
50
|
+
"| Model ID | Name | Description |",
|
|
51
|
+
"|----------|------|-------------|",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
for model in models:
|
|
55
|
+
model_id = model.id_override if model.id_override else model.id
|
|
56
|
+
name = model.name or ""
|
|
57
|
+
description = model.description or ""
|
|
58
|
+
# Escape pipe characters in fields
|
|
59
|
+
name = name.replace("|", "\\|")
|
|
60
|
+
description = description.replace("|", "\\|")
|
|
61
|
+
lines.append(f"| `{model_id}` | {name} | {description} |")
|
|
62
|
+
|
|
63
|
+
await ctx.print("\n".join(lines))
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
67
|
+
from agentpool import ACPAgent, Agent, ClaudeCodeAgent
|
|
68
|
+
|
|
69
|
+
return isinstance(node, Agent | ACPAgent | ClaudeCodeAgent)
|
|
70
|
+
|
|
71
|
+
|
|
19
72
|
class SetModelCommand(NodeCommand):
|
|
20
73
|
"""Change the language model for the current conversation.
|
|
21
74
|
|
|
@@ -46,7 +99,7 @@ class SetModelCommand(NodeCommand):
|
|
|
46
99
|
"""
|
|
47
100
|
try:
|
|
48
101
|
# Create new session with model override
|
|
49
|
-
ctx.context.
|
|
102
|
+
await ctx.context.agent.set_model(model)
|
|
50
103
|
await ctx.print(f"✅ **Model changed to:** `{model}`")
|
|
51
104
|
except Exception as e: # noqa: BLE001
|
|
52
105
|
await ctx.print(f"❌ **Failed to change model:** {e}")
|
|
@@ -57,6 +110,6 @@ class SetModelCommand(NodeCommand):
|
|
|
57
110
|
|
|
58
111
|
@classmethod
|
|
59
112
|
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
60
|
-
from agentpool import Agent
|
|
113
|
+
from agentpool import ACPAgent, Agent, ClaudeCodeAgent
|
|
61
114
|
|
|
62
|
-
return isinstance(node, Agent)
|
|
115
|
+
return isinstance(node, Agent | ACPAgent | ClaudeCodeAgent)
|
agentpool_commands/tools.py
CHANGED
|
@@ -264,6 +264,63 @@ class RegisterToolCommand(NodeCommand):
|
|
|
264
264
|
raise CommandError(msg) from e
|
|
265
265
|
|
|
266
266
|
|
|
267
|
+
class RegisterCodeToolCommand(NodeCommand):
|
|
268
|
+
"""Register a new tool from Python code.
|
|
269
|
+
|
|
270
|
+
The code should define a function that will become the tool.
|
|
271
|
+
The function name becomes the tool name unless overridden.
|
|
272
|
+
|
|
273
|
+
Options:
|
|
274
|
+
--name tool_name Custom name for the tool
|
|
275
|
+
--description "text" Custom description
|
|
276
|
+
|
|
277
|
+
Examples:
|
|
278
|
+
/register-code-tool "def greet(name: str) -> str: return f'Hello {name}'"
|
|
279
|
+
/register-code-tool "def add(a: int, b: int) -> int: return a + b" --name sum
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
name = "register-code-tool"
|
|
283
|
+
category = "tools"
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
287
|
+
from agentpool import Agent
|
|
288
|
+
|
|
289
|
+
return isinstance(node, Agent)
|
|
290
|
+
|
|
291
|
+
async def execute_command(
|
|
292
|
+
self,
|
|
293
|
+
ctx: CommandContext[AgentContext],
|
|
294
|
+
code: str,
|
|
295
|
+
*,
|
|
296
|
+
name: str | None = None,
|
|
297
|
+
description: str | None = None,
|
|
298
|
+
) -> None:
|
|
299
|
+
"""Register a new tool from code string.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
ctx: Command context
|
|
303
|
+
code: Python code defining a function
|
|
304
|
+
name: Optional custom name for the tool
|
|
305
|
+
description: Optional custom description
|
|
306
|
+
"""
|
|
307
|
+
from agentpool.tools.base import Tool
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
tool_obj = Tool.from_code(code, name=name, description=description)
|
|
311
|
+
tool_obj.enabled = True
|
|
312
|
+
tool_obj.source = "dynamic"
|
|
313
|
+
|
|
314
|
+
registered = ctx.context.agent.tools.register_tool(tool_obj)
|
|
315
|
+
await ctx.print(
|
|
316
|
+
f"**Tool registered:** `{registered.name}`"
|
|
317
|
+
f" - {registered.description or '*No description*'}"
|
|
318
|
+
)
|
|
319
|
+
except Exception as e:
|
|
320
|
+
msg = f"Failed to register code tool: {e}"
|
|
321
|
+
raise CommandError(msg) from e
|
|
322
|
+
|
|
323
|
+
|
|
267
324
|
async def get_tool_names(ctx: CompletionContext[AgentContext]) -> list[str]:
|
|
268
325
|
manager = ctx.command_context.context.agent.tools
|
|
269
326
|
return list({t.name for t in await manager.get_tools()})
|
agentpool_commands/utils.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import importlib.util
|
|
6
|
+
from typing import Literal
|
|
6
7
|
import webbrowser
|
|
7
8
|
|
|
8
9
|
from anyenv.text_sharing import TextSharerStr, Visibility # noqa: TC002
|
|
@@ -12,6 +13,9 @@ from agentpool.messaging.context import NodeContext # noqa: TC001
|
|
|
12
13
|
from agentpool_commands.base import NodeCommand
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
17
|
+
|
|
18
|
+
|
|
15
19
|
class CopyClipboardCommand(NodeCommand):
|
|
16
20
|
"""Copy messages from conversation history to system clipboard.
|
|
17
21
|
|
|
@@ -187,3 +191,50 @@ class ShareHistoryCommand(NodeCommand):
|
|
|
187
191
|
def condition(cls) -> bool:
|
|
188
192
|
"""Check if anyenv text_sharing is available."""
|
|
189
193
|
return importlib.util.find_spec("anyenv.text_sharing") is not None
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class GetLogsCommand(NodeCommand):
|
|
197
|
+
"""Get recent log entries from memory.
|
|
198
|
+
|
|
199
|
+
Shows captured log entries with filtering options:
|
|
200
|
+
- Filter by minimum log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
201
|
+
- Filter by logger name substring
|
|
202
|
+
- Limit number of entries returned
|
|
203
|
+
|
|
204
|
+
Logs are shown newest first.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
name = "get-logs"
|
|
208
|
+
category = "debug"
|
|
209
|
+
|
|
210
|
+
async def execute_command(
|
|
211
|
+
self,
|
|
212
|
+
ctx: CommandContext[NodeContext],
|
|
213
|
+
*,
|
|
214
|
+
level: LogLevel = "INFO",
|
|
215
|
+
logger_filter: str | None = None,
|
|
216
|
+
limit: int | str = 50,
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Get recent log entries.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
ctx: Command context
|
|
222
|
+
level: Minimum log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
223
|
+
logger_filter: Only show logs from loggers containing this string
|
|
224
|
+
limit: Maximum number of log entries to return
|
|
225
|
+
"""
|
|
226
|
+
from agentpool_toolsets.builtin.debug import get_memory_handler
|
|
227
|
+
|
|
228
|
+
# Convert limit to int (slashed passes args as strings)
|
|
229
|
+
limit_int = int(limit) if isinstance(limit, str) else limit
|
|
230
|
+
|
|
231
|
+
handler = get_memory_handler()
|
|
232
|
+
records = handler.get_records(level=level, logger_filter=logger_filter, limit=limit_int)
|
|
233
|
+
|
|
234
|
+
if not records:
|
|
235
|
+
await ctx.print("No log entries found matching criteria")
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
await ctx.print(f"## 📋 {len(records)} Log Entries (newest first)\n")
|
|
239
|
+
for record in records:
|
|
240
|
+
await ctx.print(record.message)
|
|
@@ -25,17 +25,33 @@ if TYPE_CHECKING:
|
|
|
25
25
|
class BaseBuiltinToolConfig(BaseToolConfig):
|
|
26
26
|
"""Base configuration for PydanticAI builtin tools."""
|
|
27
27
|
|
|
28
|
+
type: Literal["builtin"] = Field("builtin", init=False)
|
|
29
|
+
"""Top-level discriminator - always 'builtin' for builtin tools."""
|
|
30
|
+
|
|
31
|
+
builtin_type: str = Field(init=False)
|
|
32
|
+
"""Sub-discriminator for specific builtin tool type."""
|
|
33
|
+
|
|
28
34
|
def get_builtin_tool(self) -> AbstractBuiltinTool:
|
|
29
35
|
"""Convert config to PydanticAI builtin tool instance."""
|
|
30
36
|
raise NotImplementedError
|
|
31
37
|
|
|
32
38
|
|
|
33
39
|
class WebSearchToolConfig(BaseBuiltinToolConfig):
|
|
34
|
-
"""Configuration for PydanticAI web search builtin tool.
|
|
40
|
+
"""Configuration for PydanticAI web search builtin tool.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
```yaml
|
|
44
|
+
tools:
|
|
45
|
+
- type: builtin
|
|
46
|
+
builtin_type: web_search
|
|
47
|
+
search_context_size: high
|
|
48
|
+
blocked_domains: ["spam.com"]
|
|
49
|
+
```
|
|
50
|
+
"""
|
|
35
51
|
|
|
36
52
|
model_config = ConfigDict(title="Web Search Tool")
|
|
37
53
|
|
|
38
|
-
|
|
54
|
+
builtin_type: Literal["web_search"] = Field("web_search", init=False)
|
|
39
55
|
"""Web search builtin tool."""
|
|
40
56
|
|
|
41
57
|
search_context_size: Literal["low", "medium", "high"] = Field(
|
|
@@ -77,11 +93,19 @@ class WebSearchToolConfig(BaseBuiltinToolConfig):
|
|
|
77
93
|
|
|
78
94
|
|
|
79
95
|
class CodeExecutionToolConfig(BaseBuiltinToolConfig):
|
|
80
|
-
"""Configuration for PydanticAI code execution builtin tool.
|
|
96
|
+
"""Configuration for PydanticAI code execution builtin tool.
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
```yaml
|
|
100
|
+
tools:
|
|
101
|
+
- type: builtin
|
|
102
|
+
builtin_type: code_execution
|
|
103
|
+
```
|
|
104
|
+
"""
|
|
81
105
|
|
|
82
106
|
model_config = ConfigDict(title="Code Execution Tool")
|
|
83
107
|
|
|
84
|
-
|
|
108
|
+
builtin_type: Literal["code_execution"] = Field("code_execution", init=False)
|
|
85
109
|
"""Code execution builtin tool."""
|
|
86
110
|
|
|
87
111
|
def get_builtin_tool(self) -> CodeExecutionTool:
|
|
@@ -90,12 +114,20 @@ class CodeExecutionToolConfig(BaseBuiltinToolConfig):
|
|
|
90
114
|
|
|
91
115
|
|
|
92
116
|
class WebFetchToolConfig(BaseBuiltinToolConfig):
|
|
93
|
-
"""Configuration for PydanticAI
|
|
117
|
+
"""Configuration for PydanticAI web fetch builtin tool.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
```yaml
|
|
121
|
+
tools:
|
|
122
|
+
- type: builtin
|
|
123
|
+
builtin_type: web_fetch
|
|
124
|
+
```
|
|
125
|
+
"""
|
|
94
126
|
|
|
95
|
-
model_config = ConfigDict(title="
|
|
127
|
+
model_config = ConfigDict(title="Web Fetch Tool")
|
|
96
128
|
|
|
97
|
-
|
|
98
|
-
"""
|
|
129
|
+
builtin_type: Literal["web_fetch"] = Field("web_fetch", init=False)
|
|
130
|
+
"""Web fetch builtin tool."""
|
|
99
131
|
|
|
100
132
|
def get_builtin_tool(self) -> WebFetchTool:
|
|
101
133
|
"""Convert config to WebFetchTool instance."""
|
|
@@ -103,11 +135,21 @@ class WebFetchToolConfig(BaseBuiltinToolConfig):
|
|
|
103
135
|
|
|
104
136
|
|
|
105
137
|
class ImageGenerationToolConfig(BaseBuiltinToolConfig):
|
|
106
|
-
"""Configuration for PydanticAI image generation builtin tool.
|
|
138
|
+
"""Configuration for PydanticAI image generation builtin tool.
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
```yaml
|
|
142
|
+
tools:
|
|
143
|
+
- type: builtin
|
|
144
|
+
builtin_type: image_generation
|
|
145
|
+
quality: high
|
|
146
|
+
size: 1024x1024
|
|
147
|
+
```
|
|
148
|
+
"""
|
|
107
149
|
|
|
108
150
|
model_config = ConfigDict(title="Image Generation Tool")
|
|
109
151
|
|
|
110
|
-
|
|
152
|
+
builtin_type: Literal["image_generation"] = Field("image_generation", init=False)
|
|
111
153
|
"""Image generation builtin tool."""
|
|
112
154
|
|
|
113
155
|
background: Literal["transparent", "opaque", "auto"] = Field(
|
|
@@ -184,11 +226,19 @@ class ImageGenerationToolConfig(BaseBuiltinToolConfig):
|
|
|
184
226
|
|
|
185
227
|
|
|
186
228
|
class MemoryToolConfig(BaseBuiltinToolConfig):
|
|
187
|
-
"""Configuration for PydanticAI memory builtin tool.
|
|
229
|
+
"""Configuration for PydanticAI memory builtin tool.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
```yaml
|
|
233
|
+
tools:
|
|
234
|
+
- type: builtin
|
|
235
|
+
builtin_type: memory
|
|
236
|
+
```
|
|
237
|
+
"""
|
|
188
238
|
|
|
189
239
|
model_config = ConfigDict(title="Memory Tool")
|
|
190
240
|
|
|
191
|
-
|
|
241
|
+
builtin_type: Literal["memory"] = Field("memory", init=False)
|
|
192
242
|
"""Memory builtin tool."""
|
|
193
243
|
|
|
194
244
|
def get_builtin_tool(self) -> MemoryTool:
|
|
@@ -197,11 +247,21 @@ class MemoryToolConfig(BaseBuiltinToolConfig):
|
|
|
197
247
|
|
|
198
248
|
|
|
199
249
|
class MCPServerToolConfig(BaseBuiltinToolConfig):
|
|
200
|
-
"""Configuration for PydanticAI MCP server builtin tool.
|
|
250
|
+
"""Configuration for PydanticAI MCP server builtin tool.
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
```yaml
|
|
254
|
+
tools:
|
|
255
|
+
- type: builtin
|
|
256
|
+
builtin_type: mcp_server
|
|
257
|
+
id: my_server
|
|
258
|
+
url: https://api.example.com/mcp
|
|
259
|
+
```
|
|
260
|
+
"""
|
|
201
261
|
|
|
202
262
|
model_config = ConfigDict(title="MCP Server Tool")
|
|
203
263
|
|
|
204
|
-
|
|
264
|
+
builtin_type: Literal["mcp_server"] = Field("mcp_server", init=False)
|
|
205
265
|
"""MCP server builtin tool."""
|
|
206
266
|
|
|
207
267
|
server_id: str = Field(
|
|
@@ -224,12 +284,7 @@ class MCPServerToolConfig(BaseBuiltinToolConfig):
|
|
|
224
284
|
)
|
|
225
285
|
"""Authorization header to use when making requests to the MCP server."""
|
|
226
286
|
|
|
227
|
-
description
|
|
228
|
-
default=None,
|
|
229
|
-
examples=["External API tools", "Code execution server"],
|
|
230
|
-
title="Server description",
|
|
231
|
-
)
|
|
232
|
-
"""A description of the MCP server."""
|
|
287
|
+
# description is inherited from BaseToolConfig
|
|
233
288
|
|
|
234
289
|
allowed_tools: list[str] | None = Field(
|
|
235
290
|
default=None,
|
|
@@ -253,7 +308,7 @@ class MCPServerToolConfig(BaseBuiltinToolConfig):
|
|
|
253
308
|
)
|
|
254
309
|
|
|
255
310
|
|
|
256
|
-
# Union type for builtin tool configs
|
|
311
|
+
# Union type for builtin tool configs (sub-discriminated by builtin_type)
|
|
257
312
|
BuiltinToolConfig = Annotated[
|
|
258
313
|
WebSearchToolConfig
|
|
259
314
|
| CodeExecutionToolConfig
|
|
@@ -261,5 +316,5 @@ BuiltinToolConfig = Annotated[
|
|
|
261
316
|
| ImageGenerationToolConfig
|
|
262
317
|
| MemoryToolConfig
|
|
263
318
|
| MCPServerToolConfig,
|
|
264
|
-
Field(discriminator="
|
|
319
|
+
Field(discriminator="builtin_type"),
|
|
265
320
|
]
|
agentpool_config/commands.py
CHANGED
|
@@ -6,12 +6,16 @@ from collections.abc import Callable
|
|
|
6
6
|
import inspect
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
import re
|
|
9
|
-
from typing import Annotated, Any, Literal
|
|
9
|
+
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
|
10
10
|
|
|
11
11
|
from pydantic import ConfigDict, Field, ImportString
|
|
12
12
|
from schemez import Schema
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from slashed import Command
|
|
17
|
+
|
|
18
|
+
|
|
15
19
|
class BaseCommandConfig(Schema):
|
|
16
20
|
"""Base configuration for commands."""
|
|
17
21
|
|
|
@@ -43,6 +47,25 @@ class BaseCommandConfig(Schema):
|
|
|
43
47
|
"""
|
|
44
48
|
raise NotImplementedError
|
|
45
49
|
|
|
50
|
+
def get_slashed_command(self, category: str = "manifest") -> Command:
|
|
51
|
+
"""Create a slashed Command from this configuration.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
category: Category to assign to the command
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A slashed Command instance ready for registration
|
|
58
|
+
"""
|
|
59
|
+
from slashed import Command
|
|
60
|
+
|
|
61
|
+
func = self.get_callable()
|
|
62
|
+
return Command(
|
|
63
|
+
func,
|
|
64
|
+
name=self.name,
|
|
65
|
+
description=self.description,
|
|
66
|
+
category=category,
|
|
67
|
+
)
|
|
68
|
+
|
|
46
69
|
|
|
47
70
|
class StaticCommandConfig(BaseCommandConfig):
|
|
48
71
|
"""Static command with inline content."""
|