ag2 0.9.7__py3-none-any.whl → 0.9.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ag2 might be problematic. Click here for more details.

Files changed (236) hide show
  1. {ag2-0.9.7.dist-info → ag2-0.9.9.dist-info}/METADATA +102 -75
  2. ag2-0.9.9.dist-info/RECORD +387 -0
  3. autogen/__init__.py +1 -2
  4. autogen/_website/generate_api_references.py +4 -5
  5. autogen/_website/generate_mkdocs.py +9 -15
  6. autogen/_website/notebook_processor.py +13 -14
  7. autogen/_website/process_notebooks.py +10 -10
  8. autogen/_website/utils.py +5 -4
  9. autogen/agentchat/agent.py +13 -13
  10. autogen/agentchat/assistant_agent.py +7 -6
  11. autogen/agentchat/contrib/agent_eval/agent_eval.py +3 -3
  12. autogen/agentchat/contrib/agent_eval/critic_agent.py +3 -3
  13. autogen/agentchat/contrib/agent_eval/quantifier_agent.py +3 -3
  14. autogen/agentchat/contrib/agent_eval/subcritic_agent.py +3 -3
  15. autogen/agentchat/contrib/agent_optimizer.py +3 -3
  16. autogen/agentchat/contrib/capabilities/generate_images.py +11 -11
  17. autogen/agentchat/contrib/capabilities/teachability.py +15 -15
  18. autogen/agentchat/contrib/capabilities/transforms.py +17 -18
  19. autogen/agentchat/contrib/capabilities/transforms_util.py +5 -5
  20. autogen/agentchat/contrib/capabilities/vision_capability.py +4 -3
  21. autogen/agentchat/contrib/captainagent/agent_builder.py +30 -30
  22. autogen/agentchat/contrib/captainagent/captainagent.py +22 -21
  23. autogen/agentchat/contrib/captainagent/tool_retriever.py +2 -3
  24. autogen/agentchat/contrib/gpt_assistant_agent.py +9 -9
  25. autogen/agentchat/contrib/graph_rag/document.py +3 -3
  26. autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +3 -3
  27. autogen/agentchat/contrib/graph_rag/falkor_graph_rag_capability.py +6 -6
  28. autogen/agentchat/contrib/graph_rag/graph_query_engine.py +3 -3
  29. autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +5 -11
  30. autogen/agentchat/contrib/graph_rag/neo4j_graph_rag_capability.py +6 -6
  31. autogen/agentchat/contrib/graph_rag/neo4j_native_graph_query_engine.py +7 -7
  32. autogen/agentchat/contrib/graph_rag/neo4j_native_graph_rag_capability.py +6 -6
  33. autogen/agentchat/contrib/img_utils.py +1 -1
  34. autogen/agentchat/contrib/llamaindex_conversable_agent.py +11 -11
  35. autogen/agentchat/contrib/llava_agent.py +18 -4
  36. autogen/agentchat/contrib/math_user_proxy_agent.py +11 -11
  37. autogen/agentchat/contrib/multimodal_conversable_agent.py +8 -8
  38. autogen/agentchat/contrib/qdrant_retrieve_user_proxy_agent.py +6 -5
  39. autogen/agentchat/contrib/rag/chromadb_query_engine.py +22 -26
  40. autogen/agentchat/contrib/rag/llamaindex_query_engine.py +14 -17
  41. autogen/agentchat/contrib/rag/mongodb_query_engine.py +27 -37
  42. autogen/agentchat/contrib/rag/query_engine.py +7 -5
  43. autogen/agentchat/contrib/retrieve_assistant_agent.py +5 -5
  44. autogen/agentchat/contrib/retrieve_user_proxy_agent.py +8 -7
  45. autogen/agentchat/contrib/society_of_mind_agent.py +15 -14
  46. autogen/agentchat/contrib/swarm_agent.py +76 -98
  47. autogen/agentchat/contrib/text_analyzer_agent.py +7 -7
  48. autogen/agentchat/contrib/vectordb/base.py +10 -18
  49. autogen/agentchat/contrib/vectordb/chromadb.py +2 -1
  50. autogen/agentchat/contrib/vectordb/couchbase.py +18 -20
  51. autogen/agentchat/contrib/vectordb/mongodb.py +6 -5
  52. autogen/agentchat/contrib/vectordb/pgvectordb.py +40 -41
  53. autogen/agentchat/contrib/vectordb/qdrant.py +5 -5
  54. autogen/agentchat/contrib/web_surfer.py +20 -19
  55. autogen/agentchat/conversable_agent.py +292 -290
  56. autogen/agentchat/group/context_str.py +1 -3
  57. autogen/agentchat/group/context_variables.py +15 -25
  58. autogen/agentchat/group/group_tool_executor.py +10 -10
  59. autogen/agentchat/group/group_utils.py +15 -15
  60. autogen/agentchat/group/guardrails.py +7 -7
  61. autogen/agentchat/group/handoffs.py +19 -36
  62. autogen/agentchat/group/multi_agent_chat.py +7 -7
  63. autogen/agentchat/group/on_condition.py +4 -7
  64. autogen/agentchat/group/on_context_condition.py +4 -7
  65. autogen/agentchat/group/patterns/auto.py +8 -7
  66. autogen/agentchat/group/patterns/manual.py +7 -6
  67. autogen/agentchat/group/patterns/pattern.py +13 -12
  68. autogen/agentchat/group/patterns/random.py +3 -3
  69. autogen/agentchat/group/patterns/round_robin.py +3 -3
  70. autogen/agentchat/group/reply_result.py +2 -4
  71. autogen/agentchat/group/speaker_selection_result.py +5 -5
  72. autogen/agentchat/group/targets/group_chat_target.py +7 -6
  73. autogen/agentchat/group/targets/group_manager_target.py +4 -4
  74. autogen/agentchat/group/targets/transition_target.py +2 -1
  75. autogen/agentchat/groupchat.py +60 -63
  76. autogen/agentchat/realtime/experimental/audio_adapters/twilio_audio_adapter.py +4 -4
  77. autogen/agentchat/realtime/experimental/audio_adapters/websocket_audio_adapter.py +4 -4
  78. autogen/agentchat/realtime/experimental/clients/gemini/client.py +7 -7
  79. autogen/agentchat/realtime/experimental/clients/oai/base_client.py +8 -8
  80. autogen/agentchat/realtime/experimental/clients/oai/rtc_client.py +6 -6
  81. autogen/agentchat/realtime/experimental/clients/realtime_client.py +10 -9
  82. autogen/agentchat/realtime/experimental/realtime_agent.py +10 -9
  83. autogen/agentchat/realtime/experimental/realtime_observer.py +3 -3
  84. autogen/agentchat/realtime/experimental/realtime_swarm.py +44 -44
  85. autogen/agentchat/user_proxy_agent.py +10 -9
  86. autogen/agentchat/utils.py +3 -3
  87. autogen/agents/contrib/time/time_reply_agent.py +6 -5
  88. autogen/agents/contrib/time/time_tool_agent.py +2 -1
  89. autogen/agents/experimental/deep_research/deep_research.py +3 -3
  90. autogen/agents/experimental/discord/discord.py +2 -2
  91. autogen/agents/experimental/document_agent/chroma_query_engine.py +29 -44
  92. autogen/agents/experimental/document_agent/docling_doc_ingest_agent.py +9 -14
  93. autogen/agents/experimental/document_agent/document_agent.py +15 -16
  94. autogen/agents/experimental/document_agent/document_conditions.py +3 -3
  95. autogen/agents/experimental/document_agent/document_utils.py +5 -9
  96. autogen/agents/experimental/document_agent/inmemory_query_engine.py +14 -20
  97. autogen/agents/experimental/document_agent/parser_utils.py +4 -4
  98. autogen/agents/experimental/document_agent/url_utils.py +14 -23
  99. autogen/agents/experimental/reasoning/reasoning_agent.py +33 -33
  100. autogen/agents/experimental/slack/slack.py +2 -2
  101. autogen/agents/experimental/telegram/telegram.py +2 -3
  102. autogen/agents/experimental/websurfer/websurfer.py +4 -4
  103. autogen/agents/experimental/wikipedia/wikipedia.py +5 -7
  104. autogen/browser_utils.py +8 -8
  105. autogen/cache/abstract_cache_base.py +5 -5
  106. autogen/cache/cache.py +12 -12
  107. autogen/cache/cache_factory.py +4 -4
  108. autogen/cache/cosmos_db_cache.py +9 -9
  109. autogen/cache/disk_cache.py +6 -6
  110. autogen/cache/in_memory_cache.py +4 -4
  111. autogen/cache/redis_cache.py +4 -4
  112. autogen/code_utils.py +18 -18
  113. autogen/coding/base.py +6 -6
  114. autogen/coding/docker_commandline_code_executor.py +9 -9
  115. autogen/coding/func_with_reqs.py +7 -6
  116. autogen/coding/jupyter/base.py +3 -3
  117. autogen/coding/jupyter/docker_jupyter_server.py +3 -4
  118. autogen/coding/jupyter/import_utils.py +3 -3
  119. autogen/coding/jupyter/jupyter_client.py +5 -5
  120. autogen/coding/jupyter/jupyter_code_executor.py +3 -4
  121. autogen/coding/jupyter/local_jupyter_server.py +2 -6
  122. autogen/coding/local_commandline_code_executor.py +8 -7
  123. autogen/coding/markdown_code_extractor.py +1 -2
  124. autogen/coding/utils.py +1 -2
  125. autogen/doc_utils.py +3 -2
  126. autogen/environments/docker_python_environment.py +19 -29
  127. autogen/environments/python_environment.py +8 -17
  128. autogen/environments/system_python_environment.py +3 -4
  129. autogen/environments/venv_python_environment.py +8 -12
  130. autogen/environments/working_directory.py +1 -2
  131. autogen/events/agent_events.py +106 -109
  132. autogen/events/base_event.py +6 -5
  133. autogen/events/client_events.py +15 -14
  134. autogen/events/helpers.py +1 -1
  135. autogen/events/print_event.py +4 -5
  136. autogen/fast_depends/_compat.py +10 -15
  137. autogen/fast_depends/core/build.py +17 -36
  138. autogen/fast_depends/core/model.py +64 -113
  139. autogen/fast_depends/dependencies/model.py +2 -1
  140. autogen/fast_depends/dependencies/provider.py +3 -2
  141. autogen/fast_depends/library/model.py +4 -4
  142. autogen/fast_depends/schema.py +7 -7
  143. autogen/fast_depends/use.py +17 -25
  144. autogen/fast_depends/utils.py +10 -30
  145. autogen/formatting_utils.py +6 -6
  146. autogen/graph_utils.py +1 -4
  147. autogen/import_utils.py +38 -27
  148. autogen/interop/crewai/crewai.py +2 -2
  149. autogen/interop/interoperable.py +2 -2
  150. autogen/interop/langchain/langchain_chat_model_factory.py +3 -2
  151. autogen/interop/langchain/langchain_tool.py +2 -6
  152. autogen/interop/litellm/litellm_config_factory.py +6 -7
  153. autogen/interop/pydantic_ai/pydantic_ai.py +4 -7
  154. autogen/interop/registry.py +2 -1
  155. autogen/io/base.py +5 -5
  156. autogen/io/run_response.py +33 -32
  157. autogen/io/websockets.py +6 -5
  158. autogen/json_utils.py +1 -2
  159. autogen/llm_config/__init__.py +11 -0
  160. autogen/llm_config/client.py +58 -0
  161. autogen/llm_config/config.py +384 -0
  162. autogen/llm_config/entry.py +154 -0
  163. autogen/logger/base_logger.py +4 -3
  164. autogen/logger/file_logger.py +2 -1
  165. autogen/logger/logger_factory.py +2 -2
  166. autogen/logger/logger_utils.py +2 -2
  167. autogen/logger/sqlite_logger.py +2 -1
  168. autogen/math_utils.py +4 -5
  169. autogen/mcp/__main__.py +6 -6
  170. autogen/mcp/helpers.py +4 -4
  171. autogen/mcp/mcp_client.py +170 -29
  172. autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py +3 -4
  173. autogen/mcp/mcp_proxy/mcp_proxy.py +23 -26
  174. autogen/mcp/mcp_proxy/operation_grouping.py +4 -5
  175. autogen/mcp/mcp_proxy/operation_renaming.py +6 -10
  176. autogen/mcp/mcp_proxy/security.py +2 -3
  177. autogen/messages/agent_messages.py +96 -98
  178. autogen/messages/base_message.py +6 -5
  179. autogen/messages/client_messages.py +15 -14
  180. autogen/messages/print_message.py +4 -5
  181. autogen/oai/__init__.py +1 -2
  182. autogen/oai/anthropic.py +42 -41
  183. autogen/oai/bedrock.py +68 -57
  184. autogen/oai/cerebras.py +26 -25
  185. autogen/oai/client.py +113 -139
  186. autogen/oai/client_utils.py +3 -3
  187. autogen/oai/cohere.py +34 -11
  188. autogen/oai/gemini.py +39 -17
  189. autogen/oai/gemini_types.py +11 -12
  190. autogen/oai/groq.py +22 -10
  191. autogen/oai/mistral.py +17 -11
  192. autogen/oai/oai_models/__init__.py +14 -2
  193. autogen/oai/oai_models/_models.py +2 -2
  194. autogen/oai/oai_models/chat_completion.py +13 -14
  195. autogen/oai/oai_models/chat_completion_message.py +11 -9
  196. autogen/oai/oai_models/chat_completion_message_tool_call.py +26 -3
  197. autogen/oai/oai_models/chat_completion_token_logprob.py +3 -4
  198. autogen/oai/oai_models/completion_usage.py +8 -9
  199. autogen/oai/ollama.py +19 -9
  200. autogen/oai/openai_responses.py +40 -17
  201. autogen/oai/openai_utils.py +48 -38
  202. autogen/oai/together.py +29 -14
  203. autogen/retrieve_utils.py +6 -7
  204. autogen/runtime_logging.py +5 -4
  205. autogen/token_count_utils.py +7 -4
  206. autogen/tools/contrib/time/time.py +0 -1
  207. autogen/tools/dependency_injection.py +5 -6
  208. autogen/tools/experimental/browser_use/browser_use.py +10 -10
  209. autogen/tools/experimental/code_execution/python_code_execution.py +5 -7
  210. autogen/tools/experimental/crawl4ai/crawl4ai.py +12 -15
  211. autogen/tools/experimental/deep_research/deep_research.py +9 -8
  212. autogen/tools/experimental/duckduckgo/duckduckgo_search.py +5 -11
  213. autogen/tools/experimental/firecrawl/firecrawl_tool.py +98 -115
  214. autogen/tools/experimental/google/authentication/credentials_local_provider.py +1 -1
  215. autogen/tools/experimental/google/drive/drive_functions.py +4 -4
  216. autogen/tools/experimental/google/drive/toolkit.py +5 -5
  217. autogen/tools/experimental/google_search/google_search.py +5 -5
  218. autogen/tools/experimental/google_search/youtube_search.py +5 -5
  219. autogen/tools/experimental/messageplatform/discord/discord.py +8 -12
  220. autogen/tools/experimental/messageplatform/slack/slack.py +14 -20
  221. autogen/tools/experimental/messageplatform/telegram/telegram.py +8 -12
  222. autogen/tools/experimental/perplexity/perplexity_search.py +18 -29
  223. autogen/tools/experimental/reliable/reliable.py +68 -74
  224. autogen/tools/experimental/searxng/searxng_search.py +20 -19
  225. autogen/tools/experimental/tavily/tavily_search.py +12 -19
  226. autogen/tools/experimental/web_search_preview/web_search_preview.py +13 -7
  227. autogen/tools/experimental/wikipedia/wikipedia.py +7 -10
  228. autogen/tools/function_utils.py +7 -7
  229. autogen/tools/tool.py +8 -6
  230. autogen/types.py +2 -2
  231. autogen/version.py +1 -1
  232. ag2-0.9.7.dist-info/RECORD +0 -421
  233. autogen/llm_config.py +0 -385
  234. {ag2-0.9.7.dist-info → ag2-0.9.9.dist-info}/WHEEL +0 -0
  235. {ag2-0.9.7.dist-info → ag2-0.9.9.dist-info}/licenses/LICENSE +0 -0
  236. {ag2-0.9.7.dist-info → ag2-0.9.9.dist-info}/licenses/NOTICE.md +0 -0
@@ -14,15 +14,13 @@ import re
14
14
  import threading
15
15
  import warnings
16
16
  from collections import defaultdict
17
+ from collections.abc import Callable, Generator, Iterable
17
18
  from contextlib import contextmanager
18
19
  from dataclasses import dataclass
19
20
  from inspect import signature
20
21
  from typing import (
21
22
  TYPE_CHECKING,
22
23
  Any,
23
- Callable,
24
- Generator,
25
- Iterable,
26
24
  Literal,
27
25
  Optional,
28
26
  TypeVar,
@@ -61,11 +59,13 @@ from ..events.agent_events import (
61
59
  create_received_event_model,
62
60
  )
63
61
  from ..exception_utils import InvalidCarryOverTypeError, SenderRequiredError
62
+ from ..fast_depends.utils import is_coroutine_callable
64
63
  from ..io.base import IOStream
65
64
  from ..io.run_response import AsyncRunResponse, AsyncRunResponseProtocol, RunResponse, RunResponseProtocol
66
65
  from ..io.thread_io_stream import AsyncThreadIOStream, ThreadIOStream
67
66
  from ..llm_config import LLMConfig
68
- from ..oai.client import ModelClient, OpenAIWrapper
67
+ from ..llm_config.client import ModelClient
68
+ from ..oai.client import OpenAIWrapper
69
69
  from ..runtime_logging import log_event, log_function_use, log_new_agent, logging_enabled
70
70
  from ..tools import ChatContext, Tool, load_basemodels_if_needed, serialize_to_str
71
71
  from .agent import Agent, LLMAgent
@@ -103,7 +103,7 @@ class UpdateSystemMessage:
103
103
  def my_content_updater(agent: ConversableAgent, messages: List[Dict[str, Any]]) -> str
104
104
  """
105
105
 
106
- content_updater: Union[Callable, str]
106
+ content_updater: Callable | str
107
107
 
108
108
  def __post_init__(self):
109
109
  if isinstance(self.content_updater, str):
@@ -144,88 +144,88 @@ class ConversableAgent(LLMAgent):
144
144
 
145
145
  DEFAULT_SUMMARY_PROMPT = "Summarize the takeaway from the conversation. Do not add any introductory phrases."
146
146
  DEFAULT_SUMMARY_METHOD = "last_msg"
147
- llm_config: Union[dict[str, Any], Literal[False]]
147
+ llm_config: dict[str, Any] | Literal[False]
148
148
 
149
149
  def __init__(
150
150
  self,
151
151
  name: str,
152
- system_message: Optional[Union[str, list]] = "You are a helpful AI Assistant.",
153
- is_termination_msg: Optional[Callable[[dict[str, Any]], bool]] = None,
154
- max_consecutive_auto_reply: Optional[int] = None,
152
+ system_message: str | list | None = "You are a helpful AI Assistant.",
153
+ is_termination_msg: Callable[[dict[str, Any]], bool] | None = None,
154
+ max_consecutive_auto_reply: int | None = None,
155
155
  human_input_mode: Literal["ALWAYS", "NEVER", "TERMINATE"] = "TERMINATE",
156
- function_map: Optional[dict[str, Callable[..., Any]]] = None,
157
- code_execution_config: Union[dict[str, Any], Literal[False]] = False,
158
- llm_config: Optional[Union[LLMConfig, dict[str, Any], Literal[False]]] = None,
159
- default_auto_reply: Union[str, dict[str, Any]] = "",
160
- description: Optional[str] = None,
161
- chat_messages: Optional[dict[Agent, list[dict[str, Any]]]] = None,
162
- silent: Optional[bool] = None,
156
+ function_map: dict[str, Callable[..., Any]] | None = None,
157
+ code_execution_config: dict[str, Any] | Literal[False] = False,
158
+ llm_config: LLMConfig | dict[str, Any] | Literal[False] | None = None,
159
+ default_auto_reply: str | dict[str, Any] = "",
160
+ description: str | None = None,
161
+ chat_messages: dict[Agent, list[dict[str, Any]]] | None = None,
162
+ silent: bool | None = None,
163
163
  context_variables: Optional["ContextVariables"] = None,
164
- functions: Union[list[Callable[..., Any]], Callable[..., Any]] = None,
165
- update_agent_state_before_reply: Optional[
166
- Union[list[Union[Callable, UpdateSystemMessage]], Callable, UpdateSystemMessage]
167
- ] = None,
168
- handoffs: Optional[Handoffs] = None,
164
+ functions: list[Callable[..., Any]] | Callable[..., Any] = None,
165
+ update_agent_state_before_reply: list[Callable | UpdateSystemMessage]
166
+ | Callable
167
+ | UpdateSystemMessage
168
+ | None = None,
169
+ handoffs: Handoffs | None = None,
169
170
  ):
170
- """
171
- Args:
172
- name (str): name of the agent.
173
- system_message (str or list): system message for the ChatCompletion inference.
174
- is_termination_msg (function): a function that takes a message in the form of a dictionary
175
- and returns a boolean value indicating if this received message is a termination message.
176
- The dict can contain the following keys: "content", "role", "name", "function_call".
177
- max_consecutive_auto_reply (int): the maximum number of consecutive auto replies.
178
- default to None (no limit provided, class attribute MAX_CONSECUTIVE_AUTO_REPLY will be used as the limit in this case).
179
- When set to 0, no auto reply will be generated.
180
- human_input_mode (str): whether to ask for human inputs every time a message is received.
181
- Possible values are "ALWAYS", "TERMINATE", "NEVER".
182
- (1) When "ALWAYS", the agent prompts for human input every time a message is received.
183
- Under this mode, the conversation stops when the human input is "exit",
184
- or when is_termination_msg is True and there is no human input.
185
- (2) When "TERMINATE", the agent only prompts for human input only when a termination message is received or
186
- the number of auto reply reaches the max_consecutive_auto_reply.
187
- (3) When "NEVER", the agent will never prompt for human input. Under this mode, the conversation stops
188
- when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.
189
- function_map (dict[str, callable]): Mapping function names (passed to openai) to callable functions, also used for tool calls.
190
- code_execution_config (dict or False): config for the code execution.
191
- To disable code execution, set to False. Otherwise, set to a dictionary with the following keys:
192
- - work_dir (Optional, str): The working directory for the code execution.
193
- If None, a default working directory will be used.
194
- The default working directory is the "extensions" directory under
195
- "path_to_autogen".
196
- - use_docker (Optional, list, str or bool): The docker image to use for code execution.
197
- Default is True, which means the code will be executed in a docker container. A default list of images will be used.
198
- If a list or a str of image name(s) is provided, the code will be executed in a docker container
199
- with the first image successfully pulled.
200
- If False, the code will be executed in the current environment.
201
- We strongly recommend using docker for code execution.
202
- - timeout (Optional, int): The maximum execution time in seconds.
203
- - last_n_messages (Experimental, int or str): The number of messages to look back for code execution.
204
- If set to 'auto', it will scan backwards through all messages arriving since the agent last spoke, which is typically the last time execution was attempted. (Default: auto)
205
- llm_config (LLMConfig or dict or False or None): llm inference configuration.
206
- Please refer to [OpenAIWrapper.create](https://docs.ag2.ai/latest/docs/api-reference/autogen/OpenAIWrapper/#autogen.OpenAIWrapper.create)
207
- for available options.
208
- When using OpenAI or Azure OpenAI endpoints, please specify a non-empty 'model' either in `llm_config` or in each config of 'config_list' in `llm_config`.
209
- To disable llm-based auto reply, set to False.
210
- When set to None, will use self.DEFAULT_CONFIG, which defaults to False.
211
- default_auto_reply (str or dict): default auto reply when no code execution or llm-based reply is generated.
212
- description (str): a short description of the agent. This description is used by other agents
213
- (e.g. the GroupChatManager) to decide when to call upon this agent. (Default: system_message)
214
- chat_messages (dict or None): the previous chat messages that this agent had in the past with other agents.
215
- Can be used to give the agent a memory by providing the chat history. This will allow the agent to
216
- resume previous had conversations. Defaults to an empty chat history.
217
- silent (bool or None): (Experimental) whether to print the message sent. If None, will use the value of
218
- silent in each function.
219
- context_variables (ContextVariables or None): Context variables that provide a persistent context for the agent.
220
- Note: This will be a reference to a shared context for multi-agent chats.
221
- Behaves like a dictionary with keys and values (akin to dict[str, Any]).
222
- functions (List[Callable[..., Any]]): A list of functions to register with the agent, these will be wrapped up as tools and registered for LLM (not execution).
223
- update_agent_state_before_reply (List[Callable[..., Any]]): A list of functions, including UpdateSystemMessage's, called to update the agent before it replies.
224
- handoffs (Handoffs): Handoffs object containing all handoff transition conditions.
171
+ """Args:
172
+ name (str): name of the agent.
173
+ system_message (str or list): system message for the ChatCompletion inference.
174
+ is_termination_msg (function): a function that takes a message in the form of a dictionary
175
+ and returns a boolean value indicating if this received message is a termination message.
176
+ The dict can contain the following keys: "content", "role", "name", "function_call".
177
+ max_consecutive_auto_reply (int): the maximum number of consecutive auto replies.
178
+ default to None (no limit provided, class attribute MAX_CONSECUTIVE_AUTO_REPLY will be used as the limit in this case).
179
+ When set to 0, no auto reply will be generated.
180
+ human_input_mode (str): whether to ask for human inputs every time a message is received.
181
+ Possible values are "ALWAYS", "TERMINATE", "NEVER".
182
+ (1) When "ALWAYS", the agent prompts for human input every time a message is received.
183
+ Under this mode, the conversation stops when the human input is "exit",
184
+ or when is_termination_msg is True and there is no human input.
185
+ (2) When "TERMINATE", the agent only prompts for human input only when a termination message is received or
186
+ the number of auto reply reaches the max_consecutive_auto_reply.
187
+ (3) When "NEVER", the agent will never prompt for human input. Under this mode, the conversation stops
188
+ when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.
189
+ function_map (dict[str, callable]): Mapping function names (passed to openai) to callable functions, also used for tool calls.
190
+ code_execution_config (dict or False): config for the code execution.
191
+ To disable code execution, set to False. Otherwise, set to a dictionary with the following keys:
192
+ - work_dir (Optional, str): The working directory for the code execution.
193
+ If None, a default working directory will be used.
194
+ The default working directory is the "extensions" directory under
195
+ "path_to_autogen".
196
+ - use_docker (Optional, list, str or bool): The docker image to use for code execution.
197
+ Default is True, which means the code will be executed in a docker container. A default list of images will be used.
198
+ If a list or a str of image name(s) is provided, the code will be executed in a docker container
199
+ with the first image successfully pulled.
200
+ If False, the code will be executed in the current environment.
201
+ We strongly recommend using docker for code execution.
202
+ - timeout (Optional, int): The maximum execution time in seconds.
203
+ - last_n_messages (Experimental, int or str): The number of messages to look back for code execution.
204
+ If set to 'auto', it will scan backwards through all messages arriving since the agent last spoke, which is typically the last time execution was attempted. (Default: auto)
205
+ llm_config (LLMConfig or dict or False or None): llm inference configuration.
206
+ Please refer to [OpenAIWrapper.create](https://docs.ag2.ai/latest/docs/api-reference/autogen/OpenAIWrapper/#autogen.OpenAIWrapper.create)
207
+ for available options.
208
+ When using OpenAI or Azure OpenAI endpoints, please specify a non-empty 'model' either in `llm_config` or in each config of 'config_list' in `llm_config`.
209
+ To disable llm-based auto reply, set to False.
210
+ When set to None, will use self.DEFAULT_CONFIG, which defaults to False.
211
+ default_auto_reply (str or dict): default auto reply when no code execution or llm-based reply is generated.
212
+ description (str): a short description of the agent. This description is used by other agents
213
+ (e.g. the GroupChatManager) to decide when to call upon this agent. (Default: system_message)
214
+ chat_messages (dict or None): the previous chat messages that this agent had in the past with other agents.
215
+ Can be used to give the agent a memory by providing the chat history. This will allow the agent to
216
+ resume previous had conversations. Defaults to an empty chat history.
217
+ silent (bool or None): (Experimental) whether to print the message sent. If None, will use the value of
218
+ silent in each function.
219
+ context_variables (ContextVariables or None): Context variables that provide a persistent context for the agent.
220
+ Note: This will be a reference to a shared context for multi-agent chats.
221
+ Behaves like a dictionary with keys and values (akin to dict[str, Any]).
222
+ functions (List[Callable[..., Any]]): A list of functions to register with the agent, these will be wrapped up as tools and registered for LLM (not execution).
223
+ update_agent_state_before_reply (List[Callable[..., Any]]): A list of functions, including UpdateSystemMessage's, called to update the agent before it replies.
224
+ handoffs (Handoffs): Handoffs object containing all handoff transition conditions.
225
225
  """
226
226
  self.handoffs = handoffs if handoffs is not None else Handoffs()
227
- self.input_guardrails: list["Guardrail"] = []
228
- self.output_guardrails: list["Guardrail"] = []
227
+ self.input_guardrails: list[Guardrail] = []
228
+ self.output_guardrails: list[Guardrail] = []
229
229
 
230
230
  # we change code_execution_config below and we have to make sure we don't change the input
231
231
  # in case of UserProxyAgent, without this we could even change the default value {}
@@ -247,7 +247,7 @@ class ConversableAgent(LLMAgent):
247
247
  else (lambda x: content_str(x.get("content")) == "TERMINATE")
248
248
  )
249
249
  self.silent = silent
250
- self.run_executor: Optional[ConversableAgent] = None
250
+ self.run_executor: ConversableAgent | None = None
251
251
 
252
252
  # Take a copy to avoid modifying the given dict
253
253
  if isinstance(llm_config, dict):
@@ -320,7 +320,7 @@ class ConversableAgent(LLMAgent):
320
320
  raise ValueError("code_execution_config must be a dict or False.")
321
321
 
322
322
  # We have got a valid code_execution_config.
323
- self._code_execution_config: Union[dict[str, Any], Literal[False]] = code_execution_config
323
+ self._code_execution_config: dict[str, Any] | Literal[False] = code_execution_config
324
324
 
325
325
  if self._code_execution_config.get("executor") is not None:
326
326
  if "use_docker" in self._code_execution_config:
@@ -401,11 +401,12 @@ class ConversableAgent(LLMAgent):
401
401
  """Add (Register) a list of functions to the agent
402
402
 
403
403
  Args:
404
- func_list (list[Callable[..., Any]]): A list of functions to register with the agent."""
404
+ func_list (list[Callable[..., Any]]): A list of functions to register with the agent.
405
+ """
405
406
  for func in func_list:
406
407
  self._add_single_function(func)
407
408
 
408
- def _add_single_function(self, func: Callable, name: Optional[str] = None, description: Optional[str] = ""):
409
+ def _add_single_function(self, func: Callable, name: str | None = None, description: str | None = ""):
409
410
  """Add a single function to the agent
410
411
 
411
412
  Args:
@@ -433,10 +434,9 @@ class ConversableAgent(LLMAgent):
433
434
  self.register_for_llm(name=name, description=description, silent_override=True)(func)
434
435
 
435
436
  def _register_update_agent_state_before_reply(
436
- self, functions: Optional[Union[list[Callable[..., Any]], Callable[..., Any]]]
437
+ self, functions: list[Callable[..., Any]] | Callable[..., Any] | None
437
438
  ):
438
- """
439
- Register functions that will be called when the agent is selected and before it speaks.
439
+ """Register functions that will be called when the agent is selected and before it speaks.
440
440
  You can add your own validation or precondition functions here.
441
441
 
442
442
  Args:
@@ -483,8 +483,8 @@ class ConversableAgent(LLMAgent):
483
483
 
484
484
  @classmethod
485
485
  def _validate_llm_config(
486
- cls, llm_config: Optional[Union[LLMConfig, dict[str, Any], Literal[False]]]
487
- ) -> Union[LLMConfig, Literal[False]]:
486
+ cls, llm_config: LLMConfig | dict[str, Any] | Literal[False] | None
487
+ ) -> LLMConfig | Literal[False]:
488
488
  # if not(llm_config in (None, False) or isinstance(llm_config, [dict, LLMConfig])):
489
489
  # raise ValueError(
490
490
  # "llm_config must be a dict or False or None."
@@ -506,11 +506,11 @@ class ConversableAgent(LLMAgent):
506
506
  return llm_config
507
507
 
508
508
  @classmethod
509
- def _create_client(cls, llm_config: Union[LLMConfig, Literal[False]]) -> Optional[OpenAIWrapper]:
509
+ def _create_client(cls, llm_config: LLMConfig | Literal[False]) -> OpenAIWrapper | None:
510
510
  return None if llm_config is False else OpenAIWrapper(**llm_config)
511
511
 
512
512
  @staticmethod
513
- def _is_silent(agent: Agent, silent: Optional[bool] = False) -> bool:
513
+ def _is_silent(agent: Agent, silent: bool | None = False) -> bool:
514
514
  return agent.silent if agent.silent is not None else silent
515
515
 
516
516
  @property
@@ -529,7 +529,7 @@ class ConversableAgent(LLMAgent):
529
529
  self._description = description
530
530
 
531
531
  @property
532
- def code_executor(self) -> Optional[CodeExecutor]:
532
+ def code_executor(self) -> CodeExecutor | None:
533
533
  """The code executor used by this agent. Returns None if code execution is disabled."""
534
534
  if not hasattr(self, "_code_executor"):
535
535
  return None
@@ -537,11 +537,11 @@ class ConversableAgent(LLMAgent):
537
537
 
538
538
  def register_reply(
539
539
  self,
540
- trigger: Union[type[Agent], str, Agent, Callable[[Agent], bool], list],
540
+ trigger: type[Agent] | str | Agent | Callable[[Agent], bool] | list,
541
541
  reply_func: Callable,
542
542
  position: int = 0,
543
- config: Optional[Any] = None,
544
- reset_config: Optional[Callable[..., Any]] = None,
543
+ config: Any | None = None,
544
+ reset_config: Callable[..., Any] | None = None,
545
545
  *,
546
546
  ignore_async_in_sync_chat: bool = False,
547
547
  remove_other_reply_funcs: bool = False,
@@ -622,7 +622,7 @@ class ConversableAgent(LLMAgent):
622
622
  def _get_chats_to_run(
623
623
  chat_queue: list[dict[str, Any]],
624
624
  recipient: Agent,
625
- messages: Optional[list[dict[str, Any]]],
625
+ messages: list[dict[str, Any]] | None,
626
626
  sender: Agent,
627
627
  config: Any,
628
628
  ) -> list[dict[str, Any]]:
@@ -684,7 +684,7 @@ class ConversableAgent(LLMAgent):
684
684
  trim_n_messages: The number of latest messages to trim from the messages list
685
685
  """
686
686
 
687
- def concat_carryover(chat_message: str, carryover_message: Union[str, list[dict[str, Any]]]) -> str:
687
+ def concat_carryover(chat_message: str, carryover_message: str | list[dict[str, Any]]) -> str:
688
688
  """Concatenate the carryover message to the chat message."""
689
689
  prefix = f"{chat_message}\n" if chat_message else ""
690
690
 
@@ -755,11 +755,11 @@ class ConversableAgent(LLMAgent):
755
755
  def _process_chat_queue_carryover(
756
756
  chat_queue: list[dict[str, Any]],
757
757
  recipient: Agent,
758
- messages: Union[str, Callable[..., Any]],
758
+ messages: str | Callable[..., Any],
759
759
  sender: Agent,
760
760
  config: Any,
761
761
  trim_messages: int = 2,
762
- ) -> tuple[bool, Optional[str]]:
762
+ ) -> tuple[bool, str | None]:
763
763
  """Process carryover configuration for the first chat in the queue.
764
764
 
765
765
  Args:
@@ -801,10 +801,10 @@ class ConversableAgent(LLMAgent):
801
801
  def _summary_from_nested_chats(
802
802
  chat_queue: list[dict[str, Any]],
803
803
  recipient: Agent,
804
- messages: Optional[list[dict[str, Any]]],
804
+ messages: list[dict[str, Any]] | None,
805
805
  sender: Agent,
806
806
  config: Any,
807
- ) -> tuple[bool, Union[str, None]]:
807
+ ) -> tuple[bool, str | None]:
808
808
  """A simple chat reply function.
809
809
  This function initiate one or a sequence of chats between the "recipient" and the agents in the
810
810
  chat_queue.
@@ -840,10 +840,10 @@ class ConversableAgent(LLMAgent):
840
840
  async def _a_summary_from_nested_chats(
841
841
  chat_queue: list[dict[str, Any]],
842
842
  recipient: Agent,
843
- messages: Optional[list[dict[str, Any]]],
843
+ messages: list[dict[str, Any]] | None,
844
844
  sender: Agent,
845
845
  config: Any,
846
- ) -> tuple[bool, Union[str, None]]:
846
+ ) -> tuple[bool, str | None]:
847
847
  """A simple chat reply function.
848
848
  This function initiate one or a sequence of chats between the "recipient" and the agents in the
849
849
  chat_queue.
@@ -879,10 +879,10 @@ class ConversableAgent(LLMAgent):
879
879
  def register_nested_chats(
880
880
  self,
881
881
  chat_queue: list[dict[str, Any]],
882
- trigger: Union[type[Agent], str, Agent, Callable[[Agent], bool], list],
883
- reply_func_from_nested_chats: Union[str, Callable[..., Any]] = "summary_from_nested_chats",
882
+ trigger: type[Agent] | str | Agent | Callable[[Agent], bool] | list,
883
+ reply_func_from_nested_chats: str | Callable[..., Any] = "summary_from_nested_chats",
884
884
  position: int = 2,
885
- use_async: Union[bool, None] = None,
885
+ use_async: bool | None = None,
886
886
  **kwargs: Any,
887
887
  ) -> None:
888
888
  """Register a nested chat reply function.
@@ -957,7 +957,7 @@ class ConversableAgent(LLMAgent):
957
957
  """
958
958
  self._oai_system_message[0]["content"] = system_message
959
959
 
960
- def update_max_consecutive_auto_reply(self, value: int, sender: Optional[Agent] = None):
960
+ def update_max_consecutive_auto_reply(self, value: int, sender: Agent | None = None):
961
961
  """Update the maximum number of consecutive auto replies.
962
962
 
963
963
  Args:
@@ -971,7 +971,7 @@ class ConversableAgent(LLMAgent):
971
971
  else:
972
972
  self._max_consecutive_auto_reply_dict[sender] = value
973
973
 
974
- def max_consecutive_auto_reply(self, sender: Optional[Agent] = None) -> int:
974
+ def max_consecutive_auto_reply(self, sender: Agent | None = None) -> int:
975
975
  """The maximum number of consecutive auto replies."""
976
976
  return self._max_consecutive_auto_reply if sender is None else self._max_consecutive_auto_reply_dict[sender]
977
977
 
@@ -984,7 +984,7 @@ class ConversableAgent(LLMAgent):
984
984
  """A list of messages as a conversation to summarize."""
985
985
  return self._oai_messages[agent]
986
986
 
987
- def last_message(self, agent: Optional[Agent] = None) -> Optional[dict[str, Any]]:
987
+ def last_message(self, agent: Agent | None = None) -> dict[str, Any] | None:
988
988
  """The last message exchanged with the agent.
989
989
 
990
990
  Args:
@@ -1010,14 +1010,14 @@ class ConversableAgent(LLMAgent):
1010
1010
  return self._oai_messages[agent][-1]
1011
1011
 
1012
1012
  @property
1013
- def use_docker(self) -> Union[bool, str, None]:
1013
+ def use_docker(self) -> bool | str | None:
1014
1014
  """Bool value of whether to use docker to execute the code,
1015
1015
  or str value of the docker image name to use, or None when code execution is disabled.
1016
1016
  """
1017
1017
  return None if self._code_execution_config is False else self._code_execution_config.get("use_docker")
1018
1018
 
1019
1019
  @staticmethod
1020
- def _message_to_dict(message: Union[dict[str, Any], str]) -> dict:
1020
+ def _message_to_dict(message: dict[str, Any] | str) -> dict:
1021
1021
  """Convert a message to a dictionary.
1022
1022
 
1023
1023
  The message can be a string or a dictionary. The string will be put in the "content" field of the new dictionary.
@@ -1050,7 +1050,7 @@ class ConversableAgent(LLMAgent):
1050
1050
  return name
1051
1051
 
1052
1052
  def _append_oai_message(
1053
- self, message: Union[dict[str, Any], str], role, conversation_id: Agent, is_sending: bool
1053
+ self, message: dict[str, Any] | str, role, conversation_id: Agent, is_sending: bool
1054
1054
  ) -> bool:
1055
1055
  """Append a message to the ChatCompletion conversation.
1056
1056
 
@@ -1108,8 +1108,8 @@ class ConversableAgent(LLMAgent):
1108
1108
  return True
1109
1109
 
1110
1110
  def _process_message_before_send(
1111
- self, message: Union[dict[str, Any], str], recipient: Agent, silent: bool
1112
- ) -> Union[dict[str, Any], str]:
1111
+ self, message: dict[str, Any] | str, recipient: Agent, silent: bool
1112
+ ) -> dict[str, Any] | str:
1113
1113
  """Process the message before sending it to the recipient."""
1114
1114
  hook_list = self.hook_lists["process_message_before_send"]
1115
1115
  for hook in hook_list:
@@ -1120,10 +1120,10 @@ class ConversableAgent(LLMAgent):
1120
1120
 
1121
1121
  def send(
1122
1122
  self,
1123
- message: Union[dict[str, Any], str],
1123
+ message: dict[str, Any] | str,
1124
1124
  recipient: Agent,
1125
- request_reply: Optional[bool] = None,
1126
- silent: Optional[bool] = False,
1125
+ request_reply: bool | None = None,
1126
+ silent: bool | None = False,
1127
1127
  ):
1128
1128
  """Send a message to another agent.
1129
1129
 
@@ -1168,10 +1168,10 @@ class ConversableAgent(LLMAgent):
1168
1168
 
1169
1169
  async def a_send(
1170
1170
  self,
1171
- message: Union[dict[str, Any], str],
1171
+ message: dict[str, Any] | str,
1172
1172
  recipient: Agent,
1173
- request_reply: Optional[bool] = None,
1174
- silent: Optional[bool] = False,
1173
+ request_reply: bool | None = None,
1174
+ silent: bool | None = False,
1175
1175
  ):
1176
1176
  """(async) Send a message to another agent.
1177
1177
 
@@ -1214,14 +1214,14 @@ class ConversableAgent(LLMAgent):
1214
1214
  "Message can't be converted into a valid ChatCompletion message. Either content or function_call must be provided."
1215
1215
  )
1216
1216
 
1217
- def _print_received_message(self, message: Union[dict[str, Any], str], sender: Agent, skip_head: bool = False):
1217
+ def _print_received_message(self, message: dict[str, Any] | str, sender: Agent, skip_head: bool = False):
1218
1218
  message = self._message_to_dict(message)
1219
1219
  message_model = create_received_event_model(event=message, sender=sender, recipient=self)
1220
1220
  iostream = IOStream.get_default()
1221
1221
  # message_model.print(iostream.print)
1222
1222
  iostream.send(message_model)
1223
1223
 
1224
- def _process_received_message(self, message: Union[dict[str, Any], str], sender: Agent, silent: bool):
1224
+ def _process_received_message(self, message: dict[str, Any] | str, sender: Agent, silent: bool):
1225
1225
  # When the agent receives a message, the role of the message is "user". (If 'role' exists and is 'function', it will remain unchanged.)
1226
1226
  valid = self._append_oai_message(message, "user", sender, is_sending=False)
1227
1227
  if logging_enabled():
@@ -1237,10 +1237,10 @@ class ConversableAgent(LLMAgent):
1237
1237
 
1238
1238
  def receive(
1239
1239
  self,
1240
- message: Union[dict[str, Any], str],
1240
+ message: dict[str, Any] | str,
1241
1241
  sender: Agent,
1242
- request_reply: Optional[bool] = None,
1243
- silent: Optional[bool] = False,
1242
+ request_reply: bool | None = None,
1243
+ silent: bool | None = False,
1244
1244
  ):
1245
1245
  """Receive a message from another agent.
1246
1246
 
@@ -1274,10 +1274,10 @@ class ConversableAgent(LLMAgent):
1274
1274
 
1275
1275
  async def a_receive(
1276
1276
  self,
1277
- message: Union[dict[str, Any], str],
1277
+ message: dict[str, Any] | str,
1278
1278
  sender: Agent,
1279
- request_reply: Optional[bool] = None,
1280
- silent: Optional[bool] = False,
1279
+ request_reply: bool | None = None,
1280
+ silent: bool | None = False,
1281
1281
  ):
1282
1282
  """(async) Receive a message from another agent.
1283
1283
 
@@ -1343,16 +1343,35 @@ class ConversableAgent(LLMAgent):
1343
1343
 
1344
1344
  raise RuntimeError(msg)
1345
1345
 
1346
+ def _should_terminate_chat(self, recipient: "ConversableAgent", message: dict[str, Any]) -> bool:
1347
+ """
1348
+ Determines whether the chat should be terminated based on the message content
1349
+ and the recipient's termination condition.
1350
+
1351
+ Args:
1352
+ recipient (ConversableAgent): The agent to check for termination condition.
1353
+ message (dict[str, Any]): The message dictionary to evaluate for termination.
1354
+
1355
+ Returns:
1356
+ bool: True if the chat should be terminated, False otherwise.
1357
+ """
1358
+ return (
1359
+ isinstance(recipient, ConversableAgent)
1360
+ and isinstance(message.get("content"), str)
1361
+ and hasattr(recipient, "_is_termination_msg")
1362
+ and recipient._is_termination_msg(message)
1363
+ )
1364
+
1346
1365
  def initiate_chat(
1347
1366
  self,
1348
1367
  recipient: "ConversableAgent",
1349
1368
  clear_history: bool = True,
1350
- silent: Optional[bool] = False,
1351
- cache: Optional[AbstractCache] = None,
1352
- max_turns: Optional[int] = None,
1353
- summary_method: Optional[Union[str, Callable[..., Any]]] = DEFAULT_SUMMARY_METHOD,
1354
- summary_args: Optional[dict[str, Any]] = {},
1355
- message: Optional[Union[dict[str, Any], str, Callable[..., Any]]] = None,
1369
+ silent: bool | None = False,
1370
+ cache: AbstractCache | None = None,
1371
+ max_turns: int | None = None,
1372
+ summary_method: str | Callable[..., Any] | None = DEFAULT_SUMMARY_METHOD,
1373
+ summary_args: dict[str, Any] | None = {},
1374
+ message: dict[str, Any] | str | Callable[..., Any] | None = None,
1356
1375
  **kwargs: Any,
1357
1376
  ) -> ChatResult:
1358
1377
  """Initiate a chat with the recipient agent.
@@ -1457,6 +1476,7 @@ class ConversableAgent(LLMAgent):
1457
1476
  agent.client_cache = cache
1458
1477
  if isinstance(max_turns, int):
1459
1478
  self._prepare_chat(recipient, clear_history, reply_at_receive=False)
1479
+ is_termination = False
1460
1480
  for i in range(max_turns):
1461
1481
  # check recipient max consecutive auto reply limit
1462
1482
  if self._consecutive_auto_reply_counter[recipient] >= recipient._max_consecutive_auto_reply:
@@ -1467,11 +1487,13 @@ class ConversableAgent(LLMAgent):
1467
1487
  else:
1468
1488
  msg2send = self.generate_init_message(message, **kwargs)
1469
1489
  else:
1490
+ last_message = self.chat_messages[recipient][-1]
1491
+ if self._should_terminate_chat(recipient, last_message):
1492
+ break
1470
1493
  msg2send = self.generate_reply(messages=self.chat_messages[recipient], sender=recipient)
1471
1494
  if msg2send is None:
1472
1495
  break
1473
1496
  self.send(msg2send, recipient, request_reply=True, silent=silent)
1474
-
1475
1497
  else: # No breaks in the for loop, so we have reached max turns
1476
1498
  iostream.send(
1477
1499
  TerminationEvent(
@@ -1506,16 +1528,16 @@ class ConversableAgent(LLMAgent):
1506
1528
  self,
1507
1529
  recipient: Optional["ConversableAgent"] = None,
1508
1530
  clear_history: bool = True,
1509
- silent: Optional[bool] = False,
1510
- cache: Optional[AbstractCache] = None,
1511
- max_turns: Optional[int] = None,
1512
- summary_method: Optional[Union[str, Callable[..., Any]]] = DEFAULT_SUMMARY_METHOD,
1513
- summary_args: Optional[dict[str, Any]] = {},
1514
- message: Optional[Union[dict[str, Any], str, Callable[..., Any]]] = None,
1515
- executor_kwargs: Optional[dict[str, Any]] = None,
1516
- tools: Optional[Union[Tool, Iterable[Tool]]] = None,
1517
- user_input: Optional[bool] = False,
1518
- msg_to: Optional[str] = "agent",
1531
+ silent: bool | None = False,
1532
+ cache: AbstractCache | None = None,
1533
+ max_turns: int | None = None,
1534
+ summary_method: str | Callable[..., Any] | None = DEFAULT_SUMMARY_METHOD,
1535
+ summary_args: dict[str, Any] | None = {},
1536
+ message: dict[str, Any] | str | Callable[..., Any] | None = None,
1537
+ executor_kwargs: dict[str, Any] | None = None,
1538
+ tools: Tool | Iterable[Tool] | None = None,
1539
+ user_input: bool | None = False,
1540
+ msg_to: str | None = "agent",
1519
1541
  **kwargs: Any,
1520
1542
  ) -> RunResponseProtocol:
1521
1543
  iostream = ThreadIOStream()
@@ -1616,12 +1638,12 @@ class ConversableAgent(LLMAgent):
1616
1638
  self,
1617
1639
  recipient: "ConversableAgent",
1618
1640
  clear_history: bool = True,
1619
- silent: Optional[bool] = False,
1620
- cache: Optional[AbstractCache] = None,
1621
- max_turns: Optional[int] = None,
1622
- summary_method: Optional[Union[str, Callable[..., Any]]] = DEFAULT_SUMMARY_METHOD,
1623
- summary_args: Optional[dict[str, Any]] = {},
1624
- message: Optional[Union[str, Callable[..., Any]]] = None,
1641
+ silent: bool | None = False,
1642
+ cache: AbstractCache | None = None,
1643
+ max_turns: int | None = None,
1644
+ summary_method: str | Callable[..., Any] | None = DEFAULT_SUMMARY_METHOD,
1645
+ summary_args: dict[str, Any] | None = {},
1646
+ message: str | Callable[..., Any] | None = None,
1625
1647
  **kwargs: Any,
1626
1648
  ) -> ChatResult:
1627
1649
  """(async) Initiate a chat with the recipient agent.
@@ -1645,6 +1667,7 @@ class ConversableAgent(LLMAgent):
1645
1667
  agent.client_cache = cache
1646
1668
  if isinstance(max_turns, int):
1647
1669
  self._prepare_chat(recipient, clear_history, reply_at_receive=False)
1670
+ is_termination = False
1648
1671
  for _ in range(max_turns):
1649
1672
  if _ == 0:
1650
1673
  if isinstance(message, Callable):
@@ -1652,9 +1675,12 @@ class ConversableAgent(LLMAgent):
1652
1675
  else:
1653
1676
  msg2send = await self.a_generate_init_message(message, **kwargs)
1654
1677
  else:
1678
+ last_message = self.chat_messages[recipient][-1]
1679
+ if self._should_terminate_chat(recipient, last_message):
1680
+ break
1655
1681
  msg2send = await self.a_generate_reply(messages=self.chat_messages[recipient], sender=recipient)
1656
- if msg2send is None:
1657
- break
1682
+ if msg2send is None:
1683
+ break
1658
1684
  await self.a_send(msg2send, recipient, request_reply=True, silent=silent)
1659
1685
  else: # No breaks in the for loop, so we have reached max turns
1660
1686
  iostream.send(
@@ -1690,16 +1716,16 @@ class ConversableAgent(LLMAgent):
1690
1716
  self,
1691
1717
  recipient: Optional["ConversableAgent"] = None,
1692
1718
  clear_history: bool = True,
1693
- silent: Optional[bool] = False,
1694
- cache: Optional[AbstractCache] = None,
1695
- max_turns: Optional[int] = None,
1696
- summary_method: Optional[Union[str, Callable[..., Any]]] = DEFAULT_SUMMARY_METHOD,
1697
- summary_args: Optional[dict[str, Any]] = {},
1698
- message: Optional[Union[dict[str, Any], str, Callable[..., Any]]] = None,
1699
- executor_kwargs: Optional[dict[str, Any]] = None,
1700
- tools: Optional[Union[Tool, Iterable[Tool]]] = None,
1701
- user_input: Optional[bool] = False,
1702
- msg_to: Optional[str] = "agent",
1719
+ silent: bool | None = False,
1720
+ cache: AbstractCache | None = None,
1721
+ max_turns: int | None = None,
1722
+ summary_method: str | Callable[..., Any] | None = DEFAULT_SUMMARY_METHOD,
1723
+ summary_args: dict[str, Any] | None = {},
1724
+ message: dict[str, Any] | str | Callable[..., Any] | None = None,
1725
+ executor_kwargs: dict[str, Any] | None = None,
1726
+ tools: Tool | Iterable[Tool] | None = None,
1727
+ user_input: bool | None = False,
1728
+ msg_to: str | None = "agent",
1703
1729
  **kwargs: Any,
1704
1730
  ) -> AsyncRunResponseProtocol:
1705
1731
  iostream = AsyncThreadIOStream()
@@ -1796,8 +1822,8 @@ class ConversableAgent(LLMAgent):
1796
1822
  self,
1797
1823
  summary_method,
1798
1824
  summary_args,
1799
- recipient: Optional[Agent] = None,
1800
- cache: Optional[AbstractCache] = None,
1825
+ recipient: Agent | None = None,
1826
+ cache: AbstractCache | None = None,
1801
1827
  ) -> str:
1802
1828
  """Get a chat summary from an agent participating in a chat.
1803
1829
 
@@ -1839,6 +1865,8 @@ class ConversableAgent(LLMAgent):
1839
1865
  raise ValueError(
1840
1866
  "If not None, the summary_method must be a string from [`reflection_with_llm`, `last_msg`] or a callable."
1841
1867
  )
1868
+ if isinstance(summary, dict):
1869
+ summary = str(summary.get("content", ""))
1842
1870
  return summary
1843
1871
 
1844
1872
  @staticmethod
@@ -1884,9 +1912,9 @@ class ConversableAgent(LLMAgent):
1884
1912
  self,
1885
1913
  prompt,
1886
1914
  messages,
1887
- llm_agent: Optional[Agent] = None,
1888
- cache: Optional[AbstractCache] = None,
1889
- role: Union[str, None] = None,
1915
+ llm_agent: Agent | None = None,
1916
+ cache: AbstractCache | None = None,
1917
+ role: str | None = None,
1890
1918
  ) -> str:
1891
1919
  """Get a chat summary using reflection with an llm client based on the conversation history.
1892
1920
 
@@ -2083,7 +2111,7 @@ class ConversableAgent(LLMAgent):
2083
2111
 
2084
2112
  return responses
2085
2113
 
2086
- def get_chat_results(self, chat_index: Optional[int] = None) -> Union[list[ChatResult], ChatResult]:
2114
+ def get_chat_results(self, chat_index: int | None = None) -> list[ChatResult] | ChatResult:
2087
2115
  """A summary from the finished chats of particular agents."""
2088
2116
  if chat_index is not None:
2089
2117
  return self._finished_chats[chat_index]
@@ -2103,21 +2131,21 @@ class ConversableAgent(LLMAgent):
2103
2131
  else:
2104
2132
  reply_func_tuple["config"] = copy.copy(reply_func_tuple["init_config"])
2105
2133
 
2106
- def stop_reply_at_receive(self, sender: Optional[Agent] = None):
2134
+ def stop_reply_at_receive(self, sender: Agent | None = None):
2107
2135
  """Reset the reply_at_receive of the sender."""
2108
2136
  if sender is None:
2109
2137
  self.reply_at_receive.clear()
2110
2138
  else:
2111
2139
  self.reply_at_receive[sender] = False
2112
2140
 
2113
- def reset_consecutive_auto_reply_counter(self, sender: Optional[Agent] = None):
2141
+ def reset_consecutive_auto_reply_counter(self, sender: Agent | None = None):
2114
2142
  """Reset the consecutive_auto_reply_counter of the sender."""
2115
2143
  if sender is None:
2116
2144
  self._consecutive_auto_reply_counter.clear()
2117
2145
  else:
2118
2146
  self._consecutive_auto_reply_counter[sender] = 0
2119
2147
 
2120
- def clear_history(self, recipient: Optional[Agent] = None, nr_messages_to_preserve: Optional[int] = None):
2148
+ def clear_history(self, recipient: Agent | None = None, nr_messages_to_preserve: int | None = None):
2121
2149
  """Clear the chat history of the agent.
2122
2150
 
2123
2151
  Args:
@@ -2150,10 +2178,10 @@ class ConversableAgent(LLMAgent):
2150
2178
 
2151
2179
  def generate_oai_reply(
2152
2180
  self,
2153
- messages: Optional[list[dict[str, Any]]] = None,
2154
- sender: Optional[Agent] = None,
2155
- config: Optional[OpenAIWrapper] = None,
2156
- ) -> tuple[bool, Optional[Union[str, dict[str, Any]]]]:
2181
+ messages: list[dict[str, Any]] | None = None,
2182
+ sender: Agent | None = None,
2183
+ config: OpenAIWrapper | None = None,
2184
+ ) -> tuple[bool, str | dict[str, Any] | None]:
2157
2185
  """Generate a reply using autogen.oai."""
2158
2186
  client = self.client if config is None else config
2159
2187
  if client is None:
@@ -2165,7 +2193,7 @@ class ConversableAgent(LLMAgent):
2165
2193
  )
2166
2194
  return (False, None) if extracted_response is None else (True, extracted_response)
2167
2195
 
2168
- def _generate_oai_reply_from_client(self, llm_client, messages, cache) -> Optional[Union[str, dict[str, Any]]]:
2196
+ def _generate_oai_reply_from_client(self, llm_client, messages, cache) -> str | dict[str, Any] | None:
2169
2197
  # unroll tool_responses
2170
2198
  all_messages = []
2171
2199
  for message in messages:
@@ -2210,16 +2238,16 @@ class ConversableAgent(LLMAgent):
2210
2238
 
2211
2239
  async def a_generate_oai_reply(
2212
2240
  self,
2213
- messages: Optional[list[dict[str, Any]]] = None,
2214
- sender: Optional[Agent] = None,
2215
- config: Optional[Any] = None,
2216
- ) -> tuple[bool, Optional[Union[str, dict[str, Any]]]]:
2241
+ messages: list[dict[str, Any]] | None = None,
2242
+ sender: Agent | None = None,
2243
+ config: Any | None = None,
2244
+ ) -> tuple[bool, str | dict[str, Any] | None]:
2217
2245
  """Generate a reply using autogen.oai asynchronously."""
2218
2246
  iostream = IOStream.get_default()
2219
2247
 
2220
2248
  def _generate_oai_reply(
2221
2249
  self, iostream: IOStream, *args: Any, **kwargs: Any
2222
- ) -> tuple[bool, Optional[Union[str, dict[str, Any]]]]:
2250
+ ) -> tuple[bool, str | dict[str, Any] | None]:
2223
2251
  with IOStream.set_default(iostream):
2224
2252
  return self.generate_oai_reply(*args, **kwargs)
2225
2253
 
@@ -2232,9 +2260,9 @@ class ConversableAgent(LLMAgent):
2232
2260
 
2233
2261
  def _generate_code_execution_reply_using_executor(
2234
2262
  self,
2235
- messages: Optional[list[dict[str, Any]]] = None,
2236
- sender: Optional[Agent] = None,
2237
- config: Optional[Union[dict[str, Any], Literal[False]]] = None,
2263
+ messages: list[dict[str, Any]] | None = None,
2264
+ sender: Agent | None = None,
2265
+ config: dict[str, Any] | Literal[False] | None = None,
2238
2266
  ):
2239
2267
  """Generate a reply using code executor."""
2240
2268
  iostream = IOStream.get_default()
@@ -2283,9 +2311,9 @@ class ConversableAgent(LLMAgent):
2283
2311
 
2284
2312
  def generate_code_execution_reply(
2285
2313
  self,
2286
- messages: Optional[list[dict[str, Any]]] = None,
2287
- sender: Optional[Agent] = None,
2288
- config: Optional[Union[dict[str, Any], Literal[False]]] = None,
2314
+ messages: list[dict[str, Any]] | None = None,
2315
+ sender: Agent | None = None,
2316
+ config: dict[str, Any] | Literal[False] | None = None,
2289
2317
  ):
2290
2318
  """Generate a reply using code execution."""
2291
2319
  code_execution_config = config if config is not None else self._code_execution_config
@@ -2348,10 +2376,10 @@ class ConversableAgent(LLMAgent):
2348
2376
 
2349
2377
  def generate_function_call_reply(
2350
2378
  self,
2351
- messages: Optional[list[dict[str, Any]]] = None,
2352
- sender: Optional[Agent] = None,
2353
- config: Optional[Any] = None,
2354
- ) -> tuple[bool, Optional[dict[str, Any]]]:
2379
+ messages: list[dict[str, Any]] | None = None,
2380
+ sender: Agent | None = None,
2381
+ config: Any | None = None,
2382
+ ) -> tuple[bool, dict[str, Any] | None]:
2355
2383
  """Generate a reply using function call.
2356
2384
 
2357
2385
  "function_call" replaced by "tool_calls" as of [OpenAI API v1.1.0](https://github.com/openai/openai-python/releases/tag/v1.1.0)
@@ -2376,10 +2404,10 @@ class ConversableAgent(LLMAgent):
2376
2404
 
2377
2405
  async def a_generate_function_call_reply(
2378
2406
  self,
2379
- messages: Optional[list[dict[str, Any]]] = None,
2380
- sender: Optional[Agent] = None,
2381
- config: Optional[Any] = None,
2382
- ) -> tuple[bool, Optional[dict[str, Any]]]:
2407
+ messages: list[dict[str, Any]] | None = None,
2408
+ sender: Agent | None = None,
2409
+ config: Any | None = None,
2410
+ ) -> tuple[bool, dict[str, Any] | None]:
2383
2411
  """Generate a reply using async function call.
2384
2412
 
2385
2413
  "function_call" replaced by "tool_calls" as of [OpenAI API v1.1.0](https://github.com/openai/openai-python/releases/tag/v1.1.0)
@@ -2408,10 +2436,10 @@ class ConversableAgent(LLMAgent):
2408
2436
 
2409
2437
  def generate_tool_calls_reply(
2410
2438
  self,
2411
- messages: Optional[list[dict[str, Any]]] = None,
2412
- sender: Optional[Agent] = None,
2413
- config: Optional[Any] = None,
2414
- ) -> tuple[bool, Optional[dict[str, Any]]]:
2439
+ messages: list[dict[str, Any]] | None = None,
2440
+ sender: Agent | None = None,
2441
+ config: Any | None = None,
2442
+ ) -> tuple[bool, dict[str, Any] | None]:
2415
2443
  """Generate a reply using tool call."""
2416
2444
  if config is None:
2417
2445
  config = self
@@ -2466,10 +2494,10 @@ class ConversableAgent(LLMAgent):
2466
2494
 
2467
2495
  async def a_generate_tool_calls_reply(
2468
2496
  self,
2469
- messages: Optional[list[dict[str, Any]]] = None,
2470
- sender: Optional[Agent] = None,
2471
- config: Optional[Any] = None,
2472
- ) -> tuple[bool, Optional[dict[str, Any]]]:
2497
+ messages: list[dict[str, Any]] | None = None,
2498
+ sender: Agent | None = None,
2499
+ config: Any | None = None,
2500
+ ) -> tuple[bool, dict[str, Any] | None]:
2473
2501
  """Generate a reply using async function call."""
2474
2502
  if config is None:
2475
2503
  config = self
@@ -2491,10 +2519,10 @@ class ConversableAgent(LLMAgent):
2491
2519
 
2492
2520
  def check_termination_and_human_reply(
2493
2521
  self,
2494
- messages: Optional[list[dict[str, Any]]] = None,
2495
- sender: Optional[Agent] = None,
2496
- config: Optional[Any] = None,
2497
- ) -> tuple[bool, Union[str, None]]:
2522
+ messages: list[dict[str, Any]] | None = None,
2523
+ sender: Agent | None = None,
2524
+ config: Any | None = None,
2525
+ ) -> tuple[bool, str | None]:
2498
2526
  """Check if the conversation should be terminated, and if human reply is provided.
2499
2527
 
2500
2528
  This method checks for conditions that require the conversation to be terminated, such as reaching
@@ -2635,10 +2663,10 @@ class ConversableAgent(LLMAgent):
2635
2663
 
2636
2664
  async def a_check_termination_and_human_reply(
2637
2665
  self,
2638
- messages: Optional[list[dict[str, Any]]] = None,
2639
- sender: Optional[Agent] = None,
2640
- config: Optional[Any] = None,
2641
- ) -> tuple[bool, Union[str, None]]:
2666
+ messages: list[dict[str, Any]] | None = None,
2667
+ sender: Agent | None = None,
2668
+ config: Any | None = None,
2669
+ ) -> tuple[bool, str | None]:
2642
2670
  """(async) Check if the conversation should be terminated, and if human reply is provided.
2643
2671
 
2644
2672
  This method checks for conditions that require the conversation to be terminated, such as reaching
@@ -2775,10 +2803,10 @@ class ConversableAgent(LLMAgent):
2775
2803
 
2776
2804
  def generate_reply(
2777
2805
  self,
2778
- messages: Optional[list[dict[str, Any]]] = None,
2806
+ messages: list[dict[str, Any]] | None = None,
2779
2807
  sender: Optional["Agent"] = None,
2780
2808
  **kwargs: Any,
2781
- ) -> Optional[Union[str, dict[str, Any]]]:
2809
+ ) -> str | dict[str, Any] | None:
2782
2810
  """Reply based on the conversation history and the sender.
2783
2811
 
2784
2812
  Either messages or sender must be provided.
@@ -2849,10 +2877,10 @@ class ConversableAgent(LLMAgent):
2849
2877
 
2850
2878
  async def a_generate_reply(
2851
2879
  self,
2852
- messages: Optional[list[dict[str, Any]]] = None,
2880
+ messages: list[dict[str, Any]] | None = None,
2853
2881
  sender: Optional["Agent"] = None,
2854
2882
  **kwargs: Any,
2855
- ) -> Union[str, dict[str, Any], None]:
2883
+ ) -> str | dict[str, Any] | None:
2856
2884
  """(async) Reply based on the conversation history and the sender.
2857
2885
 
2858
2886
  Either messages or sender must be provided.
@@ -2916,7 +2944,7 @@ class ConversableAgent(LLMAgent):
2916
2944
  return reply
2917
2945
  return self._default_auto_reply
2918
2946
 
2919
- def _match_trigger(self, trigger: Union[None, str, type, Agent, Callable, list], sender: Optional[Agent]) -> bool:
2947
+ def _match_trigger(self, trigger: None | str | type | Agent | Callable | list, sender: Agent | None) -> bool:
2920
2948
  """Check if the sender matches the trigger.
2921
2949
 
2922
2950
  Args:
@@ -2978,28 +3006,18 @@ class ConversableAgent(LLMAgent):
2978
3006
  Returns:
2979
3007
  str: human input.
2980
3008
  """
3009
+
2981
3010
  iostream = IOStream.get_default()
3011
+ input_func = iostream.input
2982
3012
 
2983
- reply = await iostream.input(prompt)
3013
+ if is_coroutine_callable(input_func):
3014
+ reply = await input_func(prompt)
3015
+ else:
3016
+ reply = await asyncio.to_thread(input_func, prompt)
2984
3017
  self._human_input.append(reply)
2985
3018
  return reply
2986
3019
 
2987
- # def _get_human_input(
2988
- # self, iostream: IOStream, prompt: str,
2989
- # ) -> tuple[bool, Optional[Union[str, dict[str, Any]]]]:
2990
- # with IOStream.set_default(iostream):
2991
- # print("!"*100)
2992
- # print("Getting human input...")
2993
- # return self.get_human_input(prompt)
2994
-
2995
- # return await asyncio.get_event_loop().run_in_executor(
2996
- # None,
2997
- # functools.partial(
2998
- # _get_human_input, self=self, iostream=iostream, prompt=prompt,
2999
- # ),
3000
- # )
3001
-
3002
- def run_code(self, code: str, **kwargs: Any) -> tuple[int, str, Optional[str]]:
3020
+ def run_code(self, code: str, **kwargs: Any) -> tuple[int, str, str | None]:
3003
3021
  """Run the code and return the result.
3004
3022
 
3005
3023
  Override this function to modify the way to run the code.
@@ -3084,7 +3102,7 @@ class ConversableAgent(LLMAgent):
3084
3102
  return "".join(result)
3085
3103
 
3086
3104
  def execute_function(
3087
- self, func_call: dict[str, Any], call_id: Optional[str] = None, verbose: bool = False
3105
+ self, func_call: dict[str, Any], call_id: str | None = None, verbose: bool = False
3088
3106
  ) -> tuple[bool, dict[str, Any]]:
3089
3107
  """Execute a function call and return the result.
3090
3108
 
@@ -3153,7 +3171,7 @@ class ConversableAgent(LLMAgent):
3153
3171
  }
3154
3172
 
3155
3173
  async def a_execute_function(
3156
- self, func_call: dict[str, Any], call_id: Optional[str] = None, verbose: bool = False
3174
+ self, func_call: dict[str, Any], call_id: str | None = None, verbose: bool = False
3157
3175
  ) -> tuple[bool, dict[str, Any]]:
3158
3176
  """Execute an async function call and return the result.
3159
3177
 
@@ -3224,9 +3242,7 @@ class ConversableAgent(LLMAgent):
3224
3242
  "content": content,
3225
3243
  }
3226
3244
 
3227
- def generate_init_message(
3228
- self, message: Optional[Union[dict[str, Any], str]], **kwargs: Any
3229
- ) -> Union[str, dict[str, Any]]:
3245
+ def generate_init_message(self, message: dict[str, Any] | str | None, **kwargs: Any) -> str | dict[str, Any]:
3230
3246
  """Generate the initial message for the agent.
3231
3247
  If message is None, input() will be called to get the initial message.
3232
3248
 
@@ -3245,7 +3261,7 @@ class ConversableAgent(LLMAgent):
3245
3261
 
3246
3262
  return self._handle_carryover(message, kwargs)
3247
3263
 
3248
- def _handle_carryover(self, message: Union[str, dict[str, Any]], kwargs: dict) -> Union[str, dict[str, Any]]:
3264
+ def _handle_carryover(self, message: str | dict[str, Any], kwargs: dict) -> str | dict[str, Any]:
3249
3265
  if not kwargs.get("carryover"):
3250
3266
  return message
3251
3267
 
@@ -3291,8 +3307,8 @@ class ConversableAgent(LLMAgent):
3291
3307
  return [{"type": "text", "text": self._process_carryover("", kwargs)}] + content
3292
3308
 
3293
3309
  async def a_generate_init_message(
3294
- self, message: Optional[Union[dict[str, Any], str]], **kwargs: Any
3295
- ) -> Union[str, dict[str, Any]]:
3310
+ self, message: dict[str, Any] | str | None, **kwargs: Any
3311
+ ) -> str | dict[str, Any]:
3296
3312
  """Generate the initial message for the agent.
3297
3313
  If message is None, input() will be called to get the initial message.
3298
3314
 
@@ -3327,7 +3343,7 @@ class ConversableAgent(LLMAgent):
3327
3343
  except ValueError:
3328
3344
  raise ValueError(f"Tool {tool} not found in collection")
3329
3345
 
3330
- def register_function(self, function_map: dict[str, Union[Callable[..., Any]]], silent_override: bool = False):
3346
+ def register_function(self, function_map: dict[str, Callable[..., Any]], silent_override: bool = False):
3331
3347
  """Register functions to the agent.
3332
3348
 
3333
3349
  Args:
@@ -3344,7 +3360,7 @@ class ConversableAgent(LLMAgent):
3344
3360
  self._function_map = {k: v for k, v in self._function_map.items() if v is not None}
3345
3361
 
3346
3362
  def update_function_signature(
3347
- self, func_sig: Union[str, dict[str, Any]], is_remove: None, silent_override: bool = False
3363
+ self, func_sig: str | dict[str, Any], is_remove: bool = False, silent_override: bool = False
3348
3364
  ):
3349
3365
  """Update a function_signature in the LLM configuration for function_call.
3350
3366
 
@@ -3396,9 +3412,7 @@ class ConversableAgent(LLMAgent):
3396
3412
 
3397
3413
  self.client = OpenAIWrapper(**self.llm_config)
3398
3414
 
3399
- def update_tool_signature(
3400
- self, tool_sig: Union[str, dict[str, Any]], is_remove: bool, silent_override: bool = False
3401
- ):
3415
+ def update_tool_signature(self, tool_sig: str | dict[str, Any], is_remove: bool, silent_override: bool = False):
3402
3416
  """Update a tool_signature in the LLM configuration for tool_call.
3403
3417
 
3404
3418
  Args:
@@ -3456,7 +3470,7 @@ class ConversableAgent(LLMAgent):
3456
3470
 
3457
3471
  self.client = OpenAIWrapper(**self.llm_config)
3458
3472
 
3459
- def can_execute_function(self, name: Union[list[str], str]) -> bool:
3473
+ def can_execute_function(self, name: list[str] | str) -> bool:
3460
3474
  """Whether the agent can execute the function."""
3461
3475
  names = name if isinstance(name, list) else [name]
3462
3476
  return all([n in self._function_map for n in names])
@@ -3505,9 +3519,9 @@ class ConversableAgent(LLMAgent):
3505
3519
 
3506
3520
  @staticmethod
3507
3521
  def _create_tool_if_needed(
3508
- func_or_tool: Union[F, Tool],
3509
- name: Optional[str],
3510
- description: Optional[str],
3522
+ func_or_tool: F | Tool,
3523
+ name: str | None,
3524
+ description: str | None,
3511
3525
  ) -> Tool:
3512
3526
  if isinstance(func_or_tool, Tool):
3513
3527
  tool: Tool = func_or_tool
@@ -3524,11 +3538,11 @@ class ConversableAgent(LLMAgent):
3524
3538
  def register_for_llm(
3525
3539
  self,
3526
3540
  *,
3527
- name: Optional[str] = None,
3528
- description: Optional[str] = None,
3541
+ name: str | None = None,
3542
+ description: str | None = None,
3529
3543
  api_style: Literal["function", "tool"] = "tool",
3530
3544
  silent_override: bool = False,
3531
- ) -> Callable[[Union[F, Tool]], Tool]:
3545
+ ) -> Callable[[F | Tool], Tool]:
3532
3546
  """Decorator factory for registering a function to be used by an agent.
3533
3547
 
3534
3548
  It's return value is used to decorate a function to be registered to the agent. The function uses type hints to
@@ -3569,9 +3583,7 @@ class ConversableAgent(LLMAgent):
3569
3583
 
3570
3584
  """
3571
3585
 
3572
- def _decorator(
3573
- func_or_tool: Union[F, Tool], name: Optional[str] = name, description: Optional[str] = description
3574
- ) -> Tool:
3586
+ def _decorator(func_or_tool: F | Tool, name: str | None = name, description: str | None = description) -> Tool:
3575
3587
  """Decorator for registering a function to be used by an agent.
3576
3588
 
3577
3589
  Args:
@@ -3600,8 +3612,7 @@ class ConversableAgent(LLMAgent):
3600
3612
  def _register_for_llm(
3601
3613
  self, tool: Tool, api_style: Literal["tool", "function"], is_remove: bool = False, silent_override: bool = False
3602
3614
  ) -> None:
3603
- """
3604
- Register a tool for LLM.
3615
+ """Register a tool for LLM.
3605
3616
 
3606
3617
  Args:
3607
3618
  tool: the tool to be registered.
@@ -3672,12 +3683,12 @@ class ConversableAgent(LLMAgent):
3672
3683
 
3673
3684
  def register_for_execution(
3674
3685
  self,
3675
- name: Optional[str] = None,
3676
- description: Optional[str] = None,
3686
+ name: str | None = None,
3687
+ description: str | None = None,
3677
3688
  *,
3678
3689
  serialize: bool = True,
3679
3690
  silent_override: bool = False,
3680
- ) -> Callable[[Union[Tool, F]], Tool]:
3691
+ ) -> Callable[[Tool | F], Tool]:
3681
3692
  """Decorator factory for registering a function to be executed by an agent.
3682
3693
 
3683
3694
  It's return value is used to decorate a function to be registered to the agent.
@@ -3702,9 +3713,7 @@ class ConversableAgent(LLMAgent):
3702
3713
 
3703
3714
  """
3704
3715
 
3705
- def _decorator(
3706
- func_or_tool: Union[Tool, F], name: Optional[str] = name, description: Optional[str] = description
3707
- ) -> Tool:
3716
+ def _decorator(func_or_tool: Tool | F, name: str | None = name, description: str | None = description) -> Tool:
3708
3717
  """Decorator for registering a function to be used by an agent.
3709
3718
 
3710
3719
  Args:
@@ -3716,7 +3725,6 @@ class ConversableAgent(LLMAgent):
3716
3725
  The tool to be registered.
3717
3726
 
3718
3727
  """
3719
-
3720
3728
  tool = self._create_tool_if_needed(func_or_tool, name, description)
3721
3729
  chat_context = ChatContext(self)
3722
3730
  chat_context_params = {param: chat_context for param in tool._chat_context_param_names}
@@ -3817,7 +3825,7 @@ class ConversableAgent(LLMAgent):
3817
3825
  messages[-1]["content"] = processed_user_content
3818
3826
  return messages
3819
3827
 
3820
- def print_usage_summary(self, mode: Union[str, list[str]] = ["actual", "total"]) -> None:
3828
+ def print_usage_summary(self, mode: str | list[str] = ["actual", "total"]) -> None:
3821
3829
  """Print the usage summary."""
3822
3830
  iostream = IOStream.get_default()
3823
3831
  if self.client is None:
@@ -3828,14 +3836,14 @@ class ConversableAgent(LLMAgent):
3828
3836
  if self.client is not None:
3829
3837
  self.client.print_usage_summary(mode)
3830
3838
 
3831
- def get_actual_usage(self) -> Union[None, dict[str, int]]:
3839
+ def get_actual_usage(self) -> None | dict[str, int]:
3832
3840
  """Get the actual usage summary."""
3833
3841
  if self.client is None:
3834
3842
  return None
3835
3843
  else:
3836
3844
  return self.client.actual_usage_summary
3837
3845
 
3838
- def get_total_usage(self) -> Union[None, dict[str, int]]:
3846
+ def get_total_usage(self) -> None | dict[str, int]:
3839
3847
  """Get the total usage summary."""
3840
3848
  if self.client is None:
3841
3849
  return None
@@ -3845,8 +3853,8 @@ class ConversableAgent(LLMAgent):
3845
3853
  @contextmanager
3846
3854
  def _create_or_get_executor(
3847
3855
  self,
3848
- executor_kwargs: Optional[dict[str, Any]] = None,
3849
- tools: Optional[Union[Tool, Iterable[Tool]]] = None,
3856
+ executor_kwargs: dict[str, Any] | None = None,
3857
+ tools: Tool | Iterable[Tool] | None = None,
3850
3858
  agent_name: str = "executor",
3851
3859
  agent_human_input_mode: str = "NEVER",
3852
3860
  ) -> Generator["ConversableAgent", None, None]:
@@ -3902,13 +3910,13 @@ class ConversableAgent(LLMAgent):
3902
3910
  self,
3903
3911
  message: str,
3904
3912
  *,
3905
- tools: Optional[Union[Tool, Iterable[Tool]]] = None,
3906
- executor_kwargs: Optional[dict[str, Any]] = None,
3907
- max_turns: Optional[int] = None,
3913
+ tools: Tool | Iterable[Tool] | None = None,
3914
+ executor_kwargs: dict[str, Any] | None = None,
3915
+ max_turns: int | None = None,
3908
3916
  msg_to: Literal["agent", "user"] = "agent",
3909
3917
  clear_history: bool = False,
3910
3918
  user_input: bool = True,
3911
- summary_method: Optional[Union[str, Callable[..., Any]]] = DEFAULT_SUMMARY_METHOD,
3919
+ summary_method: str | Callable[..., Any] | None = DEFAULT_SUMMARY_METHOD,
3912
3920
  ) -> ChatResult:
3913
3921
  """Run a chat with the agent using the given message.
3914
3922
 
@@ -3953,13 +3961,13 @@ class ConversableAgent(LLMAgent):
3953
3961
  self,
3954
3962
  message: str,
3955
3963
  *,
3956
- tools: Optional[Union[Tool, Iterable[Tool]]] = None,
3957
- executor_kwargs: Optional[dict[str, Any]] = None,
3958
- max_turns: Optional[int] = None,
3964
+ tools: Tool | Iterable[Tool] | None = None,
3965
+ executor_kwargs: dict[str, Any] | None = None,
3966
+ max_turns: int | None = None,
3959
3967
  msg_to: Literal["agent", "user"] = "agent",
3960
3968
  clear_history: bool = False,
3961
3969
  user_input: bool = True,
3962
- summary_method: Optional[Union[str, Callable[..., Any]]] = DEFAULT_SUMMARY_METHOD,
3970
+ summary_method: str | Callable[..., Any] | None = DEFAULT_SUMMARY_METHOD,
3963
3971
  ) -> ChatResult:
3964
3972
  """Run a chat asynchronously with the agent using the given message.
3965
3973
 
@@ -4001,8 +4009,7 @@ class ConversableAgent(LLMAgent):
4001
4009
  )
4002
4010
 
4003
4011
  def register_handoff(self, condition: Union["OnContextCondition", "OnCondition"]) -> None:
4004
- """
4005
- Register a single handoff condition (OnContextCondition or OnCondition).
4012
+ """Register a single handoff condition (OnContextCondition or OnCondition).
4006
4013
 
4007
4014
  Args:
4008
4015
  condition: The condition to add (OnContextCondition, OnCondition)
@@ -4010,8 +4017,7 @@ class ConversableAgent(LLMAgent):
4010
4017
  self.handoffs.add(condition)
4011
4018
 
4012
4019
  def register_handoffs(self, conditions: list[Union["OnContextCondition", "OnCondition"]]) -> None:
4013
- """
4014
- Register multiple handoff conditions (OnContextCondition or OnCondition).
4020
+ """Register multiple handoff conditions (OnContextCondition or OnCondition).
4015
4021
 
4016
4022
  Args:
4017
4023
  conditions: List of conditions to add
@@ -4019,8 +4025,7 @@ class ConversableAgent(LLMAgent):
4019
4025
  self.handoffs.add_many(conditions)
4020
4026
 
4021
4027
  def register_input_guardrail(self, guardrail: "Guardrail") -> None:
4022
- """
4023
- Register a guardrail to be used for input validation.
4028
+ """Register a guardrail to be used for input validation.
4024
4029
 
4025
4030
  Args:
4026
4031
  guardrail: The guardrail to register.
@@ -4028,8 +4033,7 @@ class ConversableAgent(LLMAgent):
4028
4033
  self.input_guardrails.append(guardrail)
4029
4034
 
4030
4035
  def register_input_guardrails(self, guardrails: list["Guardrail"]) -> None:
4031
- """
4032
- Register multiple guardrails to be used for input validation.
4036
+ """Register multiple guardrails to be used for input validation.
4033
4037
 
4034
4038
  Args:
4035
4039
  guardrails: List of guardrails to register.
@@ -4037,8 +4041,7 @@ class ConversableAgent(LLMAgent):
4037
4041
  self.input_guardrails.extend(guardrails)
4038
4042
 
4039
4043
  def register_output_guardrail(self, guardrail: "Guardrail") -> None:
4040
- """
4041
- Register a guardrail to be used for output validation.
4044
+ """Register a guardrail to be used for output validation.
4042
4045
 
4043
4046
  Args:
4044
4047
  guardrail: The guardrail to register.
@@ -4046,8 +4049,7 @@ class ConversableAgent(LLMAgent):
4046
4049
  self.output_guardrails.append(guardrail)
4047
4050
 
4048
4051
  def register_output_guardrails(self, guardrails: list["Guardrail"]) -> None:
4049
- """
4050
- Register multiple guardrails to be used for output validation.
4052
+ """Register multiple guardrails to be used for output validation.
4051
4053
 
4052
4054
  Args:
4053
4055
  guardrails: List of guardrails to register.
@@ -4061,7 +4063,7 @@ def register_function(
4061
4063
  *,
4062
4064
  caller: ConversableAgent,
4063
4065
  executor: ConversableAgent,
4064
- name: Optional[str] = None,
4066
+ name: str | None = None,
4065
4067
  description: str,
4066
4068
  ) -> None:
4067
4069
  """Register a function to be proposed by an agent and executed for an executor.