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,50 @@
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
+ from typing import TYPE_CHECKING, Any, cast
6
+
7
+ from ....agentchat.group.available_condition import AvailableCondition
8
+ from .document_utils import Ingest, Query
9
+
10
+ if TYPE_CHECKING:
11
+ # Avoid circular import
12
+ from ....agentchat.conversable_agent import ConversableAgent
13
+
14
+ __all__ = ["SummaryTaskAvailableCondition"]
15
+
16
+
17
+ class SummaryTaskAvailableCondition(AvailableCondition):
18
+ """Available condition for determining if a summary task should be performed.
19
+
20
+ This condition checks if:
21
+ 1. There are no documents left to ingest
22
+ 2. There are no queries left to run
23
+ 3. The completed task count is truthy
24
+
25
+ If all conditions are met, the agent is ready for a summary task.
26
+ """
27
+
28
+ documents_var: str = "DocumentsToIngest" # Context variable name for documents to ingest list
29
+ queries_var: str = "QueriesToRun" # Context variable name for queries to run list
30
+ completed_var: str = "CompletedTaskCount" # Context variable name for completed task count
31
+
32
+ def is_available(self, agent: "ConversableAgent", messages: list[dict[str, Any]]) -> bool:
33
+ """Check if all task conditions are met.
34
+
35
+ Args:
36
+ agent: The agent with context variables
37
+ messages: The conversation history (not used)
38
+
39
+ Returns:
40
+ True if all conditions are met (ready for summary), False otherwise
41
+ """
42
+ # Get variables from context with appropriate casting
43
+ documents_to_ingest: list[Ingest] = cast(list[Ingest], agent.context_variables.get(self.documents_var, []))
44
+
45
+ queries_to_run: list[Query] = cast(list[Query], agent.context_variables.get(self.queries_var, []))
46
+
47
+ completed_task_count = bool(agent.context_variables.get(self.completed_var, 0))
48
+
49
+ # All conditions must be true for the function to return True
50
+ return len(documents_to_ingest) == 0 and len(queries_to_run) == 0 and completed_task_count
@@ -0,0 +1,376 @@
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 enum import Enum
7
+ from pathlib import Path
8
+ from typing import Any
9
+ from urllib.parse import urlparse
10
+
11
+ from pydantic import BaseModel, Field
12
+
13
+ from ....doc_utils import export_module
14
+ from ....import_utils import optional_import_block, require_optional_import
15
+ from .url_utils import ExtensionToFormat, InputFormat, URLAnalyzer
16
+
17
+ with optional_import_block():
18
+ import requests
19
+ from selenium import webdriver
20
+ from selenium.webdriver.chrome.service import Service as ChromeService
21
+ from webdriver_manager.chrome import ChromeDriverManager
22
+
23
+ __all__ = ["handle_input", "preprocess_path"]
24
+
25
+ _logger = logging.getLogger(__name__)
26
+
27
+
28
+ class QueryType(Enum):
29
+ RAG_QUERY = "RAG_QUERY"
30
+ # COMMON_QUESTION = "COMMON_QUESTION"
31
+
32
+
33
+ class Ingest(BaseModel):
34
+ path_or_url: str = Field(description="The path or URL of the documents to ingest.")
35
+
36
+
37
+ class Query(BaseModel):
38
+ query_type: QueryType = Field(description="The type of query to perform for the Document Agent.")
39
+ query: str = Field(description="The query to perform for the Document Agent.")
40
+
41
+
42
+ def is_url(url: str) -> bool:
43
+ """Check if the string is a valid URL.
44
+
45
+ It checks whether the URL has a valid scheme and network location.
46
+ """
47
+ try:
48
+ url = url.strip()
49
+ result = urlparse(url)
50
+ # urlparse will not raise an exception for invalid URLs, so we need to check the components
51
+ return_bool = bool(result.scheme and result.netloc)
52
+ return return_bool
53
+ except Exception:
54
+ return False
55
+
56
+
57
+ @require_optional_import(["selenium", "webdriver_manager", "requests"], "rag")
58
+ def _download_rendered_html(url: str) -> str:
59
+ """Downloads a rendered HTML page of a given URL using headless ChromeDriver.
60
+
61
+ Args:
62
+ url (str): URL of the page to download.
63
+
64
+ Returns:
65
+ str: The rendered HTML content of the page.
66
+ """
67
+ # Set up Chrome options
68
+ options = webdriver.ChromeOptions()
69
+ options.add_argument("--headless") # Enable headless mode
70
+ options.add_argument("--disable-gpu") # Disabling GPU hardware acceleration
71
+ options.add_argument("--no-sandbox") # Bypass OS security model
72
+ options.add_argument("--disable-dev-shm-usage") # Overcome limited resource problems
73
+
74
+ # Set the location of the ChromeDriver
75
+ service = ChromeService(ChromeDriverManager().install())
76
+
77
+ # Create a new instance of the Chrome driver with specified options
78
+ driver = webdriver.Chrome(service=service, options=options)
79
+
80
+ try:
81
+ # Open a page
82
+ driver.get(url)
83
+
84
+ # Get the rendered HTML
85
+ html_content = driver.page_source
86
+ return str(html_content)
87
+
88
+ finally:
89
+ # Close the browser
90
+ driver.quit()
91
+
92
+
93
+ @require_optional_import(["requests", "selenium", "webdriver_manager"], "rag")
94
+ def _download_binary_file(url: str, output_dir: Path) -> Path:
95
+ """Downloads a file directly from the given URL.
96
+
97
+ Uses appropriate mode (binary/text) based on file extension or content type.
98
+
99
+ Args:
100
+ url (str): URL of the file to download.
101
+ output_dir (Path): Directory to save the file.
102
+
103
+ Returns:
104
+ Path: Path to the saved file.
105
+ """
106
+ # Ensure output directory exists
107
+ output_dir.mkdir(parents=True, exist_ok=True)
108
+
109
+ # Use URLAnalyzer to get information about the URL
110
+ analyzer = URLAnalyzer(url)
111
+ analysis = analyzer.analyze(test_url=True, follow_redirects=True)
112
+
113
+ # Get file info
114
+ final_url = analysis.get("final_url", url)
115
+ file_type = analysis.get("file_type")
116
+ content_type = analysis.get("mime_type", "")
117
+
118
+ _logger.info(f"Original URL: {url}")
119
+ _logger.info(f"Final URL after redirects: {final_url}")
120
+ _logger.info(f"Detected content type: {content_type}")
121
+ _logger.info(f"Detected file type: {file_type}")
122
+
123
+ # Check if the file type is supported
124
+ if file_type == InputFormat.INVALID:
125
+ raise ValueError(f"File type is not supported: {analysis}")
126
+
127
+ # Parse URL components from the final URL
128
+ parsed_url = urlparse(final_url)
129
+ path = Path(parsed_url.path)
130
+
131
+ # Extract filename and extension from URL
132
+ filename = path.name
133
+ suffix = path.suffix.lower()
134
+
135
+ # For URLs without proper filename/extension, or with generic content types
136
+ if not filename or not suffix:
137
+ # Create a unique filename
138
+ unique_id = abs(hash(url)) % 10000
139
+
140
+ # Determine extension from file type
141
+ if file_type is not None and isinstance(file_type, InputFormat):
142
+ ext = _get_extension_from_file_type(file_type, content_type)
143
+ else:
144
+ ext = None
145
+
146
+ # Create filename
147
+ prefix = "image" if file_type == InputFormat.IMAGE else "download"
148
+ filename = f"{prefix}_{unique_id}{ext}"
149
+
150
+ # Ensure the filename has the correct extension
151
+ if suffix:
152
+ # Check if the extension is valid for the file type
153
+ current_ext = suffix[1:] if suffix.startswith(".") else suffix
154
+ if file_type is not None and isinstance(file_type, InputFormat):
155
+ if not _is_valid_extension_for_file_type(current_ext, file_type):
156
+ # If not, add the correct extension
157
+ ext = _get_extension_from_file_type(file_type, content_type)
158
+ filename = f"{Path(filename).stem}{ext}"
159
+ else:
160
+ ext = _get_extension_from_file_type(InputFormat.INVALID, content_type)
161
+ filename = f"{Path(filename).stem}{ext}"
162
+ else:
163
+ # No extension, add one based on file type
164
+ if file_type is not None and isinstance(file_type, InputFormat):
165
+ ext = _get_extension_from_file_type(file_type, content_type)
166
+ else:
167
+ ext = _get_extension_from_file_type(InputFormat.INVALID, content_type)
168
+ filename = f"{filename}{ext}"
169
+
170
+ _logger.info(f"Using filename: {filename} for URL: {url}")
171
+
172
+ # Create final filepath
173
+ filepath = output_dir / filename
174
+
175
+ # Determine if this is binary or text based on extension
176
+ suffix = Path(filename).suffix.lower()
177
+ text_extensions = [".md", ".txt", ".csv", ".html", ".htm", ".xml", ".json", ".adoc"]
178
+ is_binary = suffix not in text_extensions
179
+
180
+ # Download with appropriate mode
181
+ try:
182
+ if not is_binary:
183
+ _logger.info(f"Downloading as text file: {final_url}")
184
+ response = requests.get(final_url, timeout=30)
185
+ response.raise_for_status()
186
+
187
+ with open(filepath, "w", encoding="utf-8") as f:
188
+ f.write(response.text)
189
+ else:
190
+ _logger.info(f"Downloading as binary file: {final_url}")
191
+ response = requests.get(final_url, stream=True, timeout=30)
192
+ response.raise_for_status()
193
+
194
+ with open(filepath, "wb") as f:
195
+ for chunk in response.iter_content(chunk_size=8192):
196
+ if chunk: # Filter out keep-alive chunks
197
+ f.write(chunk)
198
+ except Exception as e:
199
+ _logger.error(f"Download failed: {e}")
200
+ raise
201
+
202
+ return filepath
203
+
204
+
205
+ def _get_extension_from_file_type(file_type: InputFormat, content_type: str = "") -> str:
206
+ """Get a file extension based on the file type and content type."""
207
+ # Create a reverse mapping from InputFormat to a default extension
208
+ # We choose the first extension found for each format
209
+ format_to_extension = {}
210
+ for ext, fmt in ExtensionToFormat.items():
211
+ if fmt not in format_to_extension:
212
+ format_to_extension[fmt] = ext
213
+
214
+ # Special case for images: use content type to determine exact image format
215
+ if file_type == InputFormat.IMAGE:
216
+ if "jpeg" in content_type or "jpg" in content_type:
217
+ return ".jpeg"
218
+ elif "png" in content_type:
219
+ return ".png"
220
+ elif "tiff" in content_type:
221
+ return ".tiff"
222
+ elif "bmp" in content_type:
223
+ return ".bmp"
224
+ # Fallback to default image extension
225
+ ext = format_to_extension.get(InputFormat.IMAGE, "png")
226
+ return f".{ext}"
227
+
228
+ # For all other formats, use the default extension
229
+ if file_type in format_to_extension:
230
+ return f".{format_to_extension[file_type]}"
231
+
232
+ return ".bin" # Default for unknown types
233
+
234
+
235
+ def _is_valid_extension_for_file_type(extension: str, file_type: InputFormat) -> bool:
236
+ """Check if the extension is valid for the given file type."""
237
+ # Remove leading dot if present
238
+ if extension.startswith("."):
239
+ extension = extension[1:]
240
+
241
+ # Check if the extension is in URLAnalyzer.ExtensionToFormat
242
+ # and if it maps to the given file type
243
+ return extension in ExtensionToFormat and ExtensionToFormat[extension] == file_type
244
+
245
+
246
+ @require_optional_import(["selenium", "webdriver_manager", "requests"], "rag")
247
+ def download_url(url: Any, output_dir: str | Path | None = None) -> Path:
248
+ """Download the content of a URL and save it as a file.
249
+
250
+ For direct file URLs (.md, .pdf, .docx, etc.), downloads the raw file.
251
+ For web pages without file extensions or .html/.htm extensions, uses Selenium to render the content.
252
+ """
253
+ url = str(url)
254
+ output_dir = Path(output_dir) if output_dir else Path()
255
+
256
+ # Use URLAnalyzer to determine what type of file the URL is
257
+ analyzer = URLAnalyzer(url)
258
+ analysis = analyzer.analyze(test_url=True, follow_redirects=True)
259
+
260
+ # Log the analysis result
261
+ _logger.info(f"URL analysis result: {analysis}")
262
+
263
+ # Get the final URL after redirects
264
+ final_url = analysis.get("final_url", url)
265
+
266
+ # Determine the file type
267
+ is_file = analysis.get("is_file", False)
268
+ file_type = analysis.get("file_type")
269
+
270
+ # If it's a direct file URL (not HTML), download it directly
271
+ if is_file and file_type != InputFormat.HTML and file_type != InputFormat.INVALID:
272
+ _logger.info("Detected direct file URL. Downloading...")
273
+ return _download_binary_file(url=final_url, output_dir=output_dir)
274
+
275
+ # If it's a web page, use Selenium to render it
276
+ if file_type == InputFormat.HTML or not is_file:
277
+ _logger.info("Detected web page. Rendering...")
278
+ rendered_html = _download_rendered_html(final_url)
279
+
280
+ # Determine filename
281
+ parsed_url = urlparse(final_url)
282
+ path = Path(parsed_url.path)
283
+ filename = path.name or "downloaded_content.html"
284
+ if not filename.endswith(".html"):
285
+ filename += ".html"
286
+
287
+ # Save the rendered HTML
288
+ filepath = output_dir / filename
289
+ with open(file=filepath, mode="w", encoding="utf-8") as f:
290
+ f.write(rendered_html)
291
+
292
+ return filepath
293
+
294
+ # Otherwise, try to download as a binary file
295
+ _logger.info("Unknown URL type. Trying to download as binary file...")
296
+ return _download_binary_file(url=final_url, output_dir=output_dir)
297
+
298
+
299
+ def list_files(directory: Path | str) -> list[Path]:
300
+ """Recursively list all files in a directory.
301
+
302
+ This function will raise an exception if the directory does not exist.
303
+ """
304
+ path = Path(directory)
305
+
306
+ if not path.is_dir():
307
+ raise ValueError(f"The directory {directory} does not exist.")
308
+
309
+ return [f for f in path.rglob("*") if f.is_file()]
310
+
311
+
312
+ @export_module("autogen.agents.experimental.document_agent")
313
+ def handle_input(input_path: Path | str, output_dir: Path | str = "./output") -> list[Path]:
314
+ """Process the input string and return the appropriate file paths"""
315
+ output_dir = preprocess_path(str_or_path=output_dir, is_dir=True, mk_path=True)
316
+ if isinstance(input_path, str) and is_url(input_path):
317
+ _logger.info("Detected URL. Downloading content...")
318
+ try:
319
+ return [download_url(url=input_path, output_dir=output_dir)]
320
+ except Exception as e:
321
+ raise e
322
+
323
+ if isinstance(input_path, str):
324
+ input_path = Path(input_path)
325
+ if not input_path.exists():
326
+ raise ValueError("The input provided does not exist.")
327
+ elif input_path.is_dir():
328
+ _logger.info("Detected directory. Listing files...")
329
+ return list_files(directory=input_path)
330
+ elif input_path.is_file():
331
+ _logger.info("Detected file. Returning file path...")
332
+ return [input_path]
333
+ else:
334
+ raise ValueError("The input provided is neither a URL, directory, nor a file path.")
335
+
336
+
337
+ @export_module("autogen.agents.experimental.document_agent")
338
+ def preprocess_path(str_or_path: Path | str, mk_path: bool = False, is_file: bool = False, is_dir: bool = True) -> Path:
339
+ """Preprocess the path for file operations.
340
+
341
+ Args:
342
+ str_or_path (Union[Path, str]): The path to be processed.
343
+ mk_path (bool, optional): Whether to create the path if it doesn't exist. Default is True.
344
+ is_file (bool, optional): Whether the path is a file. Default is False.
345
+ is_dir (bool, optional): Whether the path is a directory. Default is True.
346
+
347
+ Returns:
348
+ Path: The preprocessed path.
349
+ """
350
+ # Convert the input to a Path object if it's a string
351
+ temp_path = Path(str_or_path)
352
+
353
+ # Ensure the path is absolute
354
+ absolute_path = temp_path.absolute()
355
+ absolute_path = absolute_path.resolve()
356
+ if absolute_path.exists():
357
+ return absolute_path
358
+
359
+ # Check if the path should be a file or directory
360
+ if is_file and is_dir:
361
+ raise ValueError("Path cannot be both a file and a directory.")
362
+
363
+ # If mk_path is True, create the directory or parent directory
364
+ if mk_path:
365
+ if is_file and not absolute_path.parent.exists():
366
+ absolute_path.parent.mkdir(parents=True, exist_ok=True)
367
+ elif is_dir and not absolute_path.exists():
368
+ absolute_path.mkdir(parents=True, exist_ok=True)
369
+
370
+ # Perform checks based on is_file and is_dir flags
371
+ if is_file and not absolute_path.is_file():
372
+ raise FileNotFoundError(f"File not found: {absolute_path}")
373
+ elif is_dir and not absolute_path.is_dir():
374
+ raise NotADirectoryError(f"Directory not found: {absolute_path}")
375
+
376
+ return absolute_path
@@ -0,0 +1,214 @@
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 copy
6
+ import json
7
+ import os
8
+ from collections.abc import Sequence
9
+ from pathlib import Path
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ from pydantic import BaseModel
13
+
14
+ from .... import ConversableAgent
15
+ from ....agentchat.contrib.rag import RAGQueryEngine
16
+ from ....doc_utils import export_module
17
+ from ....llm_config import LLMConfig
18
+
19
+ __all__ = ["InMemoryQueryEngine"]
20
+
21
+ # REPLIES
22
+ QUERY_NO_INGESTIONS_REPLY = "Sorry, please ingest some documents/URLs before querying." # Default response for queries without ingested documents
23
+ EMPTY_RESPONSE_REPLY = "Sorry, I couldn't find any information on that. If you haven't ingested any documents, please try that." # Default response for queries without results
24
+ ERROR_RESPONSE_REPLY = "Sorry, there was an error processing your query: " # Default response for queries with errors
25
+ COULD_NOT_ANSWER_REPLY = "Sorry, I couldn't answer that question from the ingested documents/URLs" # Default response for queries that could not be answered
26
+
27
+
28
+ # Documents and Content structure
29
+ class DocumentStore(BaseModel):
30
+ ingestation_name: str
31
+ content: str
32
+
33
+
34
+ # Answer question structure
35
+ class QueryAnswer(BaseModel):
36
+ could_answer: bool
37
+ answer: str
38
+
39
+
40
+ @export_module("autogen.agents.experimental")
41
+ class InMemoryQueryEngine:
42
+ """This engine stores ingested documents in memory and then injects them into an internal agent's system message for answering queries.
43
+
44
+ This implements the autogen.agentchat.contrib.rag.RAGQueryEngine protocol.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ llm_config: LLMConfig | dict[str, Any],
50
+ ) -> None:
51
+ # Deep copy the llm config to avoid changing the original
52
+ structured_config = copy.deepcopy(llm_config)
53
+
54
+ # The query agent will answer with a structured output
55
+ structured_config["response_format"] = QueryAnswer
56
+
57
+ # Our agents for querying
58
+ self._query_agent = ConversableAgent(
59
+ name="inmemory_query_agent",
60
+ llm_config=structured_config,
61
+ )
62
+
63
+ # In-memory storage for ingested documents
64
+ self._ingested_documents: list[DocumentStore] = []
65
+
66
+ def query(self, question: str, *args: Any, **kwargs: Any) -> str:
67
+ """Run a query against the ingested documents and return the answer."""
68
+ # If no documents have been ingested, return an empty response
69
+ if not self._ingested_documents:
70
+ return QUERY_NO_INGESTIONS_REPLY
71
+
72
+ # Put the context into the system message
73
+ context_parts = []
74
+ for i, doc in enumerate(self._ingested_documents, 1):
75
+ context_parts.append(f"Ingested File/URL {i} - '{doc.ingestation_name}':\n{doc.content}\n")
76
+
77
+ context = "\n".join(context_parts)
78
+
79
+ system_message = (
80
+ "You are a query agent tasked with answering questions based on ingested documents.\n\n"
81
+ "AVAILABLE DOCUMENTS:\n"
82
+ + "\n".join([f"- {doc.ingestation_name}" for doc in self._ingested_documents])
83
+ + "\n\n"
84
+ "When answering questions about these documents, use ONLY the information in the following context:\n\n"
85
+ f"{context}\n\n"
86
+ "IMPORTANT: The user will ask about these documents by name. When they do, provide helpful, detailed answers based on the document content above."
87
+ )
88
+
89
+ self._query_agent.update_system_message(system_message)
90
+
91
+ message = f"Using ONLY the document content in your system message, answer this question: {question}"
92
+
93
+ response = self._query_agent.run(
94
+ message=message,
95
+ max_turns=1,
96
+ )
97
+
98
+ response.process()
99
+
100
+ try:
101
+ # Get the structured output and return the answer
102
+ answer_object = QueryAnswer.model_validate(json.loads(response.summary)) # type: ignore[arg-type]
103
+
104
+ if answer_object.could_answer:
105
+ return answer_object.answer
106
+ else:
107
+ if answer_object.answer:
108
+ return COULD_NOT_ANSWER_REPLY + ": " + answer_object.answer
109
+ else:
110
+ return COULD_NOT_ANSWER_REPLY
111
+
112
+ except Exception as e:
113
+ # Error converting the response to the structured output
114
+ return ERROR_RESPONSE_REPLY + str(e)
115
+
116
+ def add_docs(
117
+ self,
118
+ new_doc_dir: Path | str | None = None,
119
+ new_doc_paths_or_urls: Sequence[Path | str] | None = None,
120
+ ) -> None:
121
+ """Add additional documents to the in-memory store
122
+
123
+ Loads new Docling-parsed Markdown files from a specified directory or a list of file paths
124
+ and inserts them into the in-memory store.
125
+
126
+ Args:
127
+ new_doc_dir: The directory path from which to load additional documents.
128
+ If provided, all eligible files in this directory are loaded.
129
+ new_doc_paths_or_urls: A list of file paths specifying additional documents to load.
130
+ Each file should be a Docling-parsed Markdown file.
131
+ """
132
+ new_doc_dir = new_doc_dir or ""
133
+ new_doc_paths = new_doc_paths_or_urls or []
134
+ self._load_doc(input_dir=new_doc_dir, input_docs=new_doc_paths)
135
+
136
+ def _load_doc(self, input_dir: Path | str | None, input_docs: Sequence[Path | str] | None) -> None:
137
+ """Load documents from a directory and/or a list of file paths into the in-memory store.
138
+
139
+ This helper method reads files using native Python file operations and stores them
140
+ in the in-memory document store. It supports reading text-based files, with the primary
141
+ intended use being for documents processed by Docling.
142
+
143
+ Args:
144
+ input_dir (Optional[Union[Path, str]]): The directory containing documents to be loaded.
145
+ If provided, all files in the directory will be considered.
146
+ input_docs (Optional[list[Union[Path, str]]]): A list of individual file paths to load.
147
+ Each path must point to an existing file.
148
+
149
+ Raises:
150
+ ValueError: If the specified directory does not exist.
151
+ ValueError: If any provided file path does not exist.
152
+ ValueError: If neither input_dir nor input_docs is provided.
153
+ """
154
+ if not input_dir and not input_docs:
155
+ raise ValueError("No input directory or docs provided!")
156
+
157
+ # Process directory if provided
158
+ if input_dir:
159
+ # logger.info(f"Loading docs from directory: {input_dir}")
160
+ if not os.path.exists(input_dir):
161
+ raise ValueError(f"Input directory not found: {input_dir}")
162
+
163
+ # Get all files from the directory
164
+ dir_path = Path(input_dir)
165
+ for file_path in dir_path.iterdir():
166
+ if file_path.is_file():
167
+ self._read_and_store_file(file_path)
168
+
169
+ # Process individual files if provided
170
+ if input_docs:
171
+ for doc_path in input_docs:
172
+ # logger.info(f"Loading input doc: {doc_path}")
173
+ if not os.path.exists(doc_path):
174
+ raise ValueError(f"Document file not found: {doc_path}")
175
+ self._read_and_store_file(doc_path)
176
+
177
+ def _read_and_store_file(self, file_path: Path | str) -> None:
178
+ """Read a file and store its content in the in-memory document store.
179
+
180
+ Args:
181
+ file_path (Union[Path, str]): Path to the file to be read
182
+ """
183
+ file_path = Path(file_path)
184
+ try:
185
+ with open(file_path, encoding="utf-8") as file:
186
+ content = file.read()
187
+
188
+ # Store the document in the in-memory store
189
+ document = DocumentStore(ingestation_name=file_path.name, content=content)
190
+ self._ingested_documents.append(document)
191
+ except Exception as e:
192
+ raise ValueError(f"Error reading file {file_path}: {str(e)}")
193
+
194
+ def init_db(
195
+ self,
196
+ new_doc_dir: Path | str | None = None,
197
+ new_doc_paths_or_urls: Sequence[Path | str] | None = None,
198
+ *args: Any,
199
+ **kwargs: Any,
200
+ ) -> bool:
201
+ """Not required nor implemented for InMemoryQueryEngine"""
202
+ raise NotImplementedError("Method, init_db, not required nor implemented for InMemoryQueryEngine")
203
+
204
+ def connect_db(self, *args: Any, **kwargs: Any) -> bool:
205
+ """Not required nor implemented for InMemoryQueryEngine"""
206
+ raise NotImplementedError("Method, connect_db, not required nor implemented for InMemoryQueryEngine")
207
+
208
+
209
+ # mypy will fail if ChromaDBQueryEngine does not implement RAGQueryEngine protocol
210
+ if TYPE_CHECKING:
211
+ from ....agentchat.contrib.rag.query_engine import RAGQueryEngine
212
+
213
+ def _check_implement_protocol(o: InMemoryQueryEngine) -> RAGQueryEngine:
214
+ return o