ag2 0.9.9__py3-none-any.whl → 0.10.0__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 (113) hide show
  1. {ag2-0.9.9.dist-info → ag2-0.10.0.dist-info}/METADATA +243 -214
  2. {ag2-0.9.9.dist-info → ag2-0.10.0.dist-info}/RECORD +113 -87
  3. autogen/_website/generate_mkdocs.py +3 -3
  4. autogen/_website/notebook_processor.py +1 -1
  5. autogen/_website/utils.py +1 -1
  6. autogen/a2a/__init__.py +36 -0
  7. autogen/a2a/agent_executor.py +105 -0
  8. autogen/a2a/client.py +280 -0
  9. autogen/a2a/errors.py +18 -0
  10. autogen/a2a/httpx_client_factory.py +79 -0
  11. autogen/a2a/server.py +221 -0
  12. autogen/a2a/utils.py +165 -0
  13. autogen/agentchat/__init__.py +3 -0
  14. autogen/agentchat/agent.py +0 -2
  15. autogen/agentchat/assistant_agent.py +15 -15
  16. autogen/agentchat/chat.py +57 -41
  17. autogen/agentchat/contrib/agent_eval/criterion.py +1 -1
  18. autogen/agentchat/contrib/capabilities/text_compressors.py +5 -5
  19. autogen/agentchat/contrib/capabilities/tools_capability.py +1 -1
  20. autogen/agentchat/contrib/capabilities/transforms.py +1 -1
  21. autogen/agentchat/contrib/captainagent/agent_builder.py +1 -1
  22. autogen/agentchat/contrib/captainagent/captainagent.py +20 -19
  23. autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +2 -5
  24. autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +5 -5
  25. autogen/agentchat/contrib/graph_rag/neo4j_graph_query_engine.py +18 -17
  26. autogen/agentchat/contrib/llava_agent.py +1 -13
  27. autogen/agentchat/contrib/rag/mongodb_query_engine.py +2 -2
  28. autogen/agentchat/contrib/rag/query_engine.py +11 -11
  29. autogen/agentchat/contrib/retrieve_assistant_agent.py +3 -0
  30. autogen/agentchat/contrib/swarm_agent.py +3 -2
  31. autogen/agentchat/contrib/vectordb/couchbase.py +1 -1
  32. autogen/agentchat/contrib/vectordb/mongodb.py +1 -1
  33. autogen/agentchat/contrib/web_surfer.py +1 -1
  34. autogen/agentchat/conversable_agent.py +359 -150
  35. autogen/agentchat/group/context_expression.py +21 -21
  36. autogen/agentchat/group/group_tool_executor.py +46 -15
  37. autogen/agentchat/group/guardrails.py +41 -33
  38. autogen/agentchat/group/handoffs.py +11 -11
  39. autogen/agentchat/group/multi_agent_chat.py +56 -2
  40. autogen/agentchat/group/on_condition.py +11 -11
  41. autogen/agentchat/group/safeguards/__init__.py +21 -0
  42. autogen/agentchat/group/safeguards/api.py +241 -0
  43. autogen/agentchat/group/safeguards/enforcer.py +1158 -0
  44. autogen/agentchat/group/safeguards/events.py +119 -0
  45. autogen/agentchat/group/safeguards/validator.py +435 -0
  46. autogen/agentchat/groupchat.py +102 -49
  47. autogen/agentchat/realtime/experimental/clients/realtime_client.py +2 -2
  48. autogen/agentchat/realtime/experimental/function_observer.py +2 -3
  49. autogen/agentchat/realtime/experimental/realtime_agent.py +2 -3
  50. autogen/agentchat/realtime/experimental/realtime_swarm.py +22 -13
  51. autogen/agentchat/user_proxy_agent.py +55 -53
  52. autogen/agents/experimental/document_agent/document_agent.py +1 -10
  53. autogen/agents/experimental/document_agent/parser_utils.py +5 -1
  54. autogen/browser_utils.py +4 -4
  55. autogen/cache/abstract_cache_base.py +2 -6
  56. autogen/cache/disk_cache.py +1 -6
  57. autogen/cache/in_memory_cache.py +2 -6
  58. autogen/cache/redis_cache.py +1 -5
  59. autogen/coding/__init__.py +10 -2
  60. autogen/coding/base.py +2 -1
  61. autogen/coding/docker_commandline_code_executor.py +1 -6
  62. autogen/coding/factory.py +9 -0
  63. autogen/coding/jupyter/docker_jupyter_server.py +1 -7
  64. autogen/coding/jupyter/jupyter_client.py +2 -9
  65. autogen/coding/jupyter/jupyter_code_executor.py +2 -7
  66. autogen/coding/jupyter/local_jupyter_server.py +2 -6
  67. autogen/coding/local_commandline_code_executor.py +0 -65
  68. autogen/coding/yepcode_code_executor.py +197 -0
  69. autogen/environments/docker_python_environment.py +3 -3
  70. autogen/environments/system_python_environment.py +5 -5
  71. autogen/environments/venv_python_environment.py +5 -5
  72. autogen/events/agent_events.py +1 -1
  73. autogen/events/client_events.py +1 -1
  74. autogen/fast_depends/utils.py +10 -0
  75. autogen/graph_utils.py +5 -7
  76. autogen/import_utils.py +3 -1
  77. autogen/interop/pydantic_ai/pydantic_ai.py +8 -5
  78. autogen/io/processors/console_event_processor.py +8 -3
  79. autogen/llm_config/client.py +3 -2
  80. autogen/llm_config/config.py +168 -91
  81. autogen/llm_config/entry.py +38 -26
  82. autogen/llm_config/types.py +35 -0
  83. autogen/llm_config/utils.py +223 -0
  84. autogen/mcp/mcp_proxy/operation_grouping.py +48 -39
  85. autogen/messages/agent_messages.py +1 -1
  86. autogen/messages/client_messages.py +1 -1
  87. autogen/oai/__init__.py +8 -1
  88. autogen/oai/bedrock.py +0 -13
  89. autogen/oai/client.py +25 -11
  90. autogen/oai/client_utils.py +31 -1
  91. autogen/oai/cohere.py +4 -14
  92. autogen/oai/gemini.py +4 -6
  93. autogen/oai/gemini_types.py +1 -0
  94. autogen/oai/openai_utils.py +44 -115
  95. autogen/remote/__init__.py +18 -0
  96. autogen/remote/agent.py +199 -0
  97. autogen/remote/agent_service.py +142 -0
  98. autogen/remote/errors.py +17 -0
  99. autogen/remote/httpx_client_factory.py +131 -0
  100. autogen/remote/protocol.py +37 -0
  101. autogen/remote/retry.py +102 -0
  102. autogen/remote/runtime.py +96 -0
  103. autogen/testing/__init__.py +12 -0
  104. autogen/testing/messages.py +45 -0
  105. autogen/testing/test_agent.py +111 -0
  106. autogen/tools/dependency_injection.py +4 -8
  107. autogen/tools/experimental/reliable/reliable.py +3 -2
  108. autogen/tools/experimental/web_search_preview/web_search_preview.py +1 -1
  109. autogen/tools/function_utils.py +2 -1
  110. autogen/version.py +1 -1
  111. {ag2-0.9.9.dist-info → ag2-0.10.0.dist-info}/WHEEL +0 -0
  112. {ag2-0.9.9.dist-info → ag2-0.10.0.dist-info}/licenses/LICENSE +0 -0
  113. {ag2-0.9.9.dist-info → ag2-0.10.0.dist-info}/licenses/NOTICE.md +0 -0
@@ -48,6 +48,7 @@ SELECT_SPEAKER_PROMPT_TEMPLATE = (
48
48
  @export_module("autogen")
49
49
  class GroupChat:
50
50
  """(In preview) A group chat class that contains the following data fields:
51
+
51
52
  - agents: a list of participating agents.
52
53
  - messages: a list of messages in the group chat.
53
54
  - max_round: the maximum number of rounds.
@@ -103,9 +104,9 @@ class GroupChat:
103
104
  Default is 2.
104
105
  - select_speaker_transform_messages: (optional) the message transformations to apply to the nested select speaker agent-to-agent chat messages.
105
106
  Takes a TransformMessages object, defaults to None and is only utilised when the speaker selection method is "auto".
106
- - select_speaker_auto_verbose: whether to output the select speaker responses and selections
107
+ - select_speaker_auto_verbose: whether to output the select speaker responses and selections.
107
108
  If set to True, the outputs from the two agents in the nested select speaker chat will be output, along with
108
- whether the responses were successful, or not, in selecting an agent
109
+ whether the responses were successful, or not, in selecting an agent.
109
110
  Applies only to "auto" speaker selection method.
110
111
  - allow_repeat_speaker: whether to allow the same speaker to speak consecutively.
111
112
  Default is True, in which case all speakers are allowed to speak consecutively.
@@ -174,6 +175,7 @@ class GroupChat:
174
175
  )
175
176
 
176
177
  allowed_speaker_transitions_dict: dict[str, list[Agent]] = field(init=False)
178
+ _inter_agent_guardrails: list = field(default_factory=list, init=False)
177
179
 
178
180
  def __post_init__(self):
179
181
  # Post init steers clears of the automatically generated __init__ method from dataclass
@@ -1000,7 +1002,7 @@ class GroupChat:
1000
1002
  message_content = message_content["content"]
1001
1003
  message_content = content_str(message_content)
1002
1004
 
1003
- mentions = dict()
1005
+ mentions = {}
1004
1006
  for agent in agents:
1005
1007
  # Finds agent mentions, taking word boundaries into account,
1006
1008
  # accommodates escaping underscores and underscores as spaces
@@ -1019,9 +1021,7 @@ class GroupChat:
1019
1021
  return mentions
1020
1022
 
1021
1023
  def _run_input_guardrails(
1022
- self,
1023
- agent: "ConversableAgent",
1024
- messages: list[dict[str, Any]] | None = None,
1024
+ self, agent: "ConversableAgent", messages: list[dict[str, Any]] | None = None
1025
1025
  ) -> str | None:
1026
1026
  """Run input guardrails for an agent before the reply is generated.
1027
1027
 
@@ -1029,27 +1029,45 @@ class GroupChat:
1029
1029
  agent (ConversableAgent): The agent whose input guardrails to run.
1030
1030
  messages (Optional[list[dict[str, Any]]]): The messages to check against the guardrails.
1031
1031
  """
1032
- for guardrail in agent.input_guardrails:
1033
- guardrail_result = guardrail.check(context=messages)
1034
-
1035
- if guardrail_result.activated:
1036
- guardrail.target.activate_target(self)
1037
- return f"{guardrail.activation_message}\nJustification: {guardrail_result.justification}"
1032
+ if guardrail_result := agent.run_input_guardrails(messages):
1033
+ guardrail_result.guardrail.target.activate_target(self)
1034
+ return guardrail_result.reply
1038
1035
  return None
1039
1036
 
1040
- def _run_output_guardrails(self, agent: "ConversableAgent", reply: str) -> None:
1037
+ def _run_output_guardrails(self, agent: "ConversableAgent", reply: str | dict[str, Any]) -> str | None:
1041
1038
  """Run output guardrails for an agent after the reply is generated.
1042
1039
 
1043
1040
  Args:
1044
1041
  agent (ConversableAgent): The agent whose output guardrails to run.
1045
- reply (str): The reply generated by the agent.
1042
+ reply (str | dict[str, Any]): The reply generated by the agent.
1046
1043
  """
1047
- for guardrail in agent.output_guardrails:
1048
- guardrail_result = guardrail.check(context=reply)
1044
+ if guardrail_result := agent.run_output_guardrails(reply):
1045
+ guardrail_result.guardrail.target.activate_target(self)
1046
+ return guardrail_result.reply
1047
+ return None
1048
+
1049
+ def _run_inter_agent_guardrails(
1050
+ self,
1051
+ *,
1052
+ src_agent_name: str,
1053
+ dst_agent_name: str,
1054
+ message_content: str,
1055
+ ) -> str | None:
1056
+ """Run policy-driven inter-agent guardrails, if any are configured.
1049
1057
 
1050
- if guardrail_result.activated:
1051
- guardrail.target.activate_target(self)
1052
- return f"{guardrail.activation_message}\nJustification: {guardrail_result.justification}"
1058
+ Returns optional replacement content when a guardrail triggers.
1059
+ """
1060
+ guardrails = getattr(self, "_inter_agent_guardrails", None)
1061
+ if not guardrails:
1062
+ return None
1063
+ for gr in guardrails:
1064
+ reply = gr.check_and_act(
1065
+ src_agent_name=src_agent_name,
1066
+ dst_agent_name=dst_agent_name,
1067
+ message_content=message_content,
1068
+ )
1069
+ if reply is not None:
1070
+ return reply
1053
1071
  return None
1054
1072
 
1055
1073
 
@@ -1208,7 +1226,21 @@ class GroupChatManager(ConversableAgent):
1208
1226
  # broadcast the message to all agents except the speaker
1209
1227
  for agent in groupchat.agents:
1210
1228
  if agent != speaker:
1211
- self.send(message, agent, request_reply=False, silent=True)
1229
+ inter_reply = groupchat._run_inter_agent_guardrails(
1230
+ src_agent_name=speaker.name,
1231
+ dst_agent_name=agent.name,
1232
+ message_content=message,
1233
+ )
1234
+ if inter_reply is not None:
1235
+ replacement = (
1236
+ {"content": inter_reply, "name": speaker.name}
1237
+ if not isinstance(inter_reply, dict)
1238
+ else inter_reply
1239
+ )
1240
+ self.send(replacement, agent, request_reply=False, silent=True)
1241
+ else:
1242
+ self.send(message, agent, request_reply=False, silent=True)
1243
+
1212
1244
  if self._is_termination_msg(message):
1213
1245
  # The conversation is over
1214
1246
  termination_reason = f"Termination message condition on the GroupChatManager '{self.name}' met"
@@ -1263,13 +1295,16 @@ class GroupChatManager(ConversableAgent):
1263
1295
  reply = guardrails_reply
1264
1296
 
1265
1297
  # check for "clear history" phrase in reply and activate clear history function if found
1266
- if (
1267
- groupchat.enable_clear_history
1268
- and isinstance(reply, dict)
1269
- and reply["content"]
1270
- and "CLEAR HISTORY" in reply["content"].upper()
1271
- ):
1272
- reply["content"] = self.clear_agents_history(reply, groupchat)
1298
+ if groupchat.enable_clear_history and isinstance(reply, dict) and reply.get("content"):
1299
+ raw_content = reply.get("content")
1300
+ normalized_content = (
1301
+ content_str(raw_content)
1302
+ if isinstance(raw_content, (str, list)) or raw_content is None
1303
+ else str(raw_content)
1304
+ )
1305
+ if "CLEAR HISTORY" in normalized_content.upper():
1306
+ reply["content"] = normalized_content
1307
+ reply["content"] = self.clear_agents_history(reply, groupchat)
1273
1308
 
1274
1309
  # The speaker sends the message without requesting a reply
1275
1310
  speaker.send(reply, self, request_reply=False, silent=silent)
@@ -1380,13 +1415,16 @@ class GroupChatManager(ConversableAgent):
1380
1415
  reply = guardrails_reply
1381
1416
 
1382
1417
  # check for "clear history" phrase in reply and activate clear history function if found
1383
- if (
1384
- groupchat.enable_clear_history
1385
- and isinstance(reply, dict)
1386
- and reply["content"]
1387
- and "CLEAR HISTORY" in reply["content"].upper()
1388
- ):
1389
- reply["content"] = self.clear_agents_history(reply, groupchat)
1418
+ if groupchat.enable_clear_history and isinstance(reply, dict) and reply.get("content"):
1419
+ raw_content = reply.get("content")
1420
+ normalized_content = (
1421
+ content_str(raw_content)
1422
+ if isinstance(raw_content, (str, list)) or raw_content is None
1423
+ else str(raw_content)
1424
+ )
1425
+ if "CLEAR HISTORY" in normalized_content.upper():
1426
+ reply["content"] = normalized_content
1427
+ reply["content"] = self.clear_agents_history(reply, groupchat)
1390
1428
 
1391
1429
  # The speaker sends the message without requesting a reply
1392
1430
  await speaker.a_send(reply, self, request_reply=False, silent=silent)
@@ -1661,7 +1699,13 @@ class GroupChatManager(ConversableAgent):
1661
1699
  _remove_termination_string = remove_termination_string
1662
1700
 
1663
1701
  if _remove_termination_string and messages[-1].get("content"):
1664
- messages[-1]["content"] = _remove_termination_string(messages[-1]["content"])
1702
+ content_value = messages[-1]["content"]
1703
+ if isinstance(content_value, str):
1704
+ messages[-1]["content"] = _remove_termination_string(content_value)
1705
+ elif isinstance(content_value, list):
1706
+ messages[-1]["content"] = _remove_termination_string(content_str(content_value))
1707
+ else:
1708
+ messages[-1]["content"] = _remove_termination_string(str(content_value))
1665
1709
 
1666
1710
  # Check if the last message meets termination (if it has one)
1667
1711
  if self._is_termination_msg and self._is_termination_msg(last_message):
@@ -1707,23 +1751,32 @@ class GroupChatManager(ConversableAgent):
1707
1751
  agent._raise_exception_on_async_reply_functions()
1708
1752
 
1709
1753
  def clear_agents_history(self, reply: dict[str, Any], groupchat: GroupChat) -> str:
1710
- """Clears history of messages for all agents or selected one. Can preserve selected number of last messages.
1711
- That function is called when user manually provide "clear history" phrase in his reply.
1712
- When "clear history" is provided, the history of messages for all agents is cleared.
1713
- When "clear history `<agent_name>`" is provided, the history of messages for selected agent is cleared.
1714
- When "clear history `<nr_of_messages_to_preserve>`" is provided, the history of messages for all agents is cleared
1715
- except last `<nr_of_messages_to_preserve>` messages.
1716
- When "clear history `<agent_name>` `<nr_of_messages_to_preserve>`" is provided, the history of messages for selected
1717
- agent is cleared except last `<nr_of_messages_to_preserve>` messages.
1718
- Phrase "clear history" and optional arguments are cut out from the reply before it passed to the chat.
1719
-
1720
- Args:
1721
- reply (dict): reply message dict to analyze.
1722
- groupchat (GroupChat): GroupChat object.
1754
+ """Clears history of messages for all agents or a selected one. Can preserve a selected number of last messages.\n
1755
+ \n
1756
+ This function is called when the user manually provides the "clear history" phrase in their reply.\n
1757
+ When "clear history" is provided, the history of messages for all agents is cleared.\n
1758
+ When "clear history `<agent_name>`" is provided, the history of messages for the selected agent is cleared.\n
1759
+ When "clear history `<nr_of_messages_to_preserve>`" is provided, the history of messages for all agents is cleared\n
1760
+ except for the last `<nr_of_messages_to_preserve>` messages.\n
1761
+ When "clear history `<agent_name>` `<nr_of_messages_to_preserve>`" is provided, the history of messages for the selected\n
1762
+ agent is cleared except for the last `<nr_of_messages_to_preserve>` messages.\n
1763
+ The phrase "clear history" and optional arguments are cut out from the reply before it is passed to the chat.\n
1764
+ \n
1765
+ Args:\n
1766
+ reply (dict): reply message dict to analyze.\n
1767
+ groupchat (GroupChat): GroupChat object.\n
1723
1768
  """
1724
1769
  iostream = IOStream.get_default()
1725
1770
 
1726
- reply_content = reply["content"]
1771
+ raw_reply_content = reply.get("content")
1772
+ if isinstance(raw_reply_content, str):
1773
+ reply_content = raw_reply_content
1774
+ elif isinstance(raw_reply_content, (list, type(None))):
1775
+ reply_content = content_str(raw_reply_content)
1776
+ reply["content"] = reply_content
1777
+ else:
1778
+ reply_content = str(raw_reply_content)
1779
+ reply["content"] = reply_content
1727
1780
  # Split the reply into words
1728
1781
  words = reply_content.split()
1729
1782
  # Find the position of "clear" to determine where to start processing
@@ -8,7 +8,7 @@ from contextlib import AbstractAsyncContextManager
8
8
  from logging import Logger
9
9
  from typing import Any, Literal, Protocol, TypeVar, runtime_checkable
10
10
 
11
- from asyncer import create_task_group
11
+ from anyio import create_task_group
12
12
 
13
13
  from .....doc_utils import export_module
14
14
  from .....llm_config import LLMConfig
@@ -140,7 +140,7 @@ class RealtimeClientBase:
140
140
  Args:
141
141
  audio (str): The audio.
142
142
  """
143
- await self.add_event(InputAudioBufferDelta(delta=audio, item_id=None, raw_message=dict()))
143
+ await self.add_event(InputAudioBufferDelta(delta=audio, item_id=None, raw_message={}))
144
144
 
145
145
 
146
146
  _realtime_client_classes: dict[str, type[RealtimeClientProtocol]] = {}
@@ -2,14 +2,13 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- import asyncio
6
5
  import json
7
6
  from typing import TYPE_CHECKING, Any, Optional
8
7
 
9
- from asyncer import asyncify
10
8
  from pydantic import BaseModel
11
9
 
12
10
  from ....doc_utils import export_module
11
+ from ....fast_depends.utils import asyncify
13
12
  from .realtime_events import FunctionCall, RealtimeEvent
14
13
  from .realtime_observer import RealtimeObserver
15
14
 
@@ -49,7 +48,7 @@ class FunctionObserver(RealtimeObserver):
49
48
  """
50
49
  if name in self.agent.registered_realtime_tools:
51
50
  func = self.agent.registered_realtime_tools[name].func
52
- func = func if asyncio.iscoroutinefunction(func) else asyncify(func)
51
+ func = asyncify(func)
53
52
  try:
54
53
  result = await func(**kwargs)
55
54
  except Exception:
@@ -7,8 +7,7 @@ from dataclasses import dataclass
7
7
  from logging import Logger, getLogger
8
8
  from typing import Any, TypeVar
9
9
 
10
- from anyio import lowlevel
11
- from asyncer import create_task_group
10
+ from anyio import create_task_group, lowlevel
12
11
 
13
12
  from ....doc_utils import export_module
14
13
  from ....llm_config import LLMConfig
@@ -102,7 +101,7 @@ class RealtimeAgent:
102
101
 
103
102
  async def start_observers(self) -> None:
104
103
  for observer in self._observers:
105
- self._tg.soonify(observer.run)(self)
104
+ self._tg.start_soon(observer.run, self)
106
105
 
107
106
  # wait for the observers to be ready
108
107
  for observer in self._observers:
@@ -3,18 +3,21 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  import logging
6
+ import uuid
6
7
  import warnings
7
8
  from collections import defaultdict
8
9
  from collections.abc import Callable
10
+ from functools import partial
9
11
  from typing import TYPE_CHECKING, Any, Optional, TypeVar
10
12
 
11
13
  import anyio
12
- from asyncer import asyncify, create_task_group, syncify
14
+ from anyio import create_task_group, from_thread
13
15
 
14
16
  from ....agentchat.contrib.swarm_agent import AfterWorkOption, initiate_swarm_chat
15
17
  from ....cache import AbstractCache
16
18
  from ....code_utils import content_str
17
19
  from ....doc_utils import export_module
20
+ from ....fast_depends.utils import asyncify
18
21
  from ... import Agent, ChatResult, ConversableAgent, LLMAgent
19
22
  from ...utils import consolidate_chat_info, gather_usage_summary
20
23
 
@@ -182,7 +185,6 @@ class SwarmableAgent(Agent):
182
185
  self,
183
186
  messages: list[dict[str, Any]] | None = None,
184
187
  sender: Optional["Agent"] = None,
185
- **kwargs: Any,
186
188
  ) -> str | dict[str, Any] | None:
187
189
  if messages is None:
188
190
  if sender is None:
@@ -211,6 +213,7 @@ class SwarmableAgent(Agent):
211
213
  summary_args: dict[str, Any] | None = {},
212
214
  **kwargs: dict[str, Any],
213
215
  ) -> ChatResult:
216
+ chat_id = uuid.uuid4().int
214
217
  _chat_info = locals().copy()
215
218
  _chat_info["sender"] = self
216
219
  consolidate_chat_info(_chat_info, uniform_sender=self)
@@ -226,6 +229,7 @@ class SwarmableAgent(Agent):
226
229
  recipient.previous_cache = None # type: ignore[attr-defined]
227
230
 
228
231
  chat_result = ChatResult(
232
+ chat_id=chat_id,
229
233
  chat_history=self.chat_messages[recipient],
230
234
  summary=summary,
231
235
  cost=gather_usage_summary([self, recipient]), # type: ignore[arg-type]
@@ -237,9 +241,8 @@ class SwarmableAgent(Agent):
237
241
  self,
238
242
  messages: list[dict[str, Any]] | None = None,
239
243
  sender: Optional["Agent"] = None,
240
- **kwargs: Any,
241
244
  ) -> str | dict[str, Any] | None:
242
- return self.generate_reply(messages=messages, sender=sender, **kwargs)
245
+ return self.generate_reply(messages=messages, sender=sender)
243
246
 
244
247
  async def a_receive(
245
248
  self,
@@ -349,7 +352,7 @@ class SwarmableRealtimeAgent(SwarmableAgent):
349
352
  self._agents = agents
350
353
  self._realtime_agent = realtime_agent
351
354
 
352
- self._answer_event: anyio.Event = anyio.Event()
355
+ self._answer_event = anyio.Event()
353
356
  self._answer: str = ""
354
357
  self.question_message = question_message or QUESTION_MESSAGE
355
358
 
@@ -419,12 +422,13 @@ class SwarmableRealtimeAgent(SwarmableAgent):
419
422
 
420
423
  async def get_input() -> None:
421
424
  async with create_task_group() as tg:
422
- tg.soonify(self.ask_question)(
425
+ tg.start_soon(
426
+ self.ask_question,
423
427
  self.question_message.format(messages[-1]["content"]),
424
428
  question_timeout=QUESTION_TIMEOUT_SECONDS,
425
429
  )
426
430
 
427
- syncify(get_input)()
431
+ from_thread.run_sync(get_input)
428
432
 
429
433
  return True, {"role": "user", "content": self._answer} # type: ignore[return-value]
430
434
 
@@ -449,12 +453,17 @@ class SwarmableRealtimeAgent(SwarmableAgent):
449
453
  )(self.set_answer)
450
454
 
451
455
  async def on_observers_ready() -> None:
452
- self._realtime_agent._tg.soonify(asyncify(initiate_swarm_chat))(
453
- initial_agent=self._initial_agent,
454
- agents=self._agents,
455
- user_agent=self, # type: ignore[arg-type]
456
- messages="Find out what the user wants.",
457
- after_work=AfterWorkOption.REVERT_TO_USER,
456
+ self._realtime_agent._tg.start_soon(
457
+ asyncify(
458
+ partial(
459
+ initiate_swarm_chat,
460
+ initial_agent=self._initial_agent,
461
+ agents=self._agents,
462
+ user_agent=self, # type: ignore[arg-type]
463
+ messages="Find out what the user wants.",
464
+ after_work=AfterWorkOption.REVERT_TO_USER,
465
+ )
466
+ )
458
467
  )
459
468
 
460
469
  self._realtime_agent.callbacks.on_observers_ready = on_observers_ready
@@ -15,15 +15,15 @@ from .conversable_agent import ConversableAgent
15
15
 
16
16
  @export_module("autogen")
17
17
  class UserProxyAgent(ConversableAgent):
18
- """(In preview) A proxy agent for the user, that can execute code and provide feedback to the other agents.
19
-
20
- UserProxyAgent is a subclass of ConversableAgent configured with `human_input_mode` to ALWAYS
21
- and `llm_config` to False. By default, the agent will prompt for human input every time a message is received.
22
- Code execution is enabled by default. LLM-based auto reply is disabled by default.
23
- To modify auto reply, register a method with [`register_reply`](../ConversableAgent#register-reply).
24
- To modify the way to get human input, override `get_human_input` method.
25
- To modify the way to execute code blocks, single code block, or function call, override `execute_code_blocks`,
26
- `run_code`, and `execute_function` methods respectively.
18
+ """(In preview) A proxy agent for the user, that can execute code and provide feedback to the other agents.\n
19
+ \n
20
+ UserProxyAgent is a subclass of ConversableAgent configured with `human_input_mode` to ALWAYS\n
21
+ and `llm_config` to False. By default, the agent will prompt for human input every time a message is received.\n
22
+ Code execution is enabled by default. LLM-based auto reply is disabled by default.\n
23
+ To modify auto reply, register a method with [`register_reply`](../ConversableAgent#register-reply).\n
24
+ To modify the way to get human input, override `get_human_input` method.\n
25
+ To modify the way to execute code blocks, single code block, or function call, override `execute_code_blocks`,\n
26
+ `run_code`, and `execute_function` methods respectively.\n
27
27
  """
28
28
 
29
29
  # Default UserProxyAgent.description values, based on human_input_mode
@@ -47,50 +47,52 @@ class UserProxyAgent(ConversableAgent):
47
47
  description: str | None = None,
48
48
  **kwargs: Any,
49
49
  ):
50
- """Args:
51
- name (str): name of the agent.
52
- is_termination_msg (function): a function that takes a message in the form of a dictionary
53
- and returns a boolean value indicating if this received message is a termination message.
54
- The dict can contain the following keys: "content", "role", "name", "function_call".
55
- max_consecutive_auto_reply (int): the maximum number of consecutive auto replies.
56
- default to None (no limit provided, class attribute MAX_CONSECUTIVE_AUTO_REPLY will be used as the limit in this case).
57
- The limit only plays a role when human_input_mode is not "ALWAYS".
58
- human_input_mode (str): whether to ask for human inputs every time a message is received.
59
- Possible values are "ALWAYS", "TERMINATE", "NEVER".
60
- (1) When "ALWAYS", the agent prompts for human input every time a message is received.
61
- Under this mode, the conversation stops when the human input is "exit",
62
- or when is_termination_msg is True and there is no human input.
63
- (2) When "TERMINATE", the agent only prompts for human input only when a termination message is received or
64
- the number of auto reply reaches the max_consecutive_auto_reply.
65
- (3) When "NEVER", the agent will never prompt for human input. Under this mode, the conversation stops
66
- when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.
67
- function_map (dict[str, callable]): Mapping function names (passed to openai) to callable functions.
68
- code_execution_config (dict or False): config for the code execution.
69
- To disable code execution, set to False. Otherwise, set to a dictionary with the following keys:
70
- - work_dir (Optional, str): The working directory for the code execution.
71
- If None, a default working directory will be used.
72
- The default working directory is the "extensions" directory under
73
- "path_to_autogen".
74
- - use_docker (Optional, list, str or bool): The docker image to use for code execution.
75
- Default is True, which means the code will be executed in a docker container. A default list of images will be used.
76
- If a list or a str of image name(s) is provided, the code will be executed in a docker container
77
- with the first image successfully pulled.
78
- If False, the code will be executed in the current environment.
79
- We strongly recommend using docker for code execution.
80
- - timeout (Optional, int): The maximum execution time in seconds.
81
- - last_n_messages (Experimental, Optional, int): The number of messages to look back for code execution. Default to 1.
82
- default_auto_reply (str or dict or None): the default auto reply message when no code execution or llm based reply is generated.
83
- llm_config (LLMConfig or dict or False or None): llm inference configuration.
84
- Please refer to [OpenAIWrapper.create](https://docs.ag2.ai/latest/docs/api-reference/autogen/OpenAIWrapper/#autogen.OpenAIWrapper.create)
85
- for available options.
86
- Default to False, which disables llm-based auto reply.
87
- When set to None, will use self.DEFAULT_CONFIG, which defaults to False.
88
- system_message (str or List): system message for ChatCompletion inference.
89
- Only used when llm_config is not False. Use it to reprogram the agent.
90
- description (str): a short description of the agent. This description is used by other agents
91
- (e.g. the GroupChatManager) to decide when to call upon this agent. (Default: system_message)
92
- **kwargs (dict): Please refer to other kwargs in
93
- [ConversableAgent](https://docs.ag2.ai/latest/docs/api-reference/autogen/ConversableAgent).
50
+ """Initialize a UserProxyAgent.
51
+
52
+ Args:
53
+ name (str): name of the agent.\n
54
+ is_termination_msg (function): a function that takes a message in the form of a dictionary\n
55
+ and returns a boolean value indicating if this received message is a termination message.\n
56
+ The dict can contain the following keys: "content", "role", "name", "function_call".\n
57
+ max_consecutive_auto_reply (int): the maximum number of consecutive auto replies.\n
58
+ default to None (no limit provided, class attribute MAX_CONSECUTIVE_AUTO_REPLY will be used as the limit in this case).\n
59
+ The limit only plays a role when human_input_mode is not "ALWAYS".\n
60
+ human_input_mode (str): whether to ask for human inputs every time a message is received.\n
61
+ Possible values are "ALWAYS", "TERMINATE", "NEVER".\n
62
+ (1) When "ALWAYS", the agent prompts for human input every time a message is received.\n
63
+ Under this mode, the conversation stops when the human input is "exit",\n
64
+ or when is_termination_msg is True and there is no human input.\n
65
+ (2) When "TERMINATE", the agent only prompts for human input only when a termination message is received or\n
66
+ the number of auto reply reaches the max_consecutive_auto_reply.\n
67
+ (3) When "NEVER", the agent will never prompt for human input. Under this mode, the conversation stops\n
68
+ when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.\n
69
+ function_map (dict[str, callable]): Mapping function names (passed to openai) to callable functions.\n
70
+ code_execution_config (dict or False): config for the code execution.\n
71
+ To disable code execution, set to False. Otherwise, set to a dictionary with the following keys:\n
72
+ - work_dir (Optional, str): The working directory for the code execution.\n
73
+ If None, a default working directory will be used.\n
74
+ The default working directory is the "extensions" directory under\n
75
+ "path_to_autogen".\n
76
+ - use_docker (Optional, list, str or bool): The docker image to use for code execution.\n
77
+ Default is True, which means the code will be executed in a docker container. A default list of images will be used.\n
78
+ If a list or a str of image name(s) is provided, the code will be executed in a docker container\n
79
+ with the first image successfully pulled.\n
80
+ If False, the code will be executed in the current environment.\n
81
+ We strongly recommend using docker for code execution.\n
82
+ - timeout (Optional, int): The maximum execution time in seconds.\n
83
+ - last_n_messages (Experimental, Optional, int): The number of messages to look back for code execution. Default to 1.\n
84
+ default_auto_reply (str or dict or None): the default auto reply message when no code execution or llm based reply is generated.\n
85
+ llm_config (LLMConfig or dict or False or None): llm inference configuration.\n
86
+ Please refer to [OpenAIWrapper.create](https://docs.ag2.ai/latest/docs/api-reference/autogen/OpenAIWrapper/#autogen.OpenAIWrapper.create)\n
87
+ for available options.\n
88
+ Default to False, which disables llm-based auto reply.\n
89
+ When set to None, will use self.DEFAULT_CONFIG, which defaults to False.\n
90
+ system_message (str or List): system message for ChatCompletion inference.\n
91
+ Only used when llm_config is not False. Use it to reprogram the agent.\n
92
+ description (str): a short description of the agent. This description is used by other agents\n
93
+ (e.g. the GroupChatManager) to decide when to call upon this agent. (Default: system_message)\n
94
+ **kwargs (dict): Please refer to other kwargs in\n
95
+ [ConversableAgent](https://docs.ag2.ai/latest/docs/api-reference/autogen/ConversableAgent).\n
94
96
  """
95
97
  super().__init__(
96
98
  name=name,
@@ -198,15 +198,6 @@ class DocAgent(ConversableAgent):
198
198
  )
199
199
  self.register_reply([ConversableAgent, None], self.generate_inner_group_chat_reply, position=0)
200
200
 
201
- self.context_variables: ContextVariables = ContextVariables(
202
- data={
203
- "DocumentsToIngest": [],
204
- "DocumentsIngested": [],
205
- "QueriesToRun": [],
206
- "QueryResults": [],
207
- }
208
- )
209
-
210
201
  self._triage_agent = DocumentTriageAgent(llm_config=llm_config)
211
202
 
212
203
  def create_error_agent_prompt(agent: ConversableAgent, messages: list[dict[str, Any]]) -> str:
@@ -396,7 +387,7 @@ class DocAgent(ConversableAgent):
396
387
  else:
397
388
  # First time initialization - no deduplication needed
398
389
  context_variables["DocumentsToIngest"] = ingestions
399
- context_variables["QueriesToRun"] = [query for query in queries]
390
+ context_variables["QueriesToRun"] = list(queries)
400
391
  context_variables["TaskInitiated"] = True
401
392
  response_message = "Updated context variables with task decisions"
402
393
 
@@ -15,7 +15,11 @@ from .document_utils import handle_input
15
15
 
16
16
  with optional_import_block():
17
17
  from docling.datamodel.base_models import InputFormat
18
- from docling.datamodel.pipeline_options import AcceleratorDevice, AcceleratorOptions, PdfPipelineOptions
18
+ from docling.datamodel.pipeline_options import ( # type: ignore[attr-defined]
19
+ AcceleratorDevice,
20
+ AcceleratorOptions,
21
+ PdfPipelineOptions,
22
+ )
19
23
  from docling.document_converter import DocumentConverter, PdfFormatOption
20
24
 
21
25
  __all__ = ["docling_parse_docs"]
autogen/browser_utils.py CHANGED
@@ -58,10 +58,10 @@ class SimpleTextBrowser:
58
58
  self.start_page: str = start_page if start_page else "about:blank"
59
59
  self.viewport_size = viewport_size # Applies only to the standard uri types
60
60
  self.downloads_folder = downloads_folder
61
- self.history: list[str] = list()
61
+ self.history: list[str] = []
62
62
  self.page_title: str | None = None
63
63
  self.viewport_current_page = 0
64
- self.viewport_pages: list[tuple[int, int]] = list()
64
+ self.viewport_pages: list[tuple[int, int]] = []
65
65
  self.set_address(self.start_page)
66
66
  self.bing_base_url = bing_base_url
67
67
  self.bing_api_key = bing_api_key
@@ -182,7 +182,7 @@ class SimpleTextBrowser:
182
182
  def _bing_search(self, query: str) -> None:
183
183
  results = self._bing_api_call(query)
184
184
 
185
- web_snippets: list[str] = list()
185
+ web_snippets: list[str] = []
186
186
  idx = 0
187
187
  for page in results["webPages"]["value"]:
188
188
  idx += 1
@@ -194,7 +194,7 @@ class SimpleTextBrowser:
194
194
  f"{idx}. [{dl['name']}]({dl['url']})\n{dl.get('snippet', '')}" # type: ignore[index]
195
195
  )
196
196
 
197
- news_snippets = list()
197
+ news_snippets = []
198
198
  if "news" in results:
199
199
  for page in results["news"]["value"]:
200
200
  idx += 1
@@ -4,16 +4,12 @@
4
4
  #
5
5
  # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
6
  # SPDX-License-Identifier: MIT
7
- import sys
8
7
  from types import TracebackType
9
8
  from typing import Any, Protocol
10
9
 
11
- from ..doc_utils import export_module
10
+ from typing_extensions import Self
12
11
 
13
- if sys.version_info >= (3, 11):
14
- from typing import Self
15
- else:
16
- from typing_extensions import Self
12
+ from ..doc_utils import export_module
17
13
 
18
14
 
19
15
  @export_module("autogen.cache")