agentpool 2.2.3__py3-none-any.whl → 2.5.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.
- acp/__init__.py +0 -4
- acp/acp_requests.py +20 -77
- acp/agent/connection.py +8 -0
- acp/agent/implementations/debug_server/debug_server.py +6 -2
- acp/agent/protocol.py +6 -0
- acp/client/connection.py +38 -29
- acp/client/implementations/default_client.py +3 -2
- acp/client/implementations/headless_client.py +2 -2
- acp/connection.py +2 -2
- acp/notifications.py +18 -49
- acp/schema/__init__.py +2 -0
- acp/schema/agent_responses.py +21 -0
- acp/schema/client_requests.py +3 -3
- acp/schema/session_state.py +63 -29
- acp/task/supervisor.py +2 -2
- acp/utils.py +2 -2
- agentpool/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +278 -263
- agentpool/agents/acp_agent/acp_converters.py +150 -17
- agentpool/agents/acp_agent/client_handler.py +35 -24
- agentpool/agents/acp_agent/session_state.py +14 -6
- agentpool/agents/agent.py +471 -643
- agentpool/agents/agui_agent/agui_agent.py +104 -107
- agentpool/agents/agui_agent/helpers.py +3 -4
- agentpool/agents/base_agent.py +485 -32
- agentpool/agents/claude_code_agent/FORKING.md +191 -0
- agentpool/agents/claude_code_agent/__init__.py +13 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +654 -334
- agentpool/agents/claude_code_agent/converters.py +4 -141
- agentpool/agents/claude_code_agent/models.py +77 -0
- agentpool/agents/claude_code_agent/static_info.py +100 -0
- agentpool/agents/claude_code_agent/usage.py +242 -0
- agentpool/agents/events/__init__.py +22 -0
- agentpool/agents/events/builtin_handlers.py +65 -0
- agentpool/agents/events/event_emitter.py +3 -0
- agentpool/agents/events/events.py +84 -3
- agentpool/agents/events/infer_info.py +145 -0
- agentpool/agents/events/processors.py +254 -0
- agentpool/agents/interactions.py +41 -6
- agentpool/agents/modes.py +13 -0
- agentpool/agents/slashed_agent.py +5 -4
- agentpool/agents/tool_wrapping.py +18 -6
- agentpool/common_types.py +35 -21
- agentpool/config_resources/acp_assistant.yml +2 -2
- agentpool/config_resources/agents.yml +3 -0
- agentpool/config_resources/agents_template.yml +1 -0
- agentpool/config_resources/claude_code_agent.yml +9 -8
- agentpool/config_resources/external_acp_agents.yml +2 -1
- agentpool/delegation/base_team.py +4 -30
- agentpool/delegation/pool.py +104 -265
- agentpool/delegation/team.py +57 -57
- agentpool/delegation/teamrun.py +50 -55
- agentpool/functional/run.py +10 -4
- agentpool/mcp_server/client.py +73 -38
- agentpool/mcp_server/conversions.py +54 -13
- agentpool/mcp_server/manager.py +9 -23
- agentpool/mcp_server/registries/official_registry_client.py +10 -1
- agentpool/mcp_server/tool_bridge.py +114 -79
- agentpool/messaging/connection_manager.py +11 -10
- agentpool/messaging/event_manager.py +5 -5
- agentpool/messaging/message_container.py +6 -30
- agentpool/messaging/message_history.py +87 -8
- agentpool/messaging/messagenode.py +52 -14
- agentpool/messaging/messages.py +2 -26
- agentpool/messaging/processing.py +10 -22
- agentpool/models/__init__.py +1 -1
- agentpool/models/acp_agents/base.py +6 -2
- agentpool/models/acp_agents/mcp_capable.py +124 -15
- agentpool/models/acp_agents/non_mcp.py +0 -23
- agentpool/models/agents.py +66 -66
- agentpool/models/agui_agents.py +1 -1
- agentpool/models/claude_code_agents.py +111 -17
- agentpool/models/file_parsing.py +0 -1
- agentpool/models/manifest.py +70 -50
- agentpool/prompts/conversion_manager.py +1 -1
- agentpool/prompts/prompts.py +5 -2
- agentpool/resource_providers/__init__.py +2 -0
- agentpool/resource_providers/aggregating.py +4 -2
- agentpool/resource_providers/base.py +13 -3
- agentpool/resource_providers/codemode/code_executor.py +72 -5
- agentpool/resource_providers/codemode/helpers.py +2 -2
- agentpool/resource_providers/codemode/provider.py +64 -12
- agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
- agentpool/resource_providers/codemode/remote_provider.py +9 -12
- agentpool/resource_providers/filtering.py +3 -1
- agentpool/resource_providers/mcp_provider.py +66 -12
- agentpool/resource_providers/plan_provider.py +111 -18
- agentpool/resource_providers/pool.py +5 -3
- agentpool/resource_providers/resource_info.py +111 -0
- agentpool/resource_providers/static.py +2 -2
- agentpool/sessions/__init__.py +2 -0
- agentpool/sessions/manager.py +2 -3
- agentpool/sessions/models.py +9 -6
- agentpool/sessions/protocol.py +28 -0
- agentpool/sessions/session.py +11 -55
- agentpool/storage/manager.py +361 -54
- agentpool/talk/registry.py +4 -4
- agentpool/talk/talk.py +9 -10
- agentpool/testing.py +1 -1
- agentpool/tool_impls/__init__.py +6 -0
- agentpool/tool_impls/agent_cli/__init__.py +42 -0
- agentpool/tool_impls/agent_cli/tool.py +95 -0
- agentpool/tool_impls/bash/__init__.py +64 -0
- agentpool/tool_impls/bash/helpers.py +35 -0
- agentpool/tool_impls/bash/tool.py +171 -0
- agentpool/tool_impls/delete_path/__init__.py +70 -0
- agentpool/tool_impls/delete_path/tool.py +142 -0
- agentpool/tool_impls/download_file/__init__.py +80 -0
- agentpool/tool_impls/download_file/tool.py +183 -0
- agentpool/tool_impls/execute_code/__init__.py +55 -0
- agentpool/tool_impls/execute_code/tool.py +163 -0
- agentpool/tool_impls/grep/__init__.py +80 -0
- agentpool/tool_impls/grep/tool.py +200 -0
- agentpool/tool_impls/list_directory/__init__.py +73 -0
- agentpool/tool_impls/list_directory/tool.py +197 -0
- agentpool/tool_impls/question/__init__.py +42 -0
- agentpool/tool_impls/question/tool.py +127 -0
- agentpool/tool_impls/read/__init__.py +104 -0
- agentpool/tool_impls/read/tool.py +305 -0
- agentpool/tools/__init__.py +2 -1
- agentpool/tools/base.py +114 -34
- agentpool/tools/manager.py +57 -1
- agentpool/ui/base.py +2 -2
- agentpool/ui/mock_provider.py +2 -2
- agentpool/ui/stdlib_provider.py +2 -2
- agentpool/utils/streams.py +21 -96
- agentpool/vfs_registry.py +7 -2
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/METADATA +16 -22
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/RECORD +242 -195
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +20 -0
- agentpool_cli/create.py +1 -1
- agentpool_cli/serve_acp.py +59 -1
- agentpool_cli/serve_opencode.py +1 -1
- agentpool_cli/ui.py +557 -0
- agentpool_commands/__init__.py +12 -5
- agentpool_commands/agents.py +1 -1
- agentpool_commands/pool.py +260 -0
- agentpool_commands/session.py +1 -1
- agentpool_commands/text_sharing/__init__.py +119 -0
- agentpool_commands/text_sharing/base.py +123 -0
- agentpool_commands/text_sharing/github_gist.py +80 -0
- agentpool_commands/text_sharing/opencode.py +462 -0
- agentpool_commands/text_sharing/paste_rs.py +59 -0
- agentpool_commands/text_sharing/pastebin.py +116 -0
- agentpool_commands/text_sharing/shittycodingagent.py +112 -0
- agentpool_commands/utils.py +31 -32
- agentpool_config/__init__.py +30 -2
- agentpool_config/agentpool_tools.py +498 -0
- agentpool_config/converters.py +1 -1
- agentpool_config/event_handlers.py +42 -0
- agentpool_config/events.py +1 -1
- agentpool_config/forward_targets.py +1 -4
- agentpool_config/jinja.py +3 -3
- agentpool_config/mcp_server.py +1 -5
- agentpool_config/nodes.py +1 -1
- agentpool_config/observability.py +44 -0
- agentpool_config/session.py +0 -3
- agentpool_config/storage.py +38 -39
- agentpool_config/task.py +3 -3
- agentpool_config/tools.py +11 -28
- agentpool_config/toolsets.py +22 -90
- agentpool_server/a2a_server/agent_worker.py +307 -0
- agentpool_server/a2a_server/server.py +23 -18
- agentpool_server/acp_server/acp_agent.py +125 -56
- agentpool_server/acp_server/commands/acp_commands.py +46 -216
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +8 -7
- agentpool_server/acp_server/event_converter.py +651 -0
- agentpool_server/acp_server/input_provider.py +53 -10
- agentpool_server/acp_server/server.py +1 -11
- agentpool_server/acp_server/session.py +90 -410
- agentpool_server/acp_server/session_manager.py +8 -34
- agentpool_server/agui_server/server.py +3 -1
- agentpool_server/mcp_server/server.py +5 -2
- agentpool_server/opencode_server/ENDPOINTS.md +53 -14
- agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
- agentpool_server/opencode_server/__init__.py +0 -8
- agentpool_server/opencode_server/converters.py +132 -26
- agentpool_server/opencode_server/input_provider.py +160 -8
- agentpool_server/opencode_server/models/__init__.py +42 -20
- agentpool_server/opencode_server/models/app.py +12 -0
- agentpool_server/opencode_server/models/events.py +203 -29
- agentpool_server/opencode_server/models/mcp.py +19 -0
- agentpool_server/opencode_server/models/message.py +18 -1
- agentpool_server/opencode_server/models/parts.py +134 -1
- agentpool_server/opencode_server/models/question.py +56 -0
- agentpool_server/opencode_server/models/session.py +13 -1
- agentpool_server/opencode_server/routes/__init__.py +4 -0
- agentpool_server/opencode_server/routes/agent_routes.py +33 -2
- agentpool_server/opencode_server/routes/app_routes.py +66 -3
- agentpool_server/opencode_server/routes/config_routes.py +66 -5
- agentpool_server/opencode_server/routes/file_routes.py +184 -5
- agentpool_server/opencode_server/routes/global_routes.py +1 -1
- agentpool_server/opencode_server/routes/lsp_routes.py +1 -1
- agentpool_server/opencode_server/routes/message_routes.py +122 -66
- agentpool_server/opencode_server/routes/permission_routes.py +63 -0
- agentpool_server/opencode_server/routes/pty_routes.py +23 -22
- agentpool_server/opencode_server/routes/question_routes.py +128 -0
- agentpool_server/opencode_server/routes/session_routes.py +139 -68
- agentpool_server/opencode_server/routes/tui_routes.py +1 -1
- agentpool_server/opencode_server/server.py +47 -2
- agentpool_server/opencode_server/state.py +30 -0
- agentpool_storage/__init__.py +0 -4
- agentpool_storage/base.py +81 -2
- agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
- agentpool_storage/claude_provider/__init__.py +42 -0
- agentpool_storage/{claude_provider.py → claude_provider/provider.py} +190 -8
- agentpool_storage/file_provider.py +149 -15
- agentpool_storage/memory_provider.py +132 -12
- agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
- agentpool_storage/opencode_provider/__init__.py +16 -0
- agentpool_storage/opencode_provider/helpers.py +414 -0
- agentpool_storage/opencode_provider/provider.py +895 -0
- agentpool_storage/session_store.py +20 -6
- agentpool_storage/sql_provider/sql_provider.py +135 -2
- agentpool_storage/sql_provider/utils.py +2 -12
- agentpool_storage/zed_provider/__init__.py +16 -0
- agentpool_storage/zed_provider/helpers.py +281 -0
- agentpool_storage/zed_provider/models.py +130 -0
- agentpool_storage/zed_provider/provider.py +442 -0
- agentpool_storage/zed_provider.py +803 -0
- agentpool_toolsets/__init__.py +0 -2
- agentpool_toolsets/builtin/__init__.py +2 -4
- agentpool_toolsets/builtin/code.py +4 -4
- agentpool_toolsets/builtin/debug.py +115 -40
- agentpool_toolsets/builtin/execution_environment.py +54 -165
- agentpool_toolsets/builtin/skills.py +0 -77
- agentpool_toolsets/builtin/subagent_tools.py +64 -51
- agentpool_toolsets/builtin/workers.py +4 -2
- agentpool_toolsets/composio_toolset.py +2 -2
- agentpool_toolsets/entry_points.py +3 -1
- agentpool_toolsets/fsspec_toolset/grep.py +25 -5
- agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
- agentpool_toolsets/fsspec_toolset/toolset.py +350 -66
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +74 -17
- agentpool_toolsets/mcp_run_toolset.py +8 -11
- agentpool_toolsets/notifications.py +33 -33
- agentpool_toolsets/openapi.py +3 -1
- agentpool_toolsets/search_toolset.py +3 -1
- agentpool_config/resources.py +0 -33
- agentpool_server/acp_server/acp_tools.py +0 -43
- agentpool_server/acp_server/commands/spawn.py +0 -210
- agentpool_storage/opencode_provider.py +0 -730
- agentpool_storage/text_log_provider.py +0 -276
- agentpool_toolsets/builtin/chain.py +0 -288
- agentpool_toolsets/builtin/user_interaction.py +0 -52
- agentpool_toolsets/semantic_memory_toolset.py +0 -536
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,11 +8,13 @@ from agentpool.resource_providers.base import ResourceChangeEvent, ResourceProvi
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
|
|
11
13
|
from pydantic_ai import ModelRequestPart
|
|
12
14
|
|
|
13
15
|
from agentpool.prompts.prompts import BasePrompt
|
|
16
|
+
from agentpool.resource_providers.resource_info import ResourceInfo
|
|
14
17
|
from agentpool.tools.base import Tool
|
|
15
|
-
from agentpool_config.resources import ResourceInfo
|
|
16
18
|
|
|
17
19
|
_ = ResourceChangeEvent # Used at runtime in method signatures
|
|
18
20
|
|
|
@@ -78,7 +80,7 @@ class AggregatingResourceProvider(ResourceProvider):
|
|
|
78
80
|
"""Forward skills_changed signal from child provider."""
|
|
79
81
|
await self.skills_changed.emit(event)
|
|
80
82
|
|
|
81
|
-
async def get_tools(self) ->
|
|
83
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
82
84
|
"""Get tools from all providers."""
|
|
83
85
|
return [t for provider in self.providers for t in await provider.get_tools()]
|
|
84
86
|
|
|
@@ -13,16 +13,17 @@ from agentpool_config.tools import ToolHints
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
-
from collections.abc import Callable
|
|
16
|
+
from collections.abc import Callable, Sequence
|
|
17
17
|
from types import TracebackType
|
|
18
18
|
|
|
19
|
+
from fsspec.asyn import AsyncFileSystem
|
|
19
20
|
from pydantic_ai import ModelRequestPart
|
|
20
21
|
from schemez import OpenAIFunctionDefinition
|
|
21
22
|
|
|
22
23
|
from agentpool.prompts.prompts import BasePrompt
|
|
24
|
+
from agentpool.resource_providers.resource_info import ResourceInfo
|
|
23
25
|
from agentpool.skills.skill import Skill
|
|
24
26
|
from agentpool.tools.base import ToolKind
|
|
25
|
-
from agentpool_config.resources import ResourceInfo
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
logger = get_logger(__name__)
|
|
@@ -109,7 +110,7 @@ class ResourceProvider:
|
|
|
109
110
|
def __repr__(self) -> str:
|
|
110
111
|
return f"{self.__class__.__name__}(name={self.name!r})"
|
|
111
112
|
|
|
112
|
-
async def get_tools(self) ->
|
|
113
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
113
114
|
"""Get available tools. Override to provide tools."""
|
|
114
115
|
return []
|
|
115
116
|
|
|
@@ -134,6 +135,15 @@ class ResourceProvider:
|
|
|
134
135
|
"""Get available skills. Override to provide skills."""
|
|
135
136
|
return []
|
|
136
137
|
|
|
138
|
+
def get_fs(self) -> AsyncFileSystem | None:
|
|
139
|
+
"""Get filesystem view of provider state/history.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
AsyncFileSystem or None if not supported.
|
|
143
|
+
Override to expose provider state through filesystem interface.
|
|
144
|
+
"""
|
|
145
|
+
return None
|
|
146
|
+
|
|
137
147
|
async def get_skill_instructions(self, skill_name: str) -> str:
|
|
138
148
|
"""Get full instructions for a specific skill.
|
|
139
149
|
|
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import contextlib
|
|
7
7
|
from dataclasses import dataclass
|
|
8
|
+
from datetime import UTC, datetime
|
|
9
|
+
import json
|
|
8
10
|
from typing import TYPE_CHECKING, Any, Self
|
|
9
11
|
|
|
10
12
|
import anyio
|
|
@@ -18,9 +20,10 @@ if TYPE_CHECKING:
|
|
|
18
20
|
from types import TracebackType
|
|
19
21
|
|
|
20
22
|
from exxec.base import ExecutionEnvironment
|
|
21
|
-
from exxec.configs import ExecutionEnvironmentConfig
|
|
22
23
|
from exxec.models import ServerInfo
|
|
24
|
+
from exxec_config import ExecutionEnvironmentConfig
|
|
23
25
|
from fastapi import FastAPI
|
|
26
|
+
from fsspec.asyn import AsyncFileSystem
|
|
24
27
|
from schemez import ToolsetCodeGenerator
|
|
25
28
|
import uvicorn
|
|
26
29
|
|
|
@@ -46,6 +49,14 @@ class RemoteCodeExecutor:
|
|
|
46
49
|
execution_env: ExecutionEnvironment
|
|
47
50
|
"""Execution environment for running code."""
|
|
48
51
|
|
|
52
|
+
def __post_init__(self) -> None:
|
|
53
|
+
"""Initialize filesystem for script history after dataclass init."""
|
|
54
|
+
from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
|
|
55
|
+
from fsspec.implementations.memory import MemoryFileSystem
|
|
56
|
+
|
|
57
|
+
self._memory_fs = MemoryFileSystem()
|
|
58
|
+
self._fs = AsyncFileSystemWrapper(self._memory_fs)
|
|
59
|
+
|
|
49
60
|
@classmethod
|
|
50
61
|
def from_tools(
|
|
51
62
|
cls,
|
|
@@ -78,16 +89,70 @@ class RemoteCodeExecutor:
|
|
|
78
89
|
"""Get comprehensive description of available tools."""
|
|
79
90
|
return self.toolset_generator.generate_tool_description()
|
|
80
91
|
|
|
81
|
-
|
|
92
|
+
def get_fs(self) -> AsyncFileSystem:
|
|
93
|
+
"""Get filesystem view of script history.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
AsyncFileSystem containing:
|
|
97
|
+
- scripts/{timestamp}_{title}.py - Executed scripts
|
|
98
|
+
- scripts/{timestamp}_{title}.json - Execution metadata
|
|
99
|
+
"""
|
|
100
|
+
return self._fs
|
|
101
|
+
|
|
102
|
+
async def execute_code(self, code: str, title: str) -> Any:
|
|
82
103
|
"""Execute code with tools available via HTTP API.
|
|
83
104
|
|
|
84
105
|
Args:
|
|
85
106
|
code: Python code to execute
|
|
107
|
+
title: Short descriptive title for this script (3-4 words)
|
|
86
108
|
|
|
87
109
|
Returns:
|
|
88
110
|
Execution result from the environment
|
|
89
111
|
"""
|
|
90
|
-
|
|
112
|
+
start_time = datetime.now(UTC)
|
|
113
|
+
exit_code = 0
|
|
114
|
+
error_msg: str | None = None
|
|
115
|
+
exec_result = None
|
|
116
|
+
result_str = ""
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
exec_result = await self.execution_env.execute(code)
|
|
120
|
+
result_str = str(exec_result)
|
|
121
|
+
# Check if execution failed
|
|
122
|
+
if (
|
|
123
|
+
hasattr(exec_result, "exit_code")
|
|
124
|
+
and exec_result.exit_code is not None
|
|
125
|
+
and exec_result.exit_code != 0
|
|
126
|
+
):
|
|
127
|
+
exit_code = exec_result.exit_code
|
|
128
|
+
error_msg = getattr(exec_result, "error", None)
|
|
129
|
+
except Exception as e: # noqa: BLE001
|
|
130
|
+
exit_code = 1
|
|
131
|
+
error_msg = str(e)
|
|
132
|
+
result_str = f"Error executing code: {error_msg}"
|
|
133
|
+
finally:
|
|
134
|
+
# Save to filesystem
|
|
135
|
+
end_time = datetime.now(UTC)
|
|
136
|
+
duration = (end_time - start_time).total_seconds()
|
|
137
|
+
timestamp = start_time.strftime("%Y%m%d_%H%M%S")
|
|
138
|
+
|
|
139
|
+
# Write script file
|
|
140
|
+
script_path = f"scripts/{timestamp}_{title}.py"
|
|
141
|
+
self._memory_fs.pipe(script_path, code.encode("utf-8"))
|
|
142
|
+
|
|
143
|
+
# Write metadata file
|
|
144
|
+
metadata = {
|
|
145
|
+
"title": title,
|
|
146
|
+
"timestamp": start_time.isoformat(),
|
|
147
|
+
"exit_code": exit_code,
|
|
148
|
+
"duration": duration,
|
|
149
|
+
"result": result_str,
|
|
150
|
+
"error": error_msg,
|
|
151
|
+
}
|
|
152
|
+
metadata_path = f"scripts/{timestamp}_{title}.json"
|
|
153
|
+
self._memory_fs.pipe(metadata_path, json.dumps(metadata, indent=2).encode("utf-8"))
|
|
154
|
+
|
|
155
|
+
return exec_result
|
|
91
156
|
|
|
92
157
|
async def __aenter__(self) -> Self:
|
|
93
158
|
"""Async context manager entry."""
|
|
@@ -196,7 +261,7 @@ class ToolServerLifecycleHandler:
|
|
|
196
261
|
|
|
197
262
|
|
|
198
263
|
if __name__ == "__main__":
|
|
199
|
-
from
|
|
264
|
+
from exxec_config import LocalExecutionEnvironmentConfig
|
|
200
265
|
|
|
201
266
|
from agentpool.tools.base import Tool
|
|
202
267
|
|
|
@@ -209,7 +274,9 @@ if __name__ == "__main__":
|
|
|
209
274
|
config = LocalExecutionEnvironmentConfig()
|
|
210
275
|
provider = RemoteCodeExecutor.from_tools(tools, config, server_port=9876)
|
|
211
276
|
async with provider:
|
|
212
|
-
result = await provider.execute_code(
|
|
277
|
+
result = await provider.execute_code(
|
|
278
|
+
"_result = await add_numbers(5, 3)", title="test_addition"
|
|
279
|
+
)
|
|
213
280
|
print(f"Result: {result.result}")
|
|
214
281
|
|
|
215
282
|
anyio.run(main)
|
|
@@ -69,13 +69,13 @@ def tools_to_codegen(
|
|
|
69
69
|
generators = [
|
|
70
70
|
ToolCodeGenerator(
|
|
71
71
|
schema=create_schema(
|
|
72
|
-
t.
|
|
72
|
+
t.get_callable(),
|
|
73
73
|
name_override=t.name,
|
|
74
74
|
description_override=t.description,
|
|
75
75
|
mode="openai",
|
|
76
76
|
exclude_types=[AgentContext, RunContext],
|
|
77
77
|
),
|
|
78
|
-
callable=t.
|
|
78
|
+
callable=t.get_callable(),
|
|
79
79
|
name_override=t.name,
|
|
80
80
|
)
|
|
81
81
|
for t in tools
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from datetime import UTC, datetime
|
|
5
6
|
from functools import partial
|
|
6
7
|
import inspect
|
|
8
|
+
import json
|
|
7
9
|
from typing import TYPE_CHECKING, Any
|
|
8
10
|
|
|
9
11
|
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
@@ -17,6 +19,9 @@ from agentpool_toolsets.fsspec_toolset.toolset import FSSpecTools
|
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Sequence
|
|
23
|
+
|
|
24
|
+
from fsspec.asyn import AsyncFileSystem
|
|
20
25
|
from schemez import ToolsetCodeGenerator
|
|
21
26
|
|
|
22
27
|
from agentpool.resource_providers import ResourceProvider
|
|
@@ -46,7 +51,14 @@ class CodeModeResourceProvider(AggregatingResourceProvider):
|
|
|
46
51
|
self._cached_tool: Tool | None = None
|
|
47
52
|
self.usage_notes = usage_notes
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
# Filesystem for script history
|
|
55
|
+
from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
|
|
56
|
+
from fsspec.implementations.memory import MemoryFileSystem
|
|
57
|
+
|
|
58
|
+
self._memory_fs = MemoryFileSystem()
|
|
59
|
+
self._fs = AsyncFileSystemWrapper(self._memory_fs)
|
|
60
|
+
|
|
61
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
50
62
|
"""Return single meta-tool for Python execution with available tools."""
|
|
51
63
|
# Always generate fresh toolset to reflect current tools
|
|
52
64
|
toolset_generator = await self._get_code_generator()
|
|
@@ -55,9 +67,9 @@ class CodeModeResourceProvider(AggregatingResourceProvider):
|
|
|
55
67
|
|
|
56
68
|
if self._cached_tool is None:
|
|
57
69
|
# Create a closure that captures self but isn't a bound method
|
|
58
|
-
async def execute_tool(ctx: AgentContext, python_code: str) -> Any:
|
|
70
|
+
async def execute_tool(ctx: AgentContext, python_code: str, title: str) -> Any:
|
|
59
71
|
"""These docstings are overriden by description_override."""
|
|
60
|
-
return await self.execute(ctx, python_code)
|
|
72
|
+
return await self.execute(ctx, python_code, title)
|
|
61
73
|
|
|
62
74
|
self._cached_tool = self.create_tool(execute_tool, description_override=desc)
|
|
63
75
|
else:
|
|
@@ -66,11 +78,12 @@ class CodeModeResourceProvider(AggregatingResourceProvider):
|
|
|
66
78
|
|
|
67
79
|
return [self._cached_tool]
|
|
68
80
|
|
|
69
|
-
async def execute(self, ctx: AgentContext, python_code: str) -> Any: # noqa: D417
|
|
81
|
+
async def execute(self, ctx: AgentContext, python_code: str, title: str) -> Any: # noqa: D417
|
|
70
82
|
"""Execute Python code with all wrapped tools available as functions.
|
|
71
83
|
|
|
72
84
|
Args:
|
|
73
85
|
python_code: Python code to execute
|
|
86
|
+
title: Short descriptive title for this script (3-4 words)
|
|
74
87
|
|
|
75
88
|
Returns:
|
|
76
89
|
Result of the last expression or explicit return value
|
|
@@ -93,18 +106,47 @@ class CodeModeResourceProvider(AggregatingResourceProvider):
|
|
|
93
106
|
# namespace["report_progress"] = NamespaceCallable(report_progress)
|
|
94
107
|
|
|
95
108
|
validate_code(python_code)
|
|
109
|
+
start_time = datetime.now(UTC)
|
|
110
|
+
exit_code = 0
|
|
111
|
+
error_msg = None
|
|
112
|
+
result_value = None
|
|
113
|
+
|
|
96
114
|
try:
|
|
97
115
|
exec(python_code, namespace)
|
|
98
|
-
|
|
116
|
+
result_value = await namespace["main"]()
|
|
99
117
|
# Handle edge cases with coroutines and return values
|
|
100
|
-
if inspect.iscoroutine(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
if inspect.iscoroutine(result_value):
|
|
119
|
+
result_value = await result_value
|
|
120
|
+
|
|
121
|
+
if not result_value: # in order to not confuse the model, return a success message.
|
|
122
|
+
result_value = "Code executed successfully"
|
|
104
123
|
except Exception as e: # noqa: BLE001
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
exit_code = 1
|
|
125
|
+
error_msg = f"{e!s}"
|
|
126
|
+
result_value = f"Error executing code: {error_msg}"
|
|
127
|
+
finally:
|
|
128
|
+
# Save to filesystem
|
|
129
|
+
end_time = datetime.now(UTC)
|
|
130
|
+
duration = (end_time - start_time).total_seconds()
|
|
131
|
+
timestamp = start_time.strftime("%Y%m%d_%H%M%S")
|
|
132
|
+
|
|
133
|
+
# Write script file
|
|
134
|
+
script_path = f"scripts/{timestamp}_{title}.py"
|
|
135
|
+
self._memory_fs.pipe(script_path, python_code.encode("utf-8"))
|
|
136
|
+
|
|
137
|
+
# Write metadata file
|
|
138
|
+
metadata = {
|
|
139
|
+
"title": title,
|
|
140
|
+
"timestamp": start_time.isoformat(),
|
|
141
|
+
"exit_code": exit_code,
|
|
142
|
+
"duration": duration,
|
|
143
|
+
"result": str(result_value),
|
|
144
|
+
"error": error_msg,
|
|
145
|
+
}
|
|
146
|
+
metadata_path = f"scripts/{timestamp}_{title}.json"
|
|
147
|
+
self._memory_fs.pipe(metadata_path, json.dumps(metadata, indent=2).encode("utf-8"))
|
|
148
|
+
|
|
149
|
+
return result_value
|
|
108
150
|
|
|
109
151
|
def invalidate_cache(self) -> None:
|
|
110
152
|
"""Invalidate cached tool when providers change."""
|
|
@@ -116,6 +158,16 @@ class CodeModeResourceProvider(AggregatingResourceProvider):
|
|
|
116
158
|
tools = await super().get_tools()
|
|
117
159
|
return tools_to_codegen(tools=tools, include_docstrings=self.include_docstrings)
|
|
118
160
|
|
|
161
|
+
def get_fs(self) -> AsyncFileSystem:
|
|
162
|
+
"""Get filesystem view of script history.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
AsyncFileSystem containing:
|
|
166
|
+
- scripts/{timestamp}_{title}.py - Executed scripts
|
|
167
|
+
- scripts/{timestamp}_{title}.json - Execution metadata
|
|
168
|
+
"""
|
|
169
|
+
return self._fs
|
|
170
|
+
|
|
119
171
|
|
|
120
172
|
if __name__ == "__main__":
|
|
121
173
|
import anyio
|
|
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from types import TracebackType
|
|
14
14
|
|
|
15
15
|
from exxec.base import ExecutionEnvironment
|
|
16
|
-
from
|
|
16
|
+
from exxec_config import ExecutionEnvironmentConfig
|
|
17
17
|
from schemez import ToolsetCodeGenerator
|
|
18
18
|
|
|
19
19
|
from agentpool.tools.base import Tool
|
|
@@ -103,7 +103,7 @@ class RemoteMCPExecutor:
|
|
|
103
103
|
|
|
104
104
|
if __name__ == "__main__":
|
|
105
105
|
import anyio
|
|
106
|
-
from
|
|
106
|
+
from exxec_config import LocalExecutionEnvironmentConfig
|
|
107
107
|
|
|
108
108
|
from agentpool.tools.base import Tool
|
|
109
109
|
|
|
@@ -6,7 +6,7 @@ import asyncio
|
|
|
6
6
|
import contextlib
|
|
7
7
|
from typing import TYPE_CHECKING, Any, Self
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from exxec_config import LocalExecutionEnvironmentConfig
|
|
10
10
|
|
|
11
11
|
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
12
12
|
from agentpool.log import get_logger
|
|
@@ -26,7 +26,7 @@ logger = get_logger(__name__)
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
27
|
from types import TracebackType
|
|
28
28
|
|
|
29
|
-
from
|
|
29
|
+
from exxec_config import ExecutionEnvironmentConfig
|
|
30
30
|
|
|
31
31
|
from agentpool.resource_providers import ResourceProvider
|
|
32
32
|
|
|
@@ -75,11 +75,12 @@ class RemoteCodeModeResourceProvider(CodeModeResourceProvider):
|
|
|
75
75
|
self._code_executor: RemoteCodeExecutor | None = None
|
|
76
76
|
self._provider_lock = asyncio.Lock()
|
|
77
77
|
|
|
78
|
-
async def execute(self, ctx: AgentContext, python_code: str) -> Any: # noqa: D417
|
|
78
|
+
async def execute(self, ctx: AgentContext, python_code: str, title: str) -> Any: # noqa: D417
|
|
79
79
|
"""Execute Python code in secure environment with tools available via HTTP.
|
|
80
80
|
|
|
81
81
|
Args:
|
|
82
82
|
python_code: Python code to execute
|
|
83
|
+
title: Short descriptive title for this script (3-4 words)
|
|
83
84
|
|
|
84
85
|
Returns:
|
|
85
86
|
Result of the code execution
|
|
@@ -90,16 +91,12 @@ class RemoteCodeModeResourceProvider(CodeModeResourceProvider):
|
|
|
90
91
|
full_code = f"{PROGRESS_HELPER}\n\n{python_code}"
|
|
91
92
|
logger.info("Complete code", code=full_code)
|
|
92
93
|
try:
|
|
93
|
-
result = await code_provider.
|
|
94
|
-
if result.success:
|
|
95
|
-
logger.info("Code executed successfully")
|
|
96
|
-
if result.result is None:
|
|
97
|
-
return "Code executed successfully"
|
|
98
|
-
return result.result
|
|
94
|
+
result = await code_provider.execute_code(full_code, title)
|
|
99
95
|
except Exception as e: # noqa: BLE001
|
|
100
96
|
return f"Error in secure execution: {e!s}"
|
|
101
97
|
else:
|
|
102
|
-
|
|
98
|
+
logger.info("Code executed")
|
|
99
|
+
return result
|
|
103
100
|
|
|
104
101
|
async def _get_code_executor(self) -> RemoteCodeExecutor:
|
|
105
102
|
"""Get cached code execution provider with thread-safe initialization."""
|
|
@@ -139,7 +136,7 @@ if __name__ == "__main__":
|
|
|
139
136
|
import webbrowser
|
|
140
137
|
|
|
141
138
|
import anyio
|
|
142
|
-
from
|
|
139
|
+
from exxec_config import LocalExecutionEnvironmentConfig
|
|
143
140
|
|
|
144
141
|
from agentpool import Agent, log
|
|
145
142
|
from agentpool.resource_providers import StaticResourceProvider
|
|
@@ -153,7 +150,7 @@ if __name__ == "__main__":
|
|
|
153
150
|
async def main() -> None:
|
|
154
151
|
tools = [Tool.from_callable(open_browser)]
|
|
155
152
|
static_provider = StaticResourceProvider(tools=tools)
|
|
156
|
-
config = LocalExecutionEnvironmentConfig(
|
|
153
|
+
config = LocalExecutionEnvironmentConfig(default_command_timeout=30.0)
|
|
157
154
|
provider = RemoteCodeModeResourceProvider(
|
|
158
155
|
providers=[static_provider],
|
|
159
156
|
execution_config=config,
|
|
@@ -8,6 +8,8 @@ from agentpool.resource_providers import ResourceProvider
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
|
|
11
13
|
from agentpool.tools.base import Tool
|
|
12
14
|
|
|
13
15
|
|
|
@@ -32,7 +34,7 @@ class FilteringResourceProvider(ResourceProvider):
|
|
|
32
34
|
"""Delegate attribute access to wrapped provider."""
|
|
33
35
|
return getattr(self._provider, name)
|
|
34
36
|
|
|
35
|
-
async def get_tools(self) ->
|
|
37
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
36
38
|
"""Get filtered tools from wrapped provider.
|
|
37
39
|
|
|
38
40
|
Returns only tools where the filter value is True. Tools not in the filter
|
|
@@ -8,17 +8,19 @@ from typing import TYPE_CHECKING, Any, Self
|
|
|
8
8
|
|
|
9
9
|
from agentpool.log import get_logger
|
|
10
10
|
from agentpool.resource_providers import ResourceProvider
|
|
11
|
+
from agentpool.resource_providers.resource_info import ResourceInfo
|
|
11
12
|
from agentpool_config.mcp_server import BaseMCPServerConfig
|
|
12
|
-
from agentpool_config.resources import ResourceInfo
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Sequence
|
|
16
17
|
from typing import Literal
|
|
17
18
|
|
|
18
19
|
from fastmcp.client.sampling import SamplingHandler
|
|
20
|
+
from mcp.types import ResourceTemplate
|
|
19
21
|
|
|
20
22
|
from agentpool.prompts.prompts import MCPClientPrompt
|
|
21
|
-
from agentpool.tools.base import Tool
|
|
23
|
+
from agentpool.tools.base import FunctionTool, Tool
|
|
22
24
|
from agentpool_config.mcp_server import MCPServerConfig
|
|
23
25
|
|
|
24
26
|
|
|
@@ -45,19 +47,14 @@ class MCPResourceProvider(ResourceProvider):
|
|
|
45
47
|
self.server = BaseMCPServerConfig.from_string(server) if isinstance(server, str) else server
|
|
46
48
|
self.source = source
|
|
47
49
|
self.exit_stack = AsyncExitStack()
|
|
50
|
+
|
|
48
51
|
self._accessible_roots = accessible_roots
|
|
49
52
|
self._sampling_callback = sampling_callback
|
|
50
53
|
|
|
51
|
-
# Tool caching
|
|
52
|
-
self._tools_cache: list[Tool] | None = None
|
|
53
54
|
self._saved_enabled_states: dict[str, bool] = {}
|
|
54
|
-
|
|
55
|
-
# Prompt caching
|
|
55
|
+
self._tools_cache: list[FunctionTool] | None = None
|
|
56
56
|
self._prompts_cache: list[MCPClientPrompt] | None = None
|
|
57
|
-
|
|
58
|
-
# Resource caching
|
|
59
57
|
self._resources_cache: list[ResourceInfo] | None = None
|
|
60
|
-
|
|
61
58
|
self.client = MCPClient(
|
|
62
59
|
config=self.server,
|
|
63
60
|
sampling_callback=self._sampling_callback,
|
|
@@ -128,7 +125,7 @@ class MCPResourceProvider(ResourceProvider):
|
|
|
128
125
|
try:
|
|
129
126
|
# Get fresh tools from client
|
|
130
127
|
mcp_tools = await self.client.list_tools()
|
|
131
|
-
all_tools: list[
|
|
128
|
+
all_tools: list[FunctionTool] = []
|
|
132
129
|
|
|
133
130
|
for tool in mcp_tools:
|
|
134
131
|
try:
|
|
@@ -149,7 +146,7 @@ class MCPResourceProvider(ResourceProvider):
|
|
|
149
146
|
logger.exception("Failed to refresh MCP tools cache")
|
|
150
147
|
self._tools_cache = []
|
|
151
148
|
|
|
152
|
-
async def get_tools(self) ->
|
|
149
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
153
150
|
"""Get cached tools, refreshing if necessary."""
|
|
154
151
|
if self._tools_cache is None:
|
|
155
152
|
await self.refresh_tools_cache()
|
|
@@ -193,7 +190,11 @@ class MCPResourceProvider(ResourceProvider):
|
|
|
193
190
|
|
|
194
191
|
for resource in result:
|
|
195
192
|
try:
|
|
196
|
-
converted = await ResourceInfo.from_mcp_resource(
|
|
193
|
+
converted = await ResourceInfo.from_mcp_resource(
|
|
194
|
+
resource,
|
|
195
|
+
client_name=self.name,
|
|
196
|
+
reader=self.read_resource,
|
|
197
|
+
)
|
|
197
198
|
all_resources.append(converted)
|
|
198
199
|
except Exception:
|
|
199
200
|
logger.exception("Failed to convert resource", name=resource.name)
|
|
@@ -212,6 +213,59 @@ class MCPResourceProvider(ResourceProvider):
|
|
|
212
213
|
|
|
213
214
|
return self._resources_cache or []
|
|
214
215
|
|
|
216
|
+
async def read_resource(self, uri: str) -> list[str]:
|
|
217
|
+
"""Read resource content by URI.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
uri: URI of the resource to read
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
List of text contents from the resource
|
|
224
|
+
|
|
225
|
+
Raises:
|
|
226
|
+
RuntimeError: If resource cannot be read
|
|
227
|
+
"""
|
|
228
|
+
contents = await self.client.read_resource(uri)
|
|
229
|
+
result: list[str] = []
|
|
230
|
+
for content in contents:
|
|
231
|
+
if hasattr(content, "text") and content.text is not None:
|
|
232
|
+
result.append(str(content.text))
|
|
233
|
+
elif hasattr(content, "blob") and content.blob is not None:
|
|
234
|
+
# Binary content - return placeholder or base64
|
|
235
|
+
import base64
|
|
236
|
+
|
|
237
|
+
blob_data = content.blob
|
|
238
|
+
if isinstance(blob_data, str):
|
|
239
|
+
result.append(f"[Binary data: {len(blob_data)} bytes]")
|
|
240
|
+
elif isinstance(blob_data, bytes):
|
|
241
|
+
encoded = base64.b64encode(blob_data).decode("utf-8")
|
|
242
|
+
result.append(encoded)
|
|
243
|
+
else:
|
|
244
|
+
result.append("[Binary data: unknown format]")
|
|
245
|
+
return result
|
|
246
|
+
|
|
247
|
+
async def list_resource_templates(self) -> list[ResourceTemplate]:
|
|
248
|
+
"""Get available resource templates from the MCP server.
|
|
249
|
+
|
|
250
|
+
Resource templates define URI patterns with placeholders that can be
|
|
251
|
+
expanded into concrete resource URIs. For example:
|
|
252
|
+
- Template: "file:///{path}" with path="config.json"
|
|
253
|
+
- Expands to: "file:///config.json"
|
|
254
|
+
|
|
255
|
+
TODO: Decide on integration strategy:
|
|
256
|
+
- Option 1: Templates as separate concept with expand() -> ResourceInfo
|
|
257
|
+
- Option 2: Unified ResourceInfo with is_template flag and read(**kwargs)
|
|
258
|
+
- Option 3: ResourceTemplateInfo class that produces ResourceInfo
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
List of ResourceTemplate objects from the server
|
|
262
|
+
"""
|
|
263
|
+
try:
|
|
264
|
+
return await self.client.list_resource_templates()
|
|
265
|
+
except Exception:
|
|
266
|
+
logger.exception("Failed to list resource templates")
|
|
267
|
+
return []
|
|
268
|
+
|
|
215
269
|
def get_status(self) -> dict[str, str]:
|
|
216
270
|
"""Get connection status for this MCP server.
|
|
217
271
|
|