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,44 @@
|
|
|
1
|
+
"""Utilities package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
import jinja2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def setup_env(env: jinja2.Environment) -> None:
|
|
13
|
+
"""Used as extension point for the jinjarope environment.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
env: The jinjarope environment to extend
|
|
17
|
+
"""
|
|
18
|
+
from agentpool.agents.agent import Agent
|
|
19
|
+
from agentpool.functional import (
|
|
20
|
+
run_agent,
|
|
21
|
+
run_agent_sync,
|
|
22
|
+
get_structured,
|
|
23
|
+
get_structured_multiple,
|
|
24
|
+
pick_one,
|
|
25
|
+
)
|
|
26
|
+
from agentpool.jinja_filters import (
|
|
27
|
+
pydantic_playground,
|
|
28
|
+
pydantic_playground_iframe,
|
|
29
|
+
pydantic_playground_link,
|
|
30
|
+
pydantic_playground_url,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
env.globals |= dict(agent=Agent)
|
|
34
|
+
env.filters |= {
|
|
35
|
+
"run_agent": run_agent,
|
|
36
|
+
"run_agent_sync": run_agent_sync,
|
|
37
|
+
"pick_one": pick_one,
|
|
38
|
+
"get_structured": get_structured,
|
|
39
|
+
"get_structured_multiple": get_structured_multiple,
|
|
40
|
+
"pydantic_playground": pydantic_playground,
|
|
41
|
+
"pydantic_playground_url": pydantic_playground_url,
|
|
42
|
+
"pydantic_playground_iframe": pydantic_playground_iframe,
|
|
43
|
+
"pydantic_playground_link": pydantic_playground_link,
|
|
44
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Base class for component registries."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from collections.abc import MutableMapping
|
|
7
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
8
|
+
|
|
9
|
+
from psygnal.containers import EventedDict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
14
|
+
|
|
15
|
+
from psygnal.containers import DictEvents
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
TKey = TypeVar("TKey", str, int)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentPoolError(Exception):
|
|
22
|
+
"""Base exception for all AgentPool errors."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BaseRegistry[TKey, TItem](MutableMapping[TKey, TItem], ABC):
|
|
26
|
+
"""Base class for registries providing item storage and change notifications.
|
|
27
|
+
|
|
28
|
+
This registry implements a dictionary-like interface backed by an EventedDict,
|
|
29
|
+
providing automatic event emission for all mutations (additions, removals,
|
|
30
|
+
modifications).
|
|
31
|
+
|
|
32
|
+
Features:
|
|
33
|
+
- Dictionary-like access (registry[key] = item)
|
|
34
|
+
- Event emission for all changes
|
|
35
|
+
- Item validation
|
|
36
|
+
- Type safety
|
|
37
|
+
- Customizable error handling
|
|
38
|
+
|
|
39
|
+
Available events (accessed via .events):
|
|
40
|
+
- adding(key, value): Before an item is added
|
|
41
|
+
- added(key, value): After an item is added
|
|
42
|
+
- removing(key, value): Before an item is removed
|
|
43
|
+
- removed(key, value): After an item is removed
|
|
44
|
+
- changing(key, value): Before an item is modified
|
|
45
|
+
- changed(key, value): After an item is modified
|
|
46
|
+
|
|
47
|
+
To implement, override:
|
|
48
|
+
- _validate_item: Custom validation/transformation of items
|
|
49
|
+
- _error_class: Custom error type for exceptions
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self) -> None:
|
|
53
|
+
"""Initialize an empty registry."""
|
|
54
|
+
self._items = EventedDict[TKey, TItem]()
|
|
55
|
+
self._initialized = False
|
|
56
|
+
self._configs: dict[TKey, Any] = {}
|
|
57
|
+
|
|
58
|
+
def __repr__(self) -> str:
|
|
59
|
+
return f"{self.__class__.__name__}({self._items})"
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def is_empty(self) -> bool:
|
|
63
|
+
"""Check if registry has any items."""
|
|
64
|
+
return not bool(self._items)
|
|
65
|
+
|
|
66
|
+
def has_item(self, key: TKey) -> bool:
|
|
67
|
+
"""Check if an item is registered."""
|
|
68
|
+
return key in self._items
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def events(self) -> DictEvents:
|
|
72
|
+
"""Access to all dictionary events."""
|
|
73
|
+
return self._items.events
|
|
74
|
+
|
|
75
|
+
def register(self, key: TKey, item: TItem | Any, replace: bool = False) -> None:
|
|
76
|
+
"""Register an item."""
|
|
77
|
+
if key in self._items and not replace:
|
|
78
|
+
msg = f"Item already registered: {key}"
|
|
79
|
+
raise self._error_class(msg)
|
|
80
|
+
|
|
81
|
+
validated_item = self._validate_item(item)
|
|
82
|
+
self._items[key] = validated_item
|
|
83
|
+
|
|
84
|
+
def get(self, key: TKey) -> TItem: # type: ignore
|
|
85
|
+
"""Get an item by key."""
|
|
86
|
+
return self[key]
|
|
87
|
+
|
|
88
|
+
def list_items(self) -> Sequence[TKey]:
|
|
89
|
+
"""List all registered item keys."""
|
|
90
|
+
return list(self._items.keys())
|
|
91
|
+
|
|
92
|
+
def reset(self) -> None:
|
|
93
|
+
"""Reset registry to initial state."""
|
|
94
|
+
self._items.clear()
|
|
95
|
+
self._configs.clear()
|
|
96
|
+
self._initialized = False
|
|
97
|
+
|
|
98
|
+
async def startup(self) -> None:
|
|
99
|
+
"""Initialize all registered items."""
|
|
100
|
+
if self._initialized:
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
for item in self._items.values():
|
|
105
|
+
await self._initialize_item(item)
|
|
106
|
+
self._initialized = True
|
|
107
|
+
except Exception as exc:
|
|
108
|
+
await self.shutdown()
|
|
109
|
+
msg = f"Registry startup failed: {exc}"
|
|
110
|
+
raise self._error_class(msg) from exc
|
|
111
|
+
|
|
112
|
+
async def shutdown(self) -> None:
|
|
113
|
+
"""Cleanup all registered items."""
|
|
114
|
+
if not self._initialized:
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
errors: list[tuple[TKey, Exception]] = []
|
|
118
|
+
|
|
119
|
+
for key, item in self._items.items():
|
|
120
|
+
try:
|
|
121
|
+
await self._cleanup_item(item)
|
|
122
|
+
except Exception as exc: # noqa: BLE001
|
|
123
|
+
errors.append((key, exc))
|
|
124
|
+
|
|
125
|
+
self._initialized = False
|
|
126
|
+
|
|
127
|
+
if errors:
|
|
128
|
+
error_msgs = [f"{key}: {exc}" for key, exc in errors]
|
|
129
|
+
msg = f"Errors during shutdown: {', '.join(error_msgs)}"
|
|
130
|
+
raise self._error_class(msg)
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def _error_class(self) -> type[AgentPoolError]:
|
|
134
|
+
"""Error class to use for this registry."""
|
|
135
|
+
return AgentPoolError
|
|
136
|
+
|
|
137
|
+
@abstractmethod
|
|
138
|
+
def _validate_item(self, item: Any) -> TItem:
|
|
139
|
+
"""Validate and possibly transform item before registration."""
|
|
140
|
+
|
|
141
|
+
async def _initialize_item(self, item: TItem) -> None:
|
|
142
|
+
"""Initialize an item during startup."""
|
|
143
|
+
if hasattr(item, "startup") and callable(item.startup): # pyright: ignore
|
|
144
|
+
await item.startup() # pyright: ignore # ty: ignore
|
|
145
|
+
|
|
146
|
+
async def _cleanup_item(self, item: TItem) -> None:
|
|
147
|
+
"""Clean up an item during shutdown."""
|
|
148
|
+
if hasattr(item, "shutdown") and callable(item.shutdown): # pyright: ignore
|
|
149
|
+
await item.shutdown() # pyright: ignore # ty: ignore
|
|
150
|
+
|
|
151
|
+
# Implementing MutableMapping methods
|
|
152
|
+
def __getitem__(self, key: TKey) -> TItem:
|
|
153
|
+
try:
|
|
154
|
+
return self._items[key]
|
|
155
|
+
except KeyError as exc:
|
|
156
|
+
msg = f"Item not found: {key}"
|
|
157
|
+
raise self._error_class(msg) from exc
|
|
158
|
+
|
|
159
|
+
def __setitem__(self, key: TKey, value: Any) -> None:
|
|
160
|
+
"""Support dict-style assignment."""
|
|
161
|
+
self.register(key, value)
|
|
162
|
+
|
|
163
|
+
def __contains__(self, key: object) -> bool:
|
|
164
|
+
"""Support 'in' operator without raising exceptions."""
|
|
165
|
+
return key in self._items
|
|
166
|
+
|
|
167
|
+
def __delitem__(self, key: TKey) -> None:
|
|
168
|
+
if key in self._items:
|
|
169
|
+
del self._items[key]
|
|
170
|
+
else:
|
|
171
|
+
msg = f"Item not found: {key}"
|
|
172
|
+
raise self._error_class(msg)
|
|
173
|
+
|
|
174
|
+
def __iter__(self) -> Iterator[TKey]:
|
|
175
|
+
return iter(self._items)
|
|
176
|
+
|
|
177
|
+
async def __aiter__(self) -> AsyncIterator[tuple[TKey, TItem]]:
|
|
178
|
+
"""Async iterate over items, ensuring they're initialized."""
|
|
179
|
+
if not self._initialized:
|
|
180
|
+
await self.startup()
|
|
181
|
+
for key, item in self._items.items():
|
|
182
|
+
yield key, item
|
|
183
|
+
|
|
184
|
+
def __len__(self) -> int:
|
|
185
|
+
return len(self._items)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Token counting utilities with fallback strategies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from importlib.util import find_spec
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
|
|
13
|
+
DEFAULT_TOKEN_MODEL = "gpt-3.5-turbo"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@lru_cache
|
|
17
|
+
def has_tiktoken() -> bool:
|
|
18
|
+
"""Check if tiktoken is available."""
|
|
19
|
+
return bool(find_spec("tiktoken"))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def count_tokens(text: str, model: str | None = None) -> int:
|
|
23
|
+
"""Count tokens in text with fallback strategy.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
text: Text to count tokens for
|
|
27
|
+
model: Optional model name for tiktoken (ignored in fallback)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Estimated token count
|
|
31
|
+
"""
|
|
32
|
+
if has_tiktoken():
|
|
33
|
+
import tiktoken
|
|
34
|
+
|
|
35
|
+
encoding = tiktoken.encoding_for_model(model or DEFAULT_TOKEN_MODEL)
|
|
36
|
+
return len(encoding.encode(text))
|
|
37
|
+
|
|
38
|
+
# Fallback: very rough approximation
|
|
39
|
+
# Strategies could be:
|
|
40
|
+
# 1. ~4 chars per token (quick but rough)
|
|
41
|
+
# 2. Word count * 1.3 (better for English)
|
|
42
|
+
# 3. Split on common token boundaries
|
|
43
|
+
return len(text.split()) + len(text) // 4
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def batch_count_tokens(texts: Sequence[str], model: str | None = None) -> list[int]:
|
|
47
|
+
"""Count tokens for multiple texts.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
texts: Sequence of texts to count
|
|
51
|
+
model: Optional model name for tiktoken
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
List of token counts
|
|
55
|
+
"""
|
|
56
|
+
if has_tiktoken():
|
|
57
|
+
import tiktoken
|
|
58
|
+
|
|
59
|
+
encoding = tiktoken.encoding_for_model(model or DEFAULT_TOKEN_MODEL)
|
|
60
|
+
return [len(encoding.encode(text)) for text in texts]
|
|
61
|
+
|
|
62
|
+
return [count_tokens(text) for text in texts]
|
agentpool/utils/dag.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Minimal DAG (Directed Acyclic Graph) implementation.
|
|
2
|
+
|
|
3
|
+
This module provides a lightweight DAG node class for tracking message flows
|
|
4
|
+
and generating mermaid diagrams. It replaces the bigtree dependency with
|
|
5
|
+
only the functionality actually used.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Iterable
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class DAGNode:
|
|
20
|
+
"""A node in a Directed Acyclic Graph.
|
|
21
|
+
|
|
22
|
+
Nodes can have multiple parents and multiple children, representing
|
|
23
|
+
a DAG structure suitable for tracking message flows between agents.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> a = DAGNode("a")
|
|
27
|
+
>>> b = DAGNode("b")
|
|
28
|
+
>>> c = DAGNode("c")
|
|
29
|
+
>>> c.add_parent(a)
|
|
30
|
+
>>> c.add_parent(b)
|
|
31
|
+
>>> a.children
|
|
32
|
+
[DAGNode(name='c')]
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
name: str
|
|
36
|
+
"""Name/identifier of this node."""
|
|
37
|
+
|
|
38
|
+
_parents: list[DAGNode] = field(default_factory=list, repr=False)
|
|
39
|
+
_children: list[DAGNode] = field(default_factory=list, repr=False)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def parents(self) -> list[DAGNode]:
|
|
43
|
+
"""Get parent nodes."""
|
|
44
|
+
return list(self._parents)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def children(self) -> list[DAGNode]:
|
|
48
|
+
"""Get child nodes."""
|
|
49
|
+
return list(self._children)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def is_root(self) -> bool:
|
|
53
|
+
"""Check if this node has no parents."""
|
|
54
|
+
return len(self._parents) == 0
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def is_leaf(self) -> bool:
|
|
58
|
+
"""Check if this node has no children."""
|
|
59
|
+
return len(self._children) == 0
|
|
60
|
+
|
|
61
|
+
def add_parent(self, parent: DAGNode) -> None:
|
|
62
|
+
"""Add a parent node, also adding self as child of parent.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
parent: Node to add as parent
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ValueError: If adding would create a cycle
|
|
69
|
+
"""
|
|
70
|
+
if parent is self:
|
|
71
|
+
msg = "Node cannot be its own parent"
|
|
72
|
+
raise ValueError(msg)
|
|
73
|
+
if parent in self._parents:
|
|
74
|
+
return # Already a parent
|
|
75
|
+
if self._is_ancestor_of(parent):
|
|
76
|
+
msg = "Adding this parent would create a cycle"
|
|
77
|
+
raise ValueError(msg)
|
|
78
|
+
|
|
79
|
+
self._parents.append(parent)
|
|
80
|
+
parent._children.append(self)
|
|
81
|
+
|
|
82
|
+
def add_child(self, child: DAGNode) -> None:
|
|
83
|
+
"""Add a child node, also adding self as parent of child.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
child: Node to add as child
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
ValueError: If adding would create a cycle
|
|
90
|
+
"""
|
|
91
|
+
child.add_parent(self)
|
|
92
|
+
|
|
93
|
+
def _is_ancestor_of(self, node: DAGNode) -> bool:
|
|
94
|
+
"""Check if self is an ancestor of the given node."""
|
|
95
|
+
visited: set[str] = set()
|
|
96
|
+
|
|
97
|
+
def _check(current: DAGNode) -> bool:
|
|
98
|
+
if current.name in visited:
|
|
99
|
+
return False
|
|
100
|
+
visited.add(current.name)
|
|
101
|
+
if current is self:
|
|
102
|
+
return True
|
|
103
|
+
return any(_check(p) for p in current._parents)
|
|
104
|
+
|
|
105
|
+
return _check(node)
|
|
106
|
+
|
|
107
|
+
def __rshift__(self, other: DAGNode) -> DAGNode:
|
|
108
|
+
"""Set child using >> operator: parent >> child."""
|
|
109
|
+
other.add_parent(self)
|
|
110
|
+
return other
|
|
111
|
+
|
|
112
|
+
def __lshift__(self, other: DAGNode) -> DAGNode:
|
|
113
|
+
"""Set parent using << operator: child << parent."""
|
|
114
|
+
self.add_parent(other)
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def dag_iterator(root: DAGNode) -> Iterable[tuple[DAGNode, DAGNode]]:
|
|
119
|
+
"""Iterate through all edges in a DAG starting from a node.
|
|
120
|
+
|
|
121
|
+
Traverses both upward (to parents) and downward (to children) to
|
|
122
|
+
discover all edges reachable from the starting node.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
root: Starting node for iteration
|
|
126
|
+
|
|
127
|
+
Yields:
|
|
128
|
+
Tuples of (parent, child) for each edge in the DAG
|
|
129
|
+
"""
|
|
130
|
+
visited_nodes: set[str] = set()
|
|
131
|
+
visited_edges: set[tuple[str, str]] = set()
|
|
132
|
+
|
|
133
|
+
def _iterate(node: DAGNode) -> Iterable[tuple[DAGNode, DAGNode]]:
|
|
134
|
+
node_name = node.name
|
|
135
|
+
if node_name in visited_nodes:
|
|
136
|
+
return
|
|
137
|
+
visited_nodes.add(node_name)
|
|
138
|
+
|
|
139
|
+
# Yield edges to parents (upward)
|
|
140
|
+
for parent in node._parents:
|
|
141
|
+
edge = (parent.name, node_name)
|
|
142
|
+
if edge not in visited_edges:
|
|
143
|
+
visited_edges.add(edge)
|
|
144
|
+
yield parent, node
|
|
145
|
+
|
|
146
|
+
# Yield edges to children (downward)
|
|
147
|
+
for child in node._children:
|
|
148
|
+
edge = (node_name, child.name)
|
|
149
|
+
if edge not in visited_edges:
|
|
150
|
+
visited_edges.add(edge)
|
|
151
|
+
yield node, child
|
|
152
|
+
|
|
153
|
+
# Recursively visit parents
|
|
154
|
+
for parent in node._parents:
|
|
155
|
+
yield from _iterate(parent)
|
|
156
|
+
|
|
157
|
+
# Recursively visit children
|
|
158
|
+
for child in node._children:
|
|
159
|
+
yield from _iterate(child)
|
|
160
|
+
|
|
161
|
+
yield from _iterate(root)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def dag_to_list(dag: DAGNode) -> list[tuple[str, str]]:
|
|
165
|
+
"""Export DAG edges as list of (parent_name, child_name) tuples.
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
>>> a = DAGNode("a")
|
|
169
|
+
>>> b = DAGNode("b")
|
|
170
|
+
>>> c = DAGNode("c")
|
|
171
|
+
>>> c.add_parent(a)
|
|
172
|
+
>>> c.add_parent(b)
|
|
173
|
+
>>> d = DAGNode("d")
|
|
174
|
+
>>> d.add_parent(c)
|
|
175
|
+
>>> sorted(dag_to_list(a))
|
|
176
|
+
[('a', 'c'), ('b', 'c'), ('c', 'd')]
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
dag: Any node in the DAG (will traverse to find all edges)
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
List of (parent_name, child_name) tuples for all edges
|
|
183
|
+
"""
|
|
184
|
+
return [(parent.name, child.name) for parent, child in dag_iterator(dag)]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""Utilities for importing callables and classes from dotted paths."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import inspect
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import pkgutil
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from collections.abc import Callable, Generator, Iterator
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_module_source(
|
|
18
|
+
import_path: str,
|
|
19
|
+
recursive: bool = False,
|
|
20
|
+
include_tests: bool = False,
|
|
21
|
+
) -> str:
|
|
22
|
+
"""Get source code from a module or package."""
|
|
23
|
+
try:
|
|
24
|
+
module = importlib.import_module(import_path)
|
|
25
|
+
sources = _get_sources(module, recursive=recursive, include_tests=include_tests)
|
|
26
|
+
return "\n\n# " + "-" * 40 + "\n\n".join(sources)
|
|
27
|
+
|
|
28
|
+
except ImportError as exc:
|
|
29
|
+
msg = f"Could not import module: {import_path}"
|
|
30
|
+
raise ValueError(msg) from exc
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_sources(
|
|
34
|
+
module: ModuleType,
|
|
35
|
+
recursive: bool,
|
|
36
|
+
include_tests: bool,
|
|
37
|
+
) -> Generator[str]:
|
|
38
|
+
"""Generate source code for a module and optionally its submodules."""
|
|
39
|
+
# Get the module's source code
|
|
40
|
+
if hasattr(module, "__file__") and module.__file__:
|
|
41
|
+
path = Path(module.__file__)
|
|
42
|
+
if _should_include_file(path, include_tests):
|
|
43
|
+
yield f"# File: {path}\n{inspect.getsource(module)}"
|
|
44
|
+
|
|
45
|
+
# If recursive and it's a package, get all submodules
|
|
46
|
+
if recursive and hasattr(module, "__path__"):
|
|
47
|
+
for _, name, _ in pkgutil.iter_modules(module.__path__):
|
|
48
|
+
submodule_path = f"{module.__name__}.{name}"
|
|
49
|
+
try:
|
|
50
|
+
submodule = importlib.import_module(submodule_path)
|
|
51
|
+
yield from _get_sources(submodule, recursive, include_tests)
|
|
52
|
+
except ImportError:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _should_include_file(path: Path, include_tests: bool) -> bool:
|
|
57
|
+
"""Check if a file should be included in the source."""
|
|
58
|
+
if not include_tests and any(p.startswith("test") for p in path.parts):
|
|
59
|
+
return False
|
|
60
|
+
return path.suffix == ".py"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def import_callable(path: str) -> Callable[..., Any]:
|
|
64
|
+
"""Import a callable from a dotted path.
|
|
65
|
+
|
|
66
|
+
Supports both dot and colon notation:
|
|
67
|
+
- Dot notation: module.submodule.Class.method
|
|
68
|
+
- Colon notation: module.submodule:Class.method
|
|
69
|
+
|
|
70
|
+
Examples:
|
|
71
|
+
>>> import_callable("os.path.join")
|
|
72
|
+
>>> import_callable("builtins.str.upper")
|
|
73
|
+
>>> import_callable("sqlalchemy.orm:Session.query")
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
path: Import path using dots and/or colon
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Imported callable
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
ValueError: If path cannot be imported or result isn't callable
|
|
83
|
+
"""
|
|
84
|
+
if not path:
|
|
85
|
+
msg = "Import path cannot be empty"
|
|
86
|
+
raise ValueError(msg)
|
|
87
|
+
|
|
88
|
+
# Normalize path - replace colon with dot if present
|
|
89
|
+
normalized_path = path.replace(":", ".")
|
|
90
|
+
parts = normalized_path.split(".")
|
|
91
|
+
|
|
92
|
+
# Try importing progressively smaller module paths
|
|
93
|
+
for i in range(len(parts), 0, -1):
|
|
94
|
+
try:
|
|
95
|
+
# Try current module path
|
|
96
|
+
module_path = ".".join(parts[:i])
|
|
97
|
+
module = importlib.import_module(module_path)
|
|
98
|
+
|
|
99
|
+
# Walk remaining parts as attributes
|
|
100
|
+
obj = module
|
|
101
|
+
for part in parts[i:]:
|
|
102
|
+
obj = getattr(obj, part)
|
|
103
|
+
|
|
104
|
+
# Check if we got a callable
|
|
105
|
+
if callable(obj):
|
|
106
|
+
return obj
|
|
107
|
+
|
|
108
|
+
msg = f"Found object at {path} but it isn't callable"
|
|
109
|
+
raise ValueError(msg)
|
|
110
|
+
|
|
111
|
+
except ImportError:
|
|
112
|
+
# Try next shorter path
|
|
113
|
+
continue
|
|
114
|
+
except AttributeError:
|
|
115
|
+
# Attribute not found - try next shorter path
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
# If we get here, no import combination worked
|
|
119
|
+
msg = f"Could not import callable from path: {path}"
|
|
120
|
+
raise ValueError(msg)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def import_class(path: str) -> type:
|
|
124
|
+
"""Import a class from a dotted path.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
path: Dot-separated path to the class
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
The imported class
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
ValueError: If path is invalid or doesn't point to a class
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
obj = import_callable(path)
|
|
137
|
+
if not isinstance(obj, type):
|
|
138
|
+
msg = f"{path} is not a class"
|
|
139
|
+
raise TypeError(msg) # noqa: TRY301
|
|
140
|
+
except Exception as exc:
|
|
141
|
+
msg = f"Failed to import class from {path}"
|
|
142
|
+
raise ValueError(msg) from exc
|
|
143
|
+
else:
|
|
144
|
+
return obj
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_pyobject_members(
|
|
148
|
+
obj: type | ModuleType | Any,
|
|
149
|
+
*,
|
|
150
|
+
include_imported: bool = False,
|
|
151
|
+
) -> Iterator[tuple[str, Callable[..., Any]]]:
|
|
152
|
+
"""Get callable members defined in a Python object.
|
|
153
|
+
|
|
154
|
+
Works with modules, classes, and instances. Only returns public callable
|
|
155
|
+
members (functions, methods, etc.) that are defined in the object's module
|
|
156
|
+
unless include_imported is True.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
obj: Any Python object to inspect (module, class, instance)
|
|
160
|
+
include_imported: Whether to include imported/inherited callables
|
|
161
|
+
|
|
162
|
+
Yields:
|
|
163
|
+
Tuples of (name, callable) for each public callable
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
>>> class MyClass:
|
|
167
|
+
... def method(self): pass
|
|
168
|
+
... def _private(self): pass
|
|
169
|
+
>>> for name, func in get_pyobject_members(MyClass()):
|
|
170
|
+
... print(name)
|
|
171
|
+
method
|
|
172
|
+
|
|
173
|
+
>>> import my_module
|
|
174
|
+
>>> for name, func in get_pyobject_members(my_module):
|
|
175
|
+
... print(name)
|
|
176
|
+
public_function
|
|
177
|
+
"""
|
|
178
|
+
# Get the module where the object is defined
|
|
179
|
+
defining_module = obj.__name__ if isinstance(obj, ModuleType) else obj.__module__
|
|
180
|
+
|
|
181
|
+
for name, member in inspect.getmembers(obj, inspect.isroutine):
|
|
182
|
+
if name.startswith("_"):
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
# Check if callable is defined in the object's module
|
|
186
|
+
if include_imported or getattr(member, "__module__", None) == defining_module:
|
|
187
|
+
yield name, member
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
# ATTENTION: Dont modify this script.
|
|
192
|
+
import sys
|
|
193
|
+
|
|
194
|
+
if len(sys.argv) != 2: # noqa: PLR2004
|
|
195
|
+
print("Usage: python importing.py <dot.path.to.object>", file=sys.stderr)
|
|
196
|
+
sys.exit(1)
|
|
197
|
+
|
|
198
|
+
dot_path = sys.argv[1]
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
obj = import_callable(dot_path)
|
|
202
|
+
source = inspect.getsource(obj)
|
|
203
|
+
print(source)
|
|
204
|
+
except Exception as e: # noqa: BLE001
|
|
205
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
206
|
+
sys.exit(1)
|