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,655 @@
1
+ """Message and token usage models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from dataclasses import dataclass, field, replace
7
+ from decimal import Decimal
8
+ from typing import TYPE_CHECKING, Any, Literal, Self, TypeVar, assert_never
9
+ import uuid
10
+ from uuid import uuid4
11
+
12
+ from genai_prices import calc_price
13
+ from pydantic import BaseModel
14
+ from pydantic_ai import (
15
+ BaseToolReturnPart,
16
+ BinaryContent,
17
+ FilePart,
18
+ FileUrl,
19
+ ModelRequest,
20
+ ModelResponse,
21
+ RequestUsage,
22
+ TextPart,
23
+ ToolCallPart,
24
+ UserContent,
25
+ UserPromptPart,
26
+ )
27
+ import tokonomics
28
+
29
+ from agentpool.common_types import MessageRole, SimpleJsonType # noqa: TC001
30
+ from agentpool.log import get_logger
31
+ from agentpool.utils.inspection import dataclasses_no_defaults_repr
32
+ from agentpool.utils.now import get_now
33
+
34
+
35
+ if TYPE_CHECKING:
36
+ from datetime import datetime
37
+
38
+ from pydantic_ai import (
39
+ AgentRunResult,
40
+ FinishReason,
41
+ ModelMessage,
42
+ ModelRequestPart,
43
+ ModelResponsePart,
44
+ RunUsage,
45
+ )
46
+
47
+ from agentpool.tools.tool_call_info import ToolCallInfo
48
+
49
+
50
+ TContent = TypeVar("TContent", str, BaseModel)
51
+ FormatStyle = Literal["simple", "detailed", "markdown", "custom"]
52
+ logger = get_logger(__name__)
53
+
54
+ SIMPLE_TEMPLATE = """{{ name or role.title() }}: {{ content }}"""
55
+
56
+ DETAILED_TEMPLATE = """From: {{ name or role.title() }}
57
+ Time: {{ timestamp.strftime('%Y-%m-%d %H:%M:%S') }}
58
+ ----------------------------------------
59
+ {{ content }}
60
+ ----------------------------------------
61
+ {%- if show_costs and cost_info %}
62
+ Tokens: {{ "{:,}".format(cost_info.token_usage.total_tokens) }}
63
+ Cost: ${{ "%.6f"|format(cost_info.total_cost) }}
64
+ {%- if response_time %}
65
+ Response time: {{ "%.2f"|format(response_time) }}s
66
+ {%- endif %}
67
+ {%- endif %}
68
+ {%- if show_metadata and metadata %}
69
+ Metadata:
70
+ {%- for key, value in metadata.items() %}
71
+ {{ key }}: {{ value }}
72
+ {%- endfor %}
73
+ {%- endif %}
74
+ {%- if forwarded_from %}
75
+ Forwarded via: {{ forwarded_from|join(' -> ') }}
76
+ {%- endif %}"""
77
+
78
+ MARKDOWN_TEMPLATE = """## {{ name or role.title() }}
79
+ *{{ timestamp.strftime('%Y-%m-%d %H:%M:%S') }}*
80
+
81
+ {{ content }}
82
+
83
+ {%- if show_costs and cost_info %}
84
+ ---
85
+ **Stats:**
86
+ - Tokens: {{ "{:,}".format(cost_info.token_usage.total_tokens) }}
87
+ - Cost: ${{ "%.6f"|format(cost_info.total_cost) }}
88
+ {%- if response_time %}
89
+ - Response time: {{ "%.2f"|format(response_time) }}s
90
+ {%- endif %}
91
+ {%- endif %}
92
+
93
+ {%- if show_metadata and metadata %}
94
+ **Metadata:**
95
+ ```
96
+ {{ metadata|to_yaml }}
97
+ ```
98
+ {%- endif %}
99
+
100
+ {% if forwarded_from %}
101
+
102
+ *Forwarded via: {{ forwarded_from|join(' → ') }}*
103
+ {% endif %}"""
104
+
105
+ MESSAGE_TEMPLATES = {
106
+ "simple": SIMPLE_TEMPLATE,
107
+ "detailed": DETAILED_TEMPLATE,
108
+ "markdown": MARKDOWN_TEMPLATE,
109
+ }
110
+
111
+
112
+ @dataclass(frozen=True)
113
+ class TokenCost:
114
+ """Combined token and cost tracking."""
115
+
116
+ token_usage: RunUsage
117
+ """Token counts for prompt and completion"""
118
+ total_cost: Decimal
119
+ """Total cost in USD"""
120
+
121
+ @classmethod
122
+ async def from_usage(
123
+ cls,
124
+ usage: RunUsage,
125
+ model: str,
126
+ provider: str | None = None,
127
+ ) -> TokenCost | None:
128
+ """Create result from usage data.
129
+
130
+ Args:
131
+ usage: Token counts from model response
132
+ model: Name of the model used
133
+ provider: Provider ID (e.g. 'openrouter', 'openai'). If not provided,
134
+ will try to extract from model string (e.g. 'openrouter:model')
135
+
136
+ Returns:
137
+ TokenCost if usage data available, None otherwise
138
+ """
139
+ logger.debug("Token usage", usage=usage)
140
+ if model in {"None", "test"}:
141
+ price = Decimal(0)
142
+ else:
143
+ # Determine provider and model ref
144
+ parts = model.split(":", 1)
145
+ if len(parts) > 1:
146
+ # Model string has provider prefix (e.g. 'openrouter:google/gemini')
147
+ provider_id = parts[0]
148
+ model_ref = parts[1]
149
+ else:
150
+ # Use explicit provider or default to openai
151
+ provider_id = provider or "openai"
152
+ model_ref = parts[0]
153
+
154
+ try:
155
+ price_data = calc_price(
156
+ usage,
157
+ model_ref=model_ref,
158
+ provider_id=provider_id,
159
+ )
160
+ price = price_data.total_price
161
+ except Exception: # noqa: BLE001
162
+ cost = await tokonomics.calculate_token_cost(
163
+ model,
164
+ usage.input_tokens,
165
+ usage.output_tokens,
166
+ )
167
+ price = Decimal(cost.total_cost if cost else 0)
168
+
169
+ return cls(token_usage=usage, total_cost=price)
170
+
171
+
172
+ @dataclass
173
+ class ChatMessage[TContent]:
174
+ """Common message format for all UI types.
175
+
176
+ Generically typed with: ChatMessage[Type of Content]
177
+ The type can either be str or a BaseModel subclass.
178
+ """
179
+
180
+ content: TContent
181
+ """Message content, typed as TContent (either str or BaseModel)."""
182
+
183
+ role: MessageRole
184
+ """Role of the message sender (user/assistant)."""
185
+
186
+ metadata: SimpleJsonType = field(default_factory=dict)
187
+ """Additional metadata about the message."""
188
+
189
+ timestamp: datetime = field(default_factory=get_now)
190
+ """When this message was created."""
191
+
192
+ cost_info: TokenCost | None = None
193
+ """Token usage and costs for this specific message if available."""
194
+
195
+ message_id: str = field(default_factory=lambda: str(uuid4()))
196
+ """Unique identifier for this message."""
197
+
198
+ conversation_id: str | None = None
199
+ """ID of the conversation this message belongs to."""
200
+
201
+ response_time: float | None = None
202
+ """Time it took the LLM to respond."""
203
+
204
+ associated_messages: list[ChatMessage[Any]] = field(default_factory=list)
205
+ """List of messages which were generated during the the creation of this messsage."""
206
+
207
+ name: str | None = None
208
+ """Display name for the message sender in UI."""
209
+
210
+ forwarded_from: list[str] = field(default_factory=list)
211
+ """List of agent names (the chain) that forwarded this message to the sender."""
212
+
213
+ provider_details: dict[str, Any] = field(default_factory=dict)
214
+ """Provider specific metadata / extra information."""
215
+
216
+ messages: list[ModelMessage] = field(default_factory=list)
217
+ """List of messages which were generated during the the creation of this messsage."""
218
+
219
+ usage: RequestUsage = field(default_factory=RequestUsage)
220
+ """Usage information for the request.
221
+
222
+ This has a default to make tests easier,
223
+ and to support loading old messages where usage will be missing.
224
+ """
225
+
226
+ model_name: str | None = None
227
+ """The name of the model that generated the response."""
228
+
229
+ provider_name: str | None = None
230
+ """The name of the LLM provider that generated the response."""
231
+
232
+ provider_response_id: str | None = None
233
+ """request ID as specified by the model provider.
234
+
235
+ This can be used to track the specific request to the model."""
236
+
237
+ finish_reason: FinishReason | None = None
238
+ """Reason the model finished generating the response.
239
+
240
+ Normalized to OpenTelemetry values."""
241
+
242
+ __repr__ = dataclasses_no_defaults_repr
243
+
244
+ @property
245
+ def last_message(self) -> ModelMessage | None:
246
+ """Return the last message from the message history."""
247
+ # The response may not be the very last item if it contained an output tool call.
248
+ # See `CallToolsNode._handle_final_result`.
249
+ for message in reversed(self.messages):
250
+ if isinstance(message, ModelRequest | ModelResponse):
251
+ return message
252
+ return None
253
+
254
+ @property
255
+ def parts(self) -> Sequence[ModelRequestPart] | Sequence[ModelResponsePart]:
256
+ """The parts of the last model message."""
257
+ if not self.last_message:
258
+ return []
259
+ return self.last_message.parts
260
+
261
+ @property
262
+ def kind(self) -> Literal["request", "response"]:
263
+ """Role of the message."""
264
+ match self.role:
265
+ case "assistant":
266
+ return "response"
267
+ case "user":
268
+ return "request"
269
+
270
+ def to_pydantic_ai(self) -> Sequence[ModelMessage]:
271
+ """Convert this message to a Pydantic model."""
272
+ if self.messages:
273
+ return self.messages
274
+ match self.kind:
275
+ case "request":
276
+ return [ModelRequest(parts=self.parts, instructions=None, run_id=self.message_id)] # type: ignore
277
+ case "response":
278
+ return [
279
+ ModelResponse(
280
+ parts=self.parts, # type: ignore
281
+ usage=self.usage,
282
+ model_name=self.model_name,
283
+ timestamp=self.timestamp,
284
+ provider_name=self.provider_name,
285
+ provider_details=self.provider_details,
286
+ finish_reason=self.finish_reason,
287
+ provider_response_id=self.provider_response_id,
288
+ run_id=self.message_id,
289
+ )
290
+ ]
291
+
292
+ @classmethod
293
+ def user_prompt[TPromptContent: str | Sequence[UserContent] = str](
294
+ cls,
295
+ message: TPromptContent,
296
+ conversation_id: str | None = None,
297
+ instructions: str | None = None,
298
+ ) -> ChatMessage[TPromptContent]:
299
+ """Create a user prompt message."""
300
+ part = UserPromptPart(content=message)
301
+ request = ModelRequest(parts=[part], instructions=instructions)
302
+ id_ = conversation_id or str(uuid4())
303
+ return ChatMessage(messages=[request], role="user", content=message, conversation_id=id_)
304
+
305
+ @classmethod
306
+ def from_pydantic_ai[TContentType](
307
+ cls,
308
+ content: TContentType,
309
+ message: ModelMessage,
310
+ conversation_id: str | None = None,
311
+ name: str | None = None,
312
+ forwarded_from: list[str] | None = None,
313
+ ) -> ChatMessage[TContentType]:
314
+ """Convert a Pydantic model to a ChatMessage."""
315
+ match message:
316
+ case ModelRequest(instructions=_instructions, run_id=run_id):
317
+ return ChatMessage(
318
+ messages=[message],
319
+ content=content,
320
+ role="user",
321
+ message_id=run_id or str(uuid.uuid4()),
322
+ # instructions=instructions,
323
+ forwarded_from=forwarded_from or [],
324
+ name=name,
325
+ )
326
+ case ModelResponse(
327
+ usage=usage,
328
+ model_name=model_name,
329
+ timestamp=timestamp,
330
+ provider_name=provider_name,
331
+ provider_details=provider_details,
332
+ finish_reason=finish_reason,
333
+ provider_response_id=provider_response_id,
334
+ run_id=run_id,
335
+ ):
336
+ return ChatMessage(
337
+ role="assistant",
338
+ content=content,
339
+ messages=[message],
340
+ usage=usage,
341
+ message_id=run_id or str(uuid.uuid4()),
342
+ conversation_id=conversation_id,
343
+ model_name=model_name,
344
+ timestamp=timestamp,
345
+ provider_name=provider_name,
346
+ provider_details=provider_details or {},
347
+ finish_reason=finish_reason,
348
+ provider_response_id=provider_response_id,
349
+ name=name,
350
+ forwarded_from=forwarded_from or [],
351
+ )
352
+ case _ as unreachable:
353
+ assert_never(unreachable)
354
+
355
+ @classmethod
356
+ async def from_run_result[OutputDataT](
357
+ cls,
358
+ result: AgentRunResult[OutputDataT],
359
+ *,
360
+ agent_name: str | None = None,
361
+ message_id: str | None = None,
362
+ conversation_id: str | None = None,
363
+ response_time: float,
364
+ ) -> ChatMessage[OutputDataT]:
365
+ """Create a ChatMessage from a PydanticAI run result.
366
+
367
+ Args:
368
+ result: The PydanticAI run result
369
+ agent_name: Name of the agent that generated this response
370
+ message_id: Unique message identifier
371
+ conversation_id: Conversation identifier
372
+ response_time: Total time taken for the response
373
+
374
+ Returns:
375
+ A ChatMessage with all fields populated from the result
376
+ """
377
+ # Calculate costs
378
+ run_usage = result.usage()
379
+ usage = result.response.usage
380
+ cost_info = await TokenCost.from_usage(
381
+ model=result.response.model_name or "",
382
+ usage=run_usage,
383
+ provider=result.response.provider_name,
384
+ )
385
+
386
+ return ChatMessage[OutputDataT](
387
+ content=result.output,
388
+ role="assistant",
389
+ name=agent_name,
390
+ model_name=result.response.model_name,
391
+ finish_reason=result.response.finish_reason,
392
+ messages=result.new_messages(),
393
+ provider_response_id=result.response.provider_response_id,
394
+ usage=usage,
395
+ provider_name=result.response.provider_name,
396
+ message_id=message_id or str(uuid4()),
397
+ conversation_id=conversation_id,
398
+ cost_info=cost_info,
399
+ response_time=response_time,
400
+ provider_details={},
401
+ )
402
+
403
+ def forwarded(self, previous_message: ChatMessage[Any]) -> Self:
404
+ """Create new message showing it was forwarded from another message.
405
+
406
+ Args:
407
+ previous_message: The message that led to this one's creation
408
+
409
+ Returns:
410
+ New message with updated chain showing the path through previous message
411
+ """
412
+ from_ = [*previous_message.forwarded_from, previous_message.name or "unknown"]
413
+ return replace(self, forwarded_from=from_)
414
+
415
+ @property
416
+ def response(self) -> ModelResponse:
417
+ """Return the last response from the message history."""
418
+ # The response may not be the very last item if it contained an output tool call.
419
+ # See `CallToolsNode._handle_final_result`.
420
+ for message in reversed(self.messages):
421
+ if isinstance(message, ModelResponse):
422
+ return message
423
+ msg = "No response found in the message history"
424
+ raise ValueError(msg)
425
+
426
+ def to_request(self) -> Self:
427
+ """Convert this message to a request message.
428
+
429
+ If the message is already a request (user role), this is a no-op.
430
+ If it's a response (assistant role), converts response parts to user content.
431
+
432
+ Returns:
433
+ New ChatMessage with role='user' and converted parts
434
+ """
435
+ if self.role == "user":
436
+ return self # Already a request, return as-is
437
+
438
+ user_content: list[UserContent] = [] # Convert response parts to user content
439
+ for part in self.parts:
440
+ match part:
441
+ case TextPart(content=content) | FilePart(content=content):
442
+ # Text & File parts (images, etc.) become user content directly
443
+ user_content.append(content)
444
+ case BaseToolReturnPart(content=(str() | FileUrl() | BinaryContent()) as content):
445
+ user_content.append(content) # type: ignore[arg-type]
446
+ case BaseToolReturnPart(content=list() as content_list):
447
+ # Handle sequence of content items
448
+ for item in content_list:
449
+ if isinstance(item, str | FileUrl | BinaryContent):
450
+ user_content.append(item) # type: ignore[arg-type]
451
+ else:
452
+ user_content.append(str(item))
453
+ case BaseToolReturnPart():
454
+ # Other tool return parts become user content strings
455
+ user_content.append(part.model_response_str())
456
+ case ToolCallPart():
457
+ # Tool return parts become user content strings
458
+ user_content.append(part.args_as_json_str())
459
+ case _:
460
+ pass
461
+
462
+ # Create new UserPromptPart with converted content
463
+ if user_content:
464
+ converted_parts = [UserPromptPart(content=user_content)]
465
+ else:
466
+ converted_parts = [UserPromptPart(content=str(self.content))]
467
+
468
+ return replace(
469
+ self,
470
+ role="user",
471
+ messages=[ModelRequest(parts=converted_parts)],
472
+ cost_info=None,
473
+ # TODO: what about message_id?
474
+ )
475
+
476
+ @property
477
+ def data(self) -> TContent:
478
+ """Get content as typed data. Provides compat to AgentRunResult."""
479
+ return self.content
480
+
481
+ def get_tool_calls(
482
+ self,
483
+ tools: dict[str, Any] | None = None,
484
+ agent_name: str | None = None,
485
+ ) -> list[ToolCallInfo]:
486
+ """Extract tool call information from all messages lazily.
487
+
488
+ Args:
489
+ tools: Original Tool set to enrich ToolCallInfos with additional info
490
+ agent_name: Name of the caller
491
+ """
492
+ from uuid import uuid4
493
+
494
+ from pydantic_ai import ToolReturnPart
495
+
496
+ from agentpool.tools import ToolCallInfo
497
+
498
+ tools = tools or {}
499
+ parts = [part for message in self.messages for part in message.parts]
500
+ call_parts = {
501
+ part.tool_call_id: part
502
+ for part in parts
503
+ if isinstance(part, ToolCallPart) and part.tool_call_id
504
+ }
505
+
506
+ tool_calls = []
507
+ for part in parts:
508
+ if isinstance(part, ToolReturnPart) and part.tool_call_id in call_parts:
509
+ call_part = call_parts[part.tool_call_id]
510
+ tool_info = ToolCallInfo(
511
+ tool_name=call_part.tool_name,
512
+ args=call_part.args_as_dict(),
513
+ agent_name=agent_name or "UNSET",
514
+ result=part.content,
515
+ tool_call_id=call_part.tool_call_id or str(uuid4()),
516
+ timestamp=part.timestamp,
517
+ agent_tool_name=(t.agent_name if (t := tools.get(part.tool_name)) else None),
518
+ )
519
+ tool_calls.append(tool_info)
520
+
521
+ return tool_calls
522
+
523
+ def format(
524
+ self,
525
+ style: FormatStyle = "simple",
526
+ *,
527
+ template: str | None = None,
528
+ variables: dict[str, Any] | None = None,
529
+ show_metadata: bool = False,
530
+ show_costs: bool = False,
531
+ ) -> str:
532
+ """Format message with configurable style.
533
+
534
+ Args:
535
+ style: Predefined style or "custom" for custom template
536
+ template: Custom Jinja template (required if style="custom")
537
+ variables: Additional variables for template rendering
538
+ show_metadata: Whether to include metadata
539
+ show_costs: Whether to include cost information
540
+
541
+ Raises:
542
+ ValueError: If style is "custom" but no template provided
543
+ or if style is invalid
544
+ """
545
+ from jinjarope import Environment
546
+ import yamling
547
+
548
+ env = Environment(trim_blocks=True, lstrip_blocks=True)
549
+ env.filters["to_yaml"] = yamling.dump_yaml
550
+
551
+ match style:
552
+ case "custom":
553
+ if not template:
554
+ msg = "Custom style requires a template"
555
+ raise ValueError(msg)
556
+ template_str = template
557
+ case _ if style in MESSAGE_TEMPLATES:
558
+ template_str = MESSAGE_TEMPLATES[style]
559
+ case _:
560
+ msg = f"Invalid style: {style}"
561
+ raise ValueError(msg)
562
+ template_obj = env.from_string(template_str)
563
+ vars_ = {
564
+ **(self.__dict__),
565
+ "show_metadata": show_metadata,
566
+ "show_costs": show_costs,
567
+ }
568
+
569
+ if variables:
570
+ vars_.update(variables)
571
+
572
+ return template_obj.render(**vars_)
573
+
574
+
575
+ @dataclass
576
+ class AgentResponse[TResult = Any]:
577
+ """Result from an agent's execution."""
578
+
579
+ agent_name: str
580
+ """Name of the agent that produced this result"""
581
+
582
+ message: ChatMessage[TResult] | None
583
+ """The actual message with content and metadata"""
584
+
585
+ timing: float | None = None
586
+ """Time taken by this agent in seconds"""
587
+
588
+ error: str | None = None
589
+ """Error message if agent failed"""
590
+
591
+ @property
592
+ def success(self) -> bool:
593
+ """Whether the agent completed successfully."""
594
+ return self.error is None
595
+
596
+ @property
597
+ def response(self) -> TResult | None:
598
+ """Convenient access to message content."""
599
+ return self.message.content if self.message else None
600
+
601
+
602
+ class TeamResponse[TMessageContent = Any](list[AgentResponse[Any]]):
603
+ """Results from a team execution."""
604
+
605
+ def __init__(
606
+ self,
607
+ responses: list[AgentResponse[TMessageContent]],
608
+ start_time: datetime | None = None,
609
+ errors: dict[str, Exception] | None = None,
610
+ ) -> None:
611
+ super().__init__(responses)
612
+ self.start_time = start_time or get_now()
613
+ self.end_time = get_now()
614
+ self.errors = errors or {}
615
+
616
+ @property
617
+ def duration(self) -> float:
618
+ """Get execution duration in seconds."""
619
+ return (self.end_time - self.start_time).total_seconds()
620
+
621
+ @property
622
+ def success(self) -> bool:
623
+ """Whether all agents completed successfully."""
624
+ return not bool(self.errors)
625
+
626
+ @property
627
+ def failed_agents(self) -> list[str]:
628
+ """Names of agents that failed."""
629
+ return list(self.errors.keys())
630
+
631
+ def by_agent(self, name: str) -> AgentResponse[TMessageContent] | None:
632
+ """Get response from specific agent."""
633
+ return next((r for r in self if r.agent_name == name), None)
634
+
635
+ def format_durations(self) -> str:
636
+ """Format execution times."""
637
+ parts = [f"{r.agent_name}: {r.timing:.2f}s" for r in self if r.timing is not None]
638
+ return f"Individual times: {', '.join(parts)}\nTotal time: {self.duration:.2f}s"
639
+
640
+ # TODO: could keep TResultContent for len(messages) == 1
641
+ def to_chat_message(self) -> ChatMessage[str]:
642
+ """Convert team response to a single chat message."""
643
+ # Combine all responses into one structured message
644
+ content = "\n\n".join(
645
+ f"[{response.agent_name}]: {response.message.content}"
646
+ for response in self
647
+ if response.message
648
+ )
649
+ meta = {
650
+ "type": "team_response",
651
+ "agents": [r.agent_name for r in self],
652
+ "duration": self.duration,
653
+ "success_count": len(self),
654
+ }
655
+ return ChatMessage(content=content, role="assistant", metadata=meta) # type: ignore