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
agentpool/agents/interactions.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections.abc import Callable, Mapping
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
6
7
|
from typing import TYPE_CHECKING, Any, Literal, cast, overload
|
|
7
8
|
|
|
8
9
|
from schemez import Schema
|
|
@@ -12,7 +13,7 @@ from agentpool.messaging import ChatMessage
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
|
-
from collections.abc import Sequence
|
|
16
|
+
from collections.abc import AsyncIterator, Sequence
|
|
16
17
|
|
|
17
18
|
from toprompt import AnyPromptType
|
|
18
19
|
|
|
@@ -90,6 +91,33 @@ class Interactions:
|
|
|
90
91
|
def __init__(self, agent: SupportsStructuredOutput) -> None:
|
|
91
92
|
self.agent = agent
|
|
92
93
|
|
|
94
|
+
@asynccontextmanager
|
|
95
|
+
async def _with_structured_output[T](
|
|
96
|
+
self, output_type: type[T]
|
|
97
|
+
) -> AsyncIterator[SupportsStructuredOutput]:
|
|
98
|
+
"""Context manager to temporarily set structured output type and restore afterwards.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
output_type: The type to structure output as
|
|
102
|
+
|
|
103
|
+
Yields:
|
|
104
|
+
The agent configured for structured output
|
|
105
|
+
|
|
106
|
+
Example:
|
|
107
|
+
async with interactions._with_structured_output(MyType) as agent:
|
|
108
|
+
result = await agent.run(prompt)
|
|
109
|
+
"""
|
|
110
|
+
# Save original output type
|
|
111
|
+
old_output_type = getattr(self.agent, "_output_type", None)
|
|
112
|
+
try:
|
|
113
|
+
# Configure structured output
|
|
114
|
+
structured_agent = self.agent.to_structured(output_type)
|
|
115
|
+
yield structured_agent
|
|
116
|
+
finally:
|
|
117
|
+
# Restore original output type
|
|
118
|
+
if hasattr(self.agent, "_output_type"):
|
|
119
|
+
self.agent._output_type = old_output_type
|
|
120
|
+
|
|
93
121
|
# async def conversation(
|
|
94
122
|
# self,
|
|
95
123
|
# other: MessageNode[Any, Any],
|
|
@@ -218,8 +246,9 @@ Available options:
|
|
|
218
246
|
|
|
219
247
|
Select ONE option by its exact label."""
|
|
220
248
|
|
|
221
|
-
# Get LLM's string-based decision
|
|
222
|
-
|
|
249
|
+
# Get LLM's string-based decision using structured output
|
|
250
|
+
async with self._with_structured_output(LLMPick) as structured_agent:
|
|
251
|
+
result = await structured_agent.run(prompt or default_prompt)
|
|
223
252
|
|
|
224
253
|
# Convert to type-safe decision
|
|
225
254
|
if result.content.selection not in label_map:
|
|
@@ -328,7 +357,9 @@ Available options:
|
|
|
328
357
|
{picks_info} options by their exact labels.
|
|
329
358
|
List your selections, one per line, followed by your reasoning."""
|
|
330
359
|
|
|
331
|
-
|
|
360
|
+
# Get LLM's multi-selection using structured output
|
|
361
|
+
async with self._with_structured_output(LLMMultiPick) as structured_agent:
|
|
362
|
+
result = await structured_agent.run(prompt or default_prompt)
|
|
332
363
|
|
|
333
364
|
# Validate selections
|
|
334
365
|
invalid = [s for s in result.content.selections if s not in label_map]
|
|
@@ -368,7 +399,9 @@ List your selections, one per line, followed by your reasoning."""
|
|
|
368
399
|
instance: item_model # type: ignore
|
|
369
400
|
# explanation: str | None = None
|
|
370
401
|
|
|
371
|
-
|
|
402
|
+
# Use structured output via context manager
|
|
403
|
+
async with self._with_structured_output(Extraction) as structured_agent:
|
|
404
|
+
result = await structured_agent.run(final_prompt)
|
|
372
405
|
return as_type(**result.content.instance.model_dump())
|
|
373
406
|
|
|
374
407
|
async def extract_multiple[T](
|
|
@@ -404,7 +437,9 @@ List your selections, one per line, followed by your reasoning."""
|
|
|
404
437
|
instances: list[item_model] # type: ignore
|
|
405
438
|
# explanation: str | None = None
|
|
406
439
|
|
|
407
|
-
|
|
440
|
+
# Use structured output via context manager
|
|
441
|
+
async with self._with_structured_output(Extraction) as structured_agent:
|
|
442
|
+
result = await structured_agent.run(final_prompt)
|
|
408
443
|
num_instances = len(result.content.instances) # Validate counts
|
|
409
444
|
if len(result.content.instances) < min_items:
|
|
410
445
|
msg = f"Found only {num_instances} instances, need {min_items}"
|
agentpool/agents/modes.py
CHANGED
|
@@ -7,6 +7,7 @@ to clients. Each agent type can define its own mode categories.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Literal
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@dataclass
|
|
@@ -52,3 +53,15 @@ class ModeCategory:
|
|
|
52
53
|
|
|
53
54
|
current_mode_id: str = ""
|
|
54
55
|
"""ID of the currently active mode."""
|
|
56
|
+
|
|
57
|
+
category: Literal["mode", "model", "thought_level", "other"] | None = None
|
|
58
|
+
"""Optional semantic category for UX purposes (keyboard shortcuts, icons, placement).
|
|
59
|
+
|
|
60
|
+
This helps clients distinguish common selector types:
|
|
61
|
+
- 'mode': Session mode selector
|
|
62
|
+
- 'model': Model selector
|
|
63
|
+
- 'thought_level': Thought/reasoning level selector
|
|
64
|
+
- 'other': Unknown/uncategorized
|
|
65
|
+
|
|
66
|
+
MUST NOT be required for correctness. Clients should handle gracefully.
|
|
67
|
+
"""
|
|
@@ -7,10 +7,6 @@ import re
|
|
|
7
7
|
from typing import TYPE_CHECKING, Any, cast
|
|
8
8
|
|
|
9
9
|
import anyio
|
|
10
|
-
from slashed.events import (
|
|
11
|
-
CommandExecutedEvent,
|
|
12
|
-
CommandOutputEvent as SlashedCommandOutputEvent,
|
|
13
|
-
)
|
|
14
10
|
|
|
15
11
|
from agentpool.agents.events import CommandCompleteEvent, CommandOutputEvent
|
|
16
12
|
from agentpool.log import get_logger
|
|
@@ -112,6 +108,11 @@ class SlashedAgent[TDeps, OutputDataT]:
|
|
|
112
108
|
Yields:
|
|
113
109
|
Command output and completion events
|
|
114
110
|
"""
|
|
111
|
+
from slashed.events import (
|
|
112
|
+
CommandExecutedEvent,
|
|
113
|
+
CommandOutputEvent as SlashedCommandOutputEvent,
|
|
114
|
+
)
|
|
115
|
+
|
|
115
116
|
parsed = _parse_slash_command(command_text)
|
|
116
117
|
if not parsed:
|
|
117
118
|
logger.warning("Invalid slash command", command=command_text)
|
|
@@ -9,9 +9,11 @@ import time
|
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
11
|
from pydantic_ai import RunContext
|
|
12
|
+
from pydantic_ai.messages import ToolReturn
|
|
12
13
|
|
|
13
14
|
from agentpool.agents.context import AgentContext
|
|
14
15
|
from agentpool.tasks import ChainAbortedError, RunAbortedError, ToolSkippedError
|
|
16
|
+
from agentpool.tools.base import ToolResult
|
|
15
17
|
from agentpool.utils.inspection import execute, get_argument_key
|
|
16
18
|
from agentpool.utils.signatures import create_modified_signature, update_signature
|
|
17
19
|
|
|
@@ -27,7 +29,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
|
|
|
27
29
|
tool: Tool[TReturn],
|
|
28
30
|
agent_ctx: AgentContext,
|
|
29
31
|
hooks: AgentHooks | None = None,
|
|
30
|
-
) -> Callable[..., Awaitable[TReturn | None]]:
|
|
32
|
+
) -> Callable[..., Awaitable[TReturn | ToolReturn | None]]:
|
|
31
33
|
"""Wrap tool with confirmation handling and hooks.
|
|
32
34
|
|
|
33
35
|
Strategy:
|
|
@@ -41,7 +43,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
|
|
|
41
43
|
agent_ctx: Agent context for confirmation handling and dependency injection.
|
|
42
44
|
hooks: Optional AgentHooks for pre/post tool execution hooks.
|
|
43
45
|
"""
|
|
44
|
-
fn = tool.
|
|
46
|
+
fn = tool.get_callable()
|
|
45
47
|
run_ctx_key = get_argument_key(fn, RunContext)
|
|
46
48
|
agent_ctx_key = get_argument_key(fn, AgentContext)
|
|
47
49
|
|
|
@@ -58,7 +60,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
|
|
|
58
60
|
tool_input: dict[str, Any],
|
|
59
61
|
*args: Any,
|
|
60
62
|
**kwargs: Any,
|
|
61
|
-
) -> TReturn | None:
|
|
63
|
+
) -> TReturn | None | ToolReturn:
|
|
62
64
|
"""Execute tool with pre/post hooks."""
|
|
63
65
|
# Pre-tool hooks
|
|
64
66
|
if hooks:
|
|
@@ -79,9 +81,17 @@ def wrap_tool[TReturn]( # noqa: PLR0915
|
|
|
79
81
|
|
|
80
82
|
# Execute the tool
|
|
81
83
|
start_time = time.perf_counter()
|
|
82
|
-
result = await execute_fn(*args, **kwargs)
|
|
84
|
+
result: TReturn | ToolResult | ToolReturn = await execute_fn(*args, **kwargs)
|
|
83
85
|
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
84
86
|
|
|
87
|
+
# Convert AgentPool ToolResult to pydantic-ai ToolReturn
|
|
88
|
+
if isinstance(result, ToolResult):
|
|
89
|
+
result = ToolReturn(
|
|
90
|
+
return_value=result.structured_content or result.content,
|
|
91
|
+
content=result.content,
|
|
92
|
+
metadata=result.metadata,
|
|
93
|
+
)
|
|
94
|
+
|
|
85
95
|
# Post-tool hooks
|
|
86
96
|
if hooks:
|
|
87
97
|
await hooks.run_post_tool_hooks(
|
|
@@ -97,7 +107,9 @@ def wrap_tool[TReturn]( # noqa: PLR0915
|
|
|
97
107
|
|
|
98
108
|
if run_ctx_key or agent_ctx_key:
|
|
99
109
|
# Tool has RunContext and/or AgentContext
|
|
100
|
-
async def wrapped(
|
|
110
|
+
async def wrapped(
|
|
111
|
+
ctx: RunContext, *args: Any, **kwargs: Any
|
|
112
|
+
) -> TReturn | None | ToolReturn: # pyright: ignore
|
|
101
113
|
result = await agent_ctx.handle_confirmation(tool, kwargs)
|
|
102
114
|
if result == "allow":
|
|
103
115
|
# Populate AgentContext with RunContext data if needed
|
|
@@ -135,7 +147,7 @@ def wrap_tool[TReturn]( # noqa: PLR0915
|
|
|
135
147
|
|
|
136
148
|
else:
|
|
137
149
|
# Tool has no context - normal function call
|
|
138
|
-
async def wrapped(*args: Any, **kwargs: Any) -> TReturn | None: # type: ignore[misc]
|
|
150
|
+
async def wrapped(*args: Any, **kwargs: Any) -> TReturn | None | ToolReturn: # type: ignore[misc]
|
|
139
151
|
result = await agent_ctx.handle_confirmation(tool, kwargs)
|
|
140
152
|
if result == "allow":
|
|
141
153
|
tool_input = kwargs.copy()
|
agentpool/common_types.py
CHANGED
|
@@ -28,15 +28,13 @@ if TYPE_CHECKING:
|
|
|
28
28
|
from uuid import UUID
|
|
29
29
|
|
|
30
30
|
from agentpool.agents.events import RichAgentStreamEvent
|
|
31
|
-
from agentpool.messaging import ChatMessage
|
|
32
|
-
from agentpool.messaging.messagenode import MessageNode
|
|
33
31
|
from agentpool.tools.base import Tool
|
|
34
32
|
|
|
35
|
-
# Type alias for team streaming return type (node + event tuples)
|
|
36
|
-
type TeamStreamEvent = tuple[MessageNode[Any, Any], RichAgentStreamEvent[Any]]
|
|
37
33
|
type AnyTransformFn[T] = Callable[[T], T | Awaitable[T]]
|
|
38
34
|
type OptionalAwaitable[T] = T | Awaitable[T]
|
|
39
|
-
|
|
35
|
+
# Import path string for dynamic tool loading (e.g., "mymodule:my_tool")
|
|
36
|
+
type ImportPathString = str
|
|
37
|
+
type ToolType = ImportPathString | AnyCallable | Tool
|
|
40
38
|
# Define what we consider JSON-serializable
|
|
41
39
|
type JsonPrimitive = None | bool | int | float | str
|
|
42
40
|
type SessionIdType = str | UUID | None
|
|
@@ -76,7 +74,7 @@ TeamName = str
|
|
|
76
74
|
AgentName = str
|
|
77
75
|
MessageRole = Literal["user", "assistant"]
|
|
78
76
|
PartType = Literal["text", "image", "audio", "video"]
|
|
79
|
-
ModelType = Model | ModelId | str
|
|
77
|
+
ModelType = Model | ModelId | str
|
|
80
78
|
EnvironmentType = Literal["file", "inline"]
|
|
81
79
|
ToolSource = Literal["agent", "builtin", "dynamic", "task", "mcp", "toolset"]
|
|
82
80
|
AnyCallable = Callable[..., Any]
|
|
@@ -99,30 +97,46 @@ QueueStrategy = Literal["concat", "latest", "buffer"]
|
|
|
99
97
|
"""
|
|
100
98
|
|
|
101
99
|
|
|
102
|
-
class SupportsStructuredOutput(Protocol):
|
|
103
|
-
"""Protocol for nodes that support structured output via run().
|
|
104
|
-
|
|
105
|
-
This protocol is used for components that need to call run() with
|
|
106
|
-
an output_type parameter (e.g., picker agents, Interactions).
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
async def run(self, *prompts: Any, output_type: Any = ...) -> ChatMessage[Any]: ...
|
|
110
|
-
|
|
111
|
-
|
|
112
100
|
@runtime_checkable
|
|
113
101
|
class SupportsRunStream[TResult](Protocol):
|
|
114
102
|
"""Protocol for nodes that support streaming via run_stream().
|
|
115
103
|
|
|
116
104
|
Used by Team and TeamRun to check if a node can be streamed.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
- Agent returns `AsyncIterator[RichAgentStreamEvent[TResult]]`
|
|
120
|
-
- Team/TeamRun return `AsyncIterator[tuple[MessageNode, RichAgentStreamEvent]]`
|
|
105
|
+
All streaming nodes return RichAgentStreamEvent, with subagent/team
|
|
106
|
+
activity wrapped in SubAgentEvent.
|
|
121
107
|
"""
|
|
122
108
|
|
|
123
109
|
def run_stream(
|
|
124
110
|
self, *prompts: Any, **kwargs: Any
|
|
125
|
-
) -> AsyncIterator[RichAgentStreamEvent[TResult]
|
|
111
|
+
) -> AsyncIterator[RichAgentStreamEvent[TResult]]: ...
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@runtime_checkable
|
|
115
|
+
class SupportsStructuredOutput(Protocol):
|
|
116
|
+
"""Protocol for agents that support structured output via to_structured().
|
|
117
|
+
|
|
118
|
+
Used by Interactions class for pick/extract operations that require
|
|
119
|
+
structured output from agents.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
def to_structured[T](self, output_type: type[T]) -> SupportsStructuredOutput:
|
|
123
|
+
"""Create a copy of this agent configured for structured output.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
output_type: The type to structure output as
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
New agent instance configured for structured output
|
|
130
|
+
"""
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
async def run(self, *prompts: Any, **kwargs: Any) -> Any:
|
|
134
|
+
"""Run the agent with the given prompts.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
ChatMessage with content typed according to output_type from to_structured
|
|
138
|
+
"""
|
|
139
|
+
...
|
|
126
140
|
|
|
127
141
|
|
|
128
142
|
class BaseCode(BaseModel):
|
|
@@ -17,8 +17,8 @@ agents:
|
|
|
17
17
|
You are a helpful AI assistant with access to the file system and can execute code.
|
|
18
18
|
You can help with coding, writing, analysis, file operations, and more.
|
|
19
19
|
Be concise but thorough in your responses.
|
|
20
|
-
|
|
21
|
-
- type:
|
|
20
|
+
tools:
|
|
21
|
+
- type: process_management
|
|
22
22
|
- type: file_access
|
|
23
23
|
- type: code
|
|
24
24
|
- type: search
|
|
@@ -47,6 +47,7 @@ responses:
|
|
|
47
47
|
|
|
48
48
|
agents:
|
|
49
49
|
url_opener:
|
|
50
|
+
type: native
|
|
50
51
|
tools:
|
|
51
52
|
- type: import
|
|
52
53
|
import_path: "webbrowser.open"
|
|
@@ -63,6 +64,7 @@ agents:
|
|
|
63
64
|
Always confirm what you're about to open.
|
|
64
65
|
|
|
65
66
|
system_inspector:
|
|
67
|
+
type: native
|
|
66
68
|
tools:
|
|
67
69
|
- type: import
|
|
68
70
|
import_path: "platform.platform"
|
|
@@ -84,6 +86,7 @@ agents:
|
|
|
84
86
|
Format it using the template from the system_template resource.
|
|
85
87
|
|
|
86
88
|
file_explorer:
|
|
89
|
+
type: native
|
|
87
90
|
tools:
|
|
88
91
|
- type: import
|
|
89
92
|
import_path: "os.listdir"
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/phil65/agentpool/refs/heads/main/schema/config-schema.json
|
|
2
|
-
# Default ACP assistant configuration
|
|
3
|
-
# Used when starting ACP server without explicit config file
|
|
4
2
|
|
|
5
3
|
agents:
|
|
6
4
|
claude:
|
|
7
5
|
type: claude_code
|
|
8
6
|
display_name: "AI Assistant"
|
|
9
|
-
builtin_tools: []
|
|
10
|
-
|
|
11
|
-
ANTHROPIC_API_KEY: ""
|
|
7
|
+
builtin_tools: [] # we use our own tools.
|
|
8
|
+
use_subscription: true
|
|
12
9
|
description: "Claude code agent with enhanced tools"
|
|
13
|
-
|
|
14
|
-
- type:
|
|
10
|
+
tools:
|
|
11
|
+
- type: bash
|
|
12
|
+
- type: agent_cli
|
|
13
|
+
- type: process_management
|
|
15
14
|
- type: file_access
|
|
16
15
|
edit_tool: batch
|
|
17
|
-
- type: code
|
|
16
|
+
# - type: code
|
|
18
17
|
- type: search
|
|
19
18
|
- type: plan
|
|
19
|
+
- type: question
|
|
20
|
+
# - type: debug
|
|
@@ -22,7 +22,7 @@ from agentpool_config.teams import TeamConfig
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
24
24
|
|
|
25
|
-
from
|
|
25
|
+
from evented_config import EventConfig
|
|
26
26
|
from psygnal.containers._evented_list import ListEvents
|
|
27
27
|
from toprompt import AnyPromptType
|
|
28
28
|
|
|
@@ -31,7 +31,6 @@ if TYPE_CHECKING:
|
|
|
31
31
|
ModelType,
|
|
32
32
|
ProcessorCallback,
|
|
33
33
|
PromptCompatible,
|
|
34
|
-
SupportsStructuredOutput,
|
|
35
34
|
ToolType,
|
|
36
35
|
)
|
|
37
36
|
from agentpool.delegation.teamrun import ExtendedTeamTalk, TeamRun
|
|
@@ -66,9 +65,6 @@ class BaseTeam[TDeps, TResult](MessageNode[TDeps, TResult]):
|
|
|
66
65
|
display_name: str | None = None,
|
|
67
66
|
shared_prompt: str | None = None,
|
|
68
67
|
mcp_servers: list[str | MCPServerConfig] | None = None,
|
|
69
|
-
picker: SupportsStructuredOutput | None = None,
|
|
70
|
-
num_picks: int | None = None,
|
|
71
|
-
pick_prompt: str | None = None,
|
|
72
68
|
event_configs: Sequence[EventConfig] | None = None,
|
|
73
69
|
agent_pool: AgentPool | None = None,
|
|
74
70
|
) -> None:
|
|
@@ -97,28 +93,6 @@ class BaseTeam[TDeps, TResult](MessageNode[TDeps, TResult]):
|
|
|
97
93
|
self.shared_prompt = shared_prompt
|
|
98
94
|
self._main_task: asyncio.Task[ChatMessage[Any] | None] | None = None
|
|
99
95
|
self._infinite = False
|
|
100
|
-
self.picker = picker
|
|
101
|
-
self.num_picks = num_picks
|
|
102
|
-
self.pick_prompt = pick_prompt
|
|
103
|
-
|
|
104
|
-
async def pick_agents(self, task: str) -> Sequence[MessageNode[Any, Any]]:
|
|
105
|
-
"""Pick agents to run."""
|
|
106
|
-
from agentpool.agents.interactions import Interactions
|
|
107
|
-
|
|
108
|
-
if self.picker:
|
|
109
|
-
interactions = Interactions(self.picker)
|
|
110
|
-
if self.num_picks == 1:
|
|
111
|
-
result = await interactions.pick(self, task, self.pick_prompt)
|
|
112
|
-
return [result.selection]
|
|
113
|
-
multi_result = await interactions.pick_multiple(
|
|
114
|
-
self,
|
|
115
|
-
task,
|
|
116
|
-
min_picks=self.num_picks or 1,
|
|
117
|
-
max_picks=self.num_picks,
|
|
118
|
-
prompt=self.pick_prompt,
|
|
119
|
-
)
|
|
120
|
-
return multi_result.selections
|
|
121
|
-
return list(self.nodes)
|
|
122
96
|
|
|
123
97
|
def _on_node_changed(
|
|
124
98
|
self, index: int, old: MessageNode[Any, Any], new: MessageNode[Any, Any]
|
|
@@ -382,7 +356,7 @@ class BaseTeam[TDeps, TResult](MessageNode[TDeps, TResult]):
|
|
|
382
356
|
team_config = shared_pool.manifest.teams.get(self.name)
|
|
383
357
|
if not team_config:
|
|
384
358
|
mode = "parallel" if isinstance(self, Team) else "sequential"
|
|
385
|
-
team_config = TeamConfig(name=self.name, mode=mode, members=[])
|
|
359
|
+
team_config = TeamConfig(name=self.name, mode=mode, members=[]) # type: ignore[call-arg]
|
|
386
360
|
if not pool_ids:
|
|
387
361
|
logger.debug("No pool found for team.", team=self.name)
|
|
388
362
|
return TeamContext(
|
|
@@ -495,8 +469,8 @@ if __name__ == "__main__":
|
|
|
495
469
|
async def main() -> None:
|
|
496
470
|
from agentpool import Agent, Team
|
|
497
471
|
|
|
498
|
-
agent = Agent("My Agent")
|
|
499
|
-
agent_2 = Agent("My Agent")
|
|
472
|
+
agent = Agent("My Agent", model="openai:gpt-5-nano")
|
|
473
|
+
agent_2 = Agent("My Agent", model="openai:gpt-5-nano")
|
|
500
474
|
team = Team([agent, agent_2], mcp_servers=["uvx mcp-server-git"])
|
|
501
475
|
async with team:
|
|
502
476
|
print(await agent.tools.get_tools())
|