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