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,196 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
+ # SPDX-License-Identifier: MIT
7
+ """Utilities for client classes"""
8
+
9
+ import logging
10
+ import warnings
11
+ from typing import Any, Protocol, runtime_checkable
12
+
13
+
14
+ @runtime_checkable
15
+ class FormatterProtocol(Protocol):
16
+ """Structured Output classes with a format method"""
17
+
18
+ def format(self) -> str: ...
19
+
20
+
21
+ def validate_parameter(
22
+ params: dict[str, Any],
23
+ param_name: str,
24
+ allowed_types: tuple[Any, ...],
25
+ allow_None: bool, # noqa: N803
26
+ default_value: Any,
27
+ numerical_bound: tuple[float | None, float | None] | None,
28
+ allowed_values: list[Any] | None,
29
+ ) -> Any:
30
+ """Validates a given config parameter, checking its type, values, and setting defaults
31
+ Parameters:
32
+ params (Dict[str, Any]): Dictionary containing parameters to validate.
33
+ param_name (str): The name of the parameter to validate.
34
+ allowed_types (Tuple): Tuple of acceptable types for the parameter.
35
+ allow_None (bool): Whether the parameter can be `None`.
36
+ default_value (Any): The default value to use if the parameter is invalid or missing.
37
+ numerical_bound (Optional[Tuple[Optional[float], Optional[float]]]):
38
+ A tuple specifying the lower and upper bounds for numerical parameters.
39
+ Each bound can be `None` if not applicable.
40
+ allowed_values (Optional[List[Any]]): A list of acceptable values for the parameter.
41
+ Can be `None` if no specific values are required.
42
+
43
+ Returns:
44
+ Any: The validated parameter value or the default value if validation fails.
45
+
46
+ Raises:
47
+ TypeError: If `allowed_values` is provided but is not a list.
48
+
49
+ Example Usage:
50
+ ```python
51
+ # Validating a numerical parameter within specific bounds
52
+ params = {"temperature": 0.5, "safety_model": "Meta-Llama/Llama-Guard-7b"}
53
+ temperature = validate_parameter(params, "temperature", (int, float), True, 0.7, (0, 1), None)
54
+ # Result: 0.5
55
+
56
+ # Validating a parameter that can be one of a list of allowed values
57
+ model = validate_parameter(
58
+ params, "safety_model", str, True, None, None, ["Meta-Llama/Llama-Guard-7b", "Meta-Llama/Llama-Guard-13b"]
59
+ )
60
+ # If "safety_model" is missing or invalid in params, defaults to "default"
61
+ ```
62
+ """
63
+ if allowed_values is not None and not isinstance(allowed_values, list):
64
+ raise TypeError(f"allowed_values should be a list or None, got {type(allowed_values).__name__}")
65
+
66
+ param_value = params.get(param_name, default_value)
67
+ warning = ""
68
+
69
+ if param_value is None and allow_None:
70
+ pass
71
+ elif param_value is None:
72
+ if not allow_None:
73
+ warning = "cannot be None"
74
+ elif not isinstance(param_value, allowed_types):
75
+ # Check types and list possible types if invalid
76
+ if isinstance(allowed_types, tuple):
77
+ formatted_types = "(" + ", ".join(f"{t.__name__}" for t in allowed_types) + ")"
78
+ else:
79
+ formatted_types = f"{allowed_types.__name__}"
80
+ warning = f"must be of type {formatted_types}{' or None' if allow_None else ''}"
81
+ elif numerical_bound:
82
+ # Check the value fits in possible bounds
83
+ lower_bound, upper_bound = numerical_bound
84
+ if (lower_bound is not None and param_value < lower_bound) or (
85
+ upper_bound is not None and param_value > upper_bound
86
+ ):
87
+ warning = "has numerical bounds"
88
+ if lower_bound is not None:
89
+ warning += f", >= {lower_bound!s}"
90
+ if upper_bound is not None:
91
+ if lower_bound is not None:
92
+ warning += " and"
93
+ warning += f" <= {upper_bound!s}"
94
+ if allow_None:
95
+ warning += ", or can be None"
96
+
97
+ elif allowed_values: # noqa: SIM102
98
+ # Check if the value matches any allowed values
99
+ if not (allow_None and param_value is None) and param_value not in allowed_values:
100
+ warning = f"must be one of these values [{allowed_values}]{', or can be None' if allow_None else ''}"
101
+
102
+ # If we failed any checks, warn and set to default value
103
+ if warning:
104
+ warnings.warn(
105
+ f"Config error - {param_name} {warning}, defaulting to {default_value}.",
106
+ UserWarning,
107
+ )
108
+ param_value = default_value
109
+
110
+ return param_value
111
+
112
+
113
+ def merge_config_with_tools(config: dict[str, Any], client_config: dict[str, Any]) -> dict[str, Any]:
114
+ """Merge configuration dictionaries with proper tools and functions handling.
115
+
116
+ This function merges two configuration dictionaries while ensuring that:
117
+ 1. Empty 'tools' arrays are not added unnecessarily
118
+ 2. 'tools' and deprecated 'functions' parameters are not both present
119
+ 3. Actual tool configurations are properly merged
120
+
121
+ Args:
122
+ config: The base configuration dictionary (e.g., from create() call)
123
+ client_config: The client-specific configuration dictionary (e.g., from config_list)
124
+
125
+ Returns:
126
+ dict[str, Any]: The merged configuration with proper tools/functions handling
127
+ """
128
+ # Start with a clean merge of both configs
129
+ full_config = {**config, **client_config}
130
+
131
+ # Add tools if tools contains something AND are not using deprecated functions
132
+ tools = config.get("tools", []) + client_config.get("tools", [])
133
+ if tools and "functions" not in full_config:
134
+ # Don't add tools if functions parameter is present (deprecated API)
135
+ full_config["tools"] = tools
136
+
137
+ return full_config
138
+
139
+
140
+ def should_hide_tools(messages: list[dict[str, Any]], tools: list[dict[str, Any]], hide_tools_param: str) -> bool:
141
+ """Determines if tools should be hidden. This function is used to hide tools when they have been run, minimising the chance of the LLM choosing them when they shouldn't.
142
+ Parameters:
143
+ messages (List[Dict[str, Any]]): List of messages
144
+ tools (List[Dict[str, Any]]): List of tools
145
+ hide_tools_param (str): "hide_tools" parameter value. Can be "if_all_run" (hide tools if all tools have been run), "if_any_run" (hide tools if any of the tools have been run), "never" (never hide tools). Default is "never".
146
+
147
+ Returns:
148
+ bool: Indicates whether the tools should be excluded from the response create request
149
+
150
+ Example Usage:
151
+ ```python
152
+ # Validating a numerical parameter within specific bounds
153
+ messages = params.get("messages", [])
154
+ tools = params.get("tools", None)
155
+ hide_tools = should_hide_tools(messages, tools, params["hide_tools"])
156
+ """
157
+ if hide_tools_param == "never" or tools is None or len(tools) == 0:
158
+ return False
159
+ elif hide_tools_param == "if_any_run":
160
+ # Return True if any tool_call_id exists, indicating a tool call has been executed. False otherwise.
161
+ return any("tool_call_id" in dictionary for dictionary in messages)
162
+ elif hide_tools_param == "if_all_run":
163
+ # Return True if all tools have been executed at least once. False otherwise.
164
+
165
+ # Get the list of tool names
166
+ check_tool_names = [item["function"]["name"] for item in tools]
167
+
168
+ # Prepare a list of tool call ids and related function names
169
+ tool_call_ids = {}
170
+
171
+ # Loop through the messages and check if the tools have been run, removing them as we go
172
+ for message in messages:
173
+ if "tool_calls" in message:
174
+ # Register the tool ids and the function names (there could be multiple tool calls)
175
+ for tool_call in message["tool_calls"]:
176
+ tool_call_ids[tool_call["id"]] = tool_call["function"]["name"]
177
+ elif "tool_call_id" in message:
178
+ # Tool called, get the name of the function based on the id
179
+ tool_name_called = tool_call_ids[message["tool_call_id"]]
180
+
181
+ # If we had not yet called the tool, check and remove it to indicate we have
182
+ if tool_name_called in check_tool_names:
183
+ check_tool_names.remove(tool_name_called)
184
+
185
+ # Return True if all tools have been called at least once (accounted for)
186
+ return len(check_tool_names) == 0
187
+ else:
188
+ raise TypeError(
189
+ f"hide_tools_param is not a valid value ['if_all_run','if_any_run','never'], got '{hide_tools_param}'"
190
+ )
191
+
192
+
193
+ # Logging format (originally from FLAML)
194
+ logging_formatter = logging.Formatter(
195
+ "[%(name)s: %(asctime)s] {%(lineno)d} %(levelname)s - %(message)s", "%m-%d %H:%M:%S"
196
+ )
autogen/oai/cohere.py ADDED
@@ -0,0 +1,494 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
+ # SPDX-License-Identifier: MIT
7
+ """Create an OpenAI-compatible client using Cohere's API.
8
+
9
+ Example:
10
+ ```python
11
+ llm_config={
12
+ "config_list": [{
13
+ "api_type": "cohere",
14
+ "model": "command-r-plus",
15
+ "api_key": os.environ.get("COHERE_API_KEY")
16
+ "client_name": "autogen-cohere", # Optional parameter
17
+ }
18
+ ]}
19
+
20
+ agent = autogen.AssistantAgent("my_agent", llm_config=llm_config)
21
+ ```
22
+
23
+ Install Cohere's python library using: pip install --upgrade cohere
24
+
25
+ Resources:
26
+ - https://docs.cohere.com/reference/chat
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import json
32
+ import logging
33
+ import os
34
+ import sys
35
+ import time
36
+ import warnings
37
+ from typing import Any, Literal
38
+
39
+ from pydantic import BaseModel, Field
40
+ from typing_extensions import Unpack
41
+
42
+ from autogen.oai.client_utils import FormatterProtocol, logging_formatter, validate_parameter
43
+
44
+ from ..import_utils import optional_import_block, require_optional_import
45
+ from ..llm_config.entry import LLMConfigEntry, LLMConfigEntryDict
46
+ from .oai_models import ChatCompletion, ChatCompletionMessage, ChatCompletionMessageToolCall, Choice, CompletionUsage
47
+
48
+ with optional_import_block():
49
+ from cohere import ClientV2 as CohereV2
50
+ from cohere.types import ToolResult
51
+
52
+ logger = logging.getLogger(__name__)
53
+ if not logger.handlers:
54
+ # Add the console handler.
55
+ _ch = logging.StreamHandler(stream=sys.stdout)
56
+ _ch.setFormatter(logging_formatter)
57
+ logger.addHandler(_ch)
58
+
59
+
60
+ COHERE_PRICING_1K = {
61
+ "command-r-plus": (0.003, 0.015),
62
+ "command-r": (0.0005, 0.0015),
63
+ "command-nightly": (0.00025, 0.00125),
64
+ "command": (0.015, 0.075),
65
+ "command-light": (0.008, 0.024),
66
+ "command-light-nightly": (0.008, 0.024),
67
+ }
68
+
69
+
70
+ class CohereEntryDict(LLMConfigEntryDict, total=False):
71
+ api_type: Literal["cohere"]
72
+
73
+ k: int
74
+ seed: int | None
75
+ frequency_penalty: float
76
+ presence_penalty: float
77
+ client_name: str | None
78
+ strict_tools: bool
79
+ stream: bool
80
+ tool_choice: Literal["NONE", "REQUIRED"] | None
81
+
82
+
83
+ class CohereLLMConfigEntry(LLMConfigEntry):
84
+ api_type: Literal["cohere"] = "cohere"
85
+
86
+ k: int = Field(default=0, ge=0, le=500)
87
+ seed: int | None = None
88
+ frequency_penalty: float = Field(default=0, ge=0, le=1)
89
+ presence_penalty: float = Field(default=0, ge=0, le=1)
90
+ client_name: str | None = None
91
+ strict_tools: bool = False
92
+ stream: bool = False
93
+ tool_choice: Literal["NONE", "REQUIRED"] | None = None
94
+
95
+ def create_client(self):
96
+ raise NotImplementedError("CohereLLMConfigEntry.create_client is not implemented.")
97
+
98
+
99
+ class CohereClient:
100
+ """Client for Cohere's API."""
101
+
102
+ RESPONSE_USAGE_KEYS: list[str] = ["prompt_tokens", "completion_tokens", "total_tokens", "cost", "model"]
103
+
104
+ def __init__(self, **kwargs: Unpack[CohereEntryDict]):
105
+ """Requires api_key or environment variable to be set
106
+
107
+ Args:
108
+ **kwargs: The keyword arguments to pass to the Cohere API.
109
+ """
110
+ # Ensure we have the api_key upon instantiation
111
+ self.api_key = kwargs.get("api_key")
112
+ if not self.api_key:
113
+ self.api_key = os.getenv("COHERE_API_KEY")
114
+
115
+ assert self.api_key, (
116
+ "Please include the api_key in your config list entry for Cohere or set the COHERE_API_KEY env variable."
117
+ )
118
+
119
+ # Store the response format, if provided (for structured outputs)
120
+ self._response_format: type[BaseModel] | None = None
121
+
122
+ def message_retrieval(self, response) -> list:
123
+ """Retrieve and return a list of strings or a list of Choice.Message from the response.
124
+
125
+ NOTE: if a list of Choice.Message is returned, it currently needs to contain the fields of OpenAI's ChatCompletion Message object,
126
+ since that is expected for function or tool calling in the rest of the codebase at the moment, unless a custom agent is being used.
127
+ """
128
+ return [choice.message for choice in response.choices]
129
+
130
+ def cost(self, response) -> float:
131
+ return response.cost
132
+
133
+ @staticmethod
134
+ def get_usage(response) -> dict:
135
+ """Return usage summary of the response using RESPONSE_USAGE_KEYS."""
136
+ # ... # pragma: no cover
137
+ return {
138
+ "prompt_tokens": response.usage.prompt_tokens,
139
+ "completion_tokens": response.usage.completion_tokens,
140
+ "total_tokens": response.usage.total_tokens,
141
+ "cost": response.cost,
142
+ "model": response.model,
143
+ }
144
+
145
+ def parse_params(self, params: dict[str, Any]) -> dict[str, Any]:
146
+ """Loads the parameters for Cohere API from the passed in parameters and returns a validated set. Checks types, ranges, and sets defaults"""
147
+ cohere_params = {}
148
+
149
+ # Check that we have what we need to use Cohere's API
150
+ # We won't enforce the available models as they are likely to change
151
+ cohere_params["model"] = params.get("model")
152
+ assert cohere_params["model"], (
153
+ "Please specify the 'model' in your config list entry to nominate the Cohere model to use."
154
+ )
155
+
156
+ # Handle structured output response format from Pydantic model
157
+ if "response_format" in params and params["response_format"] is not None:
158
+ self._response_format = params.get("response_format")
159
+
160
+ response_format = params["response_format"]
161
+
162
+ # Check if it's a Pydantic model
163
+ if hasattr(response_format, "model_json_schema"):
164
+ # Get the JSON schema from the Pydantic model
165
+ schema = response_format.model_json_schema()
166
+
167
+ def resolve_ref(ref: str, defs: dict) -> dict:
168
+ """Resolve a $ref to its actual schema definition"""
169
+ # Extract the definition name from "#/$defs/Name"
170
+ def_name = ref.split("/")[-1]
171
+ return defs[def_name]
172
+
173
+ def ensure_type_fields(obj: dict, defs: dict) -> dict:
174
+ """Recursively ensure all objects in the schema have a type and properties field"""
175
+ if isinstance(obj, dict):
176
+ # If it has a $ref, replace it with the actual definition
177
+ if "$ref" in obj:
178
+ ref_def = resolve_ref(obj["$ref"], defs)
179
+ # Merge the reference definition with any existing fields
180
+ obj = {**ref_def, **obj}
181
+ # Remove the $ref as we've replaced it
182
+ del obj["$ref"]
183
+
184
+ # Process each value recursively
185
+ return {
186
+ k: ensure_type_fields(v, defs) if isinstance(v, (dict, list)) else v for k, v in obj.items()
187
+ }
188
+ elif isinstance(obj, list):
189
+ return [ensure_type_fields(item, defs) for item in obj]
190
+ return obj
191
+
192
+ # Make a copy of $defs before processing
193
+ defs = schema.get("$defs", {})
194
+
195
+ # Process the schema
196
+ processed_schema = ensure_type_fields(schema, defs)
197
+
198
+ cohere_params["response_format"] = {"type": "json_object", "json_schema": processed_schema}
199
+ else:
200
+ raise ValueError("response_format must be a Pydantic BaseModel")
201
+
202
+ # Handle strict tools parameter for structured outputs with tools
203
+ if "tools" in params:
204
+ cohere_params["strict_tools"] = validate_parameter(params, "strict_tools", bool, False, False, None, None)
205
+
206
+ # Validate allowed Cohere parameters
207
+ # https://docs.cohere.com/reference/chat
208
+ if "temperature" in params:
209
+ cohere_params["temperature"] = validate_parameter(
210
+ params, "temperature", (int, float), False, 0.3, (0, None), None
211
+ )
212
+
213
+ if "max_tokens" in params:
214
+ cohere_params["max_tokens"] = validate_parameter(params, "max_tokens", int, True, None, (0, None), None)
215
+
216
+ if "k" in params:
217
+ cohere_params["k"] = validate_parameter(params, "k", int, False, 0, (0, 500), None)
218
+
219
+ if "top_p" in params:
220
+ cohere_params["p"] = validate_parameter(params, "top_p", (int, float), False, 0.75, (0.01, 0.99), None)
221
+
222
+ if "seed" in params:
223
+ cohere_params["seed"] = validate_parameter(params, "seed", int, True, None, None, None)
224
+
225
+ if "frequency_penalty" in params:
226
+ cohere_params["frequency_penalty"] = validate_parameter(
227
+ params, "frequency_penalty", (int, float), True, 0, (0, 1), None
228
+ )
229
+
230
+ if "presence_penalty" in params:
231
+ cohere_params["presence_penalty"] = validate_parameter(
232
+ params, "presence_penalty", (int, float), True, 0, (0, 1), None
233
+ )
234
+
235
+ if "tool_choice" in params:
236
+ cohere_params["tool_choice"] = validate_parameter(
237
+ params, "tool_choice", str, True, None, None, ["NONE", "REQUIRED"]
238
+ )
239
+
240
+ return cohere_params
241
+
242
+ @require_optional_import("cohere", "cohere")
243
+ def create(self, params: dict) -> ChatCompletion:
244
+ messages = params.get("messages", [])
245
+ client_name = params.get("client_name") or "AG2"
246
+ cohere_tool_names = set()
247
+ tool_calls_modified_ids = set()
248
+
249
+ # Parse parameters to the Cohere API's parameters
250
+ cohere_params = self.parse_params(params)
251
+
252
+ cohere_params["messages"] = messages
253
+
254
+ if "tools" in params:
255
+ cohere_tool_names = {tool["function"]["name"] for tool in params["tools"]}
256
+ cohere_params["tools"] = params["tools"]
257
+
258
+ # Strip out name
259
+ for message in cohere_params["messages"]:
260
+ message_name = message.pop("name", "")
261
+ # Extract and prepend name to content or tool_plan if available
262
+ message["content"] = (
263
+ f"{message_name}: {(message.get('content') or message.get('tool_plan'))}"
264
+ if message_name
265
+ else (message.get("content") or message.get("tool_plan"))
266
+ )
267
+
268
+ # Handle tool calls
269
+ if message.get("tool_calls") is not None and len(message["tool_calls"]) > 0:
270
+ message["tool_plan"] = message.get("tool_plan", message["content"])
271
+ del message["content"] # Remove content as tool_plan is prioritized
272
+
273
+ # If tool call name is missing or not recognized, modify role and content
274
+ for tool_call in message["tool_calls"] or []:
275
+ if (not tool_call.get("function", {}).get("name")) or tool_call.get("function", {}).get(
276
+ "name"
277
+ ) not in cohere_tool_names:
278
+ message["role"] = "assistant"
279
+ message["content"] = f"{message.pop('tool_plan', '')}{str(message['tool_calls'])}"
280
+ tool_calls_modified_ids = tool_calls_modified_ids.union({
281
+ tool_call.get("id") for tool_call in message["tool_calls"]
282
+ })
283
+ del message["tool_calls"]
284
+ break
285
+
286
+ # Adjust role if message comes from a tool with a modified ID
287
+ if message.get("role") == "tool":
288
+ tool_id = message.get("tool_call_id")
289
+ if tool_id in tool_calls_modified_ids:
290
+ message["role"] = "user"
291
+ del message["tool_call_id"] # Remove the tool call ID
292
+
293
+ # We use chat model by default
294
+ client = CohereV2(api_key=self.api_key, client_name=client_name)
295
+
296
+ # Token counts will be returned
297
+ prompt_tokens = 0
298
+ completion_tokens = 0
299
+ total_tokens = 0
300
+
301
+ # Stream if in parameters
302
+ streaming = params.get("stream")
303
+ cohere_finish = "stop"
304
+ tool_calls = None
305
+ ans = None
306
+ if streaming:
307
+ response = client.chat_stream(**cohere_params)
308
+ # Streaming...
309
+ ans = ""
310
+ plan = ""
311
+ prompt_tokens = 0
312
+ completion_tokens = 0
313
+ for chunk in response:
314
+ if chunk.type == "content-delta":
315
+ ans = ans + chunk.delta.message.content.text
316
+ elif chunk.type == "tool-plan-delta":
317
+ plan = plan + chunk.delta.message.tool_plan
318
+ elif chunk.type == "tool-call-start":
319
+ cohere_finish = "tool_calls"
320
+
321
+ # Initialize a new tool call
322
+ tool_call = chunk.delta.message.tool_calls
323
+ current_tool = {
324
+ "id": tool_call.id,
325
+ "type": "function",
326
+ "function": {"name": tool_call.function.name, "arguments": ""},
327
+ }
328
+ elif chunk.type == "tool-call-delta":
329
+ # Progressively build the arguments as they stream in
330
+ if current_tool is not None:
331
+ current_tool["function"]["arguments"] += chunk.delta.message.tool_calls.function.arguments
332
+ elif chunk.type == "tool-call-end":
333
+ # Append the finished tool call to the list
334
+ if current_tool is not None:
335
+ if tool_calls is None:
336
+ tool_calls = []
337
+ tool_calls.append(ChatCompletionMessageToolCall(**current_tool))
338
+ current_tool = None
339
+ elif chunk.type == "message-start":
340
+ response_id = chunk.id
341
+ elif chunk.type == "message-end":
342
+ prompt_tokens = (
343
+ chunk.delta.usage.billed_units.input_tokens
344
+ ) # Note total (billed+non-billed) available with ...usage.tokens...
345
+ completion_tokens = chunk.delta.usage.billed_units.output_tokens
346
+
347
+ total_tokens = prompt_tokens + completion_tokens
348
+ else:
349
+ response = client.chat(**cohere_params)
350
+
351
+ if response.message.tool_calls is not None:
352
+ ans = response.message.tool_plan
353
+ cohere_finish = "tool_calls"
354
+ tool_calls = []
355
+ for tool_call in response.message.tool_calls:
356
+ # if parameters are null, clear them out (Cohere can return a string "null" if no parameter values)
357
+
358
+ tool_calls.append(
359
+ ChatCompletionMessageToolCall(
360
+ id=tool_call.id,
361
+ function={
362
+ "name": tool_call.function.name,
363
+ "arguments": (
364
+ "" if tool_call.function.arguments is None else tool_call.function.arguments
365
+ ),
366
+ },
367
+ type="function",
368
+ )
369
+ )
370
+ else:
371
+ ans: str = response.message.content[0].text
372
+
373
+ # Not using billed_units, but that may be better for cost purposes
374
+ prompt_tokens = (
375
+ response.usage.billed_units.input_tokens
376
+ ) # Note total (billed+non-billed) available with ...usage.tokens...
377
+ completion_tokens = response.usage.billed_units.output_tokens
378
+ total_tokens = prompt_tokens + completion_tokens
379
+
380
+ response_id = response.id
381
+
382
+ # Clean up structured output if needed
383
+ if self._response_format:
384
+ # ans = clean_return_response_format(ans)
385
+ try:
386
+ parsed_response = self._convert_json_response(ans)
387
+ ans = _format_json_response(parsed_response, ans)
388
+ except ValueError as e:
389
+ ans = str(e)
390
+
391
+ # 3. convert output
392
+ message = ChatCompletionMessage(
393
+ role="assistant",
394
+ content=ans,
395
+ function_call=None,
396
+ tool_calls=tool_calls,
397
+ )
398
+ choices = [Choice(finish_reason=cohere_finish, index=0, message=message)]
399
+
400
+ response_oai = ChatCompletion(
401
+ id=response_id,
402
+ model=cohere_params["model"],
403
+ created=int(time.time()),
404
+ object="chat.completion",
405
+ choices=choices,
406
+ usage=CompletionUsage(
407
+ prompt_tokens=prompt_tokens,
408
+ completion_tokens=completion_tokens,
409
+ total_tokens=total_tokens,
410
+ ),
411
+ cost=calculate_cohere_cost(prompt_tokens, completion_tokens, cohere_params["model"]),
412
+ )
413
+
414
+ return response_oai
415
+
416
+ def _convert_json_response(self, response: str) -> Any:
417
+ """Extract and validate JSON response from the output for structured outputs.
418
+
419
+ Args:
420
+ response (str): The response from the API.
421
+
422
+ Returns:
423
+ Any: The parsed JSON response.
424
+ """
425
+ if not self._response_format:
426
+ return response
427
+
428
+ try:
429
+ # Parse JSON and validate against the Pydantic model
430
+ json_data = json.loads(response)
431
+ return self._response_format.model_validate(json_data)
432
+ except Exception as e:
433
+ raise ValueError(
434
+ f"Failed to parse response as valid JSON matching the schema for Structured Output: {str(e)}"
435
+ )
436
+
437
+
438
+ def _format_json_response(response: Any, original_answer: str) -> str:
439
+ """Formats the JSON response for structured outputs using the format method if it exists."""
440
+ return (
441
+ response.format() if isinstance(response, FormatterProtocol) else clean_return_response_format(original_answer)
442
+ )
443
+
444
+
445
+ def extract_to_cohere_tool_results(tool_call_id: str, content_output: str, all_tool_calls) -> list[dict[str, Any]]:
446
+ temp_tool_results = []
447
+
448
+ for tool_call in all_tool_calls:
449
+ if tool_call["id"] == tool_call_id:
450
+ call = {
451
+ "name": tool_call["function"]["name"],
452
+ "parameters": json.loads(
453
+ tool_call["function"]["arguments"] if tool_call["function"]["arguments"] != "" else "{}"
454
+ ),
455
+ }
456
+ output = [{"value": content_output}]
457
+ temp_tool_results.append(ToolResult(call=call, outputs=output))
458
+ return temp_tool_results
459
+
460
+
461
+ def calculate_cohere_cost(input_tokens: int, output_tokens: int, model: str) -> float:
462
+ """Calculate the cost of the completion using the Cohere pricing."""
463
+ total = 0.0
464
+
465
+ if model in COHERE_PRICING_1K:
466
+ input_cost_per_k, output_cost_per_k = COHERE_PRICING_1K[model]
467
+ input_cost = (input_tokens / 1000) * input_cost_per_k
468
+ output_cost = (output_tokens / 1000) * output_cost_per_k
469
+ total = input_cost + output_cost
470
+ else:
471
+ warnings.warn(f"Cost calculation not available for {model} model", UserWarning)
472
+
473
+ return total
474
+
475
+
476
+ def clean_return_response_format(response_str: str) -> str:
477
+ """Clean up the response string by parsing through json library."""
478
+ # Parse the string to a JSON object to handle escapes
479
+ data = json.loads(response_str)
480
+
481
+ # Convert back to JSON string with minimal formatting
482
+ return json.dumps(data)
483
+
484
+
485
+ class CohereError(Exception):
486
+ """Base class for other Cohere exceptions"""
487
+
488
+ pass
489
+
490
+
491
+ class CohereRateLimitError(CohereError):
492
+ """Raised when rate limit is exceeded"""
493
+
494
+ pass