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