ag2 0.10.2__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.
Files changed (423) hide show
  1. ag2-0.10.2.dist-info/METADATA +819 -0
  2. ag2-0.10.2.dist-info/RECORD +423 -0
  3. ag2-0.10.2.dist-info/WHEEL +4 -0
  4. ag2-0.10.2.dist-info/licenses/LICENSE +201 -0
  5. ag2-0.10.2.dist-info/licenses/NOTICE.md +19 -0
  6. autogen/__init__.py +88 -0
  7. autogen/_website/__init__.py +3 -0
  8. autogen/_website/generate_api_references.py +426 -0
  9. autogen/_website/generate_mkdocs.py +1216 -0
  10. autogen/_website/notebook_processor.py +475 -0
  11. autogen/_website/process_notebooks.py +656 -0
  12. autogen/_website/utils.py +413 -0
  13. autogen/a2a/__init__.py +36 -0
  14. autogen/a2a/agent_executor.py +86 -0
  15. autogen/a2a/client.py +357 -0
  16. autogen/a2a/errors.py +18 -0
  17. autogen/a2a/httpx_client_factory.py +79 -0
  18. autogen/a2a/server.py +221 -0
  19. autogen/a2a/utils.py +207 -0
  20. autogen/agentchat/__init__.py +47 -0
  21. autogen/agentchat/agent.py +180 -0
  22. autogen/agentchat/assistant_agent.py +86 -0
  23. autogen/agentchat/chat.py +325 -0
  24. autogen/agentchat/contrib/__init__.py +5 -0
  25. autogen/agentchat/contrib/agent_eval/README.md +7 -0
  26. autogen/agentchat/contrib/agent_eval/agent_eval.py +108 -0
  27. autogen/agentchat/contrib/agent_eval/criterion.py +43 -0
  28. autogen/agentchat/contrib/agent_eval/critic_agent.py +44 -0
  29. autogen/agentchat/contrib/agent_eval/quantifier_agent.py +39 -0
  30. autogen/agentchat/contrib/agent_eval/subcritic_agent.py +45 -0
  31. autogen/agentchat/contrib/agent_eval/task.py +42 -0
  32. autogen/agentchat/contrib/agent_optimizer.py +432 -0
  33. autogen/agentchat/contrib/capabilities/__init__.py +5 -0
  34. autogen/agentchat/contrib/capabilities/agent_capability.py +20 -0
  35. autogen/agentchat/contrib/capabilities/generate_images.py +301 -0
  36. autogen/agentchat/contrib/capabilities/teachability.py +393 -0
  37. autogen/agentchat/contrib/capabilities/text_compressors.py +66 -0
  38. autogen/agentchat/contrib/capabilities/tools_capability.py +22 -0
  39. autogen/agentchat/contrib/capabilities/transform_messages.py +93 -0
  40. autogen/agentchat/contrib/capabilities/transforms.py +578 -0
  41. autogen/agentchat/contrib/capabilities/transforms_util.py +122 -0
  42. autogen/agentchat/contrib/capabilities/vision_capability.py +215 -0
  43. autogen/agentchat/contrib/captainagent/__init__.py +9 -0
  44. autogen/agentchat/contrib/captainagent/agent_builder.py +790 -0
  45. autogen/agentchat/contrib/captainagent/captainagent.py +514 -0
  46. autogen/agentchat/contrib/captainagent/tool_retriever.py +334 -0
  47. autogen/agentchat/contrib/captainagent/tools/README.md +44 -0
  48. autogen/agentchat/contrib/captainagent/tools/__init__.py +5 -0
  49. autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_correlation.py +40 -0
  50. autogen/agentchat/contrib/captainagent/tools/data_analysis/calculate_skewness_and_kurtosis.py +28 -0
  51. autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_iqr.py +28 -0
  52. autogen/agentchat/contrib/captainagent/tools/data_analysis/detect_outlier_zscore.py +28 -0
  53. autogen/agentchat/contrib/captainagent/tools/data_analysis/explore_csv.py +21 -0
  54. autogen/agentchat/contrib/captainagent/tools/data_analysis/shapiro_wilk_test.py +30 -0
  55. autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_download.py +27 -0
  56. autogen/agentchat/contrib/captainagent/tools/information_retrieval/arxiv_search.py +53 -0
  57. autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_image.py +53 -0
  58. autogen/agentchat/contrib/captainagent/tools/information_retrieval/extract_pdf_text.py +38 -0
  59. autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_wikipedia_text.py +21 -0
  60. autogen/agentchat/contrib/captainagent/tools/information_retrieval/get_youtube_caption.py +34 -0
  61. autogen/agentchat/contrib/captainagent/tools/information_retrieval/image_qa.py +60 -0
  62. autogen/agentchat/contrib/captainagent/tools/information_retrieval/optical_character_recognition.py +61 -0
  63. autogen/agentchat/contrib/captainagent/tools/information_retrieval/perform_web_search.py +47 -0
  64. autogen/agentchat/contrib/captainagent/tools/information_retrieval/scrape_wikipedia_tables.py +33 -0
  65. autogen/agentchat/contrib/captainagent/tools/information_retrieval/transcribe_audio_file.py +21 -0
  66. autogen/agentchat/contrib/captainagent/tools/information_retrieval/youtube_download.py +35 -0
  67. autogen/agentchat/contrib/captainagent/tools/math/calculate_circle_area_from_diameter.py +21 -0
  68. autogen/agentchat/contrib/captainagent/tools/math/calculate_day_of_the_week.py +18 -0
  69. autogen/agentchat/contrib/captainagent/tools/math/calculate_fraction_sum.py +28 -0
  70. autogen/agentchat/contrib/captainagent/tools/math/calculate_matrix_power.py +31 -0
  71. autogen/agentchat/contrib/captainagent/tools/math/calculate_reflected_point.py +16 -0
  72. autogen/agentchat/contrib/captainagent/tools/math/complex_numbers_product.py +25 -0
  73. autogen/agentchat/contrib/captainagent/tools/math/compute_currency_conversion.py +23 -0
  74. autogen/agentchat/contrib/captainagent/tools/math/count_distinct_permutations.py +27 -0
  75. autogen/agentchat/contrib/captainagent/tools/math/evaluate_expression.py +28 -0
  76. autogen/agentchat/contrib/captainagent/tools/math/find_continuity_point.py +34 -0
  77. autogen/agentchat/contrib/captainagent/tools/math/fraction_to_mixed_numbers.py +39 -0
  78. autogen/agentchat/contrib/captainagent/tools/math/modular_inverse_sum.py +23 -0
  79. autogen/agentchat/contrib/captainagent/tools/math/simplify_mixed_numbers.py +36 -0
  80. autogen/agentchat/contrib/captainagent/tools/math/sum_of_digit_factorials.py +15 -0
  81. autogen/agentchat/contrib/captainagent/tools/math/sum_of_primes_below.py +15 -0
  82. autogen/agentchat/contrib/captainagent/tools/requirements.txt +10 -0
  83. autogen/agentchat/contrib/captainagent/tools/tool_description.tsv +34 -0
  84. autogen/agentchat/contrib/gpt_assistant_agent.py +526 -0
  85. autogen/agentchat/contrib/graph_rag/__init__.py +9 -0
  86. autogen/agentchat/contrib/graph_rag/document.py +29 -0
  87. autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +167 -0
  88. autogen/agentchat/contrib/graph_rag/falkor_graph_rag_capability.py +103 -0
  89. autogen/agentchat/contrib/graph_rag/graph_query_engine.py +53 -0
  90. autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +63 -0
  91. autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +263 -0
  92. autogen/agentchat/contrib/graph_rag/neo4j_graph_rag_capability.py +83 -0
  93. autogen/agentchat/contrib/graph_rag/neo4j_native_graph_query_engine.py +210 -0
  94. autogen/agentchat/contrib/graph_rag/neo4j_native_graph_rag_capability.py +93 -0
  95. autogen/agentchat/contrib/img_utils.py +397 -0
  96. autogen/agentchat/contrib/llamaindex_conversable_agent.py +117 -0
  97. autogen/agentchat/contrib/llava_agent.py +189 -0
  98. autogen/agentchat/contrib/math_user_proxy_agent.py +464 -0
  99. autogen/agentchat/contrib/multimodal_conversable_agent.py +125 -0
  100. autogen/agentchat/contrib/qdrant_retrieve_user_proxy_agent.py +325 -0
  101. autogen/agentchat/contrib/rag/__init__.py +10 -0
  102. autogen/agentchat/contrib/rag/chromadb_query_engine.py +268 -0
  103. autogen/agentchat/contrib/rag/llamaindex_query_engine.py +195 -0
  104. autogen/agentchat/contrib/rag/mongodb_query_engine.py +319 -0
  105. autogen/agentchat/contrib/rag/query_engine.py +76 -0
  106. autogen/agentchat/contrib/retrieve_assistant_agent.py +59 -0
  107. autogen/agentchat/contrib/retrieve_user_proxy_agent.py +704 -0
  108. autogen/agentchat/contrib/society_of_mind_agent.py +200 -0
  109. autogen/agentchat/contrib/swarm_agent.py +1404 -0
  110. autogen/agentchat/contrib/text_analyzer_agent.py +79 -0
  111. autogen/agentchat/contrib/vectordb/__init__.py +5 -0
  112. autogen/agentchat/contrib/vectordb/base.py +224 -0
  113. autogen/agentchat/contrib/vectordb/chromadb.py +316 -0
  114. autogen/agentchat/contrib/vectordb/couchbase.py +405 -0
  115. autogen/agentchat/contrib/vectordb/mongodb.py +551 -0
  116. autogen/agentchat/contrib/vectordb/pgvectordb.py +927 -0
  117. autogen/agentchat/contrib/vectordb/qdrant.py +320 -0
  118. autogen/agentchat/contrib/vectordb/utils.py +126 -0
  119. autogen/agentchat/contrib/web_surfer.py +304 -0
  120. autogen/agentchat/conversable_agent.py +4307 -0
  121. autogen/agentchat/group/__init__.py +67 -0
  122. autogen/agentchat/group/available_condition.py +91 -0
  123. autogen/agentchat/group/context_condition.py +77 -0
  124. autogen/agentchat/group/context_expression.py +238 -0
  125. autogen/agentchat/group/context_str.py +39 -0
  126. autogen/agentchat/group/context_variables.py +182 -0
  127. autogen/agentchat/group/events/transition_events.py +111 -0
  128. autogen/agentchat/group/group_tool_executor.py +324 -0
  129. autogen/agentchat/group/group_utils.py +659 -0
  130. autogen/agentchat/group/guardrails.py +179 -0
  131. autogen/agentchat/group/handoffs.py +303 -0
  132. autogen/agentchat/group/llm_condition.py +93 -0
  133. autogen/agentchat/group/multi_agent_chat.py +291 -0
  134. autogen/agentchat/group/on_condition.py +55 -0
  135. autogen/agentchat/group/on_context_condition.py +51 -0
  136. autogen/agentchat/group/patterns/__init__.py +18 -0
  137. autogen/agentchat/group/patterns/auto.py +160 -0
  138. autogen/agentchat/group/patterns/manual.py +177 -0
  139. autogen/agentchat/group/patterns/pattern.py +295 -0
  140. autogen/agentchat/group/patterns/random.py +106 -0
  141. autogen/agentchat/group/patterns/round_robin.py +117 -0
  142. autogen/agentchat/group/reply_result.py +24 -0
  143. autogen/agentchat/group/safeguards/__init__.py +21 -0
  144. autogen/agentchat/group/safeguards/api.py +241 -0
  145. autogen/agentchat/group/safeguards/enforcer.py +1158 -0
  146. autogen/agentchat/group/safeguards/events.py +140 -0
  147. autogen/agentchat/group/safeguards/validator.py +435 -0
  148. autogen/agentchat/group/speaker_selection_result.py +41 -0
  149. autogen/agentchat/group/targets/__init__.py +4 -0
  150. autogen/agentchat/group/targets/function_target.py +245 -0
  151. autogen/agentchat/group/targets/group_chat_target.py +133 -0
  152. autogen/agentchat/group/targets/group_manager_target.py +151 -0
  153. autogen/agentchat/group/targets/transition_target.py +424 -0
  154. autogen/agentchat/group/targets/transition_utils.py +6 -0
  155. autogen/agentchat/groupchat.py +1832 -0
  156. autogen/agentchat/realtime/__init__.py +3 -0
  157. autogen/agentchat/realtime/experimental/__init__.py +20 -0
  158. autogen/agentchat/realtime/experimental/audio_adapters/__init__.py +8 -0
  159. autogen/agentchat/realtime/experimental/audio_adapters/twilio_audio_adapter.py +148 -0
  160. autogen/agentchat/realtime/experimental/audio_adapters/websocket_audio_adapter.py +139 -0
  161. autogen/agentchat/realtime/experimental/audio_observer.py +42 -0
  162. autogen/agentchat/realtime/experimental/clients/__init__.py +15 -0
  163. autogen/agentchat/realtime/experimental/clients/gemini/__init__.py +7 -0
  164. autogen/agentchat/realtime/experimental/clients/gemini/client.py +274 -0
  165. autogen/agentchat/realtime/experimental/clients/oai/__init__.py +8 -0
  166. autogen/agentchat/realtime/experimental/clients/oai/base_client.py +220 -0
  167. autogen/agentchat/realtime/experimental/clients/oai/rtc_client.py +243 -0
  168. autogen/agentchat/realtime/experimental/clients/oai/utils.py +48 -0
  169. autogen/agentchat/realtime/experimental/clients/realtime_client.py +191 -0
  170. autogen/agentchat/realtime/experimental/function_observer.py +84 -0
  171. autogen/agentchat/realtime/experimental/realtime_agent.py +158 -0
  172. autogen/agentchat/realtime/experimental/realtime_events.py +42 -0
  173. autogen/agentchat/realtime/experimental/realtime_observer.py +100 -0
  174. autogen/agentchat/realtime/experimental/realtime_swarm.py +533 -0
  175. autogen/agentchat/realtime/experimental/websockets.py +21 -0
  176. autogen/agentchat/realtime_agent/__init__.py +21 -0
  177. autogen/agentchat/user_proxy_agent.py +114 -0
  178. autogen/agentchat/utils.py +206 -0
  179. autogen/agents/__init__.py +3 -0
  180. autogen/agents/contrib/__init__.py +10 -0
  181. autogen/agents/contrib/time/__init__.py +8 -0
  182. autogen/agents/contrib/time/time_reply_agent.py +74 -0
  183. autogen/agents/contrib/time/time_tool_agent.py +52 -0
  184. autogen/agents/experimental/__init__.py +27 -0
  185. autogen/agents/experimental/deep_research/__init__.py +7 -0
  186. autogen/agents/experimental/deep_research/deep_research.py +52 -0
  187. autogen/agents/experimental/discord/__init__.py +7 -0
  188. autogen/agents/experimental/discord/discord.py +66 -0
  189. autogen/agents/experimental/document_agent/__init__.py +19 -0
  190. autogen/agents/experimental/document_agent/chroma_query_engine.py +301 -0
  191. autogen/agents/experimental/document_agent/docling_doc_ingest_agent.py +113 -0
  192. autogen/agents/experimental/document_agent/document_agent.py +643 -0
  193. autogen/agents/experimental/document_agent/document_conditions.py +50 -0
  194. autogen/agents/experimental/document_agent/document_utils.py +376 -0
  195. autogen/agents/experimental/document_agent/inmemory_query_engine.py +214 -0
  196. autogen/agents/experimental/document_agent/parser_utils.py +134 -0
  197. autogen/agents/experimental/document_agent/url_utils.py +417 -0
  198. autogen/agents/experimental/reasoning/__init__.py +7 -0
  199. autogen/agents/experimental/reasoning/reasoning_agent.py +1178 -0
  200. autogen/agents/experimental/slack/__init__.py +7 -0
  201. autogen/agents/experimental/slack/slack.py +73 -0
  202. autogen/agents/experimental/telegram/__init__.py +7 -0
  203. autogen/agents/experimental/telegram/telegram.py +76 -0
  204. autogen/agents/experimental/websurfer/__init__.py +7 -0
  205. autogen/agents/experimental/websurfer/websurfer.py +70 -0
  206. autogen/agents/experimental/wikipedia/__init__.py +7 -0
  207. autogen/agents/experimental/wikipedia/wikipedia.py +88 -0
  208. autogen/browser_utils.py +309 -0
  209. autogen/cache/__init__.py +10 -0
  210. autogen/cache/abstract_cache_base.py +71 -0
  211. autogen/cache/cache.py +203 -0
  212. autogen/cache/cache_factory.py +88 -0
  213. autogen/cache/cosmos_db_cache.py +144 -0
  214. autogen/cache/disk_cache.py +97 -0
  215. autogen/cache/in_memory_cache.py +54 -0
  216. autogen/cache/redis_cache.py +119 -0
  217. autogen/code_utils.py +598 -0
  218. autogen/coding/__init__.py +30 -0
  219. autogen/coding/base.py +120 -0
  220. autogen/coding/docker_commandline_code_executor.py +283 -0
  221. autogen/coding/factory.py +56 -0
  222. autogen/coding/func_with_reqs.py +203 -0
  223. autogen/coding/jupyter/__init__.py +23 -0
  224. autogen/coding/jupyter/base.py +36 -0
  225. autogen/coding/jupyter/docker_jupyter_server.py +160 -0
  226. autogen/coding/jupyter/embedded_ipython_code_executor.py +182 -0
  227. autogen/coding/jupyter/import_utils.py +82 -0
  228. autogen/coding/jupyter/jupyter_client.py +224 -0
  229. autogen/coding/jupyter/jupyter_code_executor.py +154 -0
  230. autogen/coding/jupyter/local_jupyter_server.py +164 -0
  231. autogen/coding/local_commandline_code_executor.py +341 -0
  232. autogen/coding/markdown_code_extractor.py +44 -0
  233. autogen/coding/utils.py +55 -0
  234. autogen/coding/yepcode_code_executor.py +197 -0
  235. autogen/doc_utils.py +35 -0
  236. autogen/environments/__init__.py +10 -0
  237. autogen/environments/docker_python_environment.py +365 -0
  238. autogen/environments/python_environment.py +125 -0
  239. autogen/environments/system_python_environment.py +85 -0
  240. autogen/environments/venv_python_environment.py +220 -0
  241. autogen/environments/working_directory.py +74 -0
  242. autogen/events/__init__.py +7 -0
  243. autogen/events/agent_events.py +1016 -0
  244. autogen/events/base_event.py +100 -0
  245. autogen/events/client_events.py +168 -0
  246. autogen/events/helpers.py +44 -0
  247. autogen/events/print_event.py +45 -0
  248. autogen/exception_utils.py +73 -0
  249. autogen/extensions/__init__.py +5 -0
  250. autogen/fast_depends/__init__.py +16 -0
  251. autogen/fast_depends/_compat.py +75 -0
  252. autogen/fast_depends/core/__init__.py +14 -0
  253. autogen/fast_depends/core/build.py +206 -0
  254. autogen/fast_depends/core/model.py +527 -0
  255. autogen/fast_depends/dependencies/__init__.py +15 -0
  256. autogen/fast_depends/dependencies/model.py +30 -0
  257. autogen/fast_depends/dependencies/provider.py +40 -0
  258. autogen/fast_depends/library/__init__.py +10 -0
  259. autogen/fast_depends/library/model.py +46 -0
  260. autogen/fast_depends/py.typed +6 -0
  261. autogen/fast_depends/schema.py +66 -0
  262. autogen/fast_depends/use.py +272 -0
  263. autogen/fast_depends/utils.py +177 -0
  264. autogen/formatting_utils.py +83 -0
  265. autogen/function_utils.py +13 -0
  266. autogen/graph_utils.py +173 -0
  267. autogen/import_utils.py +539 -0
  268. autogen/interop/__init__.py +22 -0
  269. autogen/interop/crewai/__init__.py +7 -0
  270. autogen/interop/crewai/crewai.py +88 -0
  271. autogen/interop/interoperability.py +71 -0
  272. autogen/interop/interoperable.py +46 -0
  273. autogen/interop/langchain/__init__.py +8 -0
  274. autogen/interop/langchain/langchain_chat_model_factory.py +156 -0
  275. autogen/interop/langchain/langchain_tool.py +78 -0
  276. autogen/interop/litellm/__init__.py +7 -0
  277. autogen/interop/litellm/litellm_config_factory.py +178 -0
  278. autogen/interop/pydantic_ai/__init__.py +7 -0
  279. autogen/interop/pydantic_ai/pydantic_ai.py +172 -0
  280. autogen/interop/registry.py +70 -0
  281. autogen/io/__init__.py +15 -0
  282. autogen/io/base.py +151 -0
  283. autogen/io/console.py +56 -0
  284. autogen/io/processors/__init__.py +12 -0
  285. autogen/io/processors/base.py +21 -0
  286. autogen/io/processors/console_event_processor.py +61 -0
  287. autogen/io/run_response.py +294 -0
  288. autogen/io/thread_io_stream.py +63 -0
  289. autogen/io/websockets.py +214 -0
  290. autogen/json_utils.py +42 -0
  291. autogen/llm_clients/MIGRATION_TO_V2.md +782 -0
  292. autogen/llm_clients/__init__.py +77 -0
  293. autogen/llm_clients/client_v2.py +122 -0
  294. autogen/llm_clients/models/__init__.py +55 -0
  295. autogen/llm_clients/models/content_blocks.py +389 -0
  296. autogen/llm_clients/models/unified_message.py +145 -0
  297. autogen/llm_clients/models/unified_response.py +83 -0
  298. autogen/llm_clients/openai_completions_client.py +444 -0
  299. autogen/llm_config/__init__.py +11 -0
  300. autogen/llm_config/client.py +59 -0
  301. autogen/llm_config/config.py +461 -0
  302. autogen/llm_config/entry.py +169 -0
  303. autogen/llm_config/types.py +37 -0
  304. autogen/llm_config/utils.py +223 -0
  305. autogen/logger/__init__.py +11 -0
  306. autogen/logger/base_logger.py +129 -0
  307. autogen/logger/file_logger.py +262 -0
  308. autogen/logger/logger_factory.py +42 -0
  309. autogen/logger/logger_utils.py +57 -0
  310. autogen/logger/sqlite_logger.py +524 -0
  311. autogen/math_utils.py +338 -0
  312. autogen/mcp/__init__.py +7 -0
  313. autogen/mcp/__main__.py +78 -0
  314. autogen/mcp/helpers.py +45 -0
  315. autogen/mcp/mcp_client.py +349 -0
  316. autogen/mcp/mcp_proxy/__init__.py +19 -0
  317. autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py +62 -0
  318. autogen/mcp/mcp_proxy/mcp_proxy.py +577 -0
  319. autogen/mcp/mcp_proxy/operation_grouping.py +166 -0
  320. autogen/mcp/mcp_proxy/operation_renaming.py +110 -0
  321. autogen/mcp/mcp_proxy/patch_fastapi_code_generator.py +98 -0
  322. autogen/mcp/mcp_proxy/security.py +399 -0
  323. autogen/mcp/mcp_proxy/security_schema_visitor.py +37 -0
  324. autogen/messages/__init__.py +7 -0
  325. autogen/messages/agent_messages.py +946 -0
  326. autogen/messages/base_message.py +108 -0
  327. autogen/messages/client_messages.py +172 -0
  328. autogen/messages/print_message.py +48 -0
  329. autogen/oai/__init__.py +61 -0
  330. autogen/oai/anthropic.py +1516 -0
  331. autogen/oai/bedrock.py +800 -0
  332. autogen/oai/cerebras.py +302 -0
  333. autogen/oai/client.py +1658 -0
  334. autogen/oai/client_utils.py +196 -0
  335. autogen/oai/cohere.py +494 -0
  336. autogen/oai/gemini.py +1045 -0
  337. autogen/oai/gemini_types.py +156 -0
  338. autogen/oai/groq.py +319 -0
  339. autogen/oai/mistral.py +311 -0
  340. autogen/oai/oai_models/__init__.py +23 -0
  341. autogen/oai/oai_models/_models.py +16 -0
  342. autogen/oai/oai_models/chat_completion.py +86 -0
  343. autogen/oai/oai_models/chat_completion_audio.py +32 -0
  344. autogen/oai/oai_models/chat_completion_message.py +97 -0
  345. autogen/oai/oai_models/chat_completion_message_tool_call.py +60 -0
  346. autogen/oai/oai_models/chat_completion_token_logprob.py +62 -0
  347. autogen/oai/oai_models/completion_usage.py +59 -0
  348. autogen/oai/ollama.py +657 -0
  349. autogen/oai/openai_responses.py +451 -0
  350. autogen/oai/openai_utils.py +897 -0
  351. autogen/oai/together.py +387 -0
  352. autogen/remote/__init__.py +18 -0
  353. autogen/remote/agent.py +199 -0
  354. autogen/remote/agent_service.py +197 -0
  355. autogen/remote/errors.py +17 -0
  356. autogen/remote/httpx_client_factory.py +131 -0
  357. autogen/remote/protocol.py +37 -0
  358. autogen/remote/retry.py +102 -0
  359. autogen/remote/runtime.py +96 -0
  360. autogen/retrieve_utils.py +490 -0
  361. autogen/runtime_logging.py +161 -0
  362. autogen/testing/__init__.py +12 -0
  363. autogen/testing/messages.py +45 -0
  364. autogen/testing/test_agent.py +111 -0
  365. autogen/token_count_utils.py +280 -0
  366. autogen/tools/__init__.py +20 -0
  367. autogen/tools/contrib/__init__.py +9 -0
  368. autogen/tools/contrib/time/__init__.py +7 -0
  369. autogen/tools/contrib/time/time.py +40 -0
  370. autogen/tools/dependency_injection.py +249 -0
  371. autogen/tools/experimental/__init__.py +54 -0
  372. autogen/tools/experimental/browser_use/__init__.py +7 -0
  373. autogen/tools/experimental/browser_use/browser_use.py +154 -0
  374. autogen/tools/experimental/code_execution/__init__.py +7 -0
  375. autogen/tools/experimental/code_execution/python_code_execution.py +86 -0
  376. autogen/tools/experimental/crawl4ai/__init__.py +7 -0
  377. autogen/tools/experimental/crawl4ai/crawl4ai.py +150 -0
  378. autogen/tools/experimental/deep_research/__init__.py +7 -0
  379. autogen/tools/experimental/deep_research/deep_research.py +329 -0
  380. autogen/tools/experimental/duckduckgo/__init__.py +7 -0
  381. autogen/tools/experimental/duckduckgo/duckduckgo_search.py +103 -0
  382. autogen/tools/experimental/firecrawl/__init__.py +7 -0
  383. autogen/tools/experimental/firecrawl/firecrawl_tool.py +836 -0
  384. autogen/tools/experimental/google/__init__.py +14 -0
  385. autogen/tools/experimental/google/authentication/__init__.py +11 -0
  386. autogen/tools/experimental/google/authentication/credentials_hosted_provider.py +43 -0
  387. autogen/tools/experimental/google/authentication/credentials_local_provider.py +91 -0
  388. autogen/tools/experimental/google/authentication/credentials_provider.py +35 -0
  389. autogen/tools/experimental/google/drive/__init__.py +9 -0
  390. autogen/tools/experimental/google/drive/drive_functions.py +124 -0
  391. autogen/tools/experimental/google/drive/toolkit.py +88 -0
  392. autogen/tools/experimental/google/model.py +17 -0
  393. autogen/tools/experimental/google/toolkit_protocol.py +19 -0
  394. autogen/tools/experimental/google_search/__init__.py +8 -0
  395. autogen/tools/experimental/google_search/google_search.py +93 -0
  396. autogen/tools/experimental/google_search/youtube_search.py +181 -0
  397. autogen/tools/experimental/messageplatform/__init__.py +17 -0
  398. autogen/tools/experimental/messageplatform/discord/__init__.py +7 -0
  399. autogen/tools/experimental/messageplatform/discord/discord.py +284 -0
  400. autogen/tools/experimental/messageplatform/slack/__init__.py +7 -0
  401. autogen/tools/experimental/messageplatform/slack/slack.py +385 -0
  402. autogen/tools/experimental/messageplatform/telegram/__init__.py +7 -0
  403. autogen/tools/experimental/messageplatform/telegram/telegram.py +271 -0
  404. autogen/tools/experimental/perplexity/__init__.py +7 -0
  405. autogen/tools/experimental/perplexity/perplexity_search.py +249 -0
  406. autogen/tools/experimental/reliable/__init__.py +10 -0
  407. autogen/tools/experimental/reliable/reliable.py +1311 -0
  408. autogen/tools/experimental/searxng/__init__.py +7 -0
  409. autogen/tools/experimental/searxng/searxng_search.py +142 -0
  410. autogen/tools/experimental/tavily/__init__.py +7 -0
  411. autogen/tools/experimental/tavily/tavily_search.py +176 -0
  412. autogen/tools/experimental/web_search_preview/__init__.py +7 -0
  413. autogen/tools/experimental/web_search_preview/web_search_preview.py +120 -0
  414. autogen/tools/experimental/wikipedia/__init__.py +7 -0
  415. autogen/tools/experimental/wikipedia/wikipedia.py +284 -0
  416. autogen/tools/function_utils.py +412 -0
  417. autogen/tools/tool.py +188 -0
  418. autogen/tools/toolkit.py +86 -0
  419. autogen/types.py +29 -0
  420. autogen/version.py +7 -0
  421. templates/client_template/main.jinja2 +72 -0
  422. templates/config_template/config.jinja2 +7 -0
  423. templates/main.jinja2 +61 -0
@@ -0,0 +1,1404 @@
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 asyncio
6
+ import copy
7
+ import inspect
8
+ import threading
9
+ import warnings
10
+ from collections.abc import Callable
11
+ from dataclasses import dataclass
12
+ from enum import Enum
13
+ from functools import partial
14
+ from types import MethodType
15
+ from typing import Annotated, Any, Literal
16
+
17
+ from pydantic import BaseModel, ConfigDict, field_serializer
18
+
19
+ from ...doc_utils import export_module
20
+ from ...events.agent_events import ErrorEvent, RunCompletionEvent
21
+ from ...io.base import IOStream
22
+ from ...io.run_response import AsyncRunResponse, AsyncRunResponseProtocol, RunResponse, RunResponseProtocol
23
+ from ...io.thread_io_stream import AsyncThreadIOStream, ThreadIOStream
24
+ from ...oai import OpenAIWrapper
25
+ from ...tools import Depends, Tool
26
+ from ...tools.dependency_injection import inject_params, on
27
+ from ..agent import Agent
28
+ from ..chat import ChatResult
29
+ from ..conversable_agent import ConversableAgent
30
+ from ..group.context_expression import ContextExpression
31
+ from ..group.context_str import ContextStr
32
+ from ..group.context_variables import __CONTEXT_VARIABLES_PARAM_NAME__, ContextVariables
33
+ from ..groupchat import SELECT_SPEAKER_PROMPT_TEMPLATE, GroupChat, GroupChatManager
34
+ from ..user_proxy_agent import UserProxyAgent
35
+
36
+ __all__ = [
37
+ "AFTER_WORK",
38
+ "ON_CONDITION",
39
+ "AfterWork",
40
+ "AfterWorkOption",
41
+ "OnCondition",
42
+ "OnContextCondition",
43
+ "SwarmAgent",
44
+ "a_initiate_swarm_chat",
45
+ "create_swarm_transition",
46
+ "initiate_swarm_chat",
47
+ "register_hand_off",
48
+ "run_swarm",
49
+ ]
50
+
51
+
52
+ # Created tool executor's name
53
+ __TOOL_EXECUTOR_NAME__ = "_Swarm_Tool_Executor"
54
+
55
+
56
+ @export_module("autogen")
57
+ class AfterWorkOption(Enum):
58
+ TERMINATE = "TERMINATE"
59
+ REVERT_TO_USER = "REVERT_TO_USER"
60
+ STAY = "STAY"
61
+ SWARM_MANAGER = "SWARM_MANAGER"
62
+
63
+
64
+ @dataclass
65
+ @export_module("autogen")
66
+ class AfterWork: # noqa: N801
67
+ """Handles the next step in the conversation when an agent doesn't suggest a tool call or a handoff.
68
+
69
+ Args:
70
+ agent (Union[AfterWorkOption, ConversableAgent, str, Callable[..., Any]]): The agent to hand off to or the after work option. Can be a ConversableAgent, a string name of a ConversableAgent, an AfterWorkOption, or a Callable.
71
+ The Callable signature is:
72
+ def my_after_work_func(last_speaker: ConversableAgent, messages: list[dict[str, Any]], groupchat: GroupChat) -> Union[AfterWorkOption, ConversableAgent, str]:
73
+ next_agent_selection_msg (Optional[Union[str, Callable[..., Any]]]): Optional message to use for the agent selection (in internal group chat), only valid for when agent is AfterWorkOption.SWARM_MANAGER.
74
+ If a string, it will be used as a template and substitute the context variables.
75
+ If a Callable, it should have the signature:
76
+ def my_selection_message(agent: ConversableAgent, messages: list[dict[str, Any]]) -> str
77
+ """
78
+
79
+ agent: AfterWorkOption | ConversableAgent | str | Callable[..., Any]
80
+ next_agent_selection_msg: str | ContextStr | Callable[[ConversableAgent, list[dict[str, Any]]], str] | None = None
81
+
82
+ def __post_init__(self) -> None:
83
+ if isinstance(self.agent, str):
84
+ self.agent = AfterWorkOption(self.agent.upper())
85
+
86
+ # next_agent_selection_msg is only valid for when agent is AfterWorkOption.SWARM_MANAGER, but isn't mandatory
87
+ if self.next_agent_selection_msg is not None:
88
+ if not (
89
+ isinstance(self.next_agent_selection_msg, (str, ContextStr)) or callable(self.next_agent_selection_msg)
90
+ ):
91
+ raise ValueError("next_agent_selection_msg must be a string, ContextStr, or a Callable")
92
+
93
+ if self.agent != AfterWorkOption.SWARM_MANAGER:
94
+ warnings.warn(
95
+ "next_agent_selection_msg is only valid for agent=AfterWorkOption.SWARM_MANAGER. Ignoring the value.",
96
+ UserWarning,
97
+ )
98
+ self.next_agent_selection_msg = None
99
+
100
+
101
+ class AFTER_WORK(AfterWork): # noqa: N801
102
+ """Deprecated: Use AfterWork instead. This class will be removed in a future version (TBD)."""
103
+
104
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
105
+ warnings.warn(
106
+ "AFTER_WORK is deprecated and will be removed in a future version (TBD). Use AfterWork instead.",
107
+ DeprecationWarning,
108
+ stacklevel=2,
109
+ )
110
+ super().__init__(*args, **kwargs)
111
+
112
+
113
+ @dataclass
114
+ @export_module("autogen")
115
+ class OnCondition: # noqa: N801
116
+ """Defines a condition for transitioning to another agent or nested chats.
117
+
118
+ This is for LLM-based condition evaluation where these conditions are translated into tools and attached to the agent.
119
+
120
+ These are evaluated after the OnCondition conditions but before the AfterWork conditions.
121
+
122
+ Args:
123
+ target (Optional[Union[ConversableAgent, dict[str, Any]]]): The agent to hand off to or the nested chat configuration. Can be a ConversableAgent or a Dict.
124
+ If a Dict, it should follow the convention of the nested chat configuration, with the exception of a carryover configuration which is unique to Swarms.
125
+ Swarm Nested chat documentation: https://docs.ag2.ai/docs/user-guide/advanced-concepts/swarm-deep-dive#registering-handoffs-to-a-nested-chat
126
+ condition (Optional[Union[str, ContextStr, Callable[[ConversableAgent, list[dict[str, Any]]], str]]]): The condition for transitioning to the target agent, evaluated by the LLM.
127
+ If a string or Callable, no automatic context variable substitution occurs.
128
+ If a ContextStr, context variable substitution occurs.
129
+ The Callable signature is:
130
+ def my_condition_string(agent: ConversableAgent, messages: list[Dict[str, Any]]) -> str
131
+ available (Optional[Union[Callable[[ConversableAgent, list[dict[str, Any]]], bool], str, ContextExpression]]): Optional condition to determine if this OnCondition is included for the LLM to evaluate.
132
+ If a string, it will look up the value of the context variable with that name, which should be a bool, to determine whether it should include this condition.
133
+ If a ContextExpression, it will evaluate the logical expression against the context variables. Can use not, and, or, and comparison operators (>, <, >=, <=, ==, !=).
134
+ Example: ContextExpression("not(${logged_in} and ${is_admin}) or (${guest_checkout})")
135
+ Example with comparison: ContextExpression("${attempts} >= 3 or ${is_premium} == True or ${tier} == 'gold'")
136
+ The Callable signature is:
137
+ def my_available_func(agent: ConversableAgent, messages: list[Dict[str, Any]]) -> bool
138
+ """
139
+
140
+ target: ConversableAgent | dict[str, Any] | None = None
141
+ condition: str | ContextStr | Callable[[ConversableAgent, list[dict[str, Any]]], str] | None = None
142
+ available: Callable[[ConversableAgent, list[dict[str, Any]]], bool] | str | ContextExpression | None = None
143
+
144
+ def __post_init__(self) -> None:
145
+ # Ensure valid types
146
+ if (self.target is not None) and (not isinstance(self.target, (ConversableAgent, dict))):
147
+ raise ValueError("'target' must be a ConversableAgent or a dict")
148
+
149
+ # Ensure they have a condition
150
+ if isinstance(self.condition, str):
151
+ if not self.condition.strip():
152
+ raise ValueError("'condition' must be a non-empty string")
153
+ else:
154
+ if not isinstance(self.condition, ContextStr) and not callable(self.condition):
155
+ raise ValueError("'condition' must be a string, ContextStr, or callable")
156
+
157
+ if (self.available is not None) and (
158
+ not (isinstance(self.available, (str, ContextExpression)) or callable(self.available))
159
+ ):
160
+ raise ValueError("'available' must be a callable, a string, or a ContextExpression")
161
+
162
+
163
+ class ON_CONDITION(OnCondition): # noqa: N801
164
+ """Deprecated: Use OnCondition instead. This class will be removed in a future version (TBD)."""
165
+
166
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
167
+ warnings.warn(
168
+ "ON_CONDITION is deprecated and will be removed in a future version (TBD). Use OnCondition instead.",
169
+ DeprecationWarning,
170
+ stacklevel=2,
171
+ )
172
+ super().__init__(*args, **kwargs)
173
+
174
+
175
+ @dataclass
176
+ @export_module("autogen")
177
+ class OnContextCondition: # noqa: N801
178
+ """Defines a condition for transitioning to another agent or nested chats using context variables and the ContextExpression class.
179
+
180
+ This is for context variable-based condition evaluation (does not use the agent's LLM).
181
+
182
+ These are evaluated before the OnCondition and AfterWork conditions.
183
+
184
+ Args:
185
+ target (Optional[Union[ConversableAgent, dict[str, Any]]]): The agent to hand off to or the nested chat configuration. Can be a ConversableAgent or a Dict.
186
+ If a Dict, it should follow the convention of the nested chat configuration, with the exception of a carryover configuration which is unique to Swarms.
187
+ Swarm Nested chat documentation: https://docs.ag2.ai/docs/user-guide/advanced-concepts/swarm-deep-dive#registering-handoffs-to-a-nested-chat
188
+ condition (Optional[Union[str, ContextExpression]]): The condition for transitioning to the target agent, evaluated by the LLM.
189
+ If a string, it needs to represent a context variable key and the value will be evaluated as a boolean
190
+ If a ContextExpression, it will evaluate the logical expression against the context variables. If it is True, the transition will occur.
191
+ Can use not, and, or, and comparison operators (>, <, >=, <=, ==, !=).
192
+ Example: ContextExpression("not(${logged_in} and ${is_admin}) or (${guest_checkout})")
193
+ Example with comparison: ContextExpression("${attempts} >= 3 or ${is_premium} == True or ${tier} == 'gold'")
194
+ available (Optional[Union[Callable[[ConversableAgent, list[dict[str, Any]]], bool], str, ContextExpression]]): Optional condition to determine if this OnContextCondition is included for the LLM to evaluate.
195
+ If a string, it will look up the value of the context variable with that name, which should be a bool, to determine whether it should include this condition.
196
+ If a ContextExpression, it will evaluate the logical expression against the context variables. Can use not, and, or, and comparison operators (>, <, >=, <=, ==, !=).
197
+ The Callable signature is:
198
+ def my_available_func(agent: ConversableAgent, messages: list[Dict[str, Any]]) -> bool
199
+
200
+ """
201
+
202
+ target: ConversableAgent | dict[str, Any] | None = None
203
+ condition: str | ContextExpression | None = None
204
+ available: Callable[[ConversableAgent, list[dict[str, Any]]], bool] | str | ContextExpression | None = None
205
+
206
+ def __post_init__(self) -> None:
207
+ # Ensure valid types
208
+ if (self.target is not None) and (not isinstance(self.target, (ConversableAgent, dict))):
209
+ raise ValueError("'target' must be a ConversableAgent or a dict")
210
+
211
+ # Ensure they have a condition
212
+ if isinstance(self.condition, str):
213
+ if not self.condition.strip():
214
+ raise ValueError("'condition' must be a non-empty string")
215
+
216
+ self._context_condition = ContextExpression("${" + self.condition + "}")
217
+ else:
218
+ if not isinstance(self.condition, ContextExpression):
219
+ raise ValueError("'condition' must be a string on ContextExpression")
220
+
221
+ self._context_condition = self.condition
222
+
223
+ if (self.available is not None) and (
224
+ not (isinstance(self.available, (str, ContextExpression)) or callable(self.available))
225
+ ):
226
+ raise ValueError("'available' must be a callable, a string, or a ContextExpression")
227
+
228
+
229
+ def _establish_swarm_agent(agent: ConversableAgent) -> None:
230
+ """Establish the swarm agent with the swarm-related attributes and hooks. Not for the tool executor.
231
+
232
+ Args:
233
+ agent (ConversableAgent): The agent to establish as a swarm agent.
234
+ """
235
+
236
+ def _swarm_agent_str(self: ConversableAgent) -> str:
237
+ """Customise the __str__ method to show the agent name for transition messages."""
238
+ return f"Swarm agent --> {self.name}"
239
+
240
+ agent._swarm_after_work = None # type: ignore[attr-defined]
241
+ agent._swarm_after_work_selection_msg = None # type: ignore[attr-defined]
242
+
243
+ # Store nested chats hand offs as we'll establish these in the initiate_swarm_chat
244
+ # List of Dictionaries containing the nested_chats and condition
245
+ agent._swarm_nested_chat_handoffs = [] # type: ignore[attr-defined]
246
+
247
+ # Store conditional functions (and their OnCondition instances) to add/remove later when transitioning to this agent
248
+ agent._swarm_conditional_functions = {} # type: ignore[attr-defined]
249
+
250
+ # Register the hook to update agent state (except tool executor)
251
+ agent.register_hook("update_agent_state", _update_conditional_functions)
252
+
253
+ # Store the OnContextConditions for evaluation (list[OnContextCondition])
254
+ agent._swarm_oncontextconditions = [] # type: ignore[attr-defined]
255
+
256
+ # Register a reply function to run Python function-based OnConditions before any other reply function
257
+ agent.register_reply(trigger=([Agent, None]), reply_func=_run_oncontextconditions, position=0)
258
+
259
+ agent._get_display_name = MethodType(_swarm_agent_str, agent) # type: ignore[method-assign]
260
+
261
+ # Mark this agent as established as a swarm agent
262
+ agent._swarm_is_established = True # type: ignore[attr-defined]
263
+
264
+
265
+ def _link_agents_to_swarm_manager(agents: list[Agent], group_chat_manager: Agent) -> None:
266
+ """Link all agents to the GroupChatManager so they can access the underlying GroupChat and other agents.
267
+
268
+ This is primarily used so that agents can set the tool executor's _swarm_next_agent attribute to control
269
+ the next agent programmatically.
270
+
271
+ Does not link the Tool Executor agent.
272
+ """
273
+ for agent in agents:
274
+ agent._swarm_manager = group_chat_manager # type: ignore[attr-defined]
275
+
276
+
277
+ def _run_oncontextconditions(
278
+ agent: ConversableAgent,
279
+ messages: list[dict[str, Any]] | None = None,
280
+ sender: Agent | None = None,
281
+ config: Any | None = None,
282
+ ) -> tuple[bool, str | dict[str, Any] | None]:
283
+ """Run OnContextConditions for an agent before any other reply function."""
284
+ for on_condition in agent._swarm_oncontextconditions: # type: ignore[attr-defined]
285
+ is_available = True
286
+
287
+ if on_condition.available is not None:
288
+ if callable(on_condition.available):
289
+ is_available = on_condition.available(agent, next(iter(agent.chat_messages.values())))
290
+ elif isinstance(on_condition.available, str):
291
+ is_available = agent.context_variables.get(on_condition.available) or False
292
+ elif isinstance(on_condition.available, ContextExpression):
293
+ is_available = on_condition.available.evaluate(agent.context_variables)
294
+
295
+ if is_available and on_condition._context_condition.evaluate(agent.context_variables):
296
+ # Condition has been met, we'll set the Tool Executor's _swarm_next_agent
297
+ # attribute and that will be picked up on the next iteration when
298
+ # _determine_next_agent is called
299
+ for agent in agent._swarm_manager.groupchat.agents: # type: ignore[attr-defined]
300
+ if agent.name == __TOOL_EXECUTOR_NAME__:
301
+ agent._swarm_next_agent = on_condition.target # type: ignore[attr-defined]
302
+ break
303
+
304
+ if isinstance(on_condition.target, ConversableAgent):
305
+ transfer_name = on_condition.target.name
306
+ else:
307
+ transfer_name = "a nested chat"
308
+
309
+ return True, "[Handing off to " + transfer_name + "]"
310
+
311
+ return False, None
312
+
313
+
314
+ def _modify_context_variables_param(f: Callable[..., Any], context_variables: ContextVariables) -> Callable[..., Any]:
315
+ """Modifies the context_variables parameter to use dependency injection and link it to the swarm context variables.
316
+
317
+ This essentially changes:
318
+ def some_function(some_variable: int, context_variables: ContextVariables) -> str:
319
+
320
+ to:
321
+
322
+ def some_function(some_variable: int, context_variables: Annotated[ContextVariables, Depends(on(self.context_variables))]) -> str:
323
+ """
324
+ sig = inspect.signature(f)
325
+
326
+ # Check if context_variables parameter exists and update it if so
327
+ if __CONTEXT_VARIABLES_PARAM_NAME__ in sig.parameters:
328
+ new_params = []
329
+ for name, param in sig.parameters.items():
330
+ if name == __CONTEXT_VARIABLES_PARAM_NAME__:
331
+ # Replace with new annotation using Depends
332
+ new_param = param.replace(annotation=Annotated[ContextVariables, Depends(on(context_variables))])
333
+ new_params.append(new_param)
334
+ else:
335
+ new_params.append(param)
336
+
337
+ # Update signature
338
+ new_sig = sig.replace(parameters=new_params)
339
+ f.__signature__ = new_sig # type: ignore[attr-defined]
340
+
341
+ return f
342
+
343
+
344
+ def _change_tool_context_variables_to_depends(
345
+ agent: ConversableAgent, current_tool: Tool, context_variables: ContextVariables
346
+ ) -> None:
347
+ """Checks for the context_variables parameter in the tool and updates it to use dependency injection."""
348
+ # If the tool has a context_variables parameter, remove the tool and reregister it without the parameter
349
+ if __CONTEXT_VARIABLES_PARAM_NAME__ in current_tool.tool_schema["function"]["parameters"]["properties"]:
350
+ # We'll replace the tool, so start with getting the underlying function
351
+ tool_func = current_tool._func
352
+
353
+ # Remove the Tool from the agent
354
+ name = current_tool._name
355
+ description = current_tool._description
356
+ agent.remove_tool_for_llm(current_tool)
357
+
358
+ # Recreate the tool without the context_variables parameter
359
+ tool_func = _modify_context_variables_param(current_tool._func, context_variables)
360
+ tool_func = inject_params(tool_func)
361
+ new_tool = ConversableAgent._create_tool_if_needed(func_or_tool=tool_func, name=name, description=description)
362
+
363
+ # Re-register with the agent
364
+ agent.register_for_llm()(new_tool)
365
+
366
+
367
+ def _prepare_swarm_agents(
368
+ initial_agent: ConversableAgent,
369
+ agents: list[ConversableAgent],
370
+ context_variables: ContextVariables,
371
+ exclude_transit_message: bool = True,
372
+ ) -> tuple[ConversableAgent, list[ConversableAgent]]:
373
+ """Validates agents, create the tool executor, configure nested chats.
374
+
375
+ Args:
376
+ initial_agent (ConversableAgent): The first agent in the conversation.
377
+ agents (list[ConversableAgent]): List of all agents in the conversation.
378
+ context_variables (ContextVariables): Context variables to assign to all agents.
379
+ exclude_transit_message (bool): Whether to exclude transit messages from the agents.
380
+
381
+ Returns:
382
+ ConversableAgent: The tool executor agent.
383
+ list[ConversableAgent]: List of nested chat agents.
384
+ """
385
+ if not isinstance(initial_agent, ConversableAgent):
386
+ raise ValueError("initial_agent must be a ConversableAgent")
387
+ if not all(isinstance(agent, ConversableAgent) for agent in agents):
388
+ raise ValueError("Agents must be a list of ConversableAgents")
389
+
390
+ # Initialize all agents as swarm agents
391
+ for agent in agents:
392
+ if not hasattr(agent, "_swarm_is_established"):
393
+ _establish_swarm_agent(agent)
394
+
395
+ # Ensure all agents in hand-off after-works are in the passed in agents list
396
+ for agent in agents:
397
+ if (agent._swarm_after_work is not None and isinstance(agent._swarm_after_work.agent, ConversableAgent)) and ( # type: ignore[attr-defined]
398
+ agent._swarm_after_work.agent not in agents # type: ignore[attr-defined]
399
+ ):
400
+ raise ValueError("Agent in hand-off must be in the agents list")
401
+
402
+ tool_execution = ConversableAgent(
403
+ name=__TOOL_EXECUTOR_NAME__,
404
+ system_message="Tool Execution, do not use this agent directly.",
405
+ )
406
+ _set_to_tool_execution(tool_execution)
407
+
408
+ nested_chat_agents: list[ConversableAgent] = []
409
+ for agent in agents:
410
+ _create_nested_chats(agent, nested_chat_agents)
411
+
412
+ # Update any agent's tools that have context_variables as a parameter
413
+ # To use Dependency Injection
414
+
415
+ # Update tool execution agent with all the functions from all the agents
416
+ for agent in agents + nested_chat_agents:
417
+ tool_execution._function_map.update(agent._function_map)
418
+
419
+ # Add conditional functions to the tool_execution agent
420
+ for func_name, (func, _) in agent._swarm_conditional_functions.items(): # type: ignore[attr-defined]
421
+ tool_execution._function_map[func_name] = func
422
+
423
+ # Update any agent tools that have context_variables parameters to use Dependency Injection
424
+ for tool in agent.tools:
425
+ _change_tool_context_variables_to_depends(agent, tool, context_variables)
426
+
427
+ # Add all tools to the Tool Executor agent
428
+ for tool in agent.tools:
429
+ tool_execution.register_for_execution(serialize=False, silent_override=True)(tool)
430
+
431
+ if exclude_transit_message:
432
+ # get all transit functions names
433
+ to_be_removed = []
434
+ for agent in agents + nested_chat_agents:
435
+ if hasattr(agent, "_swarm_conditional_functions"):
436
+ to_be_removed += list(agent._swarm_conditional_functions.keys())
437
+
438
+ # register hook to remove transit messages for swarm agents
439
+ for agent in agents + nested_chat_agents:
440
+ agent.register_hook("process_all_messages_before_reply", make_remove_function(to_be_removed))
441
+
442
+ return tool_execution, nested_chat_agents
443
+
444
+
445
+ def _create_nested_chats(agent: ConversableAgent, nested_chat_agents: list[ConversableAgent]) -> None:
446
+ """Create nested chat agents and register nested chats.
447
+
448
+ Args:
449
+ agent (ConversableAgent): The agent to create nested chat agents for, including registering the hand offs.
450
+ nested_chat_agents (list[ConversableAgent]): List for all nested chat agents, appends to this.
451
+ """
452
+
453
+ def create_nested_chat_agent(agent: ConversableAgent, nested_chats: dict[str, Any]) -> ConversableAgent:
454
+ """Create a nested chat agent for a nested chat configuration.
455
+
456
+ Args:
457
+ agent (ConversableAgent): The agent to create the nested chat agent for.
458
+ nested_chats (dict[str, Any]): The nested chat configuration.
459
+
460
+ Returns:
461
+ The created nested chat agent.
462
+ """
463
+ # Create a nested chat agent specifically for this nested chat
464
+ nested_chat_agent = ConversableAgent(name=f"nested_chat_{agent.name}_{i + 1}")
465
+
466
+ nested_chat_agent.register_nested_chats(
467
+ nested_chats["chat_queue"],
468
+ reply_func_from_nested_chats=nested_chats.get("reply_func_from_nested_chats")
469
+ or "summary_from_nested_chats",
470
+ config=nested_chats.get("config"),
471
+ trigger=lambda sender: True,
472
+ position=0,
473
+ use_async=nested_chats.get("use_async", False),
474
+ )
475
+
476
+ # After the nested chat is complete, transfer back to the parent agent
477
+ register_hand_off(nested_chat_agent, AfterWork(agent=agent))
478
+
479
+ return nested_chat_agent
480
+
481
+ for i, nested_chat_handoff in enumerate(agent._swarm_nested_chat_handoffs): # type: ignore[attr-defined]
482
+ llm_nested_chats: dict[str, Any] = nested_chat_handoff["nested_chats"]
483
+
484
+ # Create nested chat agent
485
+ nested_chat_agent = create_nested_chat_agent(agent, llm_nested_chats)
486
+ nested_chat_agents.append(nested_chat_agent)
487
+
488
+ # Nested chat is triggered through an agent transfer to this nested chat agent
489
+ condition = nested_chat_handoff["condition"]
490
+ available = nested_chat_handoff["available"]
491
+ register_hand_off(agent, OnCondition(target=nested_chat_agent, condition=condition, available=available))
492
+
493
+ for i, nested_chat_context_handoff in enumerate(agent._swarm_oncontextconditions): # type: ignore[attr-defined]
494
+ if isinstance(nested_chat_context_handoff.target, dict):
495
+ context_nested_chats: dict[str, Any] = nested_chat_context_handoff.target
496
+
497
+ # Create nested chat agent
498
+ nested_chat_agent = create_nested_chat_agent(agent, context_nested_chats)
499
+ nested_chat_agents.append(nested_chat_agent)
500
+
501
+ # Update the OnContextCondition, replacing the nested chat dictionary with the nested chat agent
502
+ nested_chat_context_handoff.target = nested_chat_agent
503
+
504
+
505
+ def _process_initial_messages(
506
+ messages: list[dict[str, Any]] | str,
507
+ user_agent: UserProxyAgent | None,
508
+ agents: list[ConversableAgent],
509
+ nested_chat_agents: list[ConversableAgent],
510
+ ) -> tuple[list[dict[str, Any]], Agent | None, list[str], list[Agent]]:
511
+ """Process initial messages, validating agent names against messages, and determining the last agent to speak.
512
+
513
+ Args:
514
+ messages: Initial messages to process.
515
+ user_agent: Optional user proxy agent passed in to a_/initiate_swarm_chat.
516
+ agents: Agents in swarm.
517
+ nested_chat_agents: List of nested chat agents.
518
+
519
+ Returns:
520
+ list[dict[str, Any]]: Processed message(s).
521
+ Agent: Last agent to speak.
522
+ list[str]: List of agent names.
523
+ list[Agent]: List of temporary user proxy agents to add to GroupChat.
524
+ """
525
+ if isinstance(messages, str):
526
+ messages = [{"role": "user", "content": messages}]
527
+
528
+ swarm_agent_names = [agent.name for agent in agents + nested_chat_agents]
529
+
530
+ # If there's only one message and there's no identified swarm agent
531
+ # Start with a user proxy agent, creating one if they haven't passed one in
532
+ last_agent: Agent | None
533
+ temp_user_proxy: Agent | None = None
534
+ temp_user_list: list[Agent] = []
535
+ if len(messages) == 1 and "name" not in messages[0] and not user_agent:
536
+ temp_user_proxy = UserProxyAgent(name="_User", code_execution_config=False)
537
+ last_agent = temp_user_proxy
538
+ temp_user_list.append(temp_user_proxy)
539
+ else:
540
+ last_message = messages[0]
541
+ if "name" in last_message:
542
+ if last_message["name"] in swarm_agent_names:
543
+ last_agent = next(agent for agent in agents + nested_chat_agents if agent.name == last_message["name"]) # type: ignore[assignment]
544
+ elif user_agent and last_message["name"] == user_agent.name:
545
+ last_agent = user_agent
546
+ else:
547
+ raise ValueError(f"Invalid swarm agent name in last message: {last_message['name']}")
548
+ else:
549
+ last_agent = user_agent if user_agent else temp_user_proxy
550
+
551
+ return messages, last_agent, swarm_agent_names, temp_user_list
552
+
553
+
554
+ def _setup_context_variables(
555
+ tool_execution: ConversableAgent,
556
+ agents: list[ConversableAgent],
557
+ manager: GroupChatManager,
558
+ context_variables: ContextVariables,
559
+ ) -> None:
560
+ """Assign a common context_variables reference to all agents in the swarm, including the tool executor and group chat manager.
561
+
562
+ Args:
563
+ tool_execution: The tool execution agent.
564
+ agents: List of all agents in the conversation.
565
+ manager: GroupChatManager instance.
566
+ context_variables: Context variables to assign to all agents.
567
+ """
568
+ for agent in agents + [tool_execution] + [manager]:
569
+ agent.context_variables = context_variables
570
+
571
+
572
+ def _cleanup_temp_user_messages(chat_result: ChatResult) -> None:
573
+ """Remove temporary user proxy agent name from messages before returning.
574
+
575
+ Args:
576
+ chat_result: ChatResult instance.
577
+ """
578
+ for message in chat_result.chat_history:
579
+ if "name" in message and message["name"] == "_User":
580
+ del message["name"]
581
+
582
+
583
+ def _prepare_groupchat_auto_speaker(
584
+ groupchat: GroupChat,
585
+ last_swarm_agent: ConversableAgent,
586
+ after_work_next_agent_selection_msg: str
587
+ | ContextStr
588
+ | Callable[[ConversableAgent, list[dict[str, Any]]], str]
589
+ | None,
590
+ ) -> None:
591
+ """Prepare the group chat for auto speaker selection, includes updating or restore the groupchat speaker selection message.
592
+
593
+ Tool Executor and Nested Chat agents will be removed from the available agents list.
594
+
595
+ Args:
596
+ groupchat (GroupChat): GroupChat instance.
597
+ last_swarm_agent (ConversableAgent): The last swarm agent for which the LLM config is used
598
+ after_work_next_agent_selection_msg (Union[str, ContextStr, Callable[..., Any]]): Optional message to use for the agent selection (in internal group chat).
599
+ if a string, it will be use the string a the prompt template, no context variable substitution however '{agentlist}' will be substituted for a list of agents.
600
+ if a ContextStr, it will substitute the agentlist first and then the context variables
601
+ if a Callable, it will not substitute the agentlist or context variables, signature:
602
+ def my_selection_message(agent: ConversableAgent, messages: list[dict[str, Any]]) -> str
603
+ """
604
+
605
+ def substitute_agentlist(template: str) -> str:
606
+ # Run through group chat's string substitution first for {agentlist}
607
+ # We need to do this so that the next substitution doesn't fail with agentlist
608
+ # and we can remove the tool executor and nested chats from the available agents list
609
+ agent_list = [
610
+ agent
611
+ for agent in groupchat.agents
612
+ if agent.name != __TOOL_EXECUTOR_NAME__ and not agent.name.startswith("nested_chat_")
613
+ ]
614
+
615
+ groupchat.select_speaker_prompt_template = template
616
+ return groupchat.select_speaker_prompt(agent_list)
617
+
618
+ if after_work_next_agent_selection_msg is None:
619
+ # If there's no selection message, restore the default and filter out the tool executor and nested chat agents
620
+ groupchat.select_speaker_prompt_template = substitute_agentlist(SELECT_SPEAKER_PROMPT_TEMPLATE)
621
+ elif isinstance(after_work_next_agent_selection_msg, str):
622
+ # No context variable substitution for string, but agentlist will be substituted
623
+ groupchat.select_speaker_prompt_template = substitute_agentlist(after_work_next_agent_selection_msg)
624
+ elif isinstance(after_work_next_agent_selection_msg, ContextStr):
625
+ # Replace the agentlist in the string first, putting it into a new ContextStr
626
+ agent_list_replaced_string = ContextStr(
627
+ template=substitute_agentlist(after_work_next_agent_selection_msg.template)
628
+ )
629
+
630
+ # Then replace the context variables
631
+ groupchat.select_speaker_prompt_template = agent_list_replaced_string.format( # type: ignore[assignment]
632
+ last_swarm_agent.context_variables
633
+ )
634
+ elif callable(after_work_next_agent_selection_msg):
635
+ groupchat.select_speaker_prompt_template = substitute_agentlist(
636
+ after_work_next_agent_selection_msg(last_swarm_agent, groupchat.messages)
637
+ )
638
+
639
+
640
+ def _determine_next_agent(
641
+ last_speaker: ConversableAgent,
642
+ groupchat: GroupChat,
643
+ initial_agent: ConversableAgent,
644
+ use_initial_agent: bool,
645
+ tool_execution: ConversableAgent,
646
+ swarm_agent_names: list[str],
647
+ user_agent: UserProxyAgent | None,
648
+ swarm_after_work: AfterWorkOption | Callable[..., Any] | None,
649
+ ) -> Agent | Literal["auto"] | None:
650
+ """Determine the next agent in the conversation.
651
+
652
+ Args:
653
+ last_speaker (ConversableAgent): The last agent to speak.
654
+ groupchat (GroupChat): GroupChat instance.
655
+ initial_agent (ConversableAgent): The initial agent in the conversation.
656
+ use_initial_agent (bool): Whether to use the initial agent straight away.
657
+ tool_execution (ConversableAgent): The tool execution agent.
658
+ swarm_agent_names (list[str]): List of agent names.
659
+ user_agent (UserProxyAgent): Optional user proxy agent.
660
+ swarm_after_work (Union[AfterWorkOption, Callable[..., Any]]): Method to handle conversation continuation when an agent doesn't select the next agent.
661
+ """
662
+ if use_initial_agent:
663
+ return initial_agent
664
+
665
+ if "tool_calls" in groupchat.messages[-1]:
666
+ return tool_execution
667
+
668
+ after_work_condition = None
669
+
670
+ if tool_execution._swarm_next_agent is not None: # type: ignore[attr-defined]
671
+ next_agent: Agent | None = tool_execution._swarm_next_agent # type: ignore[attr-defined]
672
+ tool_execution._swarm_next_agent = None # type: ignore[attr-defined]
673
+
674
+ if not isinstance(next_agent, AfterWorkOption):
675
+ # Check for string, access agent from group chat.
676
+
677
+ if isinstance(next_agent, str):
678
+ if next_agent in swarm_agent_names:
679
+ next_agent = groupchat.agent_by_name(name=next_agent)
680
+ else:
681
+ raise ValueError(
682
+ f"No agent found with the name '{next_agent}'. Ensure the agent exists in the swarm."
683
+ )
684
+
685
+ return next_agent
686
+ else:
687
+ after_work_condition = next_agent
688
+
689
+ # get the last swarm agent
690
+ last_swarm_speaker = None
691
+ for message in reversed(groupchat.messages):
692
+ if "name" in message and message["name"] in swarm_agent_names and message["name"] != __TOOL_EXECUTOR_NAME__:
693
+ agent = groupchat.agent_by_name(name=message["name"])
694
+ if isinstance(agent, ConversableAgent):
695
+ last_swarm_speaker = agent
696
+ break
697
+ if last_swarm_speaker is None:
698
+ raise ValueError("No swarm agent found in the message history")
699
+
700
+ # If the user last spoke, return to the agent prior
701
+ if after_work_condition is None and (
702
+ (user_agent and last_speaker == user_agent) or groupchat.messages[-1]["role"] == "tool"
703
+ ):
704
+ return last_swarm_speaker
705
+
706
+ after_work_next_agent_selection_msg = None
707
+
708
+ if after_work_condition is None:
709
+ # Resolve after_work condition if one hasn't been passed in (agent-level overrides global)
710
+ after_work_condition = (
711
+ last_swarm_speaker._swarm_after_work # type: ignore[attr-defined]
712
+ if last_swarm_speaker._swarm_after_work is not None # type: ignore[attr-defined]
713
+ else swarm_after_work
714
+ )
715
+
716
+ if isinstance(after_work_condition, AfterWork):
717
+ after_work_next_agent_selection_msg = after_work_condition.next_agent_selection_msg
718
+ after_work_condition = after_work_condition.agent
719
+
720
+ # Evaluate callable after_work
721
+ if callable(after_work_condition):
722
+ after_work_condition = after_work_condition(last_swarm_speaker, groupchat.messages, groupchat)
723
+
724
+ if isinstance(after_work_condition, str): # Agent name in a string
725
+ if after_work_condition in swarm_agent_names:
726
+ return groupchat.agent_by_name(name=after_work_condition)
727
+ else:
728
+ raise ValueError(f"Invalid agent name in after_work: {after_work_condition}")
729
+ elif isinstance(after_work_condition, ConversableAgent):
730
+ return after_work_condition
731
+ elif isinstance(after_work_condition, AfterWorkOption):
732
+ if after_work_condition == AfterWorkOption.TERMINATE:
733
+ return None
734
+ elif after_work_condition == AfterWorkOption.REVERT_TO_USER:
735
+ return None if user_agent is None else user_agent
736
+ elif after_work_condition == AfterWorkOption.STAY:
737
+ return last_swarm_speaker
738
+ elif after_work_condition == AfterWorkOption.SWARM_MANAGER:
739
+ _prepare_groupchat_auto_speaker(groupchat, last_swarm_speaker, after_work_next_agent_selection_msg)
740
+ return "auto"
741
+ else:
742
+ raise ValueError("Invalid After Work condition or return value from callable")
743
+
744
+
745
+ def create_swarm_transition(
746
+ initial_agent: ConversableAgent,
747
+ tool_execution: ConversableAgent,
748
+ swarm_agent_names: list[str],
749
+ user_agent: UserProxyAgent | None,
750
+ swarm_after_work: AfterWorkOption | Callable[..., Any] | None,
751
+ ) -> Callable[[ConversableAgent, GroupChat], Agent | Literal["auto"] | None]:
752
+ """Creates a transition function for swarm chat with enclosed state for the use_initial_agent.
753
+
754
+ Args:
755
+ initial_agent (ConversableAgent): The first agent to speak
756
+ tool_execution (ConversableAgent): The tool execution agent
757
+ swarm_agent_names (list[str]): List of all agent names
758
+ user_agent (UserProxyAgent): Optional user proxy agent
759
+ swarm_after_work (Union[AfterWorkOption, Callable[..., Any]]): Swarm-level after work
760
+
761
+ Returns:
762
+ Callable transition function (for sync and async swarm chats)
763
+ """
764
+ # Create enclosed state, this will be set once per creation so will only be True on the first execution
765
+ # of swarm_transition
766
+ state = {"use_initial_agent": True}
767
+
768
+ def swarm_transition(last_speaker: ConversableAgent, groupchat: GroupChat) -> Agent | Literal["auto"] | None:
769
+ result = _determine_next_agent(
770
+ last_speaker=last_speaker,
771
+ groupchat=groupchat,
772
+ initial_agent=initial_agent,
773
+ use_initial_agent=state["use_initial_agent"],
774
+ tool_execution=tool_execution,
775
+ swarm_agent_names=swarm_agent_names,
776
+ user_agent=user_agent,
777
+ swarm_after_work=swarm_after_work,
778
+ )
779
+ state["use_initial_agent"] = False
780
+ return result
781
+
782
+ return swarm_transition
783
+
784
+
785
+ def _create_swarm_manager(
786
+ groupchat: GroupChat, swarm_manager_args: dict[str, Any] | None, agents: list[ConversableAgent]
787
+ ) -> GroupChatManager:
788
+ """Create a GroupChatManager for the swarm chat utilising any arguments passed in and ensure an LLM Config exists if needed
789
+
790
+ Args:
791
+ groupchat (GroupChat): Swarm groupchat.
792
+ swarm_manager_args (dict[str, Any]): Swarm manager arguments to create the GroupChatManager.
793
+ agents (list[ConversableAgent]): List of agents in the swarm.
794
+
795
+ Returns:
796
+ GroupChatManager: GroupChatManager instance.
797
+ """
798
+ manager_args = (swarm_manager_args or {}).copy()
799
+ if "groupchat" in manager_args:
800
+ raise ValueError("'groupchat' cannot be specified in swarm_manager_args as it is set by initiate_swarm_chat")
801
+ manager = GroupChatManager(groupchat, **manager_args)
802
+
803
+ # Ensure that our manager has an LLM Config if we have any AfterWorkOption.SWARM_MANAGER after works
804
+ if manager.llm_config is False:
805
+ for agent in agents:
806
+ if (
807
+ agent._swarm_after_work # type: ignore[attr-defined]
808
+ and isinstance(agent._swarm_after_work.agent, AfterWorkOption) # type: ignore[attr-defined]
809
+ and agent._swarm_after_work.agent == AfterWorkOption.SWARM_MANAGER # type: ignore[attr-defined]
810
+ ):
811
+ raise ValueError(
812
+ "The swarm manager doesn't have an LLM Config and it is required for AfterWorkOption.SWARM_MANAGER. Use the swarm_manager_args to specify the LLM Config for the swarm manager."
813
+ )
814
+
815
+ return manager
816
+
817
+
818
+ def make_remove_function(tool_msgs_to_remove: list[str]) -> Callable[[list[dict[str, Any]]], list[dict[str, Any]]]:
819
+ """Create a function to remove messages with tool calls from the messages list.
820
+
821
+ The returned function can be registered as a hook to "process_all_messages_before_reply"" to remove messages with tool calls.
822
+ """
823
+
824
+ def remove_messages(messages: list[dict[str, Any]], tool_msgs_to_remove: list[str]) -> list[dict[str, Any]]:
825
+ copied = copy.deepcopy(messages)
826
+ new_messages = []
827
+ removed_tool_ids = []
828
+ for message in copied:
829
+ # remove tool calls
830
+ if message.get("tool_calls") is not None:
831
+ filtered_tool_calls = []
832
+ for tool_call in message["tool_calls"]:
833
+ if tool_call.get("function") is not None and tool_call["function"]["name"] in tool_msgs_to_remove:
834
+ # remove
835
+ removed_tool_ids.append(tool_call["id"])
836
+ else:
837
+ filtered_tool_calls.append(tool_call)
838
+ if len(filtered_tool_calls) > 0:
839
+ message["tool_calls"] = filtered_tool_calls
840
+ else:
841
+ del message["tool_calls"]
842
+ if (
843
+ message.get("content") is None
844
+ or message.get("content") == ""
845
+ or message.get("content") == "None"
846
+ ):
847
+ continue # if no tool call and no content, skip this message
848
+ # else: keep the message with tool_calls removed
849
+ # remove corresponding tool responses
850
+ elif message.get("tool_responses") is not None:
851
+ filtered_tool_responses = []
852
+ for tool_response in message["tool_responses"]:
853
+ if tool_response["tool_call_id"] not in removed_tool_ids:
854
+ filtered_tool_responses.append(tool_response)
855
+
856
+ if len(filtered_tool_responses) > 0:
857
+ message["tool_responses"] = filtered_tool_responses
858
+ else:
859
+ continue
860
+
861
+ new_messages.append(message)
862
+
863
+ return new_messages
864
+
865
+ return partial(remove_messages, tool_msgs_to_remove=tool_msgs_to_remove)
866
+
867
+
868
+ @export_module("autogen")
869
+ def initiate_swarm_chat(
870
+ initial_agent: ConversableAgent,
871
+ messages: list[dict[str, Any]] | str,
872
+ agents: list[ConversableAgent],
873
+ user_agent: UserProxyAgent | None = None,
874
+ swarm_manager_args: dict[str, Any] | None = None,
875
+ max_rounds: int = 20,
876
+ context_variables: ContextVariables | None = None,
877
+ after_work: AfterWorkOption
878
+ | Callable[[ConversableAgent, list[dict[str, Any]], GroupChat], AfterWorkOption | ConversableAgent | str]
879
+ | None = AfterWorkOption.TERMINATE,
880
+ exclude_transit_message: bool = True,
881
+ ) -> tuple[ChatResult, ContextVariables, ConversableAgent]:
882
+ """Initialize and run a swarm chat
883
+
884
+ Args:
885
+ initial_agent: The first receiving agent of the conversation.
886
+ messages: Initial message(s).
887
+ agents: list of swarm agents.
888
+ user_agent: Optional user proxy agent for falling back to.
889
+ swarm_manager_args: Optional group chat manager arguments used to establish the swarm's groupchat manager, required when AfterWorkOption.SWARM_MANAGER is used.
890
+ max_rounds: Maximum number of conversation rounds.
891
+ context_variables: Starting context variables.
892
+ after_work: Method to handle conversation continuation when an agent doesn't select the next agent. If no agent is selected and no tool calls are output, we will use this method to determine the next agent.
893
+ Must be a AfterWork instance (which is a dataclass accepting a ConversableAgent, AfterWorkOption, A str (of the AfterWorkOption)) or a callable.
894
+ AfterWorkOption:
895
+ - TERMINATE (Default): Terminate the conversation.
896
+ - REVERT_TO_USER : Revert to the user agent if a user agent is provided. If not provided, terminate the conversation.
897
+ - STAY : Stay with the last speaker.
898
+
899
+ Callable: A custom function that takes the current agent, messages, and groupchat as arguments and returns an AfterWorkOption or a ConversableAgent (by reference or string name).
900
+ ```python
901
+ def custom_afterwork_func(last_speaker: ConversableAgent, messages: list[dict[str, Any]], groupchat: GroupChat) -> Union[AfterWorkOption, ConversableAgent, str]:
902
+ ```
903
+ exclude_transit_message: all registered handoff function call and responses messages will be removed from message list before calling an LLM.
904
+ Note: only with transition functions added with `register_handoff` will be removed. If you pass in a function to manage workflow, it will not be removed. You may register a cumstomized hook to `process_all_messages_before_reply` to remove that.
905
+
906
+ Returns:
907
+ ChatResult: Conversations chat history.
908
+ ContextVariables: Updated Context variables.
909
+ ConversableAgent: Last speaker.
910
+ """
911
+ context_variables = context_variables or ContextVariables()
912
+
913
+ tool_execution, nested_chat_agents = _prepare_swarm_agents(
914
+ initial_agent, agents, context_variables, exclude_transit_message
915
+ )
916
+
917
+ processed_messages, last_agent, swarm_agent_names, temp_user_list = _process_initial_messages(
918
+ messages, user_agent, agents, nested_chat_agents
919
+ )
920
+
921
+ # Create transition function (has enclosed state for initial agent)
922
+ swarm_transition = create_swarm_transition(
923
+ initial_agent=initial_agent,
924
+ tool_execution=tool_execution,
925
+ swarm_agent_names=swarm_agent_names,
926
+ user_agent=user_agent,
927
+ swarm_after_work=after_work,
928
+ )
929
+
930
+ groupchat = GroupChat(
931
+ agents=[tool_execution] + agents + nested_chat_agents + ([user_agent] if user_agent else temp_user_list),
932
+ messages=[],
933
+ max_round=max_rounds,
934
+ speaker_selection_method=swarm_transition,
935
+ )
936
+
937
+ manager = _create_swarm_manager(groupchat, swarm_manager_args, agents)
938
+
939
+ # Point all ConversableAgent's context variables to this function's context_variables
940
+ _setup_context_variables(tool_execution, agents, manager, context_variables)
941
+
942
+ # Link all agents with the GroupChatManager to allow access to the group chat
943
+ # and other agents, particularly the tool executor for setting _swarm_next_agent
944
+ _link_agents_to_swarm_manager(groupchat.agents, manager) # Commented out as the function is not defined
945
+
946
+ if len(processed_messages) > 1:
947
+ last_agent, last_message = manager.resume(messages=processed_messages)
948
+ clear_history = False
949
+ else:
950
+ last_message = processed_messages[0]
951
+ clear_history = True
952
+
953
+ if last_agent is None:
954
+ raise ValueError("No agent selected to start the conversation")
955
+
956
+ chat_result = last_agent.initiate_chat( # type: ignore[attr-defined]
957
+ manager,
958
+ message=last_message,
959
+ clear_history=clear_history,
960
+ )
961
+
962
+ _cleanup_temp_user_messages(chat_result)
963
+
964
+ return chat_result, context_variables, manager.last_speaker # type: ignore[return-value]
965
+
966
+
967
+ @export_module("autogen")
968
+ def run_swarm(
969
+ initial_agent: ConversableAgent,
970
+ messages: list[dict[str, Any]] | str,
971
+ agents: list[ConversableAgent],
972
+ user_agent: UserProxyAgent | None = None,
973
+ swarm_manager_args: dict[str, Any] | None = None,
974
+ max_rounds: int = 20,
975
+ context_variables: ContextVariables | None = None,
976
+ after_work: AfterWorkOption
977
+ | Callable[[ConversableAgent, list[dict[str, Any]], GroupChat], AfterWorkOption | ConversableAgent | str]
978
+ | None = AfterWorkOption.TERMINATE,
979
+ exclude_transit_message: bool = True,
980
+ ) -> RunResponseProtocol:
981
+ iostream = ThreadIOStream()
982
+ response = RunResponse(iostream, agents) # type: ignore[arg-type]
983
+
984
+ def stream_run(
985
+ iostream: ThreadIOStream = iostream,
986
+ response: RunResponse = response,
987
+ ) -> None:
988
+ with IOStream.set_default(iostream):
989
+ try:
990
+ chat_result, returned_context_variables, last_speaker = initiate_swarm_chat(
991
+ initial_agent=initial_agent,
992
+ messages=messages,
993
+ agents=agents,
994
+ user_agent=user_agent,
995
+ swarm_manager_args=swarm_manager_args,
996
+ max_rounds=max_rounds,
997
+ context_variables=context_variables,
998
+ after_work=after_work,
999
+ exclude_transit_message=exclude_transit_message,
1000
+ )
1001
+
1002
+ IOStream.get_default().send(
1003
+ RunCompletionEvent( # type: ignore[call-arg]
1004
+ history=chat_result.chat_history,
1005
+ summary=chat_result.summary,
1006
+ cost=chat_result.cost,
1007
+ last_speaker=last_speaker.name,
1008
+ context_variables=returned_context_variables,
1009
+ )
1010
+ )
1011
+ except Exception as e:
1012
+ response.iostream.send(ErrorEvent(error=e)) # type: ignore[call-arg]
1013
+
1014
+ threading.Thread(
1015
+ target=stream_run,
1016
+ ).start()
1017
+
1018
+ return response
1019
+
1020
+
1021
+ @export_module("autogen")
1022
+ async def a_initiate_swarm_chat(
1023
+ initial_agent: ConversableAgent,
1024
+ messages: list[dict[str, Any]] | str,
1025
+ agents: list[ConversableAgent],
1026
+ user_agent: UserProxyAgent | None = None,
1027
+ swarm_manager_args: dict[str, Any] | None = None,
1028
+ max_rounds: int = 20,
1029
+ context_variables: ContextVariables | None = None,
1030
+ after_work: AfterWorkOption
1031
+ | Callable[[ConversableAgent, list[dict[str, Any]], GroupChat], AfterWorkOption | ConversableAgent | str]
1032
+ | None = AfterWorkOption.TERMINATE,
1033
+ exclude_transit_message: bool = True,
1034
+ ) -> tuple[ChatResult, ContextVariables, ConversableAgent]:
1035
+ """Initialize and run a swarm chat asynchronously
1036
+
1037
+ Args:
1038
+ initial_agent: The first receiving agent of the conversation.
1039
+ messages: Initial message(s).
1040
+ agents: List of swarm agents.
1041
+ user_agent: Optional user proxy agent for falling back to.
1042
+ swarm_manager_args: Optional group chat manager arguments used to establish the swarm's groupchat manager, required when AfterWorkOption.SWARM_MANAGER is used.
1043
+ max_rounds: Maximum number of conversation rounds.
1044
+ context_variables: Starting context variables.
1045
+ after_work: Method to handle conversation continuation when an agent doesn't select the next agent. If no agent is selected and no tool calls are output, we will use this method to determine the next agent.
1046
+ Must be a AfterWork instance (which is a dataclass accepting a ConversableAgent, AfterWorkOption, A str (of the AfterWorkOption)) or a callable.
1047
+ AfterWorkOption:
1048
+ - TERMINATE (Default): Terminate the conversation.
1049
+ - REVERT_TO_USER : Revert to the user agent if a user agent is provided. If not provided, terminate the conversation.
1050
+ - STAY : Stay with the last speaker.
1051
+
1052
+ Callable: A custom function that takes the current agent, messages, and groupchat as arguments and returns an AfterWorkOption or a ConversableAgent (by reference or string name).
1053
+ ```python
1054
+ def custom_afterwork_func(last_speaker: ConversableAgent, messages: list[dict[str, Any]], groupchat: GroupChat) -> Union[AfterWorkOption, ConversableAgent, str]:
1055
+ ```
1056
+ exclude_transit_message: all registered handoff function call and responses messages will be removed from message list before calling an LLM.
1057
+ Note: only with transition functions added with `register_handoff` will be removed. If you pass in a function to manage workflow, it will not be removed. You may register a cumstomized hook to `process_all_messages_before_reply` to remove that.
1058
+
1059
+ Returns:
1060
+ ChatResult: Conversations chat history.
1061
+ ContextVariables: Updated Context variables.
1062
+ ConversableAgent: Last speaker.
1063
+ """
1064
+ context_variables = context_variables or ContextVariables()
1065
+ tool_execution, nested_chat_agents = _prepare_swarm_agents(
1066
+ initial_agent, agents, context_variables, exclude_transit_message
1067
+ )
1068
+
1069
+ processed_messages, last_agent, swarm_agent_names, temp_user_list = _process_initial_messages(
1070
+ messages, user_agent, agents, nested_chat_agents
1071
+ )
1072
+
1073
+ # Create transition function (has enclosed state for initial agent)
1074
+ swarm_transition = create_swarm_transition(
1075
+ initial_agent=initial_agent,
1076
+ tool_execution=tool_execution,
1077
+ swarm_agent_names=swarm_agent_names,
1078
+ user_agent=user_agent,
1079
+ swarm_after_work=after_work,
1080
+ )
1081
+
1082
+ groupchat = GroupChat(
1083
+ agents=[tool_execution] + agents + nested_chat_agents + ([user_agent] if user_agent else temp_user_list),
1084
+ messages=[],
1085
+ max_round=max_rounds,
1086
+ speaker_selection_method=swarm_transition,
1087
+ )
1088
+
1089
+ manager = _create_swarm_manager(groupchat, swarm_manager_args, agents)
1090
+
1091
+ # Point all ConversableAgent's context variables to this function's context_variables
1092
+ _setup_context_variables(tool_execution, agents, manager, context_variables)
1093
+
1094
+ # Link all agents with the GroupChatManager to allow access to the group chat
1095
+ # and other agents, particularly the tool executor for setting _swarm_next_agent
1096
+ _link_agents_to_swarm_manager(groupchat.agents, manager)
1097
+
1098
+ if len(processed_messages) > 1:
1099
+ last_agent, last_message = await manager.a_resume(messages=processed_messages)
1100
+ clear_history = False
1101
+ else:
1102
+ last_message = processed_messages[0]
1103
+ clear_history = True
1104
+
1105
+ if last_agent is None:
1106
+ raise ValueError("No agent selected to start the conversation")
1107
+
1108
+ chat_result = await last_agent.a_initiate_chat( # type: ignore[attr-defined]
1109
+ manager,
1110
+ message=last_message,
1111
+ clear_history=clear_history,
1112
+ )
1113
+
1114
+ _cleanup_temp_user_messages(chat_result)
1115
+
1116
+ return chat_result, context_variables, manager.last_speaker # type: ignore[return-value]
1117
+
1118
+
1119
+ @export_module("autogen")
1120
+ async def a_run_swarm(
1121
+ initial_agent: ConversableAgent,
1122
+ messages: list[dict[str, Any]] | str,
1123
+ agents: list[ConversableAgent],
1124
+ user_agent: UserProxyAgent | None = None,
1125
+ swarm_manager_args: dict[str, Any] | None = None,
1126
+ max_rounds: int = 20,
1127
+ context_variables: ContextVariables | None = None,
1128
+ after_work: AfterWorkOption
1129
+ | Callable[[ConversableAgent, list[dict[str, Any]], GroupChat], AfterWorkOption | ConversableAgent | str]
1130
+ | None = AfterWorkOption.TERMINATE,
1131
+ exclude_transit_message: bool = True,
1132
+ ) -> AsyncRunResponseProtocol:
1133
+ iostream = AsyncThreadIOStream()
1134
+ response = AsyncRunResponse(iostream, agents) # type: ignore[arg-type]
1135
+
1136
+ async def stream_run(
1137
+ iostream: AsyncThreadIOStream = iostream,
1138
+ response: AsyncRunResponse = response,
1139
+ ) -> None:
1140
+ with IOStream.set_default(iostream):
1141
+ try:
1142
+ chat_result, returned_context_variables, last_speaker = await a_initiate_swarm_chat(
1143
+ initial_agent=initial_agent,
1144
+ messages=messages,
1145
+ agents=agents,
1146
+ user_agent=user_agent,
1147
+ swarm_manager_args=swarm_manager_args,
1148
+ max_rounds=max_rounds,
1149
+ context_variables=context_variables,
1150
+ after_work=after_work,
1151
+ exclude_transit_message=exclude_transit_message,
1152
+ )
1153
+
1154
+ IOStream.get_default().send(
1155
+ RunCompletionEvent( # type: ignore[call-arg]
1156
+ history=chat_result.chat_history,
1157
+ summary=chat_result.summary,
1158
+ cost=chat_result.cost,
1159
+ last_speaker=last_speaker.name,
1160
+ context_variables=returned_context_variables,
1161
+ )
1162
+ )
1163
+ except Exception as e:
1164
+ response.iostream.send(ErrorEvent(error=e)) # type: ignore[call-arg]
1165
+
1166
+ task = asyncio.create_task(stream_run())
1167
+ # prevent the task from being garbage collected
1168
+ response._task_ref = task # type: ignore[attr-defined]
1169
+ return response
1170
+
1171
+
1172
+ @export_module("autogen")
1173
+ class SwarmResult(BaseModel):
1174
+ """Encapsulates the possible return values for a swarm agent function."""
1175
+
1176
+ values: str = ""
1177
+ agent: ConversableAgent | AfterWorkOption | str | None = None
1178
+ context_variables: ContextVariables | None = None
1179
+
1180
+ @field_serializer("agent", when_used="json")
1181
+ def serialize_agent(self, agent: ConversableAgent | str) -> str:
1182
+ if isinstance(agent, ConversableAgent):
1183
+ return agent.name
1184
+ return agent
1185
+
1186
+ def model_post_init(self, __context: Any) -> None:
1187
+ # Initialise with a new ContextVariables object if not provided
1188
+ if self.context_variables is None:
1189
+ self.context_variables = ContextVariables()
1190
+
1191
+ model_config = ConfigDict(arbitrary_types_allowed=True)
1192
+
1193
+ def __str__(self) -> str:
1194
+ return self.values
1195
+
1196
+
1197
+ def _set_to_tool_execution(agent: ConversableAgent) -> None:
1198
+ """Set to a special instance of ConversableAgent that is responsible for executing tool calls from other swarm agents.
1199
+ This agent will be used internally and should not be visible to the user.
1200
+
1201
+ It will execute the tool calls and update the referenced context_variables and next_agent accordingly.
1202
+ """
1203
+ agent._swarm_next_agent = None # type: ignore[attr-defined]
1204
+ agent._reply_func_list.clear()
1205
+ agent.register_reply([Agent, None], _generate_swarm_tool_reply)
1206
+
1207
+
1208
+ @export_module("autogen")
1209
+ def register_hand_off(
1210
+ agent: ConversableAgent,
1211
+ hand_to: list[OnCondition | OnContextCondition | AfterWork] | OnCondition | OnContextCondition | AfterWork,
1212
+ ) -> None:
1213
+ """Register a function to hand off to another agent.
1214
+
1215
+ Args:
1216
+ agent: The agent to register the hand off with.
1217
+ hand_to: A list of OnCondition's and an, optional, AfterWork condition
1218
+
1219
+ Hand off template:
1220
+ def transfer_to_agent_name() -> ConversableAgent:
1221
+ return agent_name
1222
+ 1. register the function with the agent
1223
+ 2. register the schema with the agent, description set to the condition
1224
+ """
1225
+ # If the agent hasn't been established as a swarm agent, do so first
1226
+ if not hasattr(agent, "_swarm_is_established"):
1227
+ _establish_swarm_agent(agent)
1228
+
1229
+ # Ensure that hand_to is a list or OnCondition or AfterWork
1230
+ if not isinstance(hand_to, (list, OnCondition, OnContextCondition, AfterWork)):
1231
+ raise ValueError("hand_to must be a list of OnCondition, OnContextCondition, or AfterWork")
1232
+
1233
+ if isinstance(hand_to, (OnCondition, OnContextCondition, AfterWork)):
1234
+ hand_to = [hand_to]
1235
+
1236
+ for transit in hand_to:
1237
+ if isinstance(transit, AfterWork):
1238
+ if not (isinstance(transit.agent, (AfterWorkOption, ConversableAgent, str)) or callable(transit.agent)):
1239
+ raise ValueError(f"Invalid AfterWork agent: {transit.agent}")
1240
+ agent._swarm_after_work = transit # type: ignore[attr-defined]
1241
+ agent._swarm_after_work_selection_msg = transit.next_agent_selection_msg # type: ignore[attr-defined]
1242
+ elif isinstance(transit, OnCondition):
1243
+ if isinstance(transit.target, ConversableAgent):
1244
+ # Transition to agent
1245
+
1246
+ # Create closure with current loop transit value
1247
+ # to ensure the condition matches the one in the loop
1248
+ def make_transfer_function(current_transit: OnCondition) -> Callable[[], ConversableAgent]:
1249
+ def transfer_to_agent() -> ConversableAgent:
1250
+ return current_transit.target # type: ignore[return-value]
1251
+
1252
+ return transfer_to_agent
1253
+
1254
+ transfer_func = make_transfer_function(transit)
1255
+
1256
+ # Store function to add/remove later based on it being 'available'
1257
+ # Function names are made unique and allow multiple OnCondition's to the same agent
1258
+ base_func_name = f"transfer_{agent.name}_to_{transit.target.name}"
1259
+ func_name = base_func_name
1260
+ count = 2
1261
+ while func_name in agent._swarm_conditional_functions: # type: ignore[attr-defined]
1262
+ func_name = f"{base_func_name}_{count}"
1263
+ count += 1
1264
+
1265
+ # Store function to add/remove later based on it being 'available'
1266
+ agent._swarm_conditional_functions[func_name] = (transfer_func, transit) # type: ignore[attr-defined]
1267
+
1268
+ elif isinstance(transit.target, dict):
1269
+ # Transition to a nested chat
1270
+ # We will store them here and establish them in the initiate_swarm_chat
1271
+ agent._swarm_nested_chat_handoffs.append({ # type: ignore[attr-defined]
1272
+ "nested_chats": transit.target,
1273
+ "condition": transit.condition,
1274
+ "available": transit.available,
1275
+ })
1276
+
1277
+ elif isinstance(transit, OnContextCondition):
1278
+ agent._swarm_oncontextconditions.append(transit) # type: ignore[attr-defined]
1279
+
1280
+ else:
1281
+ raise ValueError("Invalid hand off condition, must be either OnCondition or AfterWork")
1282
+
1283
+
1284
+ def _update_conditional_functions(agent: ConversableAgent, messages: list[dict[str, Any]] | None = None) -> None:
1285
+ """Updates the agent's functions based on the OnCondition's available condition."""
1286
+ for func_name, (func, on_condition) in agent._swarm_conditional_functions.items(): # type: ignore[attr-defined]
1287
+ is_available = True
1288
+
1289
+ if on_condition.available is not None:
1290
+ if callable(on_condition.available):
1291
+ is_available = on_condition.available(agent, next(iter(agent.chat_messages.values())))
1292
+ elif isinstance(on_condition.available, str):
1293
+ is_available = agent.context_variables.get(on_condition.available) or False
1294
+ elif isinstance(on_condition.available, ContextExpression):
1295
+ is_available = on_condition.available.evaluate(agent.context_variables)
1296
+
1297
+ # first remove the function if it exists
1298
+ if func_name in agent._function_map:
1299
+ agent.update_tool_signature(func_name, is_remove=True)
1300
+ del agent._function_map[func_name]
1301
+
1302
+ # then add the function if it is available, so that the function signature is updated
1303
+ if is_available:
1304
+ condition = on_condition.condition
1305
+ if isinstance(condition, ContextStr):
1306
+ condition = condition.format(context_variables=agent.context_variables)
1307
+ elif callable(condition):
1308
+ condition = condition(agent, messages)
1309
+
1310
+ # TODO: Don't add it if it's already there
1311
+ agent._add_single_function(func, func_name, condition)
1312
+
1313
+
1314
+ def _generate_swarm_tool_reply(
1315
+ agent: ConversableAgent,
1316
+ messages: list[dict[str, Any]] | None = None,
1317
+ sender: Agent | None = None,
1318
+ config: OpenAIWrapper | None = None,
1319
+ ) -> tuple[bool, dict[str, Any] | None]:
1320
+ """Pre-processes and generates tool call replies.
1321
+
1322
+ This function:
1323
+ 1. Adds context_variables back to the tool call for the function, if necessary.
1324
+ 2. Generates the tool calls reply.
1325
+ 3. Updates context_variables and next_agent based on the tool call response.
1326
+ """
1327
+ if config is None:
1328
+ config = agent # type: ignore[assignment]
1329
+ if messages is None:
1330
+ messages = agent._oai_messages[sender]
1331
+
1332
+ message = messages[-1]
1333
+ if "tool_calls" in message:
1334
+ tool_call_count = len(message["tool_calls"])
1335
+
1336
+ # Loop through tool calls individually (so context can be updated after each function call)
1337
+ next_agent: Agent | None = None
1338
+ tool_responses_inner = []
1339
+ contents = []
1340
+ for index in range(tool_call_count):
1341
+ message_copy = copy.deepcopy(message)
1342
+
1343
+ # 1. add context_variables to the tool call arguments
1344
+ tool_call = message_copy["tool_calls"][index]
1345
+
1346
+ # Ensure we are only executing the one tool at a time
1347
+ message_copy["tool_calls"] = [tool_call]
1348
+
1349
+ # 2. generate tool calls reply
1350
+ _, tool_message = agent.generate_tool_calls_reply([message_copy])
1351
+
1352
+ if tool_message is None:
1353
+ raise ValueError("Tool call did not return a message")
1354
+
1355
+ # 3. update context_variables and next_agent, convert content to string
1356
+ for tool_response in tool_message["tool_responses"]:
1357
+ content = tool_response.get("content")
1358
+
1359
+ if isinstance(content, SwarmResult):
1360
+ if content.context_variables is not None and content.context_variables.to_dict() != {}:
1361
+ agent.context_variables.update(content.context_variables.to_dict())
1362
+ if content.agent is not None:
1363
+ next_agent = content.agent # type: ignore[assignment]
1364
+ elif isinstance(content, Agent):
1365
+ next_agent = content
1366
+
1367
+ # Serialize the content to a string
1368
+ if content is not None:
1369
+ tool_response["content"] = str(content)
1370
+
1371
+ tool_responses_inner.append(tool_response)
1372
+ contents.append(str(tool_response["content"]))
1373
+
1374
+ agent._swarm_next_agent = next_agent # type: ignore[attr-defined]
1375
+
1376
+ # Put the tool responses and content strings back into the response message
1377
+ # Caters for multiple tool calls
1378
+ if tool_message is None:
1379
+ raise ValueError("Tool call did not return a message")
1380
+
1381
+ tool_message["tool_responses"] = tool_responses_inner
1382
+ tool_message["content"] = "\n".join(contents)
1383
+
1384
+ return True, tool_message
1385
+ return False, None
1386
+
1387
+
1388
+ class SwarmAgent(ConversableAgent):
1389
+ """SwarmAgent is deprecated and has been incorporated into ConversableAgent, use ConversableAgent instead. SwarmAgent will be removed in a future version (TBD)"""
1390
+
1391
+ def __init__(self, *args: Any, **kwargs: Any):
1392
+ """Initializes a new instance of the SwarmAgent class.
1393
+
1394
+ Args:
1395
+ *args: Variable length argument list.
1396
+ **kwargs: Arbitrary keyword arguments.
1397
+ """
1398
+ warnings.warn(
1399
+ "SwarmAgent is deprecated and has been incorporated into ConversableAgent, use ConversableAgent instead. SwarmAgent will be removed in a future version (TBD).",
1400
+ DeprecationWarning,
1401
+ stacklevel=2,
1402
+ )
1403
+
1404
+ super().__init__(*args, **kwargs)