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.

Files changed (474) hide show
  1. acp/README.md +64 -0
  2. acp/__init__.py +172 -0
  3. acp/__main__.py +10 -0
  4. acp/acp_requests.py +285 -0
  5. acp/agent/__init__.py +6 -0
  6. acp/agent/connection.py +256 -0
  7. acp/agent/implementations/__init__.py +6 -0
  8. acp/agent/implementations/debug_server/__init__.py +1 -0
  9. acp/agent/implementations/debug_server/cli.py +79 -0
  10. acp/agent/implementations/debug_server/debug.html +234 -0
  11. acp/agent/implementations/debug_server/debug_server.py +496 -0
  12. acp/agent/implementations/testing.py +91 -0
  13. acp/agent/protocol.py +65 -0
  14. acp/bridge/README.md +162 -0
  15. acp/bridge/__init__.py +6 -0
  16. acp/bridge/__main__.py +91 -0
  17. acp/bridge/bridge.py +246 -0
  18. acp/bridge/py.typed +0 -0
  19. acp/bridge/settings.py +15 -0
  20. acp/client/__init__.py +7 -0
  21. acp/client/connection.py +251 -0
  22. acp/client/implementations/__init__.py +7 -0
  23. acp/client/implementations/default_client.py +185 -0
  24. acp/client/implementations/headless_client.py +266 -0
  25. acp/client/implementations/noop_client.py +110 -0
  26. acp/client/protocol.py +61 -0
  27. acp/connection.py +280 -0
  28. acp/exceptions.py +46 -0
  29. acp/filesystem.py +524 -0
  30. acp/notifications.py +832 -0
  31. acp/py.typed +0 -0
  32. acp/schema/__init__.py +265 -0
  33. acp/schema/agent_plan.py +30 -0
  34. acp/schema/agent_requests.py +126 -0
  35. acp/schema/agent_responses.py +256 -0
  36. acp/schema/base.py +39 -0
  37. acp/schema/capabilities.py +230 -0
  38. acp/schema/client_requests.py +247 -0
  39. acp/schema/client_responses.py +96 -0
  40. acp/schema/common.py +81 -0
  41. acp/schema/content_blocks.py +188 -0
  42. acp/schema/mcp.py +82 -0
  43. acp/schema/messages.py +171 -0
  44. acp/schema/notifications.py +82 -0
  45. acp/schema/protocol_stuff.md +3 -0
  46. acp/schema/session_state.py +160 -0
  47. acp/schema/session_updates.py +419 -0
  48. acp/schema/slash_commands.py +51 -0
  49. acp/schema/terminal.py +15 -0
  50. acp/schema/tool_call.py +347 -0
  51. acp/stdio.py +250 -0
  52. acp/task/__init__.py +53 -0
  53. acp/task/debug.py +197 -0
  54. acp/task/dispatcher.py +93 -0
  55. acp/task/queue.py +69 -0
  56. acp/task/sender.py +82 -0
  57. acp/task/state.py +87 -0
  58. acp/task/supervisor.py +93 -0
  59. acp/terminal_handle.py +30 -0
  60. acp/tool_call_reporter.py +199 -0
  61. acp/tool_call_state.py +178 -0
  62. acp/transports.py +104 -0
  63. acp/utils.py +240 -0
  64. agentpool/__init__.py +63 -0
  65. agentpool/__main__.py +7 -0
  66. agentpool/agents/__init__.py +30 -0
  67. agentpool/agents/acp_agent/__init__.py +5 -0
  68. agentpool/agents/acp_agent/acp_agent.py +837 -0
  69. agentpool/agents/acp_agent/acp_converters.py +294 -0
  70. agentpool/agents/acp_agent/client_handler.py +317 -0
  71. agentpool/agents/acp_agent/session_state.py +44 -0
  72. agentpool/agents/agent.py +1264 -0
  73. agentpool/agents/agui_agent/__init__.py +19 -0
  74. agentpool/agents/agui_agent/agui_agent.py +677 -0
  75. agentpool/agents/agui_agent/agui_converters.py +423 -0
  76. agentpool/agents/agui_agent/chunk_transformer.py +204 -0
  77. agentpool/agents/agui_agent/event_types.py +83 -0
  78. agentpool/agents/agui_agent/helpers.py +192 -0
  79. agentpool/agents/architect.py +71 -0
  80. agentpool/agents/base_agent.py +177 -0
  81. agentpool/agents/claude_code_agent/__init__.py +11 -0
  82. agentpool/agents/claude_code_agent/claude_code_agent.py +1021 -0
  83. agentpool/agents/claude_code_agent/converters.py +243 -0
  84. agentpool/agents/context.py +105 -0
  85. agentpool/agents/events/__init__.py +61 -0
  86. agentpool/agents/events/builtin_handlers.py +129 -0
  87. agentpool/agents/events/event_emitter.py +320 -0
  88. agentpool/agents/events/events.py +561 -0
  89. agentpool/agents/events/tts_handlers.py +186 -0
  90. agentpool/agents/interactions.py +419 -0
  91. agentpool/agents/slashed_agent.py +244 -0
  92. agentpool/agents/sys_prompts.py +178 -0
  93. agentpool/agents/tool_wrapping.py +184 -0
  94. agentpool/base_provider.py +28 -0
  95. agentpool/common_types.py +226 -0
  96. agentpool/config_resources/__init__.py +16 -0
  97. agentpool/config_resources/acp_assistant.yml +24 -0
  98. agentpool/config_resources/agents.yml +109 -0
  99. agentpool/config_resources/agents_template.yml +18 -0
  100. agentpool/config_resources/agui_test.yml +18 -0
  101. agentpool/config_resources/claude_code_agent.yml +16 -0
  102. agentpool/config_resources/claude_style_subagent.md +30 -0
  103. agentpool/config_resources/external_acp_agents.yml +77 -0
  104. agentpool/config_resources/opencode_style_subagent.md +19 -0
  105. agentpool/config_resources/tts_test_agents.yml +78 -0
  106. agentpool/delegation/__init__.py +8 -0
  107. agentpool/delegation/base_team.py +504 -0
  108. agentpool/delegation/message_flow_tracker.py +39 -0
  109. agentpool/delegation/pool.py +1129 -0
  110. agentpool/delegation/team.py +325 -0
  111. agentpool/delegation/teamrun.py +343 -0
  112. agentpool/docs/__init__.py +5 -0
  113. agentpool/docs/gen_examples.py +42 -0
  114. agentpool/docs/utils.py +370 -0
  115. agentpool/functional/__init__.py +20 -0
  116. agentpool/functional/py.typed +0 -0
  117. agentpool/functional/run.py +80 -0
  118. agentpool/functional/structure.py +136 -0
  119. agentpool/hooks/__init__.py +20 -0
  120. agentpool/hooks/agent_hooks.py +247 -0
  121. agentpool/hooks/base.py +119 -0
  122. agentpool/hooks/callable.py +140 -0
  123. agentpool/hooks/command.py +180 -0
  124. agentpool/hooks/prompt.py +122 -0
  125. agentpool/jinja_filters.py +132 -0
  126. agentpool/log.py +224 -0
  127. agentpool/mcp_server/__init__.py +17 -0
  128. agentpool/mcp_server/client.py +429 -0
  129. agentpool/mcp_server/constants.py +32 -0
  130. agentpool/mcp_server/conversions.py +172 -0
  131. agentpool/mcp_server/helpers.py +47 -0
  132. agentpool/mcp_server/manager.py +232 -0
  133. agentpool/mcp_server/message_handler.py +164 -0
  134. agentpool/mcp_server/registries/__init__.py +1 -0
  135. agentpool/mcp_server/registries/official_registry_client.py +345 -0
  136. agentpool/mcp_server/registries/pulsemcp_client.py +88 -0
  137. agentpool/mcp_server/tool_bridge.py +548 -0
  138. agentpool/messaging/__init__.py +58 -0
  139. agentpool/messaging/compaction.py +928 -0
  140. agentpool/messaging/connection_manager.py +319 -0
  141. agentpool/messaging/context.py +66 -0
  142. agentpool/messaging/event_manager.py +426 -0
  143. agentpool/messaging/events.py +39 -0
  144. agentpool/messaging/message_container.py +209 -0
  145. agentpool/messaging/message_history.py +491 -0
  146. agentpool/messaging/messagenode.py +377 -0
  147. agentpool/messaging/messages.py +655 -0
  148. agentpool/messaging/processing.py +76 -0
  149. agentpool/mime_utils.py +95 -0
  150. agentpool/models/__init__.py +21 -0
  151. agentpool/models/acp_agents/__init__.py +22 -0
  152. agentpool/models/acp_agents/base.py +308 -0
  153. agentpool/models/acp_agents/mcp_capable.py +790 -0
  154. agentpool/models/acp_agents/non_mcp.py +842 -0
  155. agentpool/models/agents.py +450 -0
  156. agentpool/models/agui_agents.py +89 -0
  157. agentpool/models/claude_code_agents.py +238 -0
  158. agentpool/models/file_agents.py +116 -0
  159. agentpool/models/file_parsing.py +367 -0
  160. agentpool/models/manifest.py +658 -0
  161. agentpool/observability/__init__.py +9 -0
  162. agentpool/observability/observability_registry.py +97 -0
  163. agentpool/prompts/__init__.py +1 -0
  164. agentpool/prompts/base.py +27 -0
  165. agentpool/prompts/builtin_provider.py +75 -0
  166. agentpool/prompts/conversion_manager.py +95 -0
  167. agentpool/prompts/convert.py +96 -0
  168. agentpool/prompts/manager.py +204 -0
  169. agentpool/prompts/parts/zed.md +33 -0
  170. agentpool/prompts/prompts.py +581 -0
  171. agentpool/py.typed +0 -0
  172. agentpool/queries/tree-sitter-language-pack/README.md +7 -0
  173. agentpool/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  174. agentpool/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  175. agentpool/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  176. agentpool/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
  177. agentpool/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  178. agentpool/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  179. agentpool/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  180. agentpool/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  181. agentpool/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  182. agentpool/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  183. agentpool/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  184. agentpool/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  185. agentpool/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  186. agentpool/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  187. agentpool/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  188. agentpool/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  189. agentpool/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  190. agentpool/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  191. agentpool/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  192. agentpool/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  193. agentpool/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  194. agentpool/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  195. agentpool/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  196. agentpool/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  197. agentpool/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  198. agentpool/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  199. agentpool/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  200. agentpool/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  201. agentpool/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  202. agentpool/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  203. agentpool/queries/tree-sitter-languages/README.md +24 -0
  204. agentpool/queries/tree-sitter-languages/c-tags.scm +9 -0
  205. agentpool/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  206. agentpool/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  207. agentpool/queries/tree-sitter-languages/dart-tags.scm +91 -0
  208. agentpool/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  209. agentpool/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  210. agentpool/queries/tree-sitter-languages/elm-tags.scm +19 -0
  211. agentpool/queries/tree-sitter-languages/fortran-tags.scm +15 -0
  212. agentpool/queries/tree-sitter-languages/go-tags.scm +30 -0
  213. agentpool/queries/tree-sitter-languages/haskell-tags.scm +3 -0
  214. agentpool/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  215. agentpool/queries/tree-sitter-languages/java-tags.scm +20 -0
  216. agentpool/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  217. agentpool/queries/tree-sitter-languages/julia-tags.scm +60 -0
  218. agentpool/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  219. agentpool/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  220. agentpool/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  221. agentpool/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  222. agentpool/queries/tree-sitter-languages/php-tags.scm +26 -0
  223. agentpool/queries/tree-sitter-languages/python-tags.scm +12 -0
  224. agentpool/queries/tree-sitter-languages/ql-tags.scm +26 -0
  225. agentpool/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  226. agentpool/queries/tree-sitter-languages/rust-tags.scm +60 -0
  227. agentpool/queries/tree-sitter-languages/scala-tags.scm +65 -0
  228. agentpool/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  229. agentpool/queries/tree-sitter-languages/zig-tags.scm +3 -0
  230. agentpool/repomap.py +1231 -0
  231. agentpool/resource_providers/__init__.py +17 -0
  232. agentpool/resource_providers/aggregating.py +54 -0
  233. agentpool/resource_providers/base.py +172 -0
  234. agentpool/resource_providers/codemode/__init__.py +9 -0
  235. agentpool/resource_providers/codemode/code_executor.py +215 -0
  236. agentpool/resource_providers/codemode/default_prompt.py +19 -0
  237. agentpool/resource_providers/codemode/helpers.py +83 -0
  238. agentpool/resource_providers/codemode/progress_executor.py +212 -0
  239. agentpool/resource_providers/codemode/provider.py +150 -0
  240. agentpool/resource_providers/codemode/remote_mcp_execution.py +143 -0
  241. agentpool/resource_providers/codemode/remote_provider.py +171 -0
  242. agentpool/resource_providers/filtering.py +42 -0
  243. agentpool/resource_providers/mcp_provider.py +246 -0
  244. agentpool/resource_providers/plan_provider.py +196 -0
  245. agentpool/resource_providers/pool.py +69 -0
  246. agentpool/resource_providers/static.py +289 -0
  247. agentpool/running/__init__.py +20 -0
  248. agentpool/running/decorators.py +56 -0
  249. agentpool/running/discovery.py +101 -0
  250. agentpool/running/executor.py +284 -0
  251. agentpool/running/injection.py +111 -0
  252. agentpool/running/py.typed +0 -0
  253. agentpool/running/run_nodes.py +87 -0
  254. agentpool/server.py +122 -0
  255. agentpool/sessions/__init__.py +13 -0
  256. agentpool/sessions/manager.py +302 -0
  257. agentpool/sessions/models.py +71 -0
  258. agentpool/sessions/session.py +239 -0
  259. agentpool/sessions/store.py +163 -0
  260. agentpool/skills/__init__.py +5 -0
  261. agentpool/skills/manager.py +120 -0
  262. agentpool/skills/registry.py +210 -0
  263. agentpool/skills/skill.py +36 -0
  264. agentpool/storage/__init__.py +17 -0
  265. agentpool/storage/manager.py +419 -0
  266. agentpool/storage/serialization.py +136 -0
  267. agentpool/talk/__init__.py +13 -0
  268. agentpool/talk/registry.py +128 -0
  269. agentpool/talk/stats.py +159 -0
  270. agentpool/talk/talk.py +604 -0
  271. agentpool/tasks/__init__.py +20 -0
  272. agentpool/tasks/exceptions.py +25 -0
  273. agentpool/tasks/registry.py +33 -0
  274. agentpool/testing.py +129 -0
  275. agentpool/text_templates/__init__.py +39 -0
  276. agentpool/text_templates/system_prompt.jinja +30 -0
  277. agentpool/text_templates/tool_call_default.jinja +13 -0
  278. agentpool/text_templates/tool_call_markdown.jinja +25 -0
  279. agentpool/text_templates/tool_call_simple.jinja +5 -0
  280. agentpool/tools/__init__.py +16 -0
  281. agentpool/tools/base.py +269 -0
  282. agentpool/tools/exceptions.py +9 -0
  283. agentpool/tools/manager.py +255 -0
  284. agentpool/tools/tool_call_info.py +87 -0
  285. agentpool/ui/__init__.py +2 -0
  286. agentpool/ui/base.py +89 -0
  287. agentpool/ui/mock_provider.py +81 -0
  288. agentpool/ui/stdlib_provider.py +150 -0
  289. agentpool/utils/__init__.py +44 -0
  290. agentpool/utils/baseregistry.py +185 -0
  291. agentpool/utils/count_tokens.py +62 -0
  292. agentpool/utils/dag.py +184 -0
  293. agentpool/utils/importing.py +206 -0
  294. agentpool/utils/inspection.py +334 -0
  295. agentpool/utils/model_capabilities.py +25 -0
  296. agentpool/utils/network.py +28 -0
  297. agentpool/utils/now.py +22 -0
  298. agentpool/utils/parse_time.py +87 -0
  299. agentpool/utils/result_utils.py +35 -0
  300. agentpool/utils/signatures.py +305 -0
  301. agentpool/utils/streams.py +112 -0
  302. agentpool/utils/tasks.py +186 -0
  303. agentpool/vfs_registry.py +250 -0
  304. agentpool-2.1.9.dist-info/METADATA +336 -0
  305. agentpool-2.1.9.dist-info/RECORD +474 -0
  306. agentpool-2.1.9.dist-info/WHEEL +4 -0
  307. agentpool-2.1.9.dist-info/entry_points.txt +14 -0
  308. agentpool-2.1.9.dist-info/licenses/LICENSE +22 -0
  309. agentpool_cli/__init__.py +34 -0
  310. agentpool_cli/__main__.py +66 -0
  311. agentpool_cli/agent.py +175 -0
  312. agentpool_cli/cli_types.py +23 -0
  313. agentpool_cli/common.py +163 -0
  314. agentpool_cli/create.py +175 -0
  315. agentpool_cli/history.py +217 -0
  316. agentpool_cli/log.py +78 -0
  317. agentpool_cli/py.typed +0 -0
  318. agentpool_cli/run.py +84 -0
  319. agentpool_cli/serve_acp.py +177 -0
  320. agentpool_cli/serve_api.py +69 -0
  321. agentpool_cli/serve_mcp.py +74 -0
  322. agentpool_cli/serve_vercel.py +233 -0
  323. agentpool_cli/store.py +171 -0
  324. agentpool_cli/task.py +84 -0
  325. agentpool_cli/utils.py +104 -0
  326. agentpool_cli/watch.py +54 -0
  327. agentpool_commands/__init__.py +180 -0
  328. agentpool_commands/agents.py +199 -0
  329. agentpool_commands/base.py +45 -0
  330. agentpool_commands/commands.py +58 -0
  331. agentpool_commands/completers.py +110 -0
  332. agentpool_commands/connections.py +175 -0
  333. agentpool_commands/markdown_utils.py +31 -0
  334. agentpool_commands/models.py +62 -0
  335. agentpool_commands/prompts.py +78 -0
  336. agentpool_commands/py.typed +0 -0
  337. agentpool_commands/read.py +77 -0
  338. agentpool_commands/resources.py +210 -0
  339. agentpool_commands/session.py +48 -0
  340. agentpool_commands/tools.py +269 -0
  341. agentpool_commands/utils.py +189 -0
  342. agentpool_commands/workers.py +163 -0
  343. agentpool_config/__init__.py +53 -0
  344. agentpool_config/builtin_tools.py +265 -0
  345. agentpool_config/commands.py +237 -0
  346. agentpool_config/conditions.py +301 -0
  347. agentpool_config/converters.py +30 -0
  348. agentpool_config/durable.py +331 -0
  349. agentpool_config/event_handlers.py +600 -0
  350. agentpool_config/events.py +153 -0
  351. agentpool_config/forward_targets.py +251 -0
  352. agentpool_config/hook_conditions.py +331 -0
  353. agentpool_config/hooks.py +241 -0
  354. agentpool_config/jinja.py +206 -0
  355. agentpool_config/knowledge.py +41 -0
  356. agentpool_config/loaders.py +350 -0
  357. agentpool_config/mcp_server.py +243 -0
  358. agentpool_config/nodes.py +202 -0
  359. agentpool_config/observability.py +191 -0
  360. agentpool_config/output_types.py +55 -0
  361. agentpool_config/pool_server.py +267 -0
  362. agentpool_config/prompt_hubs.py +105 -0
  363. agentpool_config/prompts.py +185 -0
  364. agentpool_config/py.typed +0 -0
  365. agentpool_config/resources.py +33 -0
  366. agentpool_config/session.py +119 -0
  367. agentpool_config/skills.py +17 -0
  368. agentpool_config/storage.py +288 -0
  369. agentpool_config/system_prompts.py +190 -0
  370. agentpool_config/task.py +162 -0
  371. agentpool_config/teams.py +52 -0
  372. agentpool_config/tools.py +112 -0
  373. agentpool_config/toolsets.py +1033 -0
  374. agentpool_config/workers.py +86 -0
  375. agentpool_prompts/__init__.py +1 -0
  376. agentpool_prompts/braintrust_hub.py +235 -0
  377. agentpool_prompts/fabric.py +75 -0
  378. agentpool_prompts/langfuse_hub.py +79 -0
  379. agentpool_prompts/promptlayer_provider.py +59 -0
  380. agentpool_prompts/py.typed +0 -0
  381. agentpool_server/__init__.py +9 -0
  382. agentpool_server/a2a_server/__init__.py +5 -0
  383. agentpool_server/a2a_server/a2a_types.py +41 -0
  384. agentpool_server/a2a_server/server.py +190 -0
  385. agentpool_server/a2a_server/storage.py +81 -0
  386. agentpool_server/acp_server/__init__.py +22 -0
  387. agentpool_server/acp_server/acp_agent.py +786 -0
  388. agentpool_server/acp_server/acp_tools.py +43 -0
  389. agentpool_server/acp_server/commands/__init__.py +18 -0
  390. agentpool_server/acp_server/commands/acp_commands.py +594 -0
  391. agentpool_server/acp_server/commands/debug_commands.py +376 -0
  392. agentpool_server/acp_server/commands/docs_commands/__init__.py +39 -0
  393. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +169 -0
  394. agentpool_server/acp_server/commands/docs_commands/get_schema.py +176 -0
  395. agentpool_server/acp_server/commands/docs_commands/get_source.py +110 -0
  396. agentpool_server/acp_server/commands/docs_commands/git_diff.py +111 -0
  397. agentpool_server/acp_server/commands/docs_commands/helpers.py +33 -0
  398. agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +90 -0
  399. agentpool_server/acp_server/commands/spawn.py +210 -0
  400. agentpool_server/acp_server/converters.py +235 -0
  401. agentpool_server/acp_server/input_provider.py +338 -0
  402. agentpool_server/acp_server/server.py +288 -0
  403. agentpool_server/acp_server/session.py +969 -0
  404. agentpool_server/acp_server/session_manager.py +313 -0
  405. agentpool_server/acp_server/syntax_detection.py +250 -0
  406. agentpool_server/acp_server/zed_tools.md +90 -0
  407. agentpool_server/aggregating_server.py +309 -0
  408. agentpool_server/agui_server/__init__.py +11 -0
  409. agentpool_server/agui_server/server.py +128 -0
  410. agentpool_server/base.py +189 -0
  411. agentpool_server/http_server.py +164 -0
  412. agentpool_server/mcp_server/__init__.py +6 -0
  413. agentpool_server/mcp_server/server.py +314 -0
  414. agentpool_server/mcp_server/zed_wrapper.py +110 -0
  415. agentpool_server/openai_api_server/__init__.py +5 -0
  416. agentpool_server/openai_api_server/completions/__init__.py +1 -0
  417. agentpool_server/openai_api_server/completions/helpers.py +81 -0
  418. agentpool_server/openai_api_server/completions/models.py +98 -0
  419. agentpool_server/openai_api_server/responses/__init__.py +1 -0
  420. agentpool_server/openai_api_server/responses/helpers.py +74 -0
  421. agentpool_server/openai_api_server/responses/models.py +96 -0
  422. agentpool_server/openai_api_server/server.py +242 -0
  423. agentpool_server/py.typed +0 -0
  424. agentpool_storage/__init__.py +9 -0
  425. agentpool_storage/base.py +310 -0
  426. agentpool_storage/file_provider.py +378 -0
  427. agentpool_storage/formatters.py +129 -0
  428. agentpool_storage/memory_provider.py +396 -0
  429. agentpool_storage/models.py +108 -0
  430. agentpool_storage/py.typed +0 -0
  431. agentpool_storage/session_store.py +262 -0
  432. agentpool_storage/sql_provider/__init__.py +21 -0
  433. agentpool_storage/sql_provider/cli.py +146 -0
  434. agentpool_storage/sql_provider/models.py +249 -0
  435. agentpool_storage/sql_provider/queries.py +15 -0
  436. agentpool_storage/sql_provider/sql_provider.py +444 -0
  437. agentpool_storage/sql_provider/utils.py +234 -0
  438. agentpool_storage/text_log_provider.py +275 -0
  439. agentpool_toolsets/__init__.py +15 -0
  440. agentpool_toolsets/builtin/__init__.py +33 -0
  441. agentpool_toolsets/builtin/agent_management.py +239 -0
  442. agentpool_toolsets/builtin/chain.py +288 -0
  443. agentpool_toolsets/builtin/code.py +398 -0
  444. agentpool_toolsets/builtin/debug.py +291 -0
  445. agentpool_toolsets/builtin/execution_environment.py +381 -0
  446. agentpool_toolsets/builtin/file_edit/__init__.py +11 -0
  447. agentpool_toolsets/builtin/file_edit/file_edit.py +747 -0
  448. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/__init__.py +5 -0
  449. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/example_usage.py +311 -0
  450. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/streaming_fuzzy_matcher.py +443 -0
  451. agentpool_toolsets/builtin/history.py +36 -0
  452. agentpool_toolsets/builtin/integration.py +85 -0
  453. agentpool_toolsets/builtin/skills.py +77 -0
  454. agentpool_toolsets/builtin/subagent_tools.py +324 -0
  455. agentpool_toolsets/builtin/tool_management.py +90 -0
  456. agentpool_toolsets/builtin/user_interaction.py +52 -0
  457. agentpool_toolsets/builtin/workers.py +128 -0
  458. agentpool_toolsets/composio_toolset.py +96 -0
  459. agentpool_toolsets/config_creation.py +192 -0
  460. agentpool_toolsets/entry_points.py +47 -0
  461. agentpool_toolsets/fsspec_toolset/__init__.py +7 -0
  462. agentpool_toolsets/fsspec_toolset/diagnostics.py +115 -0
  463. agentpool_toolsets/fsspec_toolset/grep.py +450 -0
  464. agentpool_toolsets/fsspec_toolset/helpers.py +631 -0
  465. agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py +249 -0
  466. agentpool_toolsets/fsspec_toolset/toolset.py +1384 -0
  467. agentpool_toolsets/mcp_run_toolset.py +61 -0
  468. agentpool_toolsets/notifications.py +146 -0
  469. agentpool_toolsets/openapi.py +118 -0
  470. agentpool_toolsets/py.typed +0 -0
  471. agentpool_toolsets/search_toolset.py +202 -0
  472. agentpool_toolsets/semantic_memory_toolset.py +536 -0
  473. agentpool_toolsets/streaming_tools.py +265 -0
  474. 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