agentpool 2.1.9__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.
Potentially problematic release.
This version of agentpool might be problematic. Click here for more details.
- acp/README.md +64 -0
- acp/__init__.py +172 -0
- acp/__main__.py +10 -0
- acp/acp_requests.py +285 -0
- acp/agent/__init__.py +6 -0
- acp/agent/connection.py +256 -0
- acp/agent/implementations/__init__.py +6 -0
- acp/agent/implementations/debug_server/__init__.py +1 -0
- acp/agent/implementations/debug_server/cli.py +79 -0
- acp/agent/implementations/debug_server/debug.html +234 -0
- acp/agent/implementations/debug_server/debug_server.py +496 -0
- acp/agent/implementations/testing.py +91 -0
- acp/agent/protocol.py +65 -0
- acp/bridge/README.md +162 -0
- acp/bridge/__init__.py +6 -0
- acp/bridge/__main__.py +91 -0
- acp/bridge/bridge.py +246 -0
- acp/bridge/py.typed +0 -0
- acp/bridge/settings.py +15 -0
- acp/client/__init__.py +7 -0
- acp/client/connection.py +251 -0
- acp/client/implementations/__init__.py +7 -0
- acp/client/implementations/default_client.py +185 -0
- acp/client/implementations/headless_client.py +266 -0
- acp/client/implementations/noop_client.py +110 -0
- acp/client/protocol.py +61 -0
- acp/connection.py +280 -0
- acp/exceptions.py +46 -0
- acp/filesystem.py +524 -0
- acp/notifications.py +832 -0
- acp/py.typed +0 -0
- acp/schema/__init__.py +265 -0
- acp/schema/agent_plan.py +30 -0
- acp/schema/agent_requests.py +126 -0
- acp/schema/agent_responses.py +256 -0
- acp/schema/base.py +39 -0
- acp/schema/capabilities.py +230 -0
- acp/schema/client_requests.py +247 -0
- acp/schema/client_responses.py +96 -0
- acp/schema/common.py +81 -0
- acp/schema/content_blocks.py +188 -0
- acp/schema/mcp.py +82 -0
- acp/schema/messages.py +171 -0
- acp/schema/notifications.py +82 -0
- acp/schema/protocol_stuff.md +3 -0
- acp/schema/session_state.py +160 -0
- acp/schema/session_updates.py +419 -0
- acp/schema/slash_commands.py +51 -0
- acp/schema/terminal.py +15 -0
- acp/schema/tool_call.py +347 -0
- acp/stdio.py +250 -0
- acp/task/__init__.py +53 -0
- acp/task/debug.py +197 -0
- acp/task/dispatcher.py +93 -0
- acp/task/queue.py +69 -0
- acp/task/sender.py +82 -0
- acp/task/state.py +87 -0
- acp/task/supervisor.py +93 -0
- acp/terminal_handle.py +30 -0
- acp/tool_call_reporter.py +199 -0
- acp/tool_call_state.py +178 -0
- acp/transports.py +104 -0
- acp/utils.py +240 -0
- agentpool/__init__.py +63 -0
- agentpool/__main__.py +7 -0
- agentpool/agents/__init__.py +30 -0
- agentpool/agents/acp_agent/__init__.py +5 -0
- agentpool/agents/acp_agent/acp_agent.py +837 -0
- agentpool/agents/acp_agent/acp_converters.py +294 -0
- agentpool/agents/acp_agent/client_handler.py +317 -0
- agentpool/agents/acp_agent/session_state.py +44 -0
- agentpool/agents/agent.py +1264 -0
- agentpool/agents/agui_agent/__init__.py +19 -0
- agentpool/agents/agui_agent/agui_agent.py +677 -0
- agentpool/agents/agui_agent/agui_converters.py +423 -0
- agentpool/agents/agui_agent/chunk_transformer.py +204 -0
- agentpool/agents/agui_agent/event_types.py +83 -0
- agentpool/agents/agui_agent/helpers.py +192 -0
- agentpool/agents/architect.py +71 -0
- agentpool/agents/base_agent.py +177 -0
- agentpool/agents/claude_code_agent/__init__.py +11 -0
- agentpool/agents/claude_code_agent/claude_code_agent.py +1021 -0
- agentpool/agents/claude_code_agent/converters.py +243 -0
- agentpool/agents/context.py +105 -0
- agentpool/agents/events/__init__.py +61 -0
- agentpool/agents/events/builtin_handlers.py +129 -0
- agentpool/agents/events/event_emitter.py +320 -0
- agentpool/agents/events/events.py +561 -0
- agentpool/agents/events/tts_handlers.py +186 -0
- agentpool/agents/interactions.py +419 -0
- agentpool/agents/slashed_agent.py +244 -0
- agentpool/agents/sys_prompts.py +178 -0
- agentpool/agents/tool_wrapping.py +184 -0
- agentpool/base_provider.py +28 -0
- agentpool/common_types.py +226 -0
- agentpool/config_resources/__init__.py +16 -0
- agentpool/config_resources/acp_assistant.yml +24 -0
- agentpool/config_resources/agents.yml +109 -0
- agentpool/config_resources/agents_template.yml +18 -0
- agentpool/config_resources/agui_test.yml +18 -0
- agentpool/config_resources/claude_code_agent.yml +16 -0
- agentpool/config_resources/claude_style_subagent.md +30 -0
- agentpool/config_resources/external_acp_agents.yml +77 -0
- agentpool/config_resources/opencode_style_subagent.md +19 -0
- agentpool/config_resources/tts_test_agents.yml +78 -0
- agentpool/delegation/__init__.py +8 -0
- agentpool/delegation/base_team.py +504 -0
- agentpool/delegation/message_flow_tracker.py +39 -0
- agentpool/delegation/pool.py +1129 -0
- agentpool/delegation/team.py +325 -0
- agentpool/delegation/teamrun.py +343 -0
- agentpool/docs/__init__.py +5 -0
- agentpool/docs/gen_examples.py +42 -0
- agentpool/docs/utils.py +370 -0
- agentpool/functional/__init__.py +20 -0
- agentpool/functional/py.typed +0 -0
- agentpool/functional/run.py +80 -0
- agentpool/functional/structure.py +136 -0
- agentpool/hooks/__init__.py +20 -0
- agentpool/hooks/agent_hooks.py +247 -0
- agentpool/hooks/base.py +119 -0
- agentpool/hooks/callable.py +140 -0
- agentpool/hooks/command.py +180 -0
- agentpool/hooks/prompt.py +122 -0
- agentpool/jinja_filters.py +132 -0
- agentpool/log.py +224 -0
- agentpool/mcp_server/__init__.py +17 -0
- agentpool/mcp_server/client.py +429 -0
- agentpool/mcp_server/constants.py +32 -0
- agentpool/mcp_server/conversions.py +172 -0
- agentpool/mcp_server/helpers.py +47 -0
- agentpool/mcp_server/manager.py +232 -0
- agentpool/mcp_server/message_handler.py +164 -0
- agentpool/mcp_server/registries/__init__.py +1 -0
- agentpool/mcp_server/registries/official_registry_client.py +345 -0
- agentpool/mcp_server/registries/pulsemcp_client.py +88 -0
- agentpool/mcp_server/tool_bridge.py +548 -0
- agentpool/messaging/__init__.py +58 -0
- agentpool/messaging/compaction.py +928 -0
- agentpool/messaging/connection_manager.py +319 -0
- agentpool/messaging/context.py +66 -0
- agentpool/messaging/event_manager.py +426 -0
- agentpool/messaging/events.py +39 -0
- agentpool/messaging/message_container.py +209 -0
- agentpool/messaging/message_history.py +491 -0
- agentpool/messaging/messagenode.py +377 -0
- agentpool/messaging/messages.py +655 -0
- agentpool/messaging/processing.py +76 -0
- agentpool/mime_utils.py +95 -0
- agentpool/models/__init__.py +21 -0
- agentpool/models/acp_agents/__init__.py +22 -0
- agentpool/models/acp_agents/base.py +308 -0
- agentpool/models/acp_agents/mcp_capable.py +790 -0
- agentpool/models/acp_agents/non_mcp.py +842 -0
- agentpool/models/agents.py +450 -0
- agentpool/models/agui_agents.py +89 -0
- agentpool/models/claude_code_agents.py +238 -0
- agentpool/models/file_agents.py +116 -0
- agentpool/models/file_parsing.py +367 -0
- agentpool/models/manifest.py +658 -0
- agentpool/observability/__init__.py +9 -0
- agentpool/observability/observability_registry.py +97 -0
- agentpool/prompts/__init__.py +1 -0
- agentpool/prompts/base.py +27 -0
- agentpool/prompts/builtin_provider.py +75 -0
- agentpool/prompts/conversion_manager.py +95 -0
- agentpool/prompts/convert.py +96 -0
- agentpool/prompts/manager.py +204 -0
- agentpool/prompts/parts/zed.md +33 -0
- agentpool/prompts/prompts.py +581 -0
- agentpool/py.typed +0 -0
- agentpool/queries/tree-sitter-language-pack/README.md +7 -0
- agentpool/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- agentpool/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- agentpool/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
- agentpool/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- agentpool/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- agentpool/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- agentpool/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- agentpool/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- agentpool/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- agentpool/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- agentpool/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- agentpool/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- agentpool/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- agentpool/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- agentpool/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- agentpool/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
- agentpool/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- agentpool/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- agentpool/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- agentpool/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- agentpool/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- agentpool/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- agentpool/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- agentpool/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- agentpool/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- agentpool/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- agentpool/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- agentpool/queries/tree-sitter-languages/README.md +24 -0
- agentpool/queries/tree-sitter-languages/c-tags.scm +9 -0
- agentpool/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- agentpool/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- agentpool/queries/tree-sitter-languages/dart-tags.scm +91 -0
- agentpool/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- agentpool/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- agentpool/queries/tree-sitter-languages/elm-tags.scm +19 -0
- agentpool/queries/tree-sitter-languages/fortran-tags.scm +15 -0
- agentpool/queries/tree-sitter-languages/go-tags.scm +30 -0
- agentpool/queries/tree-sitter-languages/haskell-tags.scm +3 -0
- agentpool/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- agentpool/queries/tree-sitter-languages/java-tags.scm +20 -0
- agentpool/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- agentpool/queries/tree-sitter-languages/julia-tags.scm +60 -0
- agentpool/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- agentpool/queries/tree-sitter-languages/matlab-tags.scm +10 -0
- agentpool/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- agentpool/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- agentpool/queries/tree-sitter-languages/php-tags.scm +26 -0
- agentpool/queries/tree-sitter-languages/python-tags.scm +12 -0
- agentpool/queries/tree-sitter-languages/ql-tags.scm +26 -0
- agentpool/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- agentpool/queries/tree-sitter-languages/rust-tags.scm +60 -0
- agentpool/queries/tree-sitter-languages/scala-tags.scm +65 -0
- agentpool/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- agentpool/queries/tree-sitter-languages/zig-tags.scm +3 -0
- agentpool/repomap.py +1231 -0
- agentpool/resource_providers/__init__.py +17 -0
- agentpool/resource_providers/aggregating.py +54 -0
- agentpool/resource_providers/base.py +172 -0
- agentpool/resource_providers/codemode/__init__.py +9 -0
- agentpool/resource_providers/codemode/code_executor.py +215 -0
- agentpool/resource_providers/codemode/default_prompt.py +19 -0
- agentpool/resource_providers/codemode/helpers.py +83 -0
- agentpool/resource_providers/codemode/progress_executor.py +212 -0
- agentpool/resource_providers/codemode/provider.py +150 -0
- agentpool/resource_providers/codemode/remote_mcp_execution.py +143 -0
- agentpool/resource_providers/codemode/remote_provider.py +171 -0
- agentpool/resource_providers/filtering.py +42 -0
- agentpool/resource_providers/mcp_provider.py +246 -0
- agentpool/resource_providers/plan_provider.py +196 -0
- agentpool/resource_providers/pool.py +69 -0
- agentpool/resource_providers/static.py +289 -0
- agentpool/running/__init__.py +20 -0
- agentpool/running/decorators.py +56 -0
- agentpool/running/discovery.py +101 -0
- agentpool/running/executor.py +284 -0
- agentpool/running/injection.py +111 -0
- agentpool/running/py.typed +0 -0
- agentpool/running/run_nodes.py +87 -0
- agentpool/server.py +122 -0
- agentpool/sessions/__init__.py +13 -0
- agentpool/sessions/manager.py +302 -0
- agentpool/sessions/models.py +71 -0
- agentpool/sessions/session.py +239 -0
- agentpool/sessions/store.py +163 -0
- agentpool/skills/__init__.py +5 -0
- agentpool/skills/manager.py +120 -0
- agentpool/skills/registry.py +210 -0
- agentpool/skills/skill.py +36 -0
- agentpool/storage/__init__.py +17 -0
- agentpool/storage/manager.py +419 -0
- agentpool/storage/serialization.py +136 -0
- agentpool/talk/__init__.py +13 -0
- agentpool/talk/registry.py +128 -0
- agentpool/talk/stats.py +159 -0
- agentpool/talk/talk.py +604 -0
- agentpool/tasks/__init__.py +20 -0
- agentpool/tasks/exceptions.py +25 -0
- agentpool/tasks/registry.py +33 -0
- agentpool/testing.py +129 -0
- agentpool/text_templates/__init__.py +39 -0
- agentpool/text_templates/system_prompt.jinja +30 -0
- agentpool/text_templates/tool_call_default.jinja +13 -0
- agentpool/text_templates/tool_call_markdown.jinja +25 -0
- agentpool/text_templates/tool_call_simple.jinja +5 -0
- agentpool/tools/__init__.py +16 -0
- agentpool/tools/base.py +269 -0
- agentpool/tools/exceptions.py +9 -0
- agentpool/tools/manager.py +255 -0
- agentpool/tools/tool_call_info.py +87 -0
- agentpool/ui/__init__.py +2 -0
- agentpool/ui/base.py +89 -0
- agentpool/ui/mock_provider.py +81 -0
- agentpool/ui/stdlib_provider.py +150 -0
- agentpool/utils/__init__.py +44 -0
- agentpool/utils/baseregistry.py +185 -0
- agentpool/utils/count_tokens.py +62 -0
- agentpool/utils/dag.py +184 -0
- agentpool/utils/importing.py +206 -0
- agentpool/utils/inspection.py +334 -0
- agentpool/utils/model_capabilities.py +25 -0
- agentpool/utils/network.py +28 -0
- agentpool/utils/now.py +22 -0
- agentpool/utils/parse_time.py +87 -0
- agentpool/utils/result_utils.py +35 -0
- agentpool/utils/signatures.py +305 -0
- agentpool/utils/streams.py +112 -0
- agentpool/utils/tasks.py +186 -0
- agentpool/vfs_registry.py +250 -0
- agentpool-2.1.9.dist-info/METADATA +336 -0
- agentpool-2.1.9.dist-info/RECORD +474 -0
- agentpool-2.1.9.dist-info/WHEEL +4 -0
- agentpool-2.1.9.dist-info/entry_points.txt +14 -0
- agentpool-2.1.9.dist-info/licenses/LICENSE +22 -0
- agentpool_cli/__init__.py +34 -0
- agentpool_cli/__main__.py +66 -0
- agentpool_cli/agent.py +175 -0
- agentpool_cli/cli_types.py +23 -0
- agentpool_cli/common.py +163 -0
- agentpool_cli/create.py +175 -0
- agentpool_cli/history.py +217 -0
- agentpool_cli/log.py +78 -0
- agentpool_cli/py.typed +0 -0
- agentpool_cli/run.py +84 -0
- agentpool_cli/serve_acp.py +177 -0
- agentpool_cli/serve_api.py +69 -0
- agentpool_cli/serve_mcp.py +74 -0
- agentpool_cli/serve_vercel.py +233 -0
- agentpool_cli/store.py +171 -0
- agentpool_cli/task.py +84 -0
- agentpool_cli/utils.py +104 -0
- agentpool_cli/watch.py +54 -0
- agentpool_commands/__init__.py +180 -0
- agentpool_commands/agents.py +199 -0
- agentpool_commands/base.py +45 -0
- agentpool_commands/commands.py +58 -0
- agentpool_commands/completers.py +110 -0
- agentpool_commands/connections.py +175 -0
- agentpool_commands/markdown_utils.py +31 -0
- agentpool_commands/models.py +62 -0
- agentpool_commands/prompts.py +78 -0
- agentpool_commands/py.typed +0 -0
- agentpool_commands/read.py +77 -0
- agentpool_commands/resources.py +210 -0
- agentpool_commands/session.py +48 -0
- agentpool_commands/tools.py +269 -0
- agentpool_commands/utils.py +189 -0
- agentpool_commands/workers.py +163 -0
- agentpool_config/__init__.py +53 -0
- agentpool_config/builtin_tools.py +265 -0
- agentpool_config/commands.py +237 -0
- agentpool_config/conditions.py +301 -0
- agentpool_config/converters.py +30 -0
- agentpool_config/durable.py +331 -0
- agentpool_config/event_handlers.py +600 -0
- agentpool_config/events.py +153 -0
- agentpool_config/forward_targets.py +251 -0
- agentpool_config/hook_conditions.py +331 -0
- agentpool_config/hooks.py +241 -0
- agentpool_config/jinja.py +206 -0
- agentpool_config/knowledge.py +41 -0
- agentpool_config/loaders.py +350 -0
- agentpool_config/mcp_server.py +243 -0
- agentpool_config/nodes.py +202 -0
- agentpool_config/observability.py +191 -0
- agentpool_config/output_types.py +55 -0
- agentpool_config/pool_server.py +267 -0
- agentpool_config/prompt_hubs.py +105 -0
- agentpool_config/prompts.py +185 -0
- agentpool_config/py.typed +0 -0
- agentpool_config/resources.py +33 -0
- agentpool_config/session.py +119 -0
- agentpool_config/skills.py +17 -0
- agentpool_config/storage.py +288 -0
- agentpool_config/system_prompts.py +190 -0
- agentpool_config/task.py +162 -0
- agentpool_config/teams.py +52 -0
- agentpool_config/tools.py +112 -0
- agentpool_config/toolsets.py +1033 -0
- agentpool_config/workers.py +86 -0
- agentpool_prompts/__init__.py +1 -0
- agentpool_prompts/braintrust_hub.py +235 -0
- agentpool_prompts/fabric.py +75 -0
- agentpool_prompts/langfuse_hub.py +79 -0
- agentpool_prompts/promptlayer_provider.py +59 -0
- agentpool_prompts/py.typed +0 -0
- agentpool_server/__init__.py +9 -0
- agentpool_server/a2a_server/__init__.py +5 -0
- agentpool_server/a2a_server/a2a_types.py +41 -0
- agentpool_server/a2a_server/server.py +190 -0
- agentpool_server/a2a_server/storage.py +81 -0
- agentpool_server/acp_server/__init__.py +22 -0
- agentpool_server/acp_server/acp_agent.py +786 -0
- agentpool_server/acp_server/acp_tools.py +43 -0
- agentpool_server/acp_server/commands/__init__.py +18 -0
- agentpool_server/acp_server/commands/acp_commands.py +594 -0
- agentpool_server/acp_server/commands/debug_commands.py +376 -0
- agentpool_server/acp_server/commands/docs_commands/__init__.py +39 -0
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +169 -0
- agentpool_server/acp_server/commands/docs_commands/get_schema.py +176 -0
- agentpool_server/acp_server/commands/docs_commands/get_source.py +110 -0
- agentpool_server/acp_server/commands/docs_commands/git_diff.py +111 -0
- agentpool_server/acp_server/commands/docs_commands/helpers.py +33 -0
- agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +90 -0
- agentpool_server/acp_server/commands/spawn.py +210 -0
- agentpool_server/acp_server/converters.py +235 -0
- agentpool_server/acp_server/input_provider.py +338 -0
- agentpool_server/acp_server/server.py +288 -0
- agentpool_server/acp_server/session.py +969 -0
- agentpool_server/acp_server/session_manager.py +313 -0
- agentpool_server/acp_server/syntax_detection.py +250 -0
- agentpool_server/acp_server/zed_tools.md +90 -0
- agentpool_server/aggregating_server.py +309 -0
- agentpool_server/agui_server/__init__.py +11 -0
- agentpool_server/agui_server/server.py +128 -0
- agentpool_server/base.py +189 -0
- agentpool_server/http_server.py +164 -0
- agentpool_server/mcp_server/__init__.py +6 -0
- agentpool_server/mcp_server/server.py +314 -0
- agentpool_server/mcp_server/zed_wrapper.py +110 -0
- agentpool_server/openai_api_server/__init__.py +5 -0
- agentpool_server/openai_api_server/completions/__init__.py +1 -0
- agentpool_server/openai_api_server/completions/helpers.py +81 -0
- agentpool_server/openai_api_server/completions/models.py +98 -0
- agentpool_server/openai_api_server/responses/__init__.py +1 -0
- agentpool_server/openai_api_server/responses/helpers.py +74 -0
- agentpool_server/openai_api_server/responses/models.py +96 -0
- agentpool_server/openai_api_server/server.py +242 -0
- agentpool_server/py.typed +0 -0
- agentpool_storage/__init__.py +9 -0
- agentpool_storage/base.py +310 -0
- agentpool_storage/file_provider.py +378 -0
- agentpool_storage/formatters.py +129 -0
- agentpool_storage/memory_provider.py +396 -0
- agentpool_storage/models.py +108 -0
- agentpool_storage/py.typed +0 -0
- agentpool_storage/session_store.py +262 -0
- agentpool_storage/sql_provider/__init__.py +21 -0
- agentpool_storage/sql_provider/cli.py +146 -0
- agentpool_storage/sql_provider/models.py +249 -0
- agentpool_storage/sql_provider/queries.py +15 -0
- agentpool_storage/sql_provider/sql_provider.py +444 -0
- agentpool_storage/sql_provider/utils.py +234 -0
- agentpool_storage/text_log_provider.py +275 -0
- agentpool_toolsets/__init__.py +15 -0
- agentpool_toolsets/builtin/__init__.py +33 -0
- agentpool_toolsets/builtin/agent_management.py +239 -0
- agentpool_toolsets/builtin/chain.py +288 -0
- agentpool_toolsets/builtin/code.py +398 -0
- agentpool_toolsets/builtin/debug.py +291 -0
- agentpool_toolsets/builtin/execution_environment.py +381 -0
- agentpool_toolsets/builtin/file_edit/__init__.py +11 -0
- agentpool_toolsets/builtin/file_edit/file_edit.py +747 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/__init__.py +5 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/example_usage.py +311 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/streaming_fuzzy_matcher.py +443 -0
- agentpool_toolsets/builtin/history.py +36 -0
- agentpool_toolsets/builtin/integration.py +85 -0
- agentpool_toolsets/builtin/skills.py +77 -0
- agentpool_toolsets/builtin/subagent_tools.py +324 -0
- agentpool_toolsets/builtin/tool_management.py +90 -0
- agentpool_toolsets/builtin/user_interaction.py +52 -0
- agentpool_toolsets/builtin/workers.py +128 -0
- agentpool_toolsets/composio_toolset.py +96 -0
- agentpool_toolsets/config_creation.py +192 -0
- agentpool_toolsets/entry_points.py +47 -0
- agentpool_toolsets/fsspec_toolset/__init__.py +7 -0
- agentpool_toolsets/fsspec_toolset/diagnostics.py +115 -0
- agentpool_toolsets/fsspec_toolset/grep.py +450 -0
- agentpool_toolsets/fsspec_toolset/helpers.py +631 -0
- agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py +249 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +1384 -0
- agentpool_toolsets/mcp_run_toolset.py +61 -0
- agentpool_toolsets/notifications.py +146 -0
- agentpool_toolsets/openapi.py +118 -0
- agentpool_toolsets/py.typed +0 -0
- agentpool_toolsets/search_toolset.py +202 -0
- agentpool_toolsets/semantic_memory_toolset.py +536 -0
- agentpool_toolsets/streaming_tools.py +265 -0
- agentpool_toolsets/vfs_toolset.py +124 -0
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
"""Message and token usage models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from dataclasses import dataclass, field, replace
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Literal, Self, TypeVar, assert_never
|
|
9
|
+
import uuid
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
|
|
12
|
+
from genai_prices import calc_price
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
from pydantic_ai import (
|
|
15
|
+
BaseToolReturnPart,
|
|
16
|
+
BinaryContent,
|
|
17
|
+
FilePart,
|
|
18
|
+
FileUrl,
|
|
19
|
+
ModelRequest,
|
|
20
|
+
ModelResponse,
|
|
21
|
+
RequestUsage,
|
|
22
|
+
TextPart,
|
|
23
|
+
ToolCallPart,
|
|
24
|
+
UserContent,
|
|
25
|
+
UserPromptPart,
|
|
26
|
+
)
|
|
27
|
+
import tokonomics
|
|
28
|
+
|
|
29
|
+
from agentpool.common_types import MessageRole, SimpleJsonType # noqa: TC001
|
|
30
|
+
from agentpool.log import get_logger
|
|
31
|
+
from agentpool.utils.inspection import dataclasses_no_defaults_repr
|
|
32
|
+
from agentpool.utils.now import get_now
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from datetime import datetime
|
|
37
|
+
|
|
38
|
+
from pydantic_ai import (
|
|
39
|
+
AgentRunResult,
|
|
40
|
+
FinishReason,
|
|
41
|
+
ModelMessage,
|
|
42
|
+
ModelRequestPart,
|
|
43
|
+
ModelResponsePart,
|
|
44
|
+
RunUsage,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
from agentpool.tools.tool_call_info import ToolCallInfo
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
TContent = TypeVar("TContent", str, BaseModel)
|
|
51
|
+
FormatStyle = Literal["simple", "detailed", "markdown", "custom"]
|
|
52
|
+
logger = get_logger(__name__)
|
|
53
|
+
|
|
54
|
+
SIMPLE_TEMPLATE = """{{ name or role.title() }}: {{ content }}"""
|
|
55
|
+
|
|
56
|
+
DETAILED_TEMPLATE = """From: {{ name or role.title() }}
|
|
57
|
+
Time: {{ timestamp.strftime('%Y-%m-%d %H:%M:%S') }}
|
|
58
|
+
----------------------------------------
|
|
59
|
+
{{ content }}
|
|
60
|
+
----------------------------------------
|
|
61
|
+
{%- if show_costs and cost_info %}
|
|
62
|
+
Tokens: {{ "{:,}".format(cost_info.token_usage.total_tokens) }}
|
|
63
|
+
Cost: ${{ "%.6f"|format(cost_info.total_cost) }}
|
|
64
|
+
{%- if response_time %}
|
|
65
|
+
Response time: {{ "%.2f"|format(response_time) }}s
|
|
66
|
+
{%- endif %}
|
|
67
|
+
{%- endif %}
|
|
68
|
+
{%- if show_metadata and metadata %}
|
|
69
|
+
Metadata:
|
|
70
|
+
{%- for key, value in metadata.items() %}
|
|
71
|
+
{{ key }}: {{ value }}
|
|
72
|
+
{%- endfor %}
|
|
73
|
+
{%- endif %}
|
|
74
|
+
{%- if forwarded_from %}
|
|
75
|
+
Forwarded via: {{ forwarded_from|join(' -> ') }}
|
|
76
|
+
{%- endif %}"""
|
|
77
|
+
|
|
78
|
+
MARKDOWN_TEMPLATE = """## {{ name or role.title() }}
|
|
79
|
+
*{{ timestamp.strftime('%Y-%m-%d %H:%M:%S') }}*
|
|
80
|
+
|
|
81
|
+
{{ content }}
|
|
82
|
+
|
|
83
|
+
{%- if show_costs and cost_info %}
|
|
84
|
+
---
|
|
85
|
+
**Stats:**
|
|
86
|
+
- Tokens: {{ "{:,}".format(cost_info.token_usage.total_tokens) }}
|
|
87
|
+
- Cost: ${{ "%.6f"|format(cost_info.total_cost) }}
|
|
88
|
+
{%- if response_time %}
|
|
89
|
+
- Response time: {{ "%.2f"|format(response_time) }}s
|
|
90
|
+
{%- endif %}
|
|
91
|
+
{%- endif %}
|
|
92
|
+
|
|
93
|
+
{%- if show_metadata and metadata %}
|
|
94
|
+
**Metadata:**
|
|
95
|
+
```
|
|
96
|
+
{{ metadata|to_yaml }}
|
|
97
|
+
```
|
|
98
|
+
{%- endif %}
|
|
99
|
+
|
|
100
|
+
{% if forwarded_from %}
|
|
101
|
+
|
|
102
|
+
*Forwarded via: {{ forwarded_from|join(' → ') }}*
|
|
103
|
+
{% endif %}"""
|
|
104
|
+
|
|
105
|
+
MESSAGE_TEMPLATES = {
|
|
106
|
+
"simple": SIMPLE_TEMPLATE,
|
|
107
|
+
"detailed": DETAILED_TEMPLATE,
|
|
108
|
+
"markdown": MARKDOWN_TEMPLATE,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass(frozen=True)
|
|
113
|
+
class TokenCost:
|
|
114
|
+
"""Combined token and cost tracking."""
|
|
115
|
+
|
|
116
|
+
token_usage: RunUsage
|
|
117
|
+
"""Token counts for prompt and completion"""
|
|
118
|
+
total_cost: Decimal
|
|
119
|
+
"""Total cost in USD"""
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
async def from_usage(
|
|
123
|
+
cls,
|
|
124
|
+
usage: RunUsage,
|
|
125
|
+
model: str,
|
|
126
|
+
provider: str | None = None,
|
|
127
|
+
) -> TokenCost | None:
|
|
128
|
+
"""Create result from usage data.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
usage: Token counts from model response
|
|
132
|
+
model: Name of the model used
|
|
133
|
+
provider: Provider ID (e.g. 'openrouter', 'openai'). If not provided,
|
|
134
|
+
will try to extract from model string (e.g. 'openrouter:model')
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
TokenCost if usage data available, None otherwise
|
|
138
|
+
"""
|
|
139
|
+
logger.debug("Token usage", usage=usage)
|
|
140
|
+
if model in {"None", "test"}:
|
|
141
|
+
price = Decimal(0)
|
|
142
|
+
else:
|
|
143
|
+
# Determine provider and model ref
|
|
144
|
+
parts = model.split(":", 1)
|
|
145
|
+
if len(parts) > 1:
|
|
146
|
+
# Model string has provider prefix (e.g. 'openrouter:google/gemini')
|
|
147
|
+
provider_id = parts[0]
|
|
148
|
+
model_ref = parts[1]
|
|
149
|
+
else:
|
|
150
|
+
# Use explicit provider or default to openai
|
|
151
|
+
provider_id = provider or "openai"
|
|
152
|
+
model_ref = parts[0]
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
price_data = calc_price(
|
|
156
|
+
usage,
|
|
157
|
+
model_ref=model_ref,
|
|
158
|
+
provider_id=provider_id,
|
|
159
|
+
)
|
|
160
|
+
price = price_data.total_price
|
|
161
|
+
except Exception: # noqa: BLE001
|
|
162
|
+
cost = await tokonomics.calculate_token_cost(
|
|
163
|
+
model,
|
|
164
|
+
usage.input_tokens,
|
|
165
|
+
usage.output_tokens,
|
|
166
|
+
)
|
|
167
|
+
price = Decimal(cost.total_cost if cost else 0)
|
|
168
|
+
|
|
169
|
+
return cls(token_usage=usage, total_cost=price)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass
|
|
173
|
+
class ChatMessage[TContent]:
|
|
174
|
+
"""Common message format for all UI types.
|
|
175
|
+
|
|
176
|
+
Generically typed with: ChatMessage[Type of Content]
|
|
177
|
+
The type can either be str or a BaseModel subclass.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
content: TContent
|
|
181
|
+
"""Message content, typed as TContent (either str or BaseModel)."""
|
|
182
|
+
|
|
183
|
+
role: MessageRole
|
|
184
|
+
"""Role of the message sender (user/assistant)."""
|
|
185
|
+
|
|
186
|
+
metadata: SimpleJsonType = field(default_factory=dict)
|
|
187
|
+
"""Additional metadata about the message."""
|
|
188
|
+
|
|
189
|
+
timestamp: datetime = field(default_factory=get_now)
|
|
190
|
+
"""When this message was created."""
|
|
191
|
+
|
|
192
|
+
cost_info: TokenCost | None = None
|
|
193
|
+
"""Token usage and costs for this specific message if available."""
|
|
194
|
+
|
|
195
|
+
message_id: str = field(default_factory=lambda: str(uuid4()))
|
|
196
|
+
"""Unique identifier for this message."""
|
|
197
|
+
|
|
198
|
+
conversation_id: str | None = None
|
|
199
|
+
"""ID of the conversation this message belongs to."""
|
|
200
|
+
|
|
201
|
+
response_time: float | None = None
|
|
202
|
+
"""Time it took the LLM to respond."""
|
|
203
|
+
|
|
204
|
+
associated_messages: list[ChatMessage[Any]] = field(default_factory=list)
|
|
205
|
+
"""List of messages which were generated during the the creation of this messsage."""
|
|
206
|
+
|
|
207
|
+
name: str | None = None
|
|
208
|
+
"""Display name for the message sender in UI."""
|
|
209
|
+
|
|
210
|
+
forwarded_from: list[str] = field(default_factory=list)
|
|
211
|
+
"""List of agent names (the chain) that forwarded this message to the sender."""
|
|
212
|
+
|
|
213
|
+
provider_details: dict[str, Any] = field(default_factory=dict)
|
|
214
|
+
"""Provider specific metadata / extra information."""
|
|
215
|
+
|
|
216
|
+
messages: list[ModelMessage] = field(default_factory=list)
|
|
217
|
+
"""List of messages which were generated during the the creation of this messsage."""
|
|
218
|
+
|
|
219
|
+
usage: RequestUsage = field(default_factory=RequestUsage)
|
|
220
|
+
"""Usage information for the request.
|
|
221
|
+
|
|
222
|
+
This has a default to make tests easier,
|
|
223
|
+
and to support loading old messages where usage will be missing.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
model_name: str | None = None
|
|
227
|
+
"""The name of the model that generated the response."""
|
|
228
|
+
|
|
229
|
+
provider_name: str | None = None
|
|
230
|
+
"""The name of the LLM provider that generated the response."""
|
|
231
|
+
|
|
232
|
+
provider_response_id: str | None = None
|
|
233
|
+
"""request ID as specified by the model provider.
|
|
234
|
+
|
|
235
|
+
This can be used to track the specific request to the model."""
|
|
236
|
+
|
|
237
|
+
finish_reason: FinishReason | None = None
|
|
238
|
+
"""Reason the model finished generating the response.
|
|
239
|
+
|
|
240
|
+
Normalized to OpenTelemetry values."""
|
|
241
|
+
|
|
242
|
+
__repr__ = dataclasses_no_defaults_repr
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def last_message(self) -> ModelMessage | None:
|
|
246
|
+
"""Return the last message from the message history."""
|
|
247
|
+
# The response may not be the very last item if it contained an output tool call.
|
|
248
|
+
# See `CallToolsNode._handle_final_result`.
|
|
249
|
+
for message in reversed(self.messages):
|
|
250
|
+
if isinstance(message, ModelRequest | ModelResponse):
|
|
251
|
+
return message
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def parts(self) -> Sequence[ModelRequestPart] | Sequence[ModelResponsePart]:
|
|
256
|
+
"""The parts of the last model message."""
|
|
257
|
+
if not self.last_message:
|
|
258
|
+
return []
|
|
259
|
+
return self.last_message.parts
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def kind(self) -> Literal["request", "response"]:
|
|
263
|
+
"""Role of the message."""
|
|
264
|
+
match self.role:
|
|
265
|
+
case "assistant":
|
|
266
|
+
return "response"
|
|
267
|
+
case "user":
|
|
268
|
+
return "request"
|
|
269
|
+
|
|
270
|
+
def to_pydantic_ai(self) -> Sequence[ModelMessage]:
|
|
271
|
+
"""Convert this message to a Pydantic model."""
|
|
272
|
+
if self.messages:
|
|
273
|
+
return self.messages
|
|
274
|
+
match self.kind:
|
|
275
|
+
case "request":
|
|
276
|
+
return [ModelRequest(parts=self.parts, instructions=None, run_id=self.message_id)] # type: ignore
|
|
277
|
+
case "response":
|
|
278
|
+
return [
|
|
279
|
+
ModelResponse(
|
|
280
|
+
parts=self.parts, # type: ignore
|
|
281
|
+
usage=self.usage,
|
|
282
|
+
model_name=self.model_name,
|
|
283
|
+
timestamp=self.timestamp,
|
|
284
|
+
provider_name=self.provider_name,
|
|
285
|
+
provider_details=self.provider_details,
|
|
286
|
+
finish_reason=self.finish_reason,
|
|
287
|
+
provider_response_id=self.provider_response_id,
|
|
288
|
+
run_id=self.message_id,
|
|
289
|
+
)
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
@classmethod
|
|
293
|
+
def user_prompt[TPromptContent: str | Sequence[UserContent] = str](
|
|
294
|
+
cls,
|
|
295
|
+
message: TPromptContent,
|
|
296
|
+
conversation_id: str | None = None,
|
|
297
|
+
instructions: str | None = None,
|
|
298
|
+
) -> ChatMessage[TPromptContent]:
|
|
299
|
+
"""Create a user prompt message."""
|
|
300
|
+
part = UserPromptPart(content=message)
|
|
301
|
+
request = ModelRequest(parts=[part], instructions=instructions)
|
|
302
|
+
id_ = conversation_id or str(uuid4())
|
|
303
|
+
return ChatMessage(messages=[request], role="user", content=message, conversation_id=id_)
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
def from_pydantic_ai[TContentType](
|
|
307
|
+
cls,
|
|
308
|
+
content: TContentType,
|
|
309
|
+
message: ModelMessage,
|
|
310
|
+
conversation_id: str | None = None,
|
|
311
|
+
name: str | None = None,
|
|
312
|
+
forwarded_from: list[str] | None = None,
|
|
313
|
+
) -> ChatMessage[TContentType]:
|
|
314
|
+
"""Convert a Pydantic model to a ChatMessage."""
|
|
315
|
+
match message:
|
|
316
|
+
case ModelRequest(instructions=_instructions, run_id=run_id):
|
|
317
|
+
return ChatMessage(
|
|
318
|
+
messages=[message],
|
|
319
|
+
content=content,
|
|
320
|
+
role="user",
|
|
321
|
+
message_id=run_id or str(uuid.uuid4()),
|
|
322
|
+
# instructions=instructions,
|
|
323
|
+
forwarded_from=forwarded_from or [],
|
|
324
|
+
name=name,
|
|
325
|
+
)
|
|
326
|
+
case ModelResponse(
|
|
327
|
+
usage=usage,
|
|
328
|
+
model_name=model_name,
|
|
329
|
+
timestamp=timestamp,
|
|
330
|
+
provider_name=provider_name,
|
|
331
|
+
provider_details=provider_details,
|
|
332
|
+
finish_reason=finish_reason,
|
|
333
|
+
provider_response_id=provider_response_id,
|
|
334
|
+
run_id=run_id,
|
|
335
|
+
):
|
|
336
|
+
return ChatMessage(
|
|
337
|
+
role="assistant",
|
|
338
|
+
content=content,
|
|
339
|
+
messages=[message],
|
|
340
|
+
usage=usage,
|
|
341
|
+
message_id=run_id or str(uuid.uuid4()),
|
|
342
|
+
conversation_id=conversation_id,
|
|
343
|
+
model_name=model_name,
|
|
344
|
+
timestamp=timestamp,
|
|
345
|
+
provider_name=provider_name,
|
|
346
|
+
provider_details=provider_details or {},
|
|
347
|
+
finish_reason=finish_reason,
|
|
348
|
+
provider_response_id=provider_response_id,
|
|
349
|
+
name=name,
|
|
350
|
+
forwarded_from=forwarded_from or [],
|
|
351
|
+
)
|
|
352
|
+
case _ as unreachable:
|
|
353
|
+
assert_never(unreachable)
|
|
354
|
+
|
|
355
|
+
@classmethod
|
|
356
|
+
async def from_run_result[OutputDataT](
|
|
357
|
+
cls,
|
|
358
|
+
result: AgentRunResult[OutputDataT],
|
|
359
|
+
*,
|
|
360
|
+
agent_name: str | None = None,
|
|
361
|
+
message_id: str | None = None,
|
|
362
|
+
conversation_id: str | None = None,
|
|
363
|
+
response_time: float,
|
|
364
|
+
) -> ChatMessage[OutputDataT]:
|
|
365
|
+
"""Create a ChatMessage from a PydanticAI run result.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
result: The PydanticAI run result
|
|
369
|
+
agent_name: Name of the agent that generated this response
|
|
370
|
+
message_id: Unique message identifier
|
|
371
|
+
conversation_id: Conversation identifier
|
|
372
|
+
response_time: Total time taken for the response
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
A ChatMessage with all fields populated from the result
|
|
376
|
+
"""
|
|
377
|
+
# Calculate costs
|
|
378
|
+
run_usage = result.usage()
|
|
379
|
+
usage = result.response.usage
|
|
380
|
+
cost_info = await TokenCost.from_usage(
|
|
381
|
+
model=result.response.model_name or "",
|
|
382
|
+
usage=run_usage,
|
|
383
|
+
provider=result.response.provider_name,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
return ChatMessage[OutputDataT](
|
|
387
|
+
content=result.output,
|
|
388
|
+
role="assistant",
|
|
389
|
+
name=agent_name,
|
|
390
|
+
model_name=result.response.model_name,
|
|
391
|
+
finish_reason=result.response.finish_reason,
|
|
392
|
+
messages=result.new_messages(),
|
|
393
|
+
provider_response_id=result.response.provider_response_id,
|
|
394
|
+
usage=usage,
|
|
395
|
+
provider_name=result.response.provider_name,
|
|
396
|
+
message_id=message_id or str(uuid4()),
|
|
397
|
+
conversation_id=conversation_id,
|
|
398
|
+
cost_info=cost_info,
|
|
399
|
+
response_time=response_time,
|
|
400
|
+
provider_details={},
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
def forwarded(self, previous_message: ChatMessage[Any]) -> Self:
|
|
404
|
+
"""Create new message showing it was forwarded from another message.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
previous_message: The message that led to this one's creation
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
New message with updated chain showing the path through previous message
|
|
411
|
+
"""
|
|
412
|
+
from_ = [*previous_message.forwarded_from, previous_message.name or "unknown"]
|
|
413
|
+
return replace(self, forwarded_from=from_)
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def response(self) -> ModelResponse:
|
|
417
|
+
"""Return the last response from the message history."""
|
|
418
|
+
# The response may not be the very last item if it contained an output tool call.
|
|
419
|
+
# See `CallToolsNode._handle_final_result`.
|
|
420
|
+
for message in reversed(self.messages):
|
|
421
|
+
if isinstance(message, ModelResponse):
|
|
422
|
+
return message
|
|
423
|
+
msg = "No response found in the message history"
|
|
424
|
+
raise ValueError(msg)
|
|
425
|
+
|
|
426
|
+
def to_request(self) -> Self:
|
|
427
|
+
"""Convert this message to a request message.
|
|
428
|
+
|
|
429
|
+
If the message is already a request (user role), this is a no-op.
|
|
430
|
+
If it's a response (assistant role), converts response parts to user content.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
New ChatMessage with role='user' and converted parts
|
|
434
|
+
"""
|
|
435
|
+
if self.role == "user":
|
|
436
|
+
return self # Already a request, return as-is
|
|
437
|
+
|
|
438
|
+
user_content: list[UserContent] = [] # Convert response parts to user content
|
|
439
|
+
for part in self.parts:
|
|
440
|
+
match part:
|
|
441
|
+
case TextPart(content=content) | FilePart(content=content):
|
|
442
|
+
# Text & File parts (images, etc.) become user content directly
|
|
443
|
+
user_content.append(content)
|
|
444
|
+
case BaseToolReturnPart(content=(str() | FileUrl() | BinaryContent()) as content):
|
|
445
|
+
user_content.append(content) # type: ignore[arg-type]
|
|
446
|
+
case BaseToolReturnPart(content=list() as content_list):
|
|
447
|
+
# Handle sequence of content items
|
|
448
|
+
for item in content_list:
|
|
449
|
+
if isinstance(item, str | FileUrl | BinaryContent):
|
|
450
|
+
user_content.append(item) # type: ignore[arg-type]
|
|
451
|
+
else:
|
|
452
|
+
user_content.append(str(item))
|
|
453
|
+
case BaseToolReturnPart():
|
|
454
|
+
# Other tool return parts become user content strings
|
|
455
|
+
user_content.append(part.model_response_str())
|
|
456
|
+
case ToolCallPart():
|
|
457
|
+
# Tool return parts become user content strings
|
|
458
|
+
user_content.append(part.args_as_json_str())
|
|
459
|
+
case _:
|
|
460
|
+
pass
|
|
461
|
+
|
|
462
|
+
# Create new UserPromptPart with converted content
|
|
463
|
+
if user_content:
|
|
464
|
+
converted_parts = [UserPromptPart(content=user_content)]
|
|
465
|
+
else:
|
|
466
|
+
converted_parts = [UserPromptPart(content=str(self.content))]
|
|
467
|
+
|
|
468
|
+
return replace(
|
|
469
|
+
self,
|
|
470
|
+
role="user",
|
|
471
|
+
messages=[ModelRequest(parts=converted_parts)],
|
|
472
|
+
cost_info=None,
|
|
473
|
+
# TODO: what about message_id?
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def data(self) -> TContent:
|
|
478
|
+
"""Get content as typed data. Provides compat to AgentRunResult."""
|
|
479
|
+
return self.content
|
|
480
|
+
|
|
481
|
+
def get_tool_calls(
|
|
482
|
+
self,
|
|
483
|
+
tools: dict[str, Any] | None = None,
|
|
484
|
+
agent_name: str | None = None,
|
|
485
|
+
) -> list[ToolCallInfo]:
|
|
486
|
+
"""Extract tool call information from all messages lazily.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
tools: Original Tool set to enrich ToolCallInfos with additional info
|
|
490
|
+
agent_name: Name of the caller
|
|
491
|
+
"""
|
|
492
|
+
from uuid import uuid4
|
|
493
|
+
|
|
494
|
+
from pydantic_ai import ToolReturnPart
|
|
495
|
+
|
|
496
|
+
from agentpool.tools import ToolCallInfo
|
|
497
|
+
|
|
498
|
+
tools = tools or {}
|
|
499
|
+
parts = [part for message in self.messages for part in message.parts]
|
|
500
|
+
call_parts = {
|
|
501
|
+
part.tool_call_id: part
|
|
502
|
+
for part in parts
|
|
503
|
+
if isinstance(part, ToolCallPart) and part.tool_call_id
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
tool_calls = []
|
|
507
|
+
for part in parts:
|
|
508
|
+
if isinstance(part, ToolReturnPart) and part.tool_call_id in call_parts:
|
|
509
|
+
call_part = call_parts[part.tool_call_id]
|
|
510
|
+
tool_info = ToolCallInfo(
|
|
511
|
+
tool_name=call_part.tool_name,
|
|
512
|
+
args=call_part.args_as_dict(),
|
|
513
|
+
agent_name=agent_name or "UNSET",
|
|
514
|
+
result=part.content,
|
|
515
|
+
tool_call_id=call_part.tool_call_id or str(uuid4()),
|
|
516
|
+
timestamp=part.timestamp,
|
|
517
|
+
agent_tool_name=(t.agent_name if (t := tools.get(part.tool_name)) else None),
|
|
518
|
+
)
|
|
519
|
+
tool_calls.append(tool_info)
|
|
520
|
+
|
|
521
|
+
return tool_calls
|
|
522
|
+
|
|
523
|
+
def format(
|
|
524
|
+
self,
|
|
525
|
+
style: FormatStyle = "simple",
|
|
526
|
+
*,
|
|
527
|
+
template: str | None = None,
|
|
528
|
+
variables: dict[str, Any] | None = None,
|
|
529
|
+
show_metadata: bool = False,
|
|
530
|
+
show_costs: bool = False,
|
|
531
|
+
) -> str:
|
|
532
|
+
"""Format message with configurable style.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
style: Predefined style or "custom" for custom template
|
|
536
|
+
template: Custom Jinja template (required if style="custom")
|
|
537
|
+
variables: Additional variables for template rendering
|
|
538
|
+
show_metadata: Whether to include metadata
|
|
539
|
+
show_costs: Whether to include cost information
|
|
540
|
+
|
|
541
|
+
Raises:
|
|
542
|
+
ValueError: If style is "custom" but no template provided
|
|
543
|
+
or if style is invalid
|
|
544
|
+
"""
|
|
545
|
+
from jinjarope import Environment
|
|
546
|
+
import yamling
|
|
547
|
+
|
|
548
|
+
env = Environment(trim_blocks=True, lstrip_blocks=True)
|
|
549
|
+
env.filters["to_yaml"] = yamling.dump_yaml
|
|
550
|
+
|
|
551
|
+
match style:
|
|
552
|
+
case "custom":
|
|
553
|
+
if not template:
|
|
554
|
+
msg = "Custom style requires a template"
|
|
555
|
+
raise ValueError(msg)
|
|
556
|
+
template_str = template
|
|
557
|
+
case _ if style in MESSAGE_TEMPLATES:
|
|
558
|
+
template_str = MESSAGE_TEMPLATES[style]
|
|
559
|
+
case _:
|
|
560
|
+
msg = f"Invalid style: {style}"
|
|
561
|
+
raise ValueError(msg)
|
|
562
|
+
template_obj = env.from_string(template_str)
|
|
563
|
+
vars_ = {
|
|
564
|
+
**(self.__dict__),
|
|
565
|
+
"show_metadata": show_metadata,
|
|
566
|
+
"show_costs": show_costs,
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if variables:
|
|
570
|
+
vars_.update(variables)
|
|
571
|
+
|
|
572
|
+
return template_obj.render(**vars_)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
@dataclass
|
|
576
|
+
class AgentResponse[TResult = Any]:
|
|
577
|
+
"""Result from an agent's execution."""
|
|
578
|
+
|
|
579
|
+
agent_name: str
|
|
580
|
+
"""Name of the agent that produced this result"""
|
|
581
|
+
|
|
582
|
+
message: ChatMessage[TResult] | None
|
|
583
|
+
"""The actual message with content and metadata"""
|
|
584
|
+
|
|
585
|
+
timing: float | None = None
|
|
586
|
+
"""Time taken by this agent in seconds"""
|
|
587
|
+
|
|
588
|
+
error: str | None = None
|
|
589
|
+
"""Error message if agent failed"""
|
|
590
|
+
|
|
591
|
+
@property
|
|
592
|
+
def success(self) -> bool:
|
|
593
|
+
"""Whether the agent completed successfully."""
|
|
594
|
+
return self.error is None
|
|
595
|
+
|
|
596
|
+
@property
|
|
597
|
+
def response(self) -> TResult | None:
|
|
598
|
+
"""Convenient access to message content."""
|
|
599
|
+
return self.message.content if self.message else None
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
class TeamResponse[TMessageContent = Any](list[AgentResponse[Any]]):
|
|
603
|
+
"""Results from a team execution."""
|
|
604
|
+
|
|
605
|
+
def __init__(
|
|
606
|
+
self,
|
|
607
|
+
responses: list[AgentResponse[TMessageContent]],
|
|
608
|
+
start_time: datetime | None = None,
|
|
609
|
+
errors: dict[str, Exception] | None = None,
|
|
610
|
+
) -> None:
|
|
611
|
+
super().__init__(responses)
|
|
612
|
+
self.start_time = start_time or get_now()
|
|
613
|
+
self.end_time = get_now()
|
|
614
|
+
self.errors = errors or {}
|
|
615
|
+
|
|
616
|
+
@property
|
|
617
|
+
def duration(self) -> float:
|
|
618
|
+
"""Get execution duration in seconds."""
|
|
619
|
+
return (self.end_time - self.start_time).total_seconds()
|
|
620
|
+
|
|
621
|
+
@property
|
|
622
|
+
def success(self) -> bool:
|
|
623
|
+
"""Whether all agents completed successfully."""
|
|
624
|
+
return not bool(self.errors)
|
|
625
|
+
|
|
626
|
+
@property
|
|
627
|
+
def failed_agents(self) -> list[str]:
|
|
628
|
+
"""Names of agents that failed."""
|
|
629
|
+
return list(self.errors.keys())
|
|
630
|
+
|
|
631
|
+
def by_agent(self, name: str) -> AgentResponse[TMessageContent] | None:
|
|
632
|
+
"""Get response from specific agent."""
|
|
633
|
+
return next((r for r in self if r.agent_name == name), None)
|
|
634
|
+
|
|
635
|
+
def format_durations(self) -> str:
|
|
636
|
+
"""Format execution times."""
|
|
637
|
+
parts = [f"{r.agent_name}: {r.timing:.2f}s" for r in self if r.timing is not None]
|
|
638
|
+
return f"Individual times: {', '.join(parts)}\nTotal time: {self.duration:.2f}s"
|
|
639
|
+
|
|
640
|
+
# TODO: could keep TResultContent for len(messages) == 1
|
|
641
|
+
def to_chat_message(self) -> ChatMessage[str]:
|
|
642
|
+
"""Convert team response to a single chat message."""
|
|
643
|
+
# Combine all responses into one structured message
|
|
644
|
+
content = "\n\n".join(
|
|
645
|
+
f"[{response.agent_name}]: {response.message.content}"
|
|
646
|
+
for response in self
|
|
647
|
+
if response.message
|
|
648
|
+
)
|
|
649
|
+
meta = {
|
|
650
|
+
"type": "team_response",
|
|
651
|
+
"agents": [r.agent_name for r in self],
|
|
652
|
+
"duration": self.duration,
|
|
653
|
+
"success_count": len(self),
|
|
654
|
+
}
|
|
655
|
+
return ChatMessage(content=content, role="assistant", metadata=meta) # type: ignore
|