ag2 0.9.1a1__py3-none-any.whl → 0.9.1.post0__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 ag2 might be problematic. Click here for more details.

Files changed (357) hide show
  1. {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info}/METADATA +264 -73
  2. ag2-0.9.1.post0.dist-info/RECORD +392 -0
  3. {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info}/WHEEL +1 -2
  4. autogen/__init__.py +89 -0
  5. autogen/_website/__init__.py +3 -0
  6. autogen/_website/generate_api_references.py +427 -0
  7. autogen/_website/generate_mkdocs.py +1174 -0
  8. autogen/_website/notebook_processor.py +476 -0
  9. autogen/_website/process_notebooks.py +656 -0
  10. autogen/_website/utils.py +412 -0
  11. autogen/agentchat/__init__.py +44 -0
  12. autogen/agentchat/agent.py +182 -0
  13. autogen/agentchat/assistant_agent.py +85 -0
  14. autogen/agentchat/chat.py +309 -0
  15. autogen/agentchat/contrib/__init__.py +5 -0
  16. autogen/agentchat/contrib/agent_eval/README.md +7 -0
  17. autogen/agentchat/contrib/agent_eval/agent_eval.py +108 -0
  18. autogen/agentchat/contrib/agent_eval/criterion.py +43 -0
  19. autogen/agentchat/contrib/agent_eval/critic_agent.py +44 -0
  20. autogen/agentchat/contrib/agent_eval/quantifier_agent.py +39 -0
  21. autogen/agentchat/contrib/agent_eval/subcritic_agent.py +45 -0
  22. autogen/agentchat/contrib/agent_eval/task.py +42 -0
  23. autogen/agentchat/contrib/agent_optimizer.py +429 -0
  24. autogen/agentchat/contrib/capabilities/__init__.py +5 -0
  25. autogen/agentchat/contrib/capabilities/agent_capability.py +20 -0
  26. autogen/agentchat/contrib/capabilities/generate_images.py +301 -0
  27. autogen/agentchat/contrib/capabilities/teachability.py +393 -0
  28. autogen/agentchat/contrib/capabilities/text_compressors.py +66 -0
  29. autogen/agentchat/contrib/capabilities/tools_capability.py +22 -0
  30. autogen/agentchat/contrib/capabilities/transform_messages.py +93 -0
  31. autogen/agentchat/contrib/capabilities/transforms.py +566 -0
  32. autogen/agentchat/contrib/capabilities/transforms_util.py +122 -0
  33. autogen/agentchat/contrib/capabilities/vision_capability.py +214 -0
  34. autogen/agentchat/contrib/captainagent/__init__.py +9 -0
  35. autogen/agentchat/contrib/captainagent/agent_builder.py +790 -0
  36. autogen/agentchat/contrib/captainagent/captainagent.py +512 -0
  37. autogen/agentchat/contrib/captainagent/tool_retriever.py +335 -0
  38. autogen/agentchat/contrib/captainagent/tools/README.md +44 -0
  39. autogen/agentchat/contrib/captainagent/tools/__init__.py +5 -0
  40. autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_correlation.py +40 -0
  41. autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_skewness_and_kurtosis.py +28 -0
  42. autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_iqr.py +28 -0
  43. autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_zscore.py +28 -0
  44. autogen/agentchat/contrib/captainagent/tools/data_analysis/explore_csv.py +21 -0
  45. autogen/agentchat/contrib/captainagent/tools/data_analysis/shapiro_wilk_test.py +30 -0
  46. autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_download.py +27 -0
  47. autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_search.py +53 -0
  48. autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_image.py +53 -0
  49. autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_text.py +38 -0
  50. autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_wikipedia_text.py +21 -0
  51. autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_youtube_caption.py +34 -0
  52. autogen/agentchat/contrib/captainagent/tools/information_retrieval/image_qa.py +60 -0
  53. autogen/agentchat/contrib/captainagent/tools/information_retrieval/optical_character_recognition.py +61 -0
  54. autogen/agentchat/contrib/captainagent/tools/information_retrieval/perform_web_search.py +47 -0
  55. autogen/agentchat/contrib/captainagent/tools/information_retrieval/scrape_wikipedia_tables.py +33 -0
  56. autogen/agentchat/contrib/captainagent/tools/information_retrieval/transcribe_audio_file.py +21 -0
  57. autogen/agentchat/contrib/captainagent/tools/information_retrieval/youtube_download.py +35 -0
  58. autogen/agentchat/contrib/captainagent/tools/math/calculate_circle_area_from_diameter.py +21 -0
  59. autogen/agentchat/contrib/captainagent/tools/math/calculate_day_of_the_week.py +18 -0
  60. autogen/agentchat/contrib/captainagent/tools/math/calculate_fraction_sum.py +28 -0
  61. autogen/agentchat/contrib/captainagent/tools/math/calculate_matrix_power.py +31 -0
  62. autogen/agentchat/contrib/captainagent/tools/math/calculate_reflected_point.py +16 -0
  63. autogen/agentchat/contrib/captainagent/tools/math/complex_numbers_product.py +25 -0
  64. autogen/agentchat/contrib/captainagent/tools/math/compute_currency_conversion.py +23 -0
  65. autogen/agentchat/contrib/captainagent/tools/math/count_distinct_permutations.py +27 -0
  66. autogen/agentchat/contrib/captainagent/tools/math/evaluate_expression.py +28 -0
  67. autogen/agentchat/contrib/captainagent/tools/math/find_continuity_point.py +34 -0
  68. autogen/agentchat/contrib/captainagent/tools/math/fraction_to_mixed_numbers.py +39 -0
  69. autogen/agentchat/contrib/captainagent/tools/math/modular_inverse_sum.py +23 -0
  70. autogen/agentchat/contrib/captainagent/tools/math/simplify_mixed_numbers.py +36 -0
  71. autogen/agentchat/contrib/captainagent/tools/math/sum_of_digit_factorials.py +15 -0
  72. autogen/agentchat/contrib/captainagent/tools/math/sum_of_primes_below.py +15 -0
  73. autogen/agentchat/contrib/captainagent/tools/requirements.txt +10 -0
  74. autogen/agentchat/contrib/captainagent/tools/tool_description.tsv +34 -0
  75. autogen/agentchat/contrib/gpt_assistant_agent.py +526 -0
  76. autogen/agentchat/contrib/graph_rag/__init__.py +9 -0
  77. autogen/agentchat/contrib/graph_rag/document.py +29 -0
  78. autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +170 -0
  79. autogen/agentchat/contrib/graph_rag/falkor_graph_rag_capability.py +103 -0
  80. autogen/agentchat/contrib/graph_rag/graph_query_engine.py +53 -0
  81. autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +63 -0
  82. autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +268 -0
  83. autogen/agentchat/contrib/graph_rag/neo4j_graph_rag_capability.py +83 -0
  84. autogen/agentchat/contrib/graph_rag/neo4j_native_graph_query_engine.py +210 -0
  85. autogen/agentchat/contrib/graph_rag/neo4j_native_graph_rag_capability.py +93 -0
  86. autogen/agentchat/contrib/img_utils.py +397 -0
  87. autogen/agentchat/contrib/llamaindex_conversable_agent.py +117 -0
  88. autogen/agentchat/contrib/llava_agent.py +187 -0
  89. autogen/agentchat/contrib/math_user_proxy_agent.py +464 -0
  90. autogen/agentchat/contrib/multimodal_conversable_agent.py +125 -0
  91. autogen/agentchat/contrib/qdrant_retrieve_user_proxy_agent.py +324 -0
  92. autogen/agentchat/contrib/rag/__init__.py +10 -0
  93. autogen/agentchat/contrib/rag/chromadb_query_engine.py +272 -0
  94. autogen/agentchat/contrib/rag/llamaindex_query_engine.py +198 -0
  95. autogen/agentchat/contrib/rag/mongodb_query_engine.py +329 -0
  96. autogen/agentchat/contrib/rag/query_engine.py +74 -0
  97. autogen/agentchat/contrib/retrieve_assistant_agent.py +56 -0
  98. autogen/agentchat/contrib/retrieve_user_proxy_agent.py +703 -0
  99. autogen/agentchat/contrib/society_of_mind_agent.py +199 -0
  100. autogen/agentchat/contrib/swarm_agent.py +1425 -0
  101. autogen/agentchat/contrib/text_analyzer_agent.py +79 -0
  102. autogen/agentchat/contrib/vectordb/__init__.py +5 -0
  103. autogen/agentchat/contrib/vectordb/base.py +232 -0
  104. autogen/agentchat/contrib/vectordb/chromadb.py +315 -0
  105. autogen/agentchat/contrib/vectordb/couchbase.py +407 -0
  106. autogen/agentchat/contrib/vectordb/mongodb.py +550 -0
  107. autogen/agentchat/contrib/vectordb/pgvectordb.py +928 -0
  108. autogen/agentchat/contrib/vectordb/qdrant.py +320 -0
  109. autogen/agentchat/contrib/vectordb/utils.py +126 -0
  110. autogen/agentchat/contrib/web_surfer.py +303 -0
  111. autogen/agentchat/conversable_agent.py +4020 -0
  112. autogen/agentchat/group/__init__.py +64 -0
  113. autogen/agentchat/group/available_condition.py +91 -0
  114. autogen/agentchat/group/context_condition.py +77 -0
  115. autogen/agentchat/group/context_expression.py +238 -0
  116. autogen/agentchat/group/context_str.py +41 -0
  117. autogen/agentchat/group/context_variables.py +192 -0
  118. autogen/agentchat/group/group_tool_executor.py +202 -0
  119. autogen/agentchat/group/group_utils.py +591 -0
  120. autogen/agentchat/group/handoffs.py +244 -0
  121. autogen/agentchat/group/llm_condition.py +93 -0
  122. autogen/agentchat/group/multi_agent_chat.py +237 -0
  123. autogen/agentchat/group/on_condition.py +58 -0
  124. autogen/agentchat/group/on_context_condition.py +54 -0
  125. autogen/agentchat/group/patterns/__init__.py +18 -0
  126. autogen/agentchat/group/patterns/auto.py +159 -0
  127. autogen/agentchat/group/patterns/manual.py +176 -0
  128. autogen/agentchat/group/patterns/pattern.py +288 -0
  129. autogen/agentchat/group/patterns/random.py +106 -0
  130. autogen/agentchat/group/patterns/round_robin.py +117 -0
  131. autogen/agentchat/group/reply_result.py +26 -0
  132. autogen/agentchat/group/speaker_selection_result.py +41 -0
  133. autogen/agentchat/group/targets/__init__.py +4 -0
  134. autogen/agentchat/group/targets/group_chat_target.py +132 -0
  135. autogen/agentchat/group/targets/group_manager_target.py +151 -0
  136. autogen/agentchat/group/targets/transition_target.py +413 -0
  137. autogen/agentchat/group/targets/transition_utils.py +6 -0
  138. autogen/agentchat/groupchat.py +1694 -0
  139. autogen/agentchat/realtime/__init__.py +3 -0
  140. autogen/agentchat/realtime/experimental/__init__.py +20 -0
  141. autogen/agentchat/realtime/experimental/audio_adapters/__init__.py +8 -0
  142. autogen/agentchat/realtime/experimental/audio_adapters/twilio_audio_adapter.py +148 -0
  143. autogen/agentchat/realtime/experimental/audio_adapters/websocket_audio_adapter.py +139 -0
  144. autogen/agentchat/realtime/experimental/audio_observer.py +42 -0
  145. autogen/agentchat/realtime/experimental/clients/__init__.py +15 -0
  146. autogen/agentchat/realtime/experimental/clients/gemini/__init__.py +7 -0
  147. autogen/agentchat/realtime/experimental/clients/gemini/client.py +274 -0
  148. autogen/agentchat/realtime/experimental/clients/oai/__init__.py +8 -0
  149. autogen/agentchat/realtime/experimental/clients/oai/base_client.py +220 -0
  150. autogen/agentchat/realtime/experimental/clients/oai/rtc_client.py +243 -0
  151. autogen/agentchat/realtime/experimental/clients/oai/utils.py +48 -0
  152. autogen/agentchat/realtime/experimental/clients/realtime_client.py +190 -0
  153. autogen/agentchat/realtime/experimental/function_observer.py +85 -0
  154. autogen/agentchat/realtime/experimental/realtime_agent.py +158 -0
  155. autogen/agentchat/realtime/experimental/realtime_events.py +42 -0
  156. autogen/agentchat/realtime/experimental/realtime_observer.py +100 -0
  157. autogen/agentchat/realtime/experimental/realtime_swarm.py +475 -0
  158. autogen/agentchat/realtime/experimental/websockets.py +21 -0
  159. autogen/agentchat/realtime_agent/__init__.py +21 -0
  160. autogen/agentchat/user_proxy_agent.py +111 -0
  161. autogen/agentchat/utils.py +206 -0
  162. autogen/agents/__init__.py +3 -0
  163. autogen/agents/contrib/__init__.py +10 -0
  164. autogen/agents/contrib/time/__init__.py +8 -0
  165. autogen/agents/contrib/time/time_reply_agent.py +73 -0
  166. autogen/agents/contrib/time/time_tool_agent.py +51 -0
  167. autogen/agents/experimental/__init__.py +27 -0
  168. autogen/agents/experimental/deep_research/__init__.py +7 -0
  169. autogen/agents/experimental/deep_research/deep_research.py +52 -0
  170. autogen/agents/experimental/discord/__init__.py +7 -0
  171. autogen/agents/experimental/discord/discord.py +66 -0
  172. autogen/agents/experimental/document_agent/__init__.py +19 -0
  173. autogen/agents/experimental/document_agent/chroma_query_engine.py +316 -0
  174. autogen/agents/experimental/document_agent/docling_doc_ingest_agent.py +118 -0
  175. autogen/agents/experimental/document_agent/document_agent.py +461 -0
  176. autogen/agents/experimental/document_agent/document_conditions.py +50 -0
  177. autogen/agents/experimental/document_agent/document_utils.py +380 -0
  178. autogen/agents/experimental/document_agent/inmemory_query_engine.py +220 -0
  179. autogen/agents/experimental/document_agent/parser_utils.py +130 -0
  180. autogen/agents/experimental/document_agent/url_utils.py +426 -0
  181. autogen/agents/experimental/reasoning/__init__.py +7 -0
  182. autogen/agents/experimental/reasoning/reasoning_agent.py +1178 -0
  183. autogen/agents/experimental/slack/__init__.py +7 -0
  184. autogen/agents/experimental/slack/slack.py +73 -0
  185. autogen/agents/experimental/telegram/__init__.py +7 -0
  186. autogen/agents/experimental/telegram/telegram.py +77 -0
  187. autogen/agents/experimental/websurfer/__init__.py +7 -0
  188. autogen/agents/experimental/websurfer/websurfer.py +62 -0
  189. autogen/agents/experimental/wikipedia/__init__.py +7 -0
  190. autogen/agents/experimental/wikipedia/wikipedia.py +90 -0
  191. autogen/browser_utils.py +309 -0
  192. autogen/cache/__init__.py +10 -0
  193. autogen/cache/abstract_cache_base.py +75 -0
  194. autogen/cache/cache.py +203 -0
  195. autogen/cache/cache_factory.py +88 -0
  196. autogen/cache/cosmos_db_cache.py +144 -0
  197. autogen/cache/disk_cache.py +102 -0
  198. autogen/cache/in_memory_cache.py +58 -0
  199. autogen/cache/redis_cache.py +123 -0
  200. autogen/code_utils.py +596 -0
  201. autogen/coding/__init__.py +22 -0
  202. autogen/coding/base.py +119 -0
  203. autogen/coding/docker_commandline_code_executor.py +268 -0
  204. autogen/coding/factory.py +47 -0
  205. autogen/coding/func_with_reqs.py +202 -0
  206. autogen/coding/jupyter/__init__.py +23 -0
  207. autogen/coding/jupyter/base.py +36 -0
  208. autogen/coding/jupyter/docker_jupyter_server.py +167 -0
  209. autogen/coding/jupyter/embedded_ipython_code_executor.py +182 -0
  210. autogen/coding/jupyter/import_utils.py +82 -0
  211. autogen/coding/jupyter/jupyter_client.py +231 -0
  212. autogen/coding/jupyter/jupyter_code_executor.py +160 -0
  213. autogen/coding/jupyter/local_jupyter_server.py +172 -0
  214. autogen/coding/local_commandline_code_executor.py +405 -0
  215. autogen/coding/markdown_code_extractor.py +45 -0
  216. autogen/coding/utils.py +56 -0
  217. autogen/doc_utils.py +34 -0
  218. autogen/events/__init__.py +7 -0
  219. autogen/events/agent_events.py +1010 -0
  220. autogen/events/base_event.py +99 -0
  221. autogen/events/client_events.py +167 -0
  222. autogen/events/helpers.py +36 -0
  223. autogen/events/print_event.py +46 -0
  224. autogen/exception_utils.py +73 -0
  225. autogen/extensions/__init__.py +5 -0
  226. autogen/fast_depends/__init__.py +16 -0
  227. autogen/fast_depends/_compat.py +80 -0
  228. autogen/fast_depends/core/__init__.py +14 -0
  229. autogen/fast_depends/core/build.py +225 -0
  230. autogen/fast_depends/core/model.py +576 -0
  231. autogen/fast_depends/dependencies/__init__.py +15 -0
  232. autogen/fast_depends/dependencies/model.py +29 -0
  233. autogen/fast_depends/dependencies/provider.py +39 -0
  234. autogen/fast_depends/library/__init__.py +10 -0
  235. autogen/fast_depends/library/model.py +46 -0
  236. autogen/fast_depends/py.typed +6 -0
  237. autogen/fast_depends/schema.py +66 -0
  238. autogen/fast_depends/use.py +280 -0
  239. autogen/fast_depends/utils.py +187 -0
  240. autogen/formatting_utils.py +83 -0
  241. autogen/function_utils.py +13 -0
  242. autogen/graph_utils.py +178 -0
  243. autogen/import_utils.py +526 -0
  244. autogen/interop/__init__.py +22 -0
  245. autogen/interop/crewai/__init__.py +7 -0
  246. autogen/interop/crewai/crewai.py +88 -0
  247. autogen/interop/interoperability.py +71 -0
  248. autogen/interop/interoperable.py +46 -0
  249. autogen/interop/langchain/__init__.py +8 -0
  250. autogen/interop/langchain/langchain_chat_model_factory.py +155 -0
  251. autogen/interop/langchain/langchain_tool.py +82 -0
  252. autogen/interop/litellm/__init__.py +7 -0
  253. autogen/interop/litellm/litellm_config_factory.py +113 -0
  254. autogen/interop/pydantic_ai/__init__.py +7 -0
  255. autogen/interop/pydantic_ai/pydantic_ai.py +168 -0
  256. autogen/interop/registry.py +69 -0
  257. autogen/io/__init__.py +15 -0
  258. autogen/io/base.py +151 -0
  259. autogen/io/console.py +56 -0
  260. autogen/io/processors/__init__.py +12 -0
  261. autogen/io/processors/base.py +21 -0
  262. autogen/io/processors/console_event_processor.py +56 -0
  263. autogen/io/run_response.py +293 -0
  264. autogen/io/thread_io_stream.py +63 -0
  265. autogen/io/websockets.py +213 -0
  266. autogen/json_utils.py +43 -0
  267. autogen/llm_config.py +379 -0
  268. autogen/logger/__init__.py +11 -0
  269. autogen/logger/base_logger.py +128 -0
  270. autogen/logger/file_logger.py +261 -0
  271. autogen/logger/logger_factory.py +42 -0
  272. autogen/logger/logger_utils.py +57 -0
  273. autogen/logger/sqlite_logger.py +523 -0
  274. autogen/math_utils.py +339 -0
  275. autogen/mcp/__init__.py +7 -0
  276. autogen/mcp/mcp_client.py +208 -0
  277. autogen/messages/__init__.py +7 -0
  278. autogen/messages/agent_messages.py +948 -0
  279. autogen/messages/base_message.py +107 -0
  280. autogen/messages/client_messages.py +171 -0
  281. autogen/messages/print_message.py +49 -0
  282. autogen/oai/__init__.py +53 -0
  283. autogen/oai/anthropic.py +714 -0
  284. autogen/oai/bedrock.py +628 -0
  285. autogen/oai/cerebras.py +299 -0
  286. autogen/oai/client.py +1435 -0
  287. autogen/oai/client_utils.py +169 -0
  288. autogen/oai/cohere.py +479 -0
  289. autogen/oai/gemini.py +990 -0
  290. autogen/oai/gemini_types.py +129 -0
  291. autogen/oai/groq.py +305 -0
  292. autogen/oai/mistral.py +303 -0
  293. autogen/oai/oai_models/__init__.py +11 -0
  294. autogen/oai/oai_models/_models.py +16 -0
  295. autogen/oai/oai_models/chat_completion.py +87 -0
  296. autogen/oai/oai_models/chat_completion_audio.py +32 -0
  297. autogen/oai/oai_models/chat_completion_message.py +86 -0
  298. autogen/oai/oai_models/chat_completion_message_tool_call.py +37 -0
  299. autogen/oai/oai_models/chat_completion_token_logprob.py +63 -0
  300. autogen/oai/oai_models/completion_usage.py +60 -0
  301. autogen/oai/ollama.py +643 -0
  302. autogen/oai/openai_utils.py +881 -0
  303. autogen/oai/together.py +370 -0
  304. autogen/retrieve_utils.py +491 -0
  305. autogen/runtime_logging.py +160 -0
  306. autogen/token_count_utils.py +267 -0
  307. autogen/tools/__init__.py +20 -0
  308. autogen/tools/contrib/__init__.py +9 -0
  309. autogen/tools/contrib/time/__init__.py +7 -0
  310. autogen/tools/contrib/time/time.py +41 -0
  311. autogen/tools/dependency_injection.py +254 -0
  312. autogen/tools/experimental/__init__.py +43 -0
  313. autogen/tools/experimental/browser_use/__init__.py +7 -0
  314. autogen/tools/experimental/browser_use/browser_use.py +161 -0
  315. autogen/tools/experimental/crawl4ai/__init__.py +7 -0
  316. autogen/tools/experimental/crawl4ai/crawl4ai.py +153 -0
  317. autogen/tools/experimental/deep_research/__init__.py +7 -0
  318. autogen/tools/experimental/deep_research/deep_research.py +328 -0
  319. autogen/tools/experimental/duckduckgo/__init__.py +7 -0
  320. autogen/tools/experimental/duckduckgo/duckduckgo_search.py +109 -0
  321. autogen/tools/experimental/google/__init__.py +14 -0
  322. autogen/tools/experimental/google/authentication/__init__.py +11 -0
  323. autogen/tools/experimental/google/authentication/credentials_hosted_provider.py +43 -0
  324. autogen/tools/experimental/google/authentication/credentials_local_provider.py +91 -0
  325. autogen/tools/experimental/google/authentication/credentials_provider.py +35 -0
  326. autogen/tools/experimental/google/drive/__init__.py +9 -0
  327. autogen/tools/experimental/google/drive/drive_functions.py +124 -0
  328. autogen/tools/experimental/google/drive/toolkit.py +88 -0
  329. autogen/tools/experimental/google/model.py +17 -0
  330. autogen/tools/experimental/google/toolkit_protocol.py +19 -0
  331. autogen/tools/experimental/google_search/__init__.py +8 -0
  332. autogen/tools/experimental/google_search/google_search.py +93 -0
  333. autogen/tools/experimental/google_search/youtube_search.py +181 -0
  334. autogen/tools/experimental/messageplatform/__init__.py +17 -0
  335. autogen/tools/experimental/messageplatform/discord/__init__.py +7 -0
  336. autogen/tools/experimental/messageplatform/discord/discord.py +288 -0
  337. autogen/tools/experimental/messageplatform/slack/__init__.py +7 -0
  338. autogen/tools/experimental/messageplatform/slack/slack.py +391 -0
  339. autogen/tools/experimental/messageplatform/telegram/__init__.py +7 -0
  340. autogen/tools/experimental/messageplatform/telegram/telegram.py +275 -0
  341. autogen/tools/experimental/perplexity/__init__.py +7 -0
  342. autogen/tools/experimental/perplexity/perplexity_search.py +260 -0
  343. autogen/tools/experimental/tavily/__init__.py +7 -0
  344. autogen/tools/experimental/tavily/tavily_search.py +183 -0
  345. autogen/tools/experimental/web_search_preview/__init__.py +7 -0
  346. autogen/tools/experimental/web_search_preview/web_search_preview.py +114 -0
  347. autogen/tools/experimental/wikipedia/__init__.py +7 -0
  348. autogen/tools/experimental/wikipedia/wikipedia.py +287 -0
  349. autogen/tools/function_utils.py +411 -0
  350. autogen/tools/tool.py +187 -0
  351. autogen/tools/toolkit.py +86 -0
  352. autogen/types.py +29 -0
  353. autogen/version.py +7 -0
  354. ag2-0.9.1a1.dist-info/RECORD +0 -6
  355. ag2-0.9.1a1.dist-info/top_level.txt +0 -1
  356. {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info/licenses}/LICENSE +0 -0
  357. {ag2-0.9.1a1.dist-info → ag2-0.9.1.post0.dist-info/licenses}/NOTICE.md +0 -0
autogen/graph_utils.py ADDED
@@ -0,0 +1,178 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
+ # SPDX-License-Identifier: MIT
7
+
8
+ import logging
9
+ from typing import Optional
10
+
11
+ from .agentchat import Agent
12
+ from .import_utils import optional_import_block, require_optional_import
13
+
14
+ with optional_import_block():
15
+ import matplotlib.pyplot as plt
16
+ import networkx as nx
17
+
18
+
19
+ def has_self_loops(allowed_speaker_transitions: dict[str, list[Agent]]) -> bool:
20
+ """Check if there are self loops in the allowed_speaker_transitions.
21
+
22
+ Args:
23
+ allowed_speaker_transitions (dict[str, list[Agent]]): A dictionary of keys and list as values. The keys are the names of the agents, and the values are the names of the agents that the key agent can transition
24
+
25
+ Returns:
26
+ True if there are self loops in the allowed_speaker_transitions_Dict.
27
+ """
28
+
29
+ return any([key in value for key, value in allowed_speaker_transitions.items()])
30
+
31
+
32
+ def check_graph_validity(
33
+ allowed_speaker_transitions_dict: dict[str, list[Agent]],
34
+ agents: list[Agent],
35
+ ) -> None:
36
+ """Check the validity of the allowed_speaker_transitions_dict.
37
+
38
+ Args:
39
+ allowed_speaker_transitions_dict (dict[str, list[Agent]]):
40
+ A dictionary of keys and list as values. The keys are the names of the agents, and the values are the names of the agents that the key agent can transition to.
41
+ agents (list[Agent]): A list of Agents
42
+
43
+ agents: A list of Agents
44
+
45
+ Checks for the following:
46
+ Errors
47
+ 1. The dictionary must have a structure of keys and list as values
48
+ 2. Every key exists in agents.
49
+ 3. Every value is a list of Agents (not string).
50
+
51
+ Warnings:
52
+ 1. Warning if there are isolated agent nodes
53
+ 2. Warning if the set of agents in allowed_speaker_transitions do not match agents
54
+ 3. Warning if there are duplicated agents in any values of `allowed_speaker_transitions_dict`
55
+ """
56
+ # Errors
57
+
58
+ # Check 1. The dictionary must have a structure of keys and list as values
59
+ if not isinstance(allowed_speaker_transitions_dict, dict):
60
+ raise ValueError("allowed_speaker_transitions_dict must be a dictionary.")
61
+
62
+ # All values must be lists of Agent or empty
63
+ if not all([isinstance(value, list) for value in allowed_speaker_transitions_dict.values()]):
64
+ raise ValueError("allowed_speaker_transitions_dict must be a dictionary with lists as values.")
65
+
66
+ # Check 2. Every key exists in agents
67
+ if not all([key in agents for key in allowed_speaker_transitions_dict]):
68
+ raise ValueError("allowed_speaker_transitions_dict has keys not in agents.")
69
+
70
+ # Check 3. Every value is a list of Agents or empty list (not string).
71
+ if not all([
72
+ all([isinstance(agent, Agent) for agent in value]) for value in allowed_speaker_transitions_dict.values()
73
+ ]):
74
+ raise ValueError("allowed_speaker_transitions_dict has values that are not lists of Agents.")
75
+
76
+ # Warnings
77
+ # Warning 1. Warning if there are isolated agent nodes, there are not incoming nor outgoing edges
78
+ # Concat keys if len(value) is positive
79
+ has_outgoing_edge = []
80
+ for key, agent_list in allowed_speaker_transitions_dict.items():
81
+ if len(agent_list) > 0:
82
+ has_outgoing_edge.append(key)
83
+ no_outgoing_edges = [agent for agent in agents if agent not in has_outgoing_edge]
84
+
85
+ # allowed_speaker_transitions_dict.values() is a list of list of Agents
86
+ # values_all_agents is a list of all agents in allowed_speaker_transitions_dict.values()
87
+ has_incoming_edge = []
88
+ for agent_list in allowed_speaker_transitions_dict.values():
89
+ if len(agent_list) > 0:
90
+ has_incoming_edge.extend(agent_list)
91
+
92
+ no_incoming_edges = [agent for agent in agents if agent not in has_incoming_edge]
93
+
94
+ isolated_agents = set(no_incoming_edges).intersection(set(no_outgoing_edges))
95
+ if len(isolated_agents) > 0:
96
+ logging.warning(
97
+ f"""Warning: There are isolated agent nodes, there are not incoming nor outgoing edges. Isolated agents: {[agent.name for agent in isolated_agents]}"""
98
+ )
99
+
100
+ # Warning 2. Warning if the set of agents in allowed_speaker_transitions do not match agents
101
+ # Get set of agents
102
+ agents_in_allowed_speaker_transitions = set(has_incoming_edge).union(set(has_outgoing_edge))
103
+ full_anti_join = set(agents_in_allowed_speaker_transitions).symmetric_difference(set(agents))
104
+ if len(full_anti_join) > 0:
105
+ logging.warning(
106
+ f"""Warning: The set of agents in allowed_speaker_transitions do not match agents. Offending agents: {[agent.name for agent in full_anti_join]}"""
107
+ )
108
+
109
+ # Warning 3. Warning if there are duplicated agents in any values of `allowed_speaker_transitions_dict`
110
+ for key, values in allowed_speaker_transitions_dict.items():
111
+ duplicates = [item for item in values if values.count(item) > 1]
112
+ unique_duplicates = list(set(duplicates))
113
+ if unique_duplicates:
114
+ logging.warning(
115
+ f"Agent '{key.name}' has duplicate elements: {[agent.name for agent in unique_duplicates]}. Please remove duplicates manually."
116
+ )
117
+
118
+
119
+ def invert_disallowed_to_allowed(
120
+ disallowed_speaker_transitions_dict: dict[str, list[Agent]], agents: list[Agent]
121
+ ) -> dict[str, list[Agent]]:
122
+ """Invert the disallowed_speaker_transitions_dict to form the allowed_speaker_transitions_dict.
123
+
124
+ Start with a fully connected allowed_speaker_transitions_dict of all agents. Remove edges from the fully connected allowed_speaker_transitions_dict according to the disallowed_speaker_transitions_dict to form the allowed_speaker_transitions_dict.
125
+
126
+ Args:
127
+ disallowed_speaker_transitions_dict: A dictionary of keys and list as values. The keys are the names of the agents, and the values are the names of the agents that the key agent cannot transition to.
128
+ agents: A list of Agents
129
+
130
+ Returns:
131
+ allowed_speaker_transitions_dict: A dictionary of keys and list as values. The keys are the names of the agents, and the values are the names of the agents that the key agent can transition to.
132
+ """
133
+ # Create a fully connected allowed_speaker_transitions_dict of all agents
134
+ allowed_speaker_transitions_dict = {agent: [other_agent for other_agent in agents] for agent in agents}
135
+
136
+ # Remove edges from allowed_speaker_transitions_dict according to the disallowed_speaker_transitions_dict
137
+ for key, value in disallowed_speaker_transitions_dict.items():
138
+ allowed_speaker_transitions_dict[key] = [
139
+ agent for agent in allowed_speaker_transitions_dict[key] if agent not in value
140
+ ]
141
+
142
+ return allowed_speaker_transitions_dict
143
+
144
+
145
+ @require_optional_import(["matplotlib", "networkx"], "graph")
146
+ def visualize_speaker_transitions_dict(
147
+ speaker_transitions_dict: dict[str, list[Agent]], agents: list[Agent], export_path: Optional[str] = None
148
+ ) -> None:
149
+ """Visualize the speaker_transitions_dict using networkx.
150
+
151
+ Args:
152
+ speaker_transitions_dict: A dictionary of keys and list as values. The keys are the names of the agents, and the values are the names of the agents that the key agent can transition to.
153
+ agents: A list of Agents
154
+ export_path: The path to export the graph. If None, the graph will be shown.
155
+
156
+ Returns:
157
+ None
158
+
159
+
160
+ """
161
+
162
+ g = nx.DiGraph()
163
+
164
+ # Add nodes
165
+ g.add_nodes_from([agent.name for agent in agents])
166
+
167
+ # Add edges
168
+ for key, value in speaker_transitions_dict.items():
169
+ for agent in value:
170
+ g.add_edge(key.name, agent.name)
171
+
172
+ # Visualize
173
+ nx.draw(g, with_labels=True, font_weight="bold")
174
+
175
+ if export_path is not None:
176
+ plt.savefig(export_path)
177
+ else:
178
+ plt.show()
@@ -0,0 +1,526 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import inspect
6
+ import re
7
+ import sys
8
+ from abc import ABC, abstractmethod
9
+ from contextlib import contextmanager, suppress
10
+ from dataclasses import dataclass
11
+ from functools import wraps
12
+ from logging import getLogger
13
+ from pathlib import Path
14
+ from typing import Any, Callable, Generator, Generic, Iterable, Optional, TypeVar, Union
15
+
16
+ __all__ = [
17
+ "optional_import_block",
18
+ "patch_object",
19
+ "require_optional_import",
20
+ "run_for_optional_imports",
21
+ "skip_on_missing_imports",
22
+ ]
23
+
24
+ logger = getLogger(__name__)
25
+
26
+
27
+ @dataclass
28
+ class ModuleInfo:
29
+ name: str
30
+ min_version: Optional[str] = None
31
+ max_version: Optional[str] = None
32
+ min_inclusive: bool = False
33
+ max_inclusive: bool = False
34
+
35
+ def is_in_sys_modules(self) -> Optional[str]:
36
+ """Check if the module is installed and satisfies the version constraints
37
+
38
+ Returns:
39
+ None if the module is installed and satisfies the version constraints, otherwise a message indicating the issue.
40
+
41
+ """
42
+ if self.name not in sys.modules:
43
+ return f"'{self.name}' is not installed."
44
+ else:
45
+ if hasattr(sys.modules[self.name], "__file__") and sys.modules[self.name].__file__ is not None:
46
+ autogen_path = (Path(__file__).parent).resolve()
47
+ test_path = (Path(__file__).parent.parent / "test").resolve()
48
+ module_path = Path(sys.modules[self.name].__file__).resolve() # type: ignore[arg-type]
49
+
50
+ if str(autogen_path) in str(module_path) or str(test_path) in str(module_path):
51
+ # The module is in the autogen or test directory
52
+ # Aka similarly named module in the autogen or test directory
53
+ return f"'{self.name}' is not installed."
54
+
55
+ installed_version = (
56
+ sys.modules[self.name].__version__ if hasattr(sys.modules[self.name], "__version__") else None
57
+ )
58
+ if installed_version is None and (self.min_version or self.max_version):
59
+ return f"'{self.name}' is installed, but the version is not available."
60
+
61
+ if self.min_version:
62
+ msg = f"'{self.name}' is installed, but the installed version {installed_version} is too low (required '{self}')."
63
+ if not self.min_inclusive and installed_version == self.min_version:
64
+ return msg
65
+ if self.min_inclusive and installed_version < self.min_version: # type: ignore[operator]
66
+ return msg
67
+
68
+ if self.max_version:
69
+ msg = f"'{self.name}' is installed, but the installed version {installed_version} is too high (required '{self}')."
70
+ if not self.max_inclusive and installed_version == self.max_version:
71
+ return msg
72
+ if self.max_inclusive and installed_version > self.max_version: # type: ignore[operator]
73
+ return msg
74
+
75
+ return None
76
+
77
+ def __repr__(self) -> str:
78
+ s = self.name
79
+ if self.min_version:
80
+ s += f">={self.min_version}" if self.min_inclusive else f">{self.min_version}"
81
+ if self.max_version:
82
+ s += f"<={self.max_version}" if self.max_inclusive else f"<{self.max_version}"
83
+ return s
84
+
85
+ @classmethod
86
+ def from_str(cls, module_info: str) -> "ModuleInfo":
87
+ """Parse a string to create a ModuleInfo object
88
+
89
+ Args:
90
+ module_info (str): A string containing the module name and optional version constraints
91
+
92
+ Returns:
93
+ ModuleInfo: A ModuleInfo object with the parsed information
94
+
95
+ Raises:
96
+ ValueError: If the module information is invalid
97
+ """
98
+
99
+ pattern = re.compile(r"^(?P<name>[a-zA-Z0-9-_]+)(?P<constraint>.*)$")
100
+ match = pattern.match(module_info.strip())
101
+
102
+ if not match:
103
+ raise ValueError(f"Invalid package information: {module_info}")
104
+
105
+ name = match.group("name")
106
+ constraints = match.group("constraint").strip()
107
+ min_version = max_version = None
108
+ min_inclusive = max_inclusive = False
109
+
110
+ if constraints:
111
+ constraint_pattern = re.findall(r"(>=|<=|>|<)([0-9\.]+)?", constraints)
112
+
113
+ if not all(version for _, version in constraint_pattern):
114
+ raise ValueError(f"Invalid module information: {module_info}")
115
+
116
+ for operator, version in constraint_pattern:
117
+ if operator == ">=":
118
+ min_version = version
119
+ min_inclusive = True
120
+ elif operator == "<=":
121
+ max_version = version
122
+ max_inclusive = True
123
+ elif operator == ">":
124
+ min_version = version
125
+ min_inclusive = False
126
+ elif operator == "<":
127
+ max_version = version
128
+ max_inclusive = False
129
+ else:
130
+ raise ValueError(f"Invalid package information: {module_info}")
131
+
132
+ return ModuleInfo(
133
+ name=name,
134
+ min_version=min_version,
135
+ max_version=max_version,
136
+ min_inclusive=min_inclusive,
137
+ max_inclusive=max_inclusive,
138
+ )
139
+
140
+
141
+ class Result:
142
+ def __init__(self) -> None:
143
+ self._failed: Optional[bool] = None
144
+
145
+ @property
146
+ def is_successful(self) -> bool:
147
+ if self._failed is None:
148
+ raise ValueError("Result not set")
149
+ return not self._failed
150
+
151
+
152
+ @contextmanager
153
+ def optional_import_block() -> Generator[Result, None, None]:
154
+ """Guard a block of code to suppress ImportErrors
155
+
156
+ A context manager to temporarily suppress ImportErrors.
157
+ Use this to attempt imports without failing immediately on missing modules.
158
+
159
+ Example:
160
+ ```python
161
+ with optional_import_block():
162
+ import some_module
163
+ import some_other_module
164
+ ```
165
+ """
166
+ result = Result()
167
+ try:
168
+ yield result
169
+ result._failed = False
170
+ except ImportError as e:
171
+ # Ignore ImportErrors during this context
172
+ logger.debug(f"Ignoring ImportError: {e}")
173
+ result._failed = True
174
+
175
+
176
+ def get_missing_imports(modules: Union[str, Iterable[str]]) -> dict[str, str]:
177
+ """Get missing modules from a list of module names
178
+
179
+ Args:
180
+ modules (Union[str, Iterable[str]]): Module name or list of module names
181
+
182
+ Returns:
183
+ List of missing module names
184
+ """
185
+ if isinstance(modules, str):
186
+ modules = [modules]
187
+
188
+ module_infos = [ModuleInfo.from_str(module) for module in modules]
189
+ x = {m.name: m.is_in_sys_modules() for m in module_infos}
190
+ return {k: v for k, v in x.items() if v}
191
+
192
+
193
+ T = TypeVar("T")
194
+ G = TypeVar("G", bound=Union[Callable[..., Any], type])
195
+ F = TypeVar("F", bound=Callable[..., Any])
196
+
197
+
198
+ class PatchObject(ABC, Generic[T]):
199
+ def __init__(self, o: T, missing_modules: dict[str, str], dep_target: str):
200
+ if not self.accept(o):
201
+ raise ValueError(f"Cannot patch object of type {type(o)}")
202
+
203
+ self.o = o
204
+ self.missing_modules = missing_modules
205
+ self.dep_target = dep_target
206
+
207
+ @classmethod
208
+ @abstractmethod
209
+ def accept(cls, o: Any) -> bool: ...
210
+
211
+ @abstractmethod
212
+ def patch(self, except_for: Iterable[str]) -> T: ...
213
+
214
+ def get_object_with_metadata(self) -> Any:
215
+ return self.o
216
+
217
+ @property
218
+ def msg(self) -> str:
219
+ o = self.get_object_with_metadata()
220
+ plural = len(self.missing_modules) > 1
221
+ fqn = f"{o.__module__}.{o.__name__}" if hasattr(o, "__module__") else o.__name__
222
+ # modules_str = ", ".join([f"'{m}'" for m in self.missing_modules])
223
+ msg = f"{'Modules' if plural else 'A module'} needed for {fqn} {'are' if plural else 'is'} missing:\n"
224
+ for _, status in self.missing_modules.items():
225
+ msg += f" - {status}\n"
226
+ msg += f"Please install {'them' if plural else 'it'} using:\n'pip install ag2[{self.dep_target}]'"
227
+ return msg
228
+
229
+ def copy_metadata(self, retval: T) -> None:
230
+ """Copy metadata from original object to patched object
231
+
232
+ Args:
233
+ retval: Patched object
234
+
235
+ """
236
+ o = self.o
237
+ if hasattr(o, "__doc__"):
238
+ retval.__doc__ = o.__doc__
239
+ if hasattr(o, "__name__"):
240
+ retval.__name__ = o.__name__ # type: ignore[attr-defined]
241
+ if hasattr(o, "__module__"):
242
+ retval.__module__ = o.__module__
243
+
244
+ _registry: list[type["PatchObject[Any]"]] = []
245
+
246
+ @classmethod
247
+ def register(cls) -> Callable[[type["PatchObject[Any]"]], type["PatchObject[Any]"]]:
248
+ def decorator(subclass: type["PatchObject[Any]"]) -> type["PatchObject[Any]"]:
249
+ cls._registry.append(subclass)
250
+ return subclass
251
+
252
+ return decorator
253
+
254
+ @classmethod
255
+ def create(
256
+ cls,
257
+ o: T,
258
+ *,
259
+ missing_modules: dict[str, str],
260
+ dep_target: str,
261
+ ) -> Optional["PatchObject[T]"]:
262
+ for subclass in cls._registry:
263
+ if subclass.accept(o):
264
+ return subclass(o, missing_modules, dep_target)
265
+ return None
266
+
267
+
268
+ @PatchObject.register()
269
+ class PatchCallable(PatchObject[F]):
270
+ @classmethod
271
+ def accept(cls, o: Any) -> bool:
272
+ return inspect.isfunction(o) or inspect.ismethod(o)
273
+
274
+ def patch(self, except_for: Iterable[str]) -> F:
275
+ if self.o.__name__ in except_for:
276
+ return self.o
277
+
278
+ f: Callable[..., Any] = self.o
279
+
280
+ # @wraps(f.__call__) # type: ignore[operator]
281
+ @wraps(f)
282
+ def _call(*args: Any, **kwargs: Any) -> Any:
283
+ raise ImportError(self.msg)
284
+
285
+ self.copy_metadata(_call) # type: ignore[arg-type]
286
+
287
+ return _call # type: ignore[return-value]
288
+
289
+
290
+ @PatchObject.register()
291
+ class PatchStatic(PatchObject[F]):
292
+ @classmethod
293
+ def accept(cls, o: Any) -> bool:
294
+ # return inspect.ismethoddescriptor(o)
295
+ return isinstance(o, staticmethod)
296
+
297
+ def patch(self, except_for: Iterable[str]) -> F:
298
+ if hasattr(self.o, "__name__"):
299
+ name = self.o.__name__
300
+ elif hasattr(self.o, "__func__"):
301
+ name = self.o.__func__.__name__
302
+ else:
303
+ raise ValueError(f"Cannot determine name for object {self.o}")
304
+ if name in except_for:
305
+ return self.o
306
+
307
+ f: Callable[..., Any] = self.o.__func__ # type: ignore[attr-defined]
308
+
309
+ @wraps(f)
310
+ def _call(*args: Any, **kwargs: Any) -> Any:
311
+ raise ImportError(self.msg)
312
+
313
+ self.copy_metadata(_call) # type: ignore[arg-type]
314
+
315
+ return staticmethod(_call) # type: ignore[return-value]
316
+
317
+ def get_object_with_metadata(self) -> Any:
318
+ return self.o.__func__ # type: ignore[attr-defined]
319
+
320
+
321
+ @PatchObject.register()
322
+ class PatchInit(PatchObject[F]):
323
+ @classmethod
324
+ def accept(cls, o: Any) -> bool:
325
+ return inspect.ismethoddescriptor(o) and o.__name__ == "__init__"
326
+
327
+ def patch(self, except_for: Iterable[str]) -> F:
328
+ if self.o.__name__ in except_for:
329
+ return self.o
330
+
331
+ f: Callable[..., Any] = self.o
332
+
333
+ @wraps(f)
334
+ def _call(*args: Any, **kwargs: Any) -> Any:
335
+ raise ImportError(self.msg)
336
+
337
+ self.copy_metadata(_call) # type: ignore[arg-type]
338
+
339
+ return staticmethod(_call) # type: ignore[return-value]
340
+
341
+ def get_object_with_metadata(self) -> Any:
342
+ return self.o
343
+
344
+
345
+ @PatchObject.register()
346
+ class PatchProperty(PatchObject[Any]):
347
+ @classmethod
348
+ def accept(cls, o: Any) -> bool:
349
+ return inspect.isdatadescriptor(o) and hasattr(o, "fget")
350
+
351
+ def patch(self, except_for: Iterable[str]) -> property:
352
+ if not hasattr(self.o, "fget"):
353
+ raise ValueError(f"Cannot patch property without getter: {self.o}")
354
+ f: Callable[..., Any] = self.o.fget
355
+
356
+ if f.__name__ in except_for:
357
+ return self.o # type: ignore[no-any-return]
358
+
359
+ @wraps(f)
360
+ def _call(*args: Any, **kwargs: Any) -> Any:
361
+ raise ImportError(self.msg)
362
+
363
+ self.copy_metadata(_call)
364
+
365
+ return property(_call)
366
+
367
+ def get_object_with_metadata(self) -> Any:
368
+ return self.o.fget
369
+
370
+
371
+ @PatchObject.register()
372
+ class PatchClass(PatchObject[type[Any]]):
373
+ @classmethod
374
+ def accept(cls, o: Any) -> bool:
375
+ return inspect.isclass(o)
376
+
377
+ def patch(self, except_for: Iterable[str]) -> type[Any]:
378
+ if self.o.__name__ in except_for:
379
+ return self.o
380
+
381
+ for name, member in inspect.getmembers(self.o):
382
+ # Patch __init__ method if possible, but not other internal methods
383
+ if name.startswith("__") and name != "__init__":
384
+ continue
385
+ patched = patch_object(
386
+ member,
387
+ missing_modules=self.missing_modules,
388
+ dep_target=self.dep_target,
389
+ fail_if_not_patchable=False,
390
+ except_for=except_for,
391
+ )
392
+ with suppress(AttributeError):
393
+ setattr(self.o, name, patched)
394
+
395
+ return self.o
396
+
397
+
398
+ def patch_object(
399
+ o: T,
400
+ *,
401
+ missing_modules: dict[str, str],
402
+ dep_target: str,
403
+ fail_if_not_patchable: bool = True,
404
+ except_for: Optional[Union[str, Iterable[str]]] = None,
405
+ ) -> T:
406
+ patcher = PatchObject.create(o, missing_modules=missing_modules, dep_target=dep_target)
407
+ if fail_if_not_patchable and patcher is None:
408
+ raise ValueError(f"Cannot patch object of type {type(o)}")
409
+
410
+ except_for = except_for if except_for is not None else []
411
+ except_for = [except_for] if isinstance(except_for, str) else except_for
412
+
413
+ return patcher.patch(except_for=except_for) if patcher else o
414
+
415
+
416
+ def require_optional_import(
417
+ modules: Union[str, Iterable[str]],
418
+ dep_target: str,
419
+ *,
420
+ except_for: Optional[Union[str, Iterable[str]]] = None,
421
+ ) -> Callable[[T], T]:
422
+ """Decorator to handle optional module dependencies
423
+
424
+ Args:
425
+ modules: Module name or list of module names required
426
+ dep_target: Target name for pip installation (e.g. 'test' in pip install ag2[test])
427
+ except_for: Name or list of names of objects to exclude from patching
428
+ """
429
+ missing_modules = get_missing_imports(modules)
430
+
431
+ if not missing_modules:
432
+
433
+ def decorator(o: T) -> T:
434
+ return o
435
+
436
+ else:
437
+
438
+ def decorator(o: T) -> T:
439
+ return patch_object(o, missing_modules=missing_modules, dep_target=dep_target, except_for=except_for)
440
+
441
+ return decorator
442
+
443
+
444
+ def _mark_object(o: T, dep_target: str) -> T:
445
+ import pytest
446
+
447
+ markname = dep_target.replace("-", "_")
448
+ pytest_mark_markname = getattr(pytest.mark, markname)
449
+ pytest_mark_o = pytest_mark_markname(o)
450
+
451
+ pytest_mark_o = pytest.mark.aux_neg_flag(pytest_mark_o)
452
+
453
+ return pytest_mark_o # type: ignore[no-any-return]
454
+
455
+
456
+ def run_for_optional_imports(modules: Union[str, Iterable[str]], dep_target: str) -> Callable[[G], G]:
457
+ """Decorator to run a test if and only if optional modules are installed
458
+
459
+ Args:
460
+ modules: Module name or list of module names
461
+ dep_target: Target name for pip installation (e.g. 'test' in pip install ag2[test])
462
+ """
463
+ # missing_modules = get_missing_imports(modules)
464
+ # if missing_modules:
465
+ # raise ImportError(f"Missing module{'s' if len(missing_modules) > 1 else ''}: {', '.join(missing_modules)}. Install using 'pip install ag2[{dep_target}]'")
466
+
467
+ def decorator(o: G) -> G:
468
+ missing_modules = get_missing_imports(modules)
469
+
470
+ if isinstance(o, type):
471
+ wrapped = require_optional_import(modules, dep_target)(o)
472
+ else:
473
+ if inspect.iscoroutinefunction(o):
474
+
475
+ @wraps(o)
476
+ async def wrapped(*args: Any, **kwargs: Any) -> Any:
477
+ if missing_modules:
478
+ raise ImportError(
479
+ f"Missing module{'s' if len(missing_modules) > 1 else ''}: {', '.join(missing_modules)}. Install using 'pip install ag2[{dep_target}]'"
480
+ )
481
+ return await o(*args, **kwargs)
482
+
483
+ else:
484
+
485
+ @wraps(o)
486
+ def wrapped(*args: Any, **kwargs: Any) -> Any:
487
+ if missing_modules:
488
+ raise ImportError(
489
+ f"Missing module{'s' if len(missing_modules) > 1 else ''}: {', '.join(missing_modules)}. Install using 'pip install ag2[{dep_target}]'"
490
+ )
491
+ return o(*args, **kwargs)
492
+
493
+ pytest_mark_o: G = _mark_object(wrapped, dep_target) # type: ignore[assignment]
494
+
495
+ return pytest_mark_o
496
+
497
+ return decorator
498
+
499
+
500
+ def skip_on_missing_imports(modules: Union[str, Iterable[str]], dep_target: str) -> Callable[[T], T]:
501
+ """Decorator to skip a test if an optional module is missing
502
+
503
+ Args:
504
+ modules: Module name or list of module names
505
+ dep_target: Target name for pip installation (e.g. 'test' in pip install ag2[test])
506
+ """
507
+ import pytest
508
+
509
+ missing_modules = get_missing_imports(modules)
510
+
511
+ if not missing_modules:
512
+
513
+ def decorator(o: T) -> T:
514
+ pytest_mark_o = _mark_object(o, dep_target)
515
+ return pytest_mark_o # type: ignore[no-any-return]
516
+
517
+ else:
518
+
519
+ def decorator(o: T) -> T:
520
+ pytest_mark_o = _mark_object(o, dep_target)
521
+
522
+ return pytest.mark.skip( # type: ignore[return-value,no-any-return]
523
+ f"Missing module{'s' if len(missing_modules) > 1 else ''}: {', '.join(missing_modules)}. Install using 'pip install ag2[{dep_target}]'"
524
+ )(pytest_mark_o)
525
+
526
+ return decorator