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,643 @@
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 logging
6
+ from copy import deepcopy
7
+ from pathlib import Path
8
+ from typing import Annotated, Any, cast
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+ from .... import Agent, ConversableAgent, UpdateSystemMessage
13
+ from ....agentchat.contrib.rag.query_engine import RAGQueryEngine
14
+ from ....agentchat.group.context_condition import ExpressionContextCondition
15
+ from ....agentchat.group.context_expression import ContextExpression
16
+ from ....agentchat.group.context_variables import ContextVariables
17
+ from ....agentchat.group.llm_condition import StringLLMCondition
18
+ from ....agentchat.group.multi_agent_chat import initiate_group_chat
19
+ from ....agentchat.group.on_condition import OnCondition
20
+ from ....agentchat.group.on_context_condition import OnContextCondition
21
+ from ....agentchat.group.patterns.pattern import DefaultPattern
22
+ from ....agentchat.group.reply_result import ReplyResult
23
+ from ....agentchat.group.targets.transition_target import AgentNameTarget, AgentTarget, StayTarget, TerminateTarget
24
+ from ....doc_utils import export_module
25
+ from ....llm_config import LLMConfig
26
+ from ....oai.client import OpenAIWrapper
27
+ from .chroma_query_engine import VectorChromaQueryEngine
28
+ from .docling_doc_ingest_agent import DoclingDocIngestAgent
29
+ from .document_conditions import SummaryTaskAvailableCondition
30
+ from .document_utils import Ingest, Query
31
+
32
+ __all__ = ["DocAgent"]
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ DEFAULT_SYSTEM_MESSAGE = """
37
+ You are a document agent.
38
+ You are given a list of documents to ingest and a list of queries to perform.
39
+ You are responsible for ingesting the documents and answering the queries.
40
+ """
41
+ TASK_MANAGER_NAME = "TaskManagerAgent"
42
+ TASK_MANAGER_SYSTEM_MESSAGE = """
43
+ You are a task manager agent. You have 2 priorities:
44
+ 1. You initiate the tasks which updates the context variables based on the task decisions (DocumentTask) from the DocumentTriageAgent.
45
+ ALWAYS call initiate_tasks first when you receive a message from the DocumentTriageAgent, even if you think there are no new tasks.
46
+ This ensures that any new ingestions or queries from the triage agent are properly recorded.
47
+ Put all ingestion and query tasks into the one tool call.
48
+ i.e. output
49
+ {
50
+ "ingestions": [
51
+ {
52
+ "path_or_url": "path_or_url"
53
+ }
54
+ ],
55
+ "queries": [
56
+ {
57
+ "query_type": "RAG_QUERY",
58
+ "query": "query"
59
+ }
60
+ ],
61
+ "query_results": [
62
+ {
63
+ "query": "query",
64
+ "result": "result"
65
+ }
66
+ ]
67
+ }
68
+ 2. If there are no documents to ingest and no queries to run, hand control off to the summary agent.
69
+
70
+ Put all file paths and URLs into the ingestions. A http/https URL is also a valid path and should be ingested.
71
+
72
+ Use the initiate_tasks tool to incorporate all ingestions and queries. Don't call it again until new ingestions or queries are raised.
73
+
74
+ New ingestions and queries may be raised from time to time, so use the initiate_tasks again if you see new ingestions/queries.
75
+
76
+ Once the ingestion and query tasks are done, return to the summary agent, do not ask questions of the user.
77
+ """
78
+
79
+ DEFAULT_ERROR_GROUP_CHAT_MESSAGE: str = """
80
+ Document Agent failed to perform task.
81
+ """
82
+
83
+ ERROR_MANAGER_NAME = "ErrorManagerAgent"
84
+ ERROR_MANAGER_SYSTEM_MESSAGE = """
85
+ You communicate errors to the user. Include the original error messages in full. Use the format:
86
+ The following error(s) have occurred:
87
+ - Error 1
88
+ - Error 2
89
+ """
90
+
91
+
92
+ class DocumentTask(BaseModel):
93
+ """The structured output format for task decisions."""
94
+
95
+ ingestions: list[Ingest] = Field(description="The list of documents to ingest.")
96
+ queries: list[Query] = Field(description="The list of queries to perform.")
97
+
98
+ def format(self) -> str:
99
+ """Format the DocumentTask as a string for the TaskManager to work with."""
100
+ if len(self.ingestions) == 0 and len(self.queries) == 0:
101
+ return "There were no ingestion or query tasks detected."
102
+
103
+ instructions = "Tasks:\n\n"
104
+ order = 1
105
+
106
+ if len(self.ingestions) > 0:
107
+ instructions += "Ingestions:\n"
108
+ for ingestion in self.ingestions:
109
+ instructions += f"{order}: {ingestion.path_or_url}\n"
110
+ order += 1
111
+
112
+ instructions += "\n"
113
+
114
+ if len(self.queries) > 0:
115
+ instructions += "Queries:\n"
116
+ for query in self.queries:
117
+ instructions += f"{order}: {query.query}\n"
118
+ order += 1
119
+
120
+ return instructions
121
+
122
+
123
+ class DocumentTriageAgent(ConversableAgent):
124
+ """The DocumentTriageAgent is responsible for deciding what type of task to perform from user requests."""
125
+
126
+ def __init__(self, llm_config: LLMConfig | dict[str, Any] | None = None):
127
+ # Add the structured message to the LLM configuration
128
+ structured_config_list = deepcopy(llm_config)
129
+ structured_config_list["response_format"] = DocumentTask # type: ignore[index]
130
+
131
+ super().__init__(
132
+ name="DocumentTriageAgent",
133
+ system_message=(
134
+ "You are a document triage agent. "
135
+ "You are responsible for deciding what type of task to perform from a user's request and populating a DocumentTask formatted response. "
136
+ "If the user specifies files or URLs, add them as individual 'ingestions' to DocumentTask. "
137
+ "You can access external websites if given a URL, so put them in as ingestions. "
138
+ "Add the user's questions about the files/URLs as individual 'RAG_QUERY' queries to the 'query' list in the DocumentTask. "
139
+ "Don't make up questions, keep it as concise and close to the user's request as possible."
140
+ ),
141
+ human_input_mode="NEVER",
142
+ llm_config=structured_config_list,
143
+ )
144
+
145
+
146
+ @export_module("autogen.agents.experimental")
147
+ class DocAgent(ConversableAgent):
148
+ """The DocAgent is responsible for ingest and querying documents.
149
+
150
+ Internally, it generates a group chat with a set of agents to ingest, query, and summarize.
151
+ """
152
+
153
+ def __init__(
154
+ self,
155
+ name: str | None = None,
156
+ llm_config: LLMConfig | dict[str, Any] | None = None,
157
+ system_message: str | None = None,
158
+ parsed_docs_path: str | Path | None = None,
159
+ collection_name: str | None = None,
160
+ query_engine: RAGQueryEngine | None = None,
161
+ ):
162
+ """Initialize the DocAgent.
163
+
164
+ Args:
165
+ name (Optional[str]): The name of the DocAgent.
166
+ llm_config (Optional[LLMConfig, dict[str, Any]]): The configuration for the LLM.
167
+ system_message (Optional[str]): The system message for the DocAgent.
168
+ parsed_docs_path (Union[str, Path]): The path where parsed documents will be stored.
169
+ collection_name (Optional[str]): The unique name for the data store collection. If omitted, a random name will be used. Populate this to reuse previous ingested data.
170
+ query_engine (Optional[RAGQueryEngine]): The query engine to use for querying documents, defaults to VectorChromaQueryEngine if none provided.
171
+ Use enable_query_citations and implement query_with_citations method to enable citation support. e.g. VectorChromaCitationQueryEngine
172
+
173
+ The DocAgent is responsible for generating a group of agents to solve a task.
174
+
175
+ The agents that the DocAgent generates are:
176
+ - Triage Agent: responsible for deciding what type of task to perform from user requests.
177
+ - Task Manager Agent: responsible for managing the tasks.
178
+ - Parser Agent: responsible for parsing the documents.
179
+ - Data Ingestion Agent: responsible for ingesting the documents.
180
+ - Query Agent: responsible for answering the user's questions.
181
+ - Error Agent: responsible for returning errors gracefully.
182
+ - Summary Agent: responsible for generating a summary of the user's questions.
183
+ """
184
+ name = name or "DocAgent"
185
+ llm_config = llm_config or LLMConfig.get_current_llm_config()
186
+ system_message = system_message or DEFAULT_SYSTEM_MESSAGE
187
+ parsed_docs_path = parsed_docs_path or "./parsed_docs"
188
+
189
+ # Default Query Engine will be ChromaDB
190
+ if query_engine is None:
191
+ query_engine = VectorChromaQueryEngine(collection_name=collection_name)
192
+
193
+ super().__init__(
194
+ name=name,
195
+ system_message=system_message,
196
+ llm_config=llm_config,
197
+ human_input_mode="NEVER",
198
+ )
199
+ self.register_reply([ConversableAgent, None], self.generate_inner_group_chat_reply, position=0)
200
+
201
+ self._triage_agent = DocumentTriageAgent(llm_config=llm_config)
202
+
203
+ def create_error_agent_prompt(agent: ConversableAgent, messages: list[dict[str, Any]]) -> str:
204
+ """Create the error agent prompt, primarily used to update ingested documents for ending.
205
+
206
+ Args:
207
+ agent: The conversable agent requesting the prompt
208
+ messages: List of conversation messages
209
+
210
+ Returns:
211
+ str: The error manager system message
212
+ """
213
+ update_ingested_documents()
214
+
215
+ return ERROR_MANAGER_SYSTEM_MESSAGE
216
+
217
+ self._error_agent = ConversableAgent(
218
+ name=ERROR_MANAGER_NAME,
219
+ system_message=ERROR_MANAGER_SYSTEM_MESSAGE,
220
+ llm_config=llm_config,
221
+ update_agent_state_before_reply=[UpdateSystemMessage(create_error_agent_prompt)],
222
+ )
223
+
224
+ def update_ingested_documents() -> None:
225
+ """Updates the list of ingested documents, persisted so we can keep a list over multiple replies.
226
+
227
+ This function updates self.documents_ingested with any new documents that have been ingested
228
+ by the triage agent, ensuring persistence across multiple DocAgent interactions.
229
+ """
230
+ agent_documents_ingested = self._triage_agent.context_variables.get("DocumentsIngested", [])
231
+ # Update self.documents_ingested with any new documents ingested
232
+ for doc in agent_documents_ingested: # type: ignore[union-attr]
233
+ if doc not in self.documents_ingested:
234
+ self.documents_ingested.append(doc)
235
+
236
+ class TaskInitInfo(BaseModel):
237
+ ingestions: Annotated[list[Ingest], Field(description="List of documents, files, and URLs to ingest")]
238
+ queries: Annotated[list[Query], Field(description="List of queries to run")]
239
+
240
+ def _deduplicate_ingestions(
241
+ new_ingestions: list[Ingest], existing_ingestions: list[Ingest], documents_ingested: list[str]
242
+ ) -> tuple[list[Ingest], list[str]]:
243
+ """Deduplicate ingestions against existing pending and already ingested documents.
244
+
245
+ Args:
246
+ new_ingestions: List of new ingestion requests to process
247
+ existing_ingestions: List of ingestions already pending
248
+ documents_ingested: List of document paths already ingested
249
+
250
+ Returns:
251
+ tuple: (new_unique_ingestions, ignored_duplicate_paths)
252
+ """
253
+ unique_ingestions = []
254
+ ignored_paths = []
255
+
256
+ for ingestion in new_ingestions:
257
+ ingestion_path = ingestion.path_or_url
258
+ # Check if already in pending ingestions
259
+ already_pending = any(existing.path_or_url == ingestion_path for existing in existing_ingestions)
260
+ # Check if already ingested
261
+ already_ingested = ingestion_path in documents_ingested
262
+
263
+ if already_pending or already_ingested:
264
+ ignored_paths.append(ingestion_path)
265
+ else:
266
+ unique_ingestions.append(ingestion)
267
+
268
+ return unique_ingestions, ignored_paths
269
+
270
+ def _deduplicate_queries(
271
+ new_queries: list[Query], existing_queries: list[Query]
272
+ ) -> tuple[list[Query], list[str]]:
273
+ """Deduplicate queries against existing pending queries.
274
+
275
+ Args:
276
+ new_queries: List of new query requests to process
277
+ existing_queries: List of queries already pending
278
+
279
+ Returns:
280
+ tuple: (new_unique_queries, ignored_duplicate_query_texts)
281
+ """
282
+ unique_queries = []
283
+ ignored_query_texts = []
284
+
285
+ for query in new_queries:
286
+ query_text = query.query
287
+ # Check if query already exists in pending queries
288
+ already_pending = any(existing.query == query_text for existing in existing_queries)
289
+
290
+ if already_pending:
291
+ ignored_query_texts.append(query_text)
292
+ else:
293
+ unique_queries.append(query)
294
+
295
+ return unique_queries, ignored_query_texts
296
+
297
+ def _build_response_message(
298
+ added_ingestions: int, ignored_ingestions: list[str], added_queries: int, ignored_queries: list[str]
299
+ ) -> str:
300
+ """Build a descriptive response message about what was added/ignored.
301
+
302
+ Args:
303
+ added_ingestions: Number of unique ingestions added
304
+ ignored_ingestions: List of duplicate ingestion paths ignored
305
+ added_queries: Number of unique queries added
306
+ ignored_queries: List of duplicate query texts ignored
307
+
308
+ Returns:
309
+ str: Formatted message describing the results
310
+ """
311
+ messages = []
312
+
313
+ if added_ingestions > 0:
314
+ messages.append(f"Added {added_ingestions} new document(s) for ingestion")
315
+
316
+ if ignored_ingestions:
317
+ messages.append(
318
+ f"Ignored {len(ignored_ingestions)} duplicate document(s): {', '.join(ignored_ingestions)}"
319
+ )
320
+
321
+ if added_queries > 0:
322
+ messages.append(f"Added {added_queries} new query/queries")
323
+
324
+ if ignored_queries:
325
+ messages.append(f"Ignored {len(ignored_queries)} duplicate query/queries: {', '.join(ignored_queries)}")
326
+
327
+ if messages:
328
+ return "; ".join(messages)
329
+ else:
330
+ return "All requested tasks were duplicates and ignored"
331
+
332
+ def initiate_tasks(
333
+ task_init_info: Annotated[TaskInitInfo, "Documents, Files, URLs to ingest and the queries to run"],
334
+ context_variables: Annotated[ContextVariables, "Context variables"],
335
+ ) -> ReplyResult:
336
+ """Add documents to ingest and queries to answer when received.
337
+
338
+ Args:
339
+ task_init_info: Information about documents to ingest and queries to run
340
+ context_variables: The current context variables containing task state
341
+
342
+ Returns:
343
+ ReplyResult: Contains response message, updated context, and target agent
344
+ """
345
+ ingestions = task_init_info.ingestions
346
+ queries = task_init_info.queries
347
+
348
+ if "TaskInitiated" in context_variables:
349
+ # Handle follow-up tasks with deduplication
350
+ added_ingestions_count = 0
351
+ ignored_ingestions = []
352
+ added_queries_count = 0
353
+ ignored_queries = []
354
+
355
+ if ingestions:
356
+ existing_ingestions: list[Ingest] = context_variables.get("DocumentsToIngest", []) # type: ignore[assignment]
357
+ documents_ingested: list[str] = context_variables.get("DocumentsIngested", []) # type: ignore[assignment]
358
+
359
+ unique_ingestions, ignored_ingestion_paths = _deduplicate_ingestions(
360
+ ingestions, existing_ingestions, documents_ingested
361
+ )
362
+
363
+ if unique_ingestions:
364
+ context_variables["DocumentsToIngest"] = existing_ingestions + unique_ingestions
365
+ added_ingestions_count = len(unique_ingestions)
366
+
367
+ ignored_ingestions = ignored_ingestion_paths
368
+
369
+ if queries:
370
+ existing_queries: list[Query] = context_variables.get("QueriesToRun", []) # type: ignore[assignment]
371
+
372
+ unique_queries, ignored_query_texts = _deduplicate_queries(queries, existing_queries)
373
+
374
+ if unique_queries:
375
+ context_variables["QueriesToRun"] = existing_queries + unique_queries
376
+ added_queries_count = len(unique_queries)
377
+
378
+ ignored_queries = ignored_query_texts
379
+
380
+ if not ingestions and not queries:
381
+ return ReplyResult(message="No new tasks to initiate", context_variables=context_variables)
382
+
383
+ response_message = _build_response_message(
384
+ added_ingestions_count, ignored_ingestions, added_queries_count, ignored_queries
385
+ )
386
+
387
+ else:
388
+ # First time initialization - no deduplication needed
389
+ context_variables["DocumentsToIngest"] = ingestions
390
+ context_variables["QueriesToRun"] = list(queries)
391
+ context_variables["TaskInitiated"] = True
392
+ response_message = "Updated context variables with task decisions"
393
+
394
+ return ReplyResult(
395
+ message=response_message,
396
+ context_variables=context_variables,
397
+ target=AgentNameTarget(agent_name=TASK_MANAGER_NAME),
398
+ )
399
+
400
+ self._task_manager_agent = ConversableAgent(
401
+ name=TASK_MANAGER_NAME,
402
+ system_message=TASK_MANAGER_SYSTEM_MESSAGE,
403
+ llm_config=llm_config,
404
+ functions=[initiate_tasks],
405
+ )
406
+
407
+ self._triage_agent.handoffs.set_after_work(target=AgentTarget(agent=self._task_manager_agent))
408
+
409
+ self._data_ingestion_agent = DoclingDocIngestAgent(
410
+ llm_config=llm_config,
411
+ query_engine=query_engine,
412
+ parsed_docs_path=parsed_docs_path,
413
+ return_agent_success=TASK_MANAGER_NAME,
414
+ return_agent_error=ERROR_MANAGER_NAME,
415
+ )
416
+
417
+ def execute_rag_query(context_variables: ContextVariables) -> ReplyResult: # type: ignore[type-arg]
418
+ """Execute outstanding RAG queries, call the tool once for each outstanding query. Call this tool with no arguments.
419
+
420
+ Args:
421
+ context_variables: The current context variables containing queries to run
422
+
423
+ Returns:
424
+ ReplyResult: Contains query answer, updated context, and target agent
425
+ """
426
+ if len(context_variables["QueriesToRun"]) == 0:
427
+ return ReplyResult(
428
+ target=AgentNameTarget(agent_name=TASK_MANAGER_NAME),
429
+ message="No queries to run",
430
+ context_variables=context_variables,
431
+ )
432
+
433
+ query = context_variables["QueriesToRun"][0].query
434
+ try:
435
+ if (
436
+ hasattr(query_engine, "enable_query_citations")
437
+ and query_engine.enable_query_citations
438
+ and hasattr(query_engine, "query_with_citations")
439
+ and callable(query_engine.query_with_citations)
440
+ ):
441
+ answer_with_citations = query_engine.query_with_citations(query) # type: ignore[union-attr]
442
+ answer = answer_with_citations.answer
443
+ txt_citations = [
444
+ {
445
+ "text_chunk": source.node.get_text(),
446
+ "file_path": source.metadata["file_path"],
447
+ }
448
+ for source in answer_with_citations.citations
449
+ ]
450
+ logger.info(f"Citations:\n {txt_citations}")
451
+ else:
452
+ answer = query_engine.query(query)
453
+ txt_citations = []
454
+ context_variables["QueriesToRun"].pop(0)
455
+ context_variables["CompletedTaskCount"] += 1
456
+ context_variables["QueryResults"].append({"query": query, "answer": answer, "citations": txt_citations})
457
+
458
+ # Query completed
459
+
460
+ return ReplyResult(message=answer, context_variables=context_variables)
461
+ except Exception as e:
462
+ return ReplyResult(
463
+ target=AgentNameTarget(agent_name=ERROR_MANAGER_NAME),
464
+ message=f"Query failed for '{query}': {e}",
465
+ context_variables=context_variables,
466
+ )
467
+
468
+ self._query_agent = ConversableAgent(
469
+ name="QueryAgent",
470
+ system_message="""
471
+ You are a query agent.
472
+ You answer the user's questions only using the query function provided to you.
473
+ You can only call use the execute_rag_query tool once per turn.
474
+ """,
475
+ llm_config=llm_config,
476
+ functions=[execute_rag_query],
477
+ )
478
+
479
+ # Summary agent prompt will include the results of the ingestions and queries
480
+ def create_summary_agent_prompt(agent: ConversableAgent, messages: list[dict[str, Any]]) -> str:
481
+ """Create the summary agent prompt and updates ingested documents.
482
+
483
+ Args:
484
+ agent: The conversable agent requesting the prompt
485
+ messages: List of conversation messages
486
+
487
+ Returns:
488
+ str: The summary agent system message with context information
489
+ """
490
+ update_ingested_documents()
491
+
492
+ documents_to_ingest: list[Ingest] = cast(list[Ingest], agent.context_variables.get("DocumentsToIngest", []))
493
+ queries_to_run: list[Query] = cast(list[Query], agent.context_variables.get("QueriesToRun", []))
494
+
495
+ system_message = (
496
+ "You are a summary agent and you provide a summary of all completed tasks and the list of queries and their answers. "
497
+ "Output two sections: 'Ingestions:' and 'Queries:' with the results of the tasks. Number the ingestions and queries. "
498
+ "If there are no ingestions output 'No ingestions', if there are no queries output 'No queries' under their respective sections. "
499
+ "Don't add markdown formatting. "
500
+ "For each query, there is one answer and, optionally, a list of citations."
501
+ "For each citation, it contains two fields: 'text_chunk' and 'file_path'."
502
+ "Format the Query and Answers and Citations as 'Query:\nAnswer:\n\nCitations:'. Add a number to each query if more than one. Use the context below:\n"
503
+ "For each query, output the full citation contents and list them one by one,"
504
+ "format each citation as '\nSource [X] (chunk file_path here):\n\nChunk X:\n(text_chunk here)' and mark a separator between each citation using '\n#########################\n\n'."
505
+ "If there are no citations at all, DON'T INCLUDE ANY mention of citations.\n"
506
+ f"Documents ingested: {documents_to_ingest}\n"
507
+ f"Documents left to ingest: {len(documents_to_ingest)}\n"
508
+ f"Queries left to run: {len(queries_to_run)}\n"
509
+ f"Query and Answers and Citations: {queries_to_run}\n"
510
+ )
511
+
512
+ return system_message
513
+
514
+ self._summary_agent = ConversableAgent(
515
+ name="SummaryAgent",
516
+ llm_config=llm_config,
517
+ update_agent_state_before_reply=[UpdateSystemMessage(create_summary_agent_prompt)],
518
+ )
519
+
520
+ self._task_manager_agent.register_handoffs([
521
+ OnContextCondition( # Go straight to data ingestion agent if we have documents to ingest
522
+ target=AgentTarget(agent=self._data_ingestion_agent),
523
+ condition=ExpressionContextCondition(
524
+ expression=ContextExpression(expression="len(${DocumentsToIngest}) > 0")
525
+ ),
526
+ ),
527
+ OnContextCondition( # Go to Query agent if we have queries to run (ingestion above run first)
528
+ target=AgentTarget(agent=self._query_agent),
529
+ condition=ExpressionContextCondition(
530
+ expression=ContextExpression(expression="len(${QueriesToRun}) > 0")
531
+ ),
532
+ ),
533
+ # Removed automatic context condition - let task manager decide when to summarize
534
+ OnCondition(
535
+ target=AgentTarget(agent=self._summary_agent),
536
+ condition=StringLLMCondition(
537
+ prompt="Call this function if all work is done and a summary will be created"
538
+ ),
539
+ available=SummaryTaskAvailableCondition(), # Custom AvailableCondition class
540
+ ),
541
+ ])
542
+ self._task_manager_agent.handoffs.set_after_work(target=StayTarget())
543
+
544
+ self._data_ingestion_agent.handoffs.set_after_work(target=AgentTarget(agent=self._task_manager_agent))
545
+
546
+ self._query_agent.handoffs.set_after_work(target=AgentTarget(agent=self._task_manager_agent))
547
+
548
+ # Summary agent terminates the DocumentAgent
549
+ self._summary_agent.handoffs.set_after_work(target=TerminateTarget())
550
+
551
+ # The Error Agent always terminates the DocumentAgent
552
+ self._error_agent.handoffs.set_after_work(target=TerminateTarget())
553
+
554
+ self.register_reply([Agent, None], DocAgent.generate_inner_group_chat_reply)
555
+
556
+ self.documents_ingested: list[str] = []
557
+ self._group_chat_context_variables: ContextVariables | None = None
558
+
559
+ def generate_inner_group_chat_reply(
560
+ self,
561
+ messages: list[dict[str, Any]] | str | None = None,
562
+ sender: Agent | None = None,
563
+ config: OpenAIWrapper | None = None,
564
+ ) -> tuple[bool, str | dict[str, Any] | None]:
565
+ """Reply function that generates the inner group chat reply for the DocAgent.
566
+
567
+ Args:
568
+ messages: Input messages to process
569
+ sender: The agent that sent the message
570
+ config: OpenAI wrapper configuration
571
+
572
+ Returns:
573
+ tuple: (should_terminate, reply_message)
574
+ """
575
+ # Use existing context_variables if available, otherwise create new ones
576
+ if hasattr(self, "_group_chat_context_variables") and self._group_chat_context_variables is not None:
577
+ context_variables = self._group_chat_context_variables
578
+ # Reset for the new run
579
+ context_variables["DocumentsToIngest"] = [] # type: ignore[index]
580
+ else:
581
+ context_variables = ContextVariables(
582
+ data={
583
+ "CompletedTaskCount": 0,
584
+ "DocumentsToIngest": [],
585
+ "DocumentsIngested": self.documents_ingested,
586
+ "QueriesToRun": [],
587
+ "QueryResults": [],
588
+ }
589
+ )
590
+ self._group_chat_context_variables = context_variables
591
+
592
+ group_chat_agents = [
593
+ self._triage_agent,
594
+ self._task_manager_agent,
595
+ self._data_ingestion_agent,
596
+ self._query_agent,
597
+ self._summary_agent,
598
+ self._error_agent,
599
+ ]
600
+
601
+ agent_pattern = DefaultPattern(
602
+ initial_agent=self._triage_agent,
603
+ agents=group_chat_agents,
604
+ context_variables=context_variables,
605
+ group_after_work=TerminateTarget(),
606
+ )
607
+
608
+ chat_result, context_variables, last_speaker = initiate_group_chat(
609
+ pattern=agent_pattern,
610
+ messages=self._get_document_input_message(messages),
611
+ )
612
+ if last_speaker == self._error_agent:
613
+ # If we finish with the error agent, we return their message which contains the error
614
+ return True, chat_result.summary
615
+ if last_speaker != self._summary_agent:
616
+ # If the group chat finished but not with the summary agent, we assume something has gone wrong with the flow
617
+ return True, DEFAULT_ERROR_GROUP_CHAT_MESSAGE
618
+
619
+ return True, chat_result.summary
620
+
621
+ def _get_document_input_message(self, messages: list[dict[str, Any]] | str | None) -> str: # type: ignore[type-arg]
622
+ """Gets and validates the input message(s) for the document agent.
623
+
624
+ Args:
625
+ messages: Input messages as string or list of message dictionaries
626
+
627
+ Returns:
628
+ str: The extracted message content
629
+
630
+ Raises:
631
+ NotImplementedError: If messages format is invalid
632
+ """
633
+ if isinstance(messages, str):
634
+ return messages
635
+ elif (
636
+ isinstance(messages, list)
637
+ and len(messages) > 0
638
+ and "content" in messages[-1]
639
+ and isinstance(messages[-1]["content"], str)
640
+ ):
641
+ return messages[-1]["content"]
642
+ else:
643
+ raise NotImplementedError("Invalid messages format. Must be a list of messages or a string.")