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,444 @@
|
|
|
1
|
+
"""SQLModel-based storage provider."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
7
|
+
|
|
8
|
+
from pydantic_ai import RunUsage
|
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
10
|
+
from sqlmodel import SQLModel, desc, select
|
|
11
|
+
|
|
12
|
+
from agentpool.log import get_logger
|
|
13
|
+
from agentpool.messaging import TokenCost
|
|
14
|
+
from agentpool.utils.now import get_now
|
|
15
|
+
from agentpool.utils.parse_time import parse_time_period
|
|
16
|
+
from agentpool_storage.base import StorageProvider
|
|
17
|
+
from agentpool_storage.models import QueryFilters
|
|
18
|
+
from agentpool_storage.sql_provider.models import CommandHistory, Conversation, Message
|
|
19
|
+
from agentpool_storage.sql_provider.utils import (
|
|
20
|
+
build_message_query,
|
|
21
|
+
format_conversation,
|
|
22
|
+
parse_model_info,
|
|
23
|
+
to_chat_message,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from collections.abc import Sequence
|
|
29
|
+
from datetime import datetime
|
|
30
|
+
from types import TracebackType
|
|
31
|
+
|
|
32
|
+
from sqlalchemy import Connection
|
|
33
|
+
|
|
34
|
+
from agentpool.common_types import JsonValue
|
|
35
|
+
from agentpool.messaging import ChatMessage
|
|
36
|
+
from agentpool_config.session import SessionQuery
|
|
37
|
+
from agentpool_config.storage import SQLStorageConfig
|
|
38
|
+
from agentpool_storage.models import ConversationData, StatsFilters
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
logger = get_logger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SQLModelProvider(StorageProvider):
|
|
45
|
+
"""Storage provider using SQLModel.
|
|
46
|
+
|
|
47
|
+
Can work with any database supported by SQLAlchemy/SQLModel.
|
|
48
|
+
Provides efficient SQL-based filtering and storage.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
can_load_history = True
|
|
52
|
+
|
|
53
|
+
def __init__(self, config: SQLStorageConfig) -> None:
|
|
54
|
+
"""Initialize provider with async database engine.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
config: Configuration for provider
|
|
58
|
+
"""
|
|
59
|
+
super().__init__(config)
|
|
60
|
+
self.engine = config.get_engine()
|
|
61
|
+
self.auto_migrate = config.auto_migration
|
|
62
|
+
self.session: AsyncSession | None = None
|
|
63
|
+
|
|
64
|
+
async def _init_database(self, auto_migrate: bool = True) -> None:
|
|
65
|
+
"""Initialize database tables and optionally migrate columns.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
auto_migrate: Whether to automatically add missing columns
|
|
69
|
+
"""
|
|
70
|
+
from sqlmodel import SQLModel
|
|
71
|
+
|
|
72
|
+
from agentpool_storage.sql_provider.utils import auto_migrate_columns
|
|
73
|
+
|
|
74
|
+
# Create tables if they don't exist
|
|
75
|
+
async with self.engine.begin() as conn:
|
|
76
|
+
await conn.run_sync(SQLModel.metadata.create_all)
|
|
77
|
+
|
|
78
|
+
# Optionally add missing columns
|
|
79
|
+
if auto_migrate:
|
|
80
|
+
async with self.engine.begin() as conn:
|
|
81
|
+
|
|
82
|
+
def sync_migrate(sync_conn: Connection) -> None:
|
|
83
|
+
auto_migrate_columns(sync_conn, self.engine.dialect)
|
|
84
|
+
|
|
85
|
+
await conn.run_sync(sync_migrate)
|
|
86
|
+
|
|
87
|
+
async def __aenter__(self) -> Self:
|
|
88
|
+
"""Initialize async database resources."""
|
|
89
|
+
await self._init_database(auto_migrate=self.auto_migrate)
|
|
90
|
+
await super().__aenter__()
|
|
91
|
+
return self
|
|
92
|
+
|
|
93
|
+
async def __aexit__(
|
|
94
|
+
self,
|
|
95
|
+
exc_type: type[BaseException] | None,
|
|
96
|
+
exc_val: BaseException | None,
|
|
97
|
+
exc_tb: TracebackType | None,
|
|
98
|
+
) -> None:
|
|
99
|
+
"""Clean up async database resources properly."""
|
|
100
|
+
await self.engine.dispose()
|
|
101
|
+
return await super().__aexit__(exc_type, exc_val, exc_tb)
|
|
102
|
+
|
|
103
|
+
def cleanup(self) -> None:
|
|
104
|
+
"""Clean up database resources."""
|
|
105
|
+
# For sync cleanup, just pass - proper cleanup happens in __aexit__
|
|
106
|
+
|
|
107
|
+
async def filter_messages(self, query: SessionQuery) -> list[ChatMessage[str]]:
|
|
108
|
+
"""Filter messages using SQL queries."""
|
|
109
|
+
async with AsyncSession(self.engine) as session:
|
|
110
|
+
stmt = build_message_query(query)
|
|
111
|
+
result = await session.execute(stmt)
|
|
112
|
+
messages = result.scalars().all()
|
|
113
|
+
return [to_chat_message(msg) for msg in messages]
|
|
114
|
+
|
|
115
|
+
async def log_message(
|
|
116
|
+
self,
|
|
117
|
+
*,
|
|
118
|
+
conversation_id: str,
|
|
119
|
+
message_id: str,
|
|
120
|
+
content: str,
|
|
121
|
+
role: str,
|
|
122
|
+
name: str | None = None,
|
|
123
|
+
cost_info: TokenCost | None = None,
|
|
124
|
+
model: str | None = None,
|
|
125
|
+
response_time: float | None = None,
|
|
126
|
+
forwarded_from: list[str] | None = None,
|
|
127
|
+
provider_name: str | None = None,
|
|
128
|
+
provider_response_id: str | None = None,
|
|
129
|
+
messages: str | None = None,
|
|
130
|
+
finish_reason: str | None = None,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Log message to database."""
|
|
133
|
+
from agentpool_storage.sql_provider.models import Message
|
|
134
|
+
|
|
135
|
+
provider, model_name = parse_model_info(model)
|
|
136
|
+
|
|
137
|
+
async with AsyncSession(self.engine) as session:
|
|
138
|
+
msg = Message(
|
|
139
|
+
conversation_id=conversation_id,
|
|
140
|
+
id=message_id,
|
|
141
|
+
content=content,
|
|
142
|
+
role=role,
|
|
143
|
+
name=name,
|
|
144
|
+
model=model,
|
|
145
|
+
model_provider=provider,
|
|
146
|
+
model_name=model_name,
|
|
147
|
+
response_time=response_time,
|
|
148
|
+
total_tokens=cost_info.token_usage.total_tokens if cost_info else None,
|
|
149
|
+
input_tokens=cost_info.token_usage.input_tokens if cost_info else None,
|
|
150
|
+
output_tokens=cost_info.token_usage.output_tokens if cost_info else None,
|
|
151
|
+
cost=float(cost_info.total_cost) if cost_info else None,
|
|
152
|
+
forwarded_from=forwarded_from,
|
|
153
|
+
provider_name=provider_name,
|
|
154
|
+
provider_response_id=provider_response_id,
|
|
155
|
+
messages=messages,
|
|
156
|
+
finish_reason=finish_reason,
|
|
157
|
+
timestamp=get_now(),
|
|
158
|
+
)
|
|
159
|
+
session.add(msg)
|
|
160
|
+
await session.commit()
|
|
161
|
+
|
|
162
|
+
async def log_conversation(
|
|
163
|
+
self,
|
|
164
|
+
*,
|
|
165
|
+
conversation_id: str,
|
|
166
|
+
node_name: str,
|
|
167
|
+
start_time: datetime | None = None,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Log conversation to database."""
|
|
170
|
+
from agentpool_storage.sql_provider.models import Conversation
|
|
171
|
+
|
|
172
|
+
async with AsyncSession(self.engine) as session:
|
|
173
|
+
now = start_time or get_now()
|
|
174
|
+
convo = Conversation(id=conversation_id, agent_name=node_name, start_time=now)
|
|
175
|
+
session.add(convo)
|
|
176
|
+
await session.commit()
|
|
177
|
+
|
|
178
|
+
async def update_conversation_title(
|
|
179
|
+
self,
|
|
180
|
+
conversation_id: str,
|
|
181
|
+
title: str,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Update the title of a conversation."""
|
|
184
|
+
async with AsyncSession(self.engine) as session:
|
|
185
|
+
result = await session.execute(
|
|
186
|
+
select(Conversation).where(Conversation.id == conversation_id)
|
|
187
|
+
)
|
|
188
|
+
conversation = result.scalar_one_or_none()
|
|
189
|
+
if conversation:
|
|
190
|
+
conversation.title = title
|
|
191
|
+
session.add(conversation)
|
|
192
|
+
await session.commit()
|
|
193
|
+
|
|
194
|
+
async def get_conversation_title(
|
|
195
|
+
self,
|
|
196
|
+
conversation_id: str,
|
|
197
|
+
) -> str | None:
|
|
198
|
+
"""Get the title of a conversation."""
|
|
199
|
+
async with AsyncSession(self.engine) as session:
|
|
200
|
+
result = await session.execute(
|
|
201
|
+
select(Conversation.title).where(Conversation.id == conversation_id)
|
|
202
|
+
)
|
|
203
|
+
return result.scalar_one_or_none()
|
|
204
|
+
|
|
205
|
+
async def log_command(
|
|
206
|
+
self,
|
|
207
|
+
*,
|
|
208
|
+
agent_name: str,
|
|
209
|
+
session_id: str,
|
|
210
|
+
command: str,
|
|
211
|
+
context_type: type | None = None,
|
|
212
|
+
metadata: dict[str, JsonValue] | None = None,
|
|
213
|
+
) -> None:
|
|
214
|
+
"""Log command to database."""
|
|
215
|
+
async with AsyncSession(self.engine) as session:
|
|
216
|
+
history = CommandHistory(
|
|
217
|
+
session_id=session_id,
|
|
218
|
+
agent_name=agent_name,
|
|
219
|
+
command=command,
|
|
220
|
+
context_type=context_type.__name__ if context_type else None,
|
|
221
|
+
context_metadata=metadata or {},
|
|
222
|
+
)
|
|
223
|
+
session.add(history)
|
|
224
|
+
await session.commit()
|
|
225
|
+
|
|
226
|
+
async def get_filtered_conversations(
|
|
227
|
+
self,
|
|
228
|
+
agent_name: str | None = None,
|
|
229
|
+
period: str | None = None,
|
|
230
|
+
since: datetime | None = None,
|
|
231
|
+
query: str | None = None,
|
|
232
|
+
model: str | None = None,
|
|
233
|
+
limit: int | None = None,
|
|
234
|
+
*,
|
|
235
|
+
compact: bool = False,
|
|
236
|
+
include_tokens: bool = False,
|
|
237
|
+
) -> list[ConversationData]:
|
|
238
|
+
"""Get filtered conversations with formatted output."""
|
|
239
|
+
# Convert period to since if provided
|
|
240
|
+
if period:
|
|
241
|
+
since = get_now() - parse_time_period(period)
|
|
242
|
+
|
|
243
|
+
# Create filters
|
|
244
|
+
filters = QueryFilters(
|
|
245
|
+
agent_name=agent_name,
|
|
246
|
+
since=since,
|
|
247
|
+
query=query,
|
|
248
|
+
model=model,
|
|
249
|
+
limit=limit,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Use existing get_conversations method
|
|
253
|
+
conversations = await self.get_conversations(filters)
|
|
254
|
+
return [
|
|
255
|
+
format_conversation(conv, msgs, compact=compact, include_tokens=include_tokens)
|
|
256
|
+
for conv, msgs in conversations
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
async def get_commands(
|
|
260
|
+
self,
|
|
261
|
+
agent_name: str,
|
|
262
|
+
session_id: str,
|
|
263
|
+
*,
|
|
264
|
+
limit: int | None = None,
|
|
265
|
+
current_session_only: bool = False,
|
|
266
|
+
) -> list[str]:
|
|
267
|
+
"""Get command history from database."""
|
|
268
|
+
async with AsyncSession(self.engine) as session:
|
|
269
|
+
query = select(CommandHistory)
|
|
270
|
+
if current_session_only:
|
|
271
|
+
query = query.where(CommandHistory.session_id == str(session_id))
|
|
272
|
+
else:
|
|
273
|
+
query = query.where(CommandHistory.agent_name == agent_name)
|
|
274
|
+
|
|
275
|
+
query = query.order_by(desc(CommandHistory.timestamp))
|
|
276
|
+
if limit:
|
|
277
|
+
query = query.limit(limit)
|
|
278
|
+
|
|
279
|
+
result = await session.execute(query)
|
|
280
|
+
return [h.command for h in result.scalars()]
|
|
281
|
+
|
|
282
|
+
async def get_conversations(
|
|
283
|
+
self,
|
|
284
|
+
filters: QueryFilters,
|
|
285
|
+
) -> list[tuple[ConversationData, Sequence[ChatMessage[str]]]]:
|
|
286
|
+
"""Get filtered conversations using SQL queries."""
|
|
287
|
+
async with AsyncSession(self.engine) as session:
|
|
288
|
+
results: list[tuple[ConversationData, Sequence[ChatMessage[str]]]] = []
|
|
289
|
+
|
|
290
|
+
# Base conversation query
|
|
291
|
+
conv_query = select(Conversation)
|
|
292
|
+
|
|
293
|
+
if filters.agent_name:
|
|
294
|
+
conv_query = conv_query.where(Conversation.agent_name == filters.agent_name)
|
|
295
|
+
|
|
296
|
+
# Apply time filters if provided
|
|
297
|
+
if filters.since:
|
|
298
|
+
conv_query = conv_query.where(Conversation.start_time >= filters.since)
|
|
299
|
+
|
|
300
|
+
if filters.limit:
|
|
301
|
+
conv_query = conv_query.limit(filters.limit)
|
|
302
|
+
|
|
303
|
+
conv_result = await session.execute(conv_query)
|
|
304
|
+
conversations = conv_result.scalars().all()
|
|
305
|
+
|
|
306
|
+
for conv in conversations:
|
|
307
|
+
# Get messages for this conversation
|
|
308
|
+
msg_query = select(Message).where(Message.conversation_id == conv.id)
|
|
309
|
+
|
|
310
|
+
if filters.query:
|
|
311
|
+
msg_query = msg_query.where(Message.content.contains(filters.query)) # type: ignore
|
|
312
|
+
if filters.model:
|
|
313
|
+
msg_query = msg_query.where(Message.model_name == filters.model)
|
|
314
|
+
|
|
315
|
+
msg_query = msg_query.order_by(Message.timestamp.asc()) # type: ignore
|
|
316
|
+
msg_result = await session.execute(msg_query)
|
|
317
|
+
messages = msg_result.scalars().all()
|
|
318
|
+
|
|
319
|
+
if not messages:
|
|
320
|
+
continue
|
|
321
|
+
|
|
322
|
+
chat_messages = [to_chat_message(msg) for msg in messages]
|
|
323
|
+
conv_data = format_conversation(conv, messages)
|
|
324
|
+
results.append((conv_data, chat_messages))
|
|
325
|
+
|
|
326
|
+
return results
|
|
327
|
+
|
|
328
|
+
async def get_conversation_stats(
|
|
329
|
+
self,
|
|
330
|
+
filters: StatsFilters,
|
|
331
|
+
) -> dict[str, dict[str, Any]]:
|
|
332
|
+
"""Get statistics using SQL aggregations."""
|
|
333
|
+
from agentpool_storage.sql_provider.models import Conversation, Message
|
|
334
|
+
|
|
335
|
+
async with AsyncSession(self.engine) as session:
|
|
336
|
+
# Base query for stats
|
|
337
|
+
query = (
|
|
338
|
+
select( # type: ignore[call-overload]
|
|
339
|
+
Message.model,
|
|
340
|
+
Conversation.agent_name,
|
|
341
|
+
Message.timestamp,
|
|
342
|
+
Message.total_tokens,
|
|
343
|
+
Message.input_tokens,
|
|
344
|
+
Message.output_tokens,
|
|
345
|
+
)
|
|
346
|
+
.join(Conversation, Message.conversation_id == Conversation.id)
|
|
347
|
+
.where(Message.timestamp > filters.cutoff)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
if filters.agent_name:
|
|
351
|
+
query = query.where(Conversation.agent_name == filters.agent_name)
|
|
352
|
+
|
|
353
|
+
# Execute query and get raw data
|
|
354
|
+
result = await session.execute(query)
|
|
355
|
+
rows = [
|
|
356
|
+
(
|
|
357
|
+
model,
|
|
358
|
+
agent,
|
|
359
|
+
timestamp,
|
|
360
|
+
TokenCost(
|
|
361
|
+
token_usage=RunUsage(
|
|
362
|
+
input_tokens=prompt or 0,
|
|
363
|
+
output_tokens=completion or 0,
|
|
364
|
+
),
|
|
365
|
+
total_cost=Decimal(0), # We don't store this in DB
|
|
366
|
+
)
|
|
367
|
+
if total or prompt or completion
|
|
368
|
+
else None,
|
|
369
|
+
)
|
|
370
|
+
for model, agent, timestamp, total, prompt, completion in result.all()
|
|
371
|
+
]
|
|
372
|
+
|
|
373
|
+
# Use base class aggregation
|
|
374
|
+
return self.aggregate_stats(rows, filters.group_by)
|
|
375
|
+
|
|
376
|
+
async def reset(
|
|
377
|
+
self,
|
|
378
|
+
*,
|
|
379
|
+
agent_name: str | None = None,
|
|
380
|
+
hard: bool = False,
|
|
381
|
+
) -> tuple[int, int]:
|
|
382
|
+
"""Reset database storage."""
|
|
383
|
+
from sqlalchemy import text
|
|
384
|
+
|
|
385
|
+
from agentpool_storage.sql_provider.queries import (
|
|
386
|
+
DELETE_AGENT_CONVERSATIONS,
|
|
387
|
+
DELETE_AGENT_MESSAGES,
|
|
388
|
+
DELETE_ALL_CONVERSATIONS,
|
|
389
|
+
DELETE_ALL_MESSAGES,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
async with AsyncSession(self.engine) as session:
|
|
393
|
+
if hard:
|
|
394
|
+
if agent_name:
|
|
395
|
+
msg = "Hard reset cannot be used with agent_name"
|
|
396
|
+
raise ValueError(msg)
|
|
397
|
+
# Drop and recreate all tables
|
|
398
|
+
async with self.engine.begin() as conn:
|
|
399
|
+
await conn.run_sync(SQLModel.metadata.drop_all)
|
|
400
|
+
await session.commit()
|
|
401
|
+
# Recreate schema
|
|
402
|
+
await self._init_database()
|
|
403
|
+
return 0, 0
|
|
404
|
+
|
|
405
|
+
# Get counts first
|
|
406
|
+
conv_count, msg_count = await self.get_conversation_counts(agent_name=agent_name)
|
|
407
|
+
|
|
408
|
+
# Delete data
|
|
409
|
+
if agent_name:
|
|
410
|
+
await session.execute(text(DELETE_AGENT_MESSAGES), {"agent": agent_name})
|
|
411
|
+
await session.execute(text(DELETE_AGENT_CONVERSATIONS), {"agent": agent_name})
|
|
412
|
+
else:
|
|
413
|
+
await session.execute(text(DELETE_ALL_MESSAGES))
|
|
414
|
+
await session.execute(text(DELETE_ALL_CONVERSATIONS))
|
|
415
|
+
|
|
416
|
+
await session.commit()
|
|
417
|
+
return conv_count, msg_count
|
|
418
|
+
|
|
419
|
+
async def get_conversation_counts(
|
|
420
|
+
self,
|
|
421
|
+
*,
|
|
422
|
+
agent_name: str | None = None,
|
|
423
|
+
) -> tuple[int, int]:
|
|
424
|
+
"""Get conversation and message counts."""
|
|
425
|
+
from agentpool_storage.sql_provider import Conversation, Message
|
|
426
|
+
|
|
427
|
+
if not self.session:
|
|
428
|
+
msg = "Session not initialized. Use provider as async context manager."
|
|
429
|
+
raise RuntimeError(msg)
|
|
430
|
+
if agent_name:
|
|
431
|
+
conv_query = select(Conversation).where(Conversation.agent_name == agent_name)
|
|
432
|
+
msg_query = (
|
|
433
|
+
select(Message).join(Conversation).where(Conversation.agent_name == agent_name)
|
|
434
|
+
)
|
|
435
|
+
else:
|
|
436
|
+
conv_query = select(Conversation)
|
|
437
|
+
msg_query = select(Message)
|
|
438
|
+
|
|
439
|
+
conv_result = await self.session.execute(conv_query)
|
|
440
|
+
msg_result = await self.session.execute(msg_query)
|
|
441
|
+
conv_count = len(conv_result.scalars().all())
|
|
442
|
+
msg_count = len(msg_result.scalars().all())
|
|
443
|
+
|
|
444
|
+
return conv_count, msg_count
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Utilities for database storage."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from pydantic_ai import RunUsage
|
|
10
|
+
from sqlalchemy import JSON, Column, and_, or_
|
|
11
|
+
from sqlalchemy.sql import expression
|
|
12
|
+
from sqlmodel import select
|
|
13
|
+
|
|
14
|
+
from agentpool.messaging import ChatMessage, TokenCost
|
|
15
|
+
from agentpool.storage import deserialize_messages
|
|
16
|
+
from agentpool_storage.models import ConversationData, MessageData
|
|
17
|
+
from agentpool_storage.sql_provider.models import Conversation
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from collections.abc import Sequence
|
|
22
|
+
|
|
23
|
+
from sqlmodel.sql.expression import SelectOfScalar
|
|
24
|
+
from tokonomics.toko_types import TokenUsage
|
|
25
|
+
|
|
26
|
+
from agentpool_config.session import SessionQuery
|
|
27
|
+
from agentpool_storage.sql_provider.models import Message
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def aggregate_token_usage(
|
|
31
|
+
messages: Sequence[Message | ChatMessage[str]],
|
|
32
|
+
) -> TokenUsage:
|
|
33
|
+
"""Sum up tokens from a sequence of messages."""
|
|
34
|
+
from agentpool_storage.sql_provider.models import Message
|
|
35
|
+
|
|
36
|
+
total = prompt = completion = 0
|
|
37
|
+
for msg in messages:
|
|
38
|
+
if isinstance(msg, Message):
|
|
39
|
+
total += msg.total_tokens or 0
|
|
40
|
+
prompt += msg.input_tokens or 0
|
|
41
|
+
completion += msg.output_tokens or 0
|
|
42
|
+
elif msg.cost_info:
|
|
43
|
+
total += msg.cost_info.token_usage.total_tokens
|
|
44
|
+
prompt += msg.cost_info.token_usage.input_tokens
|
|
45
|
+
completion += msg.cost_info.token_usage.output_tokens
|
|
46
|
+
return {"total": total, "prompt": prompt, "completion": completion}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def to_chat_message(db_message: Message) -> ChatMessage[str]:
|
|
50
|
+
"""Convert database message to ChatMessage."""
|
|
51
|
+
cost_info = None
|
|
52
|
+
if db_message.total_tokens is not None:
|
|
53
|
+
cost_info = TokenCost(
|
|
54
|
+
token_usage=RunUsage(
|
|
55
|
+
input_tokens=db_message.input_tokens or 0,
|
|
56
|
+
output_tokens=db_message.output_tokens or 0,
|
|
57
|
+
),
|
|
58
|
+
total_cost=Decimal(db_message.cost or 0.0),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return ChatMessage[str](
|
|
62
|
+
message_id=db_message.id,
|
|
63
|
+
conversation_id=db_message.conversation_id,
|
|
64
|
+
content=db_message.content,
|
|
65
|
+
role=db_message.role, # type: ignore
|
|
66
|
+
name=db_message.name,
|
|
67
|
+
model_name=db_message.model,
|
|
68
|
+
cost_info=cost_info,
|
|
69
|
+
response_time=db_message.response_time,
|
|
70
|
+
forwarded_from=db_message.forwarded_from or [],
|
|
71
|
+
timestamp=db_message.timestamp,
|
|
72
|
+
provider_name=db_message.provider_name,
|
|
73
|
+
provider_response_id=db_message.provider_response_id,
|
|
74
|
+
messages=deserialize_messages(db_message.messages),
|
|
75
|
+
finish_reason=db_message.finish_reason, # type: ignore
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_column_default(column: Any) -> str:
|
|
80
|
+
"""Get SQL DEFAULT clause for column."""
|
|
81
|
+
if column.default is None:
|
|
82
|
+
return ""
|
|
83
|
+
if hasattr(column.default, "arg"):
|
|
84
|
+
# Simple default value
|
|
85
|
+
return f" DEFAULT {column.default.arg}"
|
|
86
|
+
if hasattr(column.default, "sqltext"):
|
|
87
|
+
# Computed default
|
|
88
|
+
return f" DEFAULT {column.default.sqltext}"
|
|
89
|
+
return ""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def auto_migrate_columns(sync_conn: Any, dialect: Any) -> None:
|
|
93
|
+
"""Automatically add missing columns to existing tables.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
sync_conn: Synchronous database connection
|
|
97
|
+
dialect: SQLAlchemy dialect for SQL type compilation
|
|
98
|
+
"""
|
|
99
|
+
from sqlalchemy import inspect
|
|
100
|
+
from sqlalchemy.sql import text
|
|
101
|
+
from sqlmodel import SQLModel
|
|
102
|
+
|
|
103
|
+
inspector = inspect(sync_conn)
|
|
104
|
+
|
|
105
|
+
# For each table in our models
|
|
106
|
+
for table_name, table in SQLModel.metadata.tables.items():
|
|
107
|
+
existing = {col["name"] for col in inspector.get_columns(table_name)}
|
|
108
|
+
|
|
109
|
+
# For each column in model that doesn't exist in DB
|
|
110
|
+
for col in table.columns:
|
|
111
|
+
if col.name not in existing:
|
|
112
|
+
# Create ALTER TABLE statement based on column type
|
|
113
|
+
type_sql = col.type.compile(dialect)
|
|
114
|
+
nullable = "" if col.nullable else " NOT NULL"
|
|
115
|
+
default = get_column_default(col)
|
|
116
|
+
sql = (
|
|
117
|
+
f"ALTER TABLE {table_name} ADD COLUMN {col.name} {type_sql}{nullable}{default}"
|
|
118
|
+
)
|
|
119
|
+
sync_conn.execute(text(sql))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_model_info(model: str | None) -> tuple[str | None, str | None]:
|
|
123
|
+
"""Parse model string into provider and name.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
model: Full model string (e.g., "openai:gpt-5", "anthropic:claude-sonnet-4-0")
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Tuple of (provider, name)
|
|
130
|
+
"""
|
|
131
|
+
if not model:
|
|
132
|
+
return None, None
|
|
133
|
+
|
|
134
|
+
# Try splitting by ':' or '/'
|
|
135
|
+
parts = model.split(":") if ":" in model else model.split("/")
|
|
136
|
+
|
|
137
|
+
if len(parts) == 2: # noqa: PLR2004
|
|
138
|
+
provider, name = parts
|
|
139
|
+
return provider.lower(), name
|
|
140
|
+
|
|
141
|
+
# No provider specified, try to infer
|
|
142
|
+
name = parts[0]
|
|
143
|
+
if name.startswith(("gpt-", "text-", "dall-e")):
|
|
144
|
+
return "openai", name
|
|
145
|
+
if name.startswith("claude"):
|
|
146
|
+
return "anthropic", name
|
|
147
|
+
if name.startswith(("llama", "mistral")):
|
|
148
|
+
return "meta", name
|
|
149
|
+
|
|
150
|
+
return None, name
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def build_message_query(query: SessionQuery) -> SelectOfScalar[Any]:
|
|
154
|
+
"""Build SQLModel query from SessionQuery."""
|
|
155
|
+
from agentpool_storage.sql_provider.models import Message
|
|
156
|
+
|
|
157
|
+
stmt = select(Message).order_by(Message.timestamp) # type: ignore
|
|
158
|
+
|
|
159
|
+
conditions: list[Any] = []
|
|
160
|
+
if query.name:
|
|
161
|
+
conditions.append(Message.conversation_id == query.name)
|
|
162
|
+
if query.agents:
|
|
163
|
+
agent_conditions = [Column("name").in_(query.agents)]
|
|
164
|
+
if query.include_forwarded:
|
|
165
|
+
agent_conditions.append(
|
|
166
|
+
and_(
|
|
167
|
+
Column("forwarded_from").isnot(None),
|
|
168
|
+
expression.cast(Column("forwarded_from"), JSON).contains(list(query.agents)), # type: ignore
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
conditions.append(or_(*agent_conditions))
|
|
172
|
+
if query.since and (cutoff := query.get_time_cutoff()):
|
|
173
|
+
conditions.append(Message.timestamp >= cutoff)
|
|
174
|
+
if query.until:
|
|
175
|
+
conditions.append(Message.timestamp <= datetime.fromisoformat(query.until))
|
|
176
|
+
if query.contains:
|
|
177
|
+
conditions.append(Message.content.contains(query.contains)) # type: ignore
|
|
178
|
+
if query.roles:
|
|
179
|
+
conditions.append(Message.role.in_(query.roles)) # type: ignore
|
|
180
|
+
|
|
181
|
+
if conditions:
|
|
182
|
+
stmt = stmt.where(and_(*conditions))
|
|
183
|
+
if query.limit:
|
|
184
|
+
stmt = stmt.limit(query.limit)
|
|
185
|
+
|
|
186
|
+
return stmt
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def format_conversation(
|
|
190
|
+
conv: Conversation | ConversationData,
|
|
191
|
+
messages: Sequence[Message | ChatMessage[str]],
|
|
192
|
+
*,
|
|
193
|
+
include_tokens: bool = False,
|
|
194
|
+
compact: bool = False,
|
|
195
|
+
) -> ConversationData:
|
|
196
|
+
"""Format SQL conversation model to ConversationData."""
|
|
197
|
+
msgs = list(messages)
|
|
198
|
+
if compact and len(msgs) > 1:
|
|
199
|
+
msgs = [msgs[0], msgs[-1]]
|
|
200
|
+
|
|
201
|
+
# Convert messages to ChatMessage format if needed
|
|
202
|
+
chat_messages = [msg if isinstance(msg, ChatMessage) else to_chat_message(msg) for msg in msgs]
|
|
203
|
+
|
|
204
|
+
# Convert Conversation to ConversationData format
|
|
205
|
+
if isinstance(conv, Conversation):
|
|
206
|
+
return ConversationData(
|
|
207
|
+
id=conv.id,
|
|
208
|
+
agent=conv.agent_name,
|
|
209
|
+
title=conv.title,
|
|
210
|
+
start_time=conv.start_time.isoformat(),
|
|
211
|
+
messages=[
|
|
212
|
+
MessageData(
|
|
213
|
+
role=msg.role,
|
|
214
|
+
content=msg.content,
|
|
215
|
+
timestamp=msg.timestamp.isoformat(),
|
|
216
|
+
model=msg.model_name,
|
|
217
|
+
name=msg.name,
|
|
218
|
+
token_usage={
|
|
219
|
+
"prompt": msg.usage.input_tokens,
|
|
220
|
+
"completion": msg.usage.output_tokens,
|
|
221
|
+
"total": msg.usage.total_tokens,
|
|
222
|
+
},
|
|
223
|
+
cost=float(msg.cost_info.total_cost) if msg.cost_info else None,
|
|
224
|
+
response_time=msg.response_time,
|
|
225
|
+
)
|
|
226
|
+
for msg in chat_messages
|
|
227
|
+
],
|
|
228
|
+
token_usage=aggregate_token_usage(msgs) if include_tokens else None,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# If it's already ConversationData, update token_usage if needed
|
|
232
|
+
if include_tokens and conv["token_usage"] is None:
|
|
233
|
+
conv["token_usage"] = aggregate_token_usage(msgs)
|
|
234
|
+
return conv
|