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,426 @@
|
|
|
1
|
+
"""Event manager for handling multiple event sources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from collections.abc import Awaitable, Callable
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from functools import wraps
|
|
10
|
+
import inspect
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
12
|
+
|
|
13
|
+
from evented.configs import EmailConfig, FileWatchConfig, TimeEventConfig, WebhookConfig
|
|
14
|
+
from evented.event_data import EventData, FunctionResultEventData
|
|
15
|
+
from psygnal import Signal
|
|
16
|
+
from pydantic import SecretStr
|
|
17
|
+
|
|
18
|
+
from agentpool.log import get_logger
|
|
19
|
+
from agentpool.utils.inspection import execute, get_fn_name
|
|
20
|
+
from agentpool.utils.now import get_now
|
|
21
|
+
from agentpool.utils.tasks import TaskManager
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from collections.abc import Coroutine, Sequence
|
|
26
|
+
from datetime import datetime, timedelta
|
|
27
|
+
from types import TracebackType
|
|
28
|
+
|
|
29
|
+
from evented.base import EventSource
|
|
30
|
+
from evented.configs import EventConfig
|
|
31
|
+
from evented.timed_watcher import TimeEventSource
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
logger = get_logger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
type EventCallback = Callable[[EventData], None | Awaitable[None]]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class EventManager:
|
|
41
|
+
"""Manages multiple event sources and their lifecycles."""
|
|
42
|
+
|
|
43
|
+
event_processed = Signal(EventData)
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
configs: list[EventConfig] | None = None,
|
|
48
|
+
event_callbacks: list[EventCallback] | None = None,
|
|
49
|
+
enable_events: bool = True,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Initialize event manager.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
configs: List of event configurations
|
|
55
|
+
event_callbacks: List of event callbacks
|
|
56
|
+
enable_events: Whether to enable event processing
|
|
57
|
+
"""
|
|
58
|
+
self.task_manager = TaskManager()
|
|
59
|
+
self.configs = configs or []
|
|
60
|
+
self.enabled = enable_events
|
|
61
|
+
self._sources: dict[str, EventSource] = {}
|
|
62
|
+
self._callbacks = event_callbacks or []
|
|
63
|
+
self._observers = defaultdict[str, list[EventObserver]](list)
|
|
64
|
+
|
|
65
|
+
def add_callback(self, callback: EventCallback) -> None:
|
|
66
|
+
"""Register an event callback."""
|
|
67
|
+
self._callbacks.append(callback)
|
|
68
|
+
|
|
69
|
+
def remove_callback(self, callback: EventCallback) -> None:
|
|
70
|
+
"""Remove a previously registered callback."""
|
|
71
|
+
self._callbacks.remove(callback)
|
|
72
|
+
|
|
73
|
+
async def emit_event(self, event: EventData) -> None:
|
|
74
|
+
"""Emit event to all callbacks and optionally handle via node."""
|
|
75
|
+
if not self.enabled:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
for callback in self._callbacks:
|
|
79
|
+
try:
|
|
80
|
+
result = callback(event)
|
|
81
|
+
if isinstance(result, Awaitable):
|
|
82
|
+
await result
|
|
83
|
+
except Exception:
|
|
84
|
+
logger.exception("Error in event callback", name=get_fn_name(callback))
|
|
85
|
+
|
|
86
|
+
self.event_processed.emit(event)
|
|
87
|
+
|
|
88
|
+
async def add_file_watch(
|
|
89
|
+
self,
|
|
90
|
+
paths: str | Sequence[str],
|
|
91
|
+
*,
|
|
92
|
+
name: str | None = None,
|
|
93
|
+
extensions: list[str] | None = None,
|
|
94
|
+
ignore_paths: list[str] | None = None,
|
|
95
|
+
recursive: bool = True,
|
|
96
|
+
debounce: int = 1600,
|
|
97
|
+
) -> EventSource:
|
|
98
|
+
"""Add file system watch event source.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
paths: Paths or patterns to watch
|
|
102
|
+
name: Optional source name (default: generated from paths)
|
|
103
|
+
extensions: File extensions to monitor
|
|
104
|
+
ignore_paths: Paths to ignore
|
|
105
|
+
recursive: Whether to watch subdirectories
|
|
106
|
+
debounce: Minimum time between events (ms)
|
|
107
|
+
"""
|
|
108
|
+
path_list = [paths] if isinstance(paths, str) else list(paths)
|
|
109
|
+
config = FileWatchConfig(
|
|
110
|
+
name=name or f"file_watch_{len(self._sources)}",
|
|
111
|
+
paths=path_list,
|
|
112
|
+
extensions=extensions,
|
|
113
|
+
ignore_paths=ignore_paths,
|
|
114
|
+
recursive=recursive,
|
|
115
|
+
debounce=debounce,
|
|
116
|
+
)
|
|
117
|
+
return await self.add_source(config)
|
|
118
|
+
|
|
119
|
+
async def add_webhook(
|
|
120
|
+
self,
|
|
121
|
+
path: str,
|
|
122
|
+
*,
|
|
123
|
+
name: str | None = None,
|
|
124
|
+
port: int = 8000,
|
|
125
|
+
secret: str | None = None,
|
|
126
|
+
) -> EventSource:
|
|
127
|
+
"""Add webhook event source.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
path: URL path to listen on
|
|
131
|
+
name: Optional source name
|
|
132
|
+
port: Port to listen on
|
|
133
|
+
secret: Optional secret for request validation
|
|
134
|
+
"""
|
|
135
|
+
name = name or f"webhook_{len(self._sources)}"
|
|
136
|
+
sec = SecretStr(secret) if secret else None
|
|
137
|
+
config = WebhookConfig(name=name, path=path, port=port, secret=sec)
|
|
138
|
+
return await self.add_source(config)
|
|
139
|
+
|
|
140
|
+
async def add_timed_event(
|
|
141
|
+
self,
|
|
142
|
+
schedule: str,
|
|
143
|
+
prompt: str,
|
|
144
|
+
*,
|
|
145
|
+
name: str | None = None,
|
|
146
|
+
timezone: str | None = None,
|
|
147
|
+
skip_missed: bool = False,
|
|
148
|
+
) -> TimeEventSource:
|
|
149
|
+
"""Add time-based event source.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
schedule: Cron expression (e.g. "0 9 * * 1-5" for weekdays at 9am)
|
|
153
|
+
prompt: Prompt to send when triggered
|
|
154
|
+
name: Optional source name
|
|
155
|
+
timezone: Optional timezone (system default if None)
|
|
156
|
+
skip_missed: Whether to skip missed executions
|
|
157
|
+
"""
|
|
158
|
+
config = TimeEventConfig(
|
|
159
|
+
name=name or f"timed_{len(self._sources)}",
|
|
160
|
+
schedule=schedule,
|
|
161
|
+
prompt=prompt,
|
|
162
|
+
timezone=timezone,
|
|
163
|
+
skip_missed=skip_missed,
|
|
164
|
+
)
|
|
165
|
+
return await self.add_source(config) # type: ignore
|
|
166
|
+
|
|
167
|
+
async def add_email_watch(
|
|
168
|
+
self,
|
|
169
|
+
host: str,
|
|
170
|
+
username: str,
|
|
171
|
+
password: str,
|
|
172
|
+
*,
|
|
173
|
+
name: str | None = None,
|
|
174
|
+
port: int = 993,
|
|
175
|
+
folder: str = "INBOX",
|
|
176
|
+
ssl: bool = True,
|
|
177
|
+
check_interval: int = 60,
|
|
178
|
+
mark_seen: bool = True,
|
|
179
|
+
filters: dict[str, str] | None = None,
|
|
180
|
+
max_size: int | None = None,
|
|
181
|
+
) -> EventSource:
|
|
182
|
+
"""Add email monitoring event source.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
host: IMAP server hostname
|
|
186
|
+
username: Email account username
|
|
187
|
+
password: Account password or app password
|
|
188
|
+
name: Optional source name
|
|
189
|
+
port: Server port (default: 993 for IMAP SSL)
|
|
190
|
+
folder: Mailbox to monitor
|
|
191
|
+
ssl: Whether to use SSL/TLS
|
|
192
|
+
check_interval: Seconds between checks
|
|
193
|
+
mark_seen: Whether to mark processed emails as seen
|
|
194
|
+
filters: Optional email filtering criteria
|
|
195
|
+
max_size: Maximum email size in bytes
|
|
196
|
+
"""
|
|
197
|
+
config = EmailConfig(
|
|
198
|
+
name=name or f"email_{len(self._sources)}",
|
|
199
|
+
host=host,
|
|
200
|
+
username=username,
|
|
201
|
+
password=SecretStr(password),
|
|
202
|
+
port=port,
|
|
203
|
+
folder=folder,
|
|
204
|
+
ssl=ssl,
|
|
205
|
+
check_interval=check_interval,
|
|
206
|
+
mark_seen=mark_seen,
|
|
207
|
+
filters=filters or {},
|
|
208
|
+
max_size=max_size,
|
|
209
|
+
)
|
|
210
|
+
return await self.add_source(config)
|
|
211
|
+
|
|
212
|
+
async def add_source(self, config: EventConfig) -> EventSource:
|
|
213
|
+
"""Add and start a new event source.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
config: Event source configuration
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
ValueError: If source already exists or is invalid
|
|
220
|
+
"""
|
|
221
|
+
logger.debug("Setting up event source", name=config.name, type=config.type)
|
|
222
|
+
from evented.base import EventSource
|
|
223
|
+
|
|
224
|
+
if config.name in self._sources:
|
|
225
|
+
msg = f"Event source already exists: {config.name}"
|
|
226
|
+
raise ValueError(msg)
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
source = EventSource.from_config(config)
|
|
230
|
+
await source.__aenter__()
|
|
231
|
+
self._sources[config.name] = source
|
|
232
|
+
# Start processing events
|
|
233
|
+
name = f"event_processor_{config.name}"
|
|
234
|
+
self.task_manager.create_task(self._process_events(source), name=name)
|
|
235
|
+
logger.debug("Added event source", name=config.name)
|
|
236
|
+
except Exception as e:
|
|
237
|
+
msg = "Failed to add event source"
|
|
238
|
+
logger.exception(msg, name=config.name)
|
|
239
|
+
raise RuntimeError(msg) from e
|
|
240
|
+
else:
|
|
241
|
+
return source
|
|
242
|
+
|
|
243
|
+
async def remove_source(self, name: str) -> None:
|
|
244
|
+
"""Stop and remove an event source.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
name: Name of source to remove
|
|
248
|
+
"""
|
|
249
|
+
if source := self._sources.pop(name, None):
|
|
250
|
+
await source.__aexit__(None, None, None)
|
|
251
|
+
logger.debug("Removed event source", name=name)
|
|
252
|
+
|
|
253
|
+
async def _process_events(self, source: EventSource) -> None:
|
|
254
|
+
"""Process events from a source.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
source: Event source to process
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
# Get the async iterator from the coroutine
|
|
261
|
+
async for event in source.events():
|
|
262
|
+
if not self.enabled:
|
|
263
|
+
logger.debug("Event processing disabled, skipping event")
|
|
264
|
+
continue
|
|
265
|
+
await self.emit_event(event)
|
|
266
|
+
|
|
267
|
+
except asyncio.CancelledError:
|
|
268
|
+
logger.debug("Event processing cancelled")
|
|
269
|
+
raise
|
|
270
|
+
|
|
271
|
+
except Exception:
|
|
272
|
+
logger.exception("Error processing events")
|
|
273
|
+
|
|
274
|
+
async def __aenter__(self) -> Self:
|
|
275
|
+
"""Allow using manager as async context manager."""
|
|
276
|
+
if not self.enabled:
|
|
277
|
+
return self
|
|
278
|
+
for trigger in self.configs:
|
|
279
|
+
await self.add_source(trigger)
|
|
280
|
+
return self
|
|
281
|
+
|
|
282
|
+
async def __aexit__(
|
|
283
|
+
self,
|
|
284
|
+
exc_type: type[BaseException] | None,
|
|
285
|
+
exc_val: BaseException | None,
|
|
286
|
+
exc_tb: TracebackType | None,
|
|
287
|
+
) -> None:
|
|
288
|
+
"""Clean up when exiting context."""
|
|
289
|
+
self.enabled = False
|
|
290
|
+
|
|
291
|
+
for name in list(self._sources):
|
|
292
|
+
await self.remove_source(name)
|
|
293
|
+
|
|
294
|
+
def track[T](
|
|
295
|
+
self,
|
|
296
|
+
event_name: str | None = None,
|
|
297
|
+
**event_metadata: Any,
|
|
298
|
+
) -> (
|
|
299
|
+
Callable[[Callable[..., Coroutine[Any, Any, T]]], Callable[..., Coroutine[Any, Any, T]]]
|
|
300
|
+
| Callable[[Callable[..., T]], Callable[..., T]]
|
|
301
|
+
):
|
|
302
|
+
"""Track function calls as events.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
event_name: Optional name for the event (defaults to function name)
|
|
306
|
+
**event_metadata: Additional metadata to include with event
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
@event_manager.track("user_search")
|
|
310
|
+
async def search_docs(query: str) -> list[Doc]:
|
|
311
|
+
results = await search(query)
|
|
312
|
+
return results # This result becomes event data
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
316
|
+
|
|
317
|
+
name = event_name or get_fn_name(func)
|
|
318
|
+
|
|
319
|
+
@wraps(func)
|
|
320
|
+
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
321
|
+
start_time = get_now()
|
|
322
|
+
meta = {"args": args, "kwargs": kwargs, **event_metadata}
|
|
323
|
+
try:
|
|
324
|
+
result = await func(*args, **kwargs)
|
|
325
|
+
if self.enabled:
|
|
326
|
+
meta |= {"status": "success", "duration": get_now() - start_time}
|
|
327
|
+
event = EventData.create(name, content=result, metadata=meta)
|
|
328
|
+
await self.emit_event(event)
|
|
329
|
+
except Exception as e:
|
|
330
|
+
if self.enabled:
|
|
331
|
+
dur = get_now() - start_time
|
|
332
|
+
meta |= {"status": "error", "error": str(e), "duration": dur}
|
|
333
|
+
event = EventData.create(name, content=str(e), metadata=meta)
|
|
334
|
+
await self.emit_event(event)
|
|
335
|
+
raise
|
|
336
|
+
else:
|
|
337
|
+
return result
|
|
338
|
+
|
|
339
|
+
@wraps(func)
|
|
340
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
341
|
+
start_time = get_now()
|
|
342
|
+
meta = {"args": args, "kwargs": kwargs, **event_metadata}
|
|
343
|
+
try:
|
|
344
|
+
result = func(*args, **kwargs)
|
|
345
|
+
if self.enabled:
|
|
346
|
+
meta |= {"status": "success", "duration": get_now() - start_time}
|
|
347
|
+
event = EventData.create(name, content=result, metadata=meta)
|
|
348
|
+
self.task_manager.run_background(self.emit_event(event))
|
|
349
|
+
except Exception as e:
|
|
350
|
+
if self.enabled:
|
|
351
|
+
dur = get_now() - start_time
|
|
352
|
+
meta |= {"status": "error", "error": str(e), "duration": dur}
|
|
353
|
+
event = EventData.create(name, content=str(e), metadata=meta)
|
|
354
|
+
self.task_manager.run_background(self.emit_event(event))
|
|
355
|
+
raise
|
|
356
|
+
else:
|
|
357
|
+
return result
|
|
358
|
+
|
|
359
|
+
return async_wrapper if inspect.iscoroutinefunction(func) else sync_wrapper
|
|
360
|
+
|
|
361
|
+
return decorator
|
|
362
|
+
|
|
363
|
+
def poll[T](
|
|
364
|
+
self,
|
|
365
|
+
event_type: str,
|
|
366
|
+
interval: timedelta | None = None,
|
|
367
|
+
) -> (
|
|
368
|
+
Callable[[Callable[..., Coroutine[Any, Any, T]]], Callable[..., Coroutine[Any, Any, T]]]
|
|
369
|
+
| Callable[[Callable[..., T]], Callable[..., T]]
|
|
370
|
+
):
|
|
371
|
+
"""Decorator to register an event observer.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
event_type: Type of event to observe
|
|
375
|
+
interval: Optional polling interval for periodic checks
|
|
376
|
+
|
|
377
|
+
Example:
|
|
378
|
+
@event_manager.observe("file_changed")
|
|
379
|
+
async def handle_file_change(event: FileEventData):
|
|
380
|
+
await process_file(event.path)
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
384
|
+
observer = EventObserver(func, interval=interval)
|
|
385
|
+
self._observers[event_type].append(observer)
|
|
386
|
+
|
|
387
|
+
@wraps(func)
|
|
388
|
+
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
389
|
+
result = await execute(func, *args, **kwargs)
|
|
390
|
+
# Convert result to event and emit
|
|
391
|
+
if self.enabled:
|
|
392
|
+
typ = type(result).__name__
|
|
393
|
+
meta = {"type": "function_result", "output_type": typ}
|
|
394
|
+
event = FunctionResultEventData(result=result, source=event_type, metadata=meta)
|
|
395
|
+
await self.emit_event(event)
|
|
396
|
+
return result
|
|
397
|
+
|
|
398
|
+
return wrapper
|
|
399
|
+
|
|
400
|
+
return decorator
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
@dataclass
|
|
404
|
+
class EventObserver:
|
|
405
|
+
"""Registered event observer."""
|
|
406
|
+
|
|
407
|
+
callback: Callable[..., Any]
|
|
408
|
+
interval: timedelta | None = None
|
|
409
|
+
last_run: datetime | None = None
|
|
410
|
+
|
|
411
|
+
async def __call__(self, event: EventData) -> None:
|
|
412
|
+
"""Handle an event."""
|
|
413
|
+
try:
|
|
414
|
+
await execute(self.callback, event)
|
|
415
|
+
except Exception:
|
|
416
|
+
logger.exception("Error in event observer")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
if __name__ == "__main__":
|
|
420
|
+
from evented.event_data import EventData
|
|
421
|
+
|
|
422
|
+
async def dummy_callback(event: EventData) -> None:
|
|
423
|
+
if prompt := event.to_prompt():
|
|
424
|
+
print(f"Received: {prompt}")
|
|
425
|
+
|
|
426
|
+
event_manager = EventManager(event_callbacks=[dummy_callback])
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Event sources for AgentPool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
|
6
|
+
|
|
7
|
+
from evented.event_data import EventData
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from agentpool.messaging import ChatMessage
|
|
12
|
+
from agentpool.talk.talk import Talk
|
|
13
|
+
from agentpool_config.events import ConnectionEventType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
ChangeType = Literal["added", "modified", "deleted"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConnectionEventData[TTransmittedData](EventData):
|
|
20
|
+
"""Event from connection activity."""
|
|
21
|
+
|
|
22
|
+
connection_name: str
|
|
23
|
+
"""Name of the connection which fired an event."""
|
|
24
|
+
|
|
25
|
+
connection: Talk[TTransmittedData]
|
|
26
|
+
"""The connection which fired the event."""
|
|
27
|
+
|
|
28
|
+
event_type: ConnectionEventType
|
|
29
|
+
"""Type of event that occurred."""
|
|
30
|
+
|
|
31
|
+
message: ChatMessage[TTransmittedData] | None = None
|
|
32
|
+
"""The message at the stage of the event."""
|
|
33
|
+
|
|
34
|
+
def to_prompt(self) -> str:
|
|
35
|
+
"""Convert event to agent prompt."""
|
|
36
|
+
base = f"Connection {self.connection_name!r} event: {self.event_type}"
|
|
37
|
+
if self.message:
|
|
38
|
+
return f"{base}\nMessage content: {self.message.content}"
|
|
39
|
+
return base
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Message container with statistics and formatting capabilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import itertools
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
7
|
+
|
|
8
|
+
from psygnal.containers import EventedList
|
|
9
|
+
|
|
10
|
+
from agentpool.log import get_logger
|
|
11
|
+
from agentpool.messaging import ChatMessage
|
|
12
|
+
from agentpool.utils.count_tokens import batch_count_tokens, count_tokens
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
|
|
18
|
+
from agentpool.common_types import MessageRole
|
|
19
|
+
from agentpool.messaging.messages import FormatStyle
|
|
20
|
+
from agentpool.utils.dag import DAGNode
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
DEFAULT_TOKEN_MODEL = "gpt-4.1"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ChatMessageList(EventedList[ChatMessage[Any]]):
|
|
28
|
+
"""Container for tracking and managing chat messages.
|
|
29
|
+
|
|
30
|
+
Extends EventedList to provide:
|
|
31
|
+
- Message statistics (tokens, costs)
|
|
32
|
+
- History formatting
|
|
33
|
+
- Token-aware context window management
|
|
34
|
+
- Role-based filtering
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def get_message_tokens(self, message: ChatMessage[Any]) -> int:
|
|
38
|
+
"""Get token count for a single message.
|
|
39
|
+
|
|
40
|
+
Uses cost_info if available, falls back to tiktoken estimation.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
message: Message to count tokens for
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Token count for the message
|
|
47
|
+
"""
|
|
48
|
+
if message.cost_info:
|
|
49
|
+
return message.cost_info.token_usage.total_tokens
|
|
50
|
+
return count_tokens(str(message.usage.total_tokens), message.model_name)
|
|
51
|
+
|
|
52
|
+
def get_history_tokens(self, fallback_model: str | None = None) -> int:
|
|
53
|
+
"""Get total token count for all messages.
|
|
54
|
+
|
|
55
|
+
Uses cost_info when available, falls back to tiktoken estimation
|
|
56
|
+
for messages without usage information.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Total token count across all messages
|
|
60
|
+
"""
|
|
61
|
+
# Use cost_info if available
|
|
62
|
+
total = sum(m.cost_info.token_usage.total_tokens for m in self if m.cost_info)
|
|
63
|
+
# For messages without cost_info, estimate using tiktoken
|
|
64
|
+
if msgs := [msg for msg in self if not msg.cost_info]:
|
|
65
|
+
if fallback_model:
|
|
66
|
+
model_name = fallback_model
|
|
67
|
+
else:
|
|
68
|
+
model_name = next((m.model_name for m in self if m.model_name), DEFAULT_TOKEN_MODEL)
|
|
69
|
+
contents = [str(msg.content) for msg in msgs]
|
|
70
|
+
total += sum(batch_count_tokens(contents, model_name))
|
|
71
|
+
|
|
72
|
+
return total
|
|
73
|
+
|
|
74
|
+
def get_total_cost(self) -> float:
|
|
75
|
+
"""Calculate total cost in USD across all messages.
|
|
76
|
+
|
|
77
|
+
Only includes messages with cost information.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Total cost in USD
|
|
81
|
+
"""
|
|
82
|
+
return sum(float(msg.cost_info.total_cost) for msg in self if msg.cost_info)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def last_message(self) -> ChatMessage[Any] | None:
|
|
86
|
+
"""Get most recent message or None if empty."""
|
|
87
|
+
return self[-1] if self else None
|
|
88
|
+
|
|
89
|
+
def format(self, *, style: FormatStyle = "simple", **kwargs: Any) -> str:
|
|
90
|
+
"""Format conversation history with configurable style.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
style: Formatting style to use
|
|
94
|
+
**kwargs: Additional formatting options passed to message.format()
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Formatted conversation history as string
|
|
98
|
+
"""
|
|
99
|
+
return "\n".join(msg.format(style=style, **kwargs) for msg in self)
|
|
100
|
+
|
|
101
|
+
def filter_by_role(
|
|
102
|
+
self,
|
|
103
|
+
role: MessageRole,
|
|
104
|
+
*,
|
|
105
|
+
max_messages: int | None = None,
|
|
106
|
+
) -> list[ChatMessage[Any]]:
|
|
107
|
+
"""Get messages with specific role.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
role: Role to filter by (user/assistant/system)
|
|
111
|
+
max_messages: Optional limit on number of messages to return
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List of messages with matching role
|
|
115
|
+
"""
|
|
116
|
+
messages = [msg for msg in self if msg.role == role]
|
|
117
|
+
return messages[-max_messages:] if max_messages else messages
|
|
118
|
+
|
|
119
|
+
def get_between(
|
|
120
|
+
self,
|
|
121
|
+
*,
|
|
122
|
+
start_time: datetime | None = None,
|
|
123
|
+
end_time: datetime | None = None,
|
|
124
|
+
) -> list[ChatMessage[Any]]:
|
|
125
|
+
"""Get messages within a time range.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
start_time: Optional start of range
|
|
129
|
+
end_time: Optional end of range
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
List of messages within the time range
|
|
133
|
+
"""
|
|
134
|
+
messages = list(self)
|
|
135
|
+
if start_time:
|
|
136
|
+
messages = [msg for msg in messages if msg.timestamp >= start_time]
|
|
137
|
+
if end_time:
|
|
138
|
+
messages = [msg for msg in messages if msg.timestamp <= end_time]
|
|
139
|
+
return messages
|
|
140
|
+
|
|
141
|
+
def _build_flow_dag(self, message: ChatMessage[Any]) -> DAGNode | None:
|
|
142
|
+
"""Build DAG from message flow.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
message: Message to build flow DAG for
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Root DAGNode of the graph
|
|
149
|
+
"""
|
|
150
|
+
from agentpool.utils.dag import DAGNode
|
|
151
|
+
|
|
152
|
+
# Get messages from this conversation
|
|
153
|
+
conv_messages = [m for m in self if m.conversation_id == message.conversation_id]
|
|
154
|
+
nodes: dict[str, DAGNode] = {}
|
|
155
|
+
for msg in conv_messages: # First create all nodes
|
|
156
|
+
if msg.forwarded_from:
|
|
157
|
+
chain = [*msg.forwarded_from, msg.name or "unknown"]
|
|
158
|
+
for name in chain:
|
|
159
|
+
if name not in nodes:
|
|
160
|
+
nodes[name] = DAGNode(name)
|
|
161
|
+
|
|
162
|
+
# Then set up parent relationships
|
|
163
|
+
for msg in conv_messages:
|
|
164
|
+
if msg.forwarded_from:
|
|
165
|
+
chain = [*msg.forwarded_from, msg.name or "unknown"]
|
|
166
|
+
# Connect consecutive nodes
|
|
167
|
+
for parent_name, child_name in itertools.pairwise(chain):
|
|
168
|
+
parent = nodes[parent_name]
|
|
169
|
+
child = nodes[child_name]
|
|
170
|
+
if parent not in child.parents:
|
|
171
|
+
child.add_parent(parent)
|
|
172
|
+
|
|
173
|
+
# Find root nodes (those without parents)
|
|
174
|
+
roots = [node for node in nodes.values() if node.is_root]
|
|
175
|
+
if not roots:
|
|
176
|
+
return None
|
|
177
|
+
return roots[0] # Return first root for now
|
|
178
|
+
|
|
179
|
+
def to_mermaid_graph(
|
|
180
|
+
self,
|
|
181
|
+
message: ChatMessage[Any],
|
|
182
|
+
*,
|
|
183
|
+
title: str = "",
|
|
184
|
+
theme: str | None = None,
|
|
185
|
+
rankdir: Literal["TB", "BT", "LR", "RL"] = "LR",
|
|
186
|
+
) -> str:
|
|
187
|
+
"""Convert message flow to mermaid graph."""
|
|
188
|
+
from agentpool.utils.dag import dag_to_list
|
|
189
|
+
|
|
190
|
+
dag = self._build_flow_dag(message)
|
|
191
|
+
if not dag:
|
|
192
|
+
return ""
|
|
193
|
+
|
|
194
|
+
connections = dag_to_list(dag)
|
|
195
|
+
|
|
196
|
+
# Convert to mermaid
|
|
197
|
+
lines = ["```mermaid"]
|
|
198
|
+
if title:
|
|
199
|
+
lines.extend(["---", f"title: {title}", "---"])
|
|
200
|
+
if theme:
|
|
201
|
+
lines.append(f'%%{{ init: {{ "theme": "{theme}" }} }}%%')
|
|
202
|
+
lines.append(f"flowchart {rankdir}")
|
|
203
|
+
|
|
204
|
+
# Add connections
|
|
205
|
+
for parent, child in connections:
|
|
206
|
+
lines.append(f" {parent}-->{child}")
|
|
207
|
+
|
|
208
|
+
lines.append("```")
|
|
209
|
+
return "\n".join(lines)
|