ag2 0.4b1__py3-none-any.whl → 0.4.2b1__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 (118) hide show
  1. ag2-0.4.2b1.dist-info/METADATA +19 -0
  2. ag2-0.4.2b1.dist-info/RECORD +6 -0
  3. ag2-0.4.2b1.dist-info/top_level.txt +1 -0
  4. ag2-0.4b1.dist-info/METADATA +0 -496
  5. ag2-0.4b1.dist-info/RECORD +0 -115
  6. ag2-0.4b1.dist-info/top_level.txt +0 -1
  7. autogen/__init__.py +0 -17
  8. autogen/_pydantic.py +0 -116
  9. autogen/agentchat/__init__.py +0 -42
  10. autogen/agentchat/agent.py +0 -142
  11. autogen/agentchat/assistant_agent.py +0 -85
  12. autogen/agentchat/chat.py +0 -306
  13. autogen/agentchat/contrib/__init__.py +0 -0
  14. autogen/agentchat/contrib/agent_builder.py +0 -787
  15. autogen/agentchat/contrib/agent_optimizer.py +0 -450
  16. autogen/agentchat/contrib/capabilities/__init__.py +0 -0
  17. autogen/agentchat/contrib/capabilities/agent_capability.py +0 -21
  18. autogen/agentchat/contrib/capabilities/generate_images.py +0 -297
  19. autogen/agentchat/contrib/capabilities/teachability.py +0 -406
  20. autogen/agentchat/contrib/capabilities/text_compressors.py +0 -72
  21. autogen/agentchat/contrib/capabilities/transform_messages.py +0 -92
  22. autogen/agentchat/contrib/capabilities/transforms.py +0 -565
  23. autogen/agentchat/contrib/capabilities/transforms_util.py +0 -120
  24. autogen/agentchat/contrib/capabilities/vision_capability.py +0 -217
  25. autogen/agentchat/contrib/captainagent.py +0 -487
  26. autogen/agentchat/contrib/gpt_assistant_agent.py +0 -545
  27. autogen/agentchat/contrib/graph_rag/__init__.py +0 -0
  28. autogen/agentchat/contrib/graph_rag/document.py +0 -24
  29. autogen/agentchat/contrib/graph_rag/falkor_graph_query_engine.py +0 -76
  30. autogen/agentchat/contrib/graph_rag/graph_query_engine.py +0 -50
  31. autogen/agentchat/contrib/graph_rag/graph_rag_capability.py +0 -56
  32. autogen/agentchat/contrib/img_utils.py +0 -390
  33. autogen/agentchat/contrib/llamaindex_conversable_agent.py +0 -123
  34. autogen/agentchat/contrib/llava_agent.py +0 -176
  35. autogen/agentchat/contrib/math_user_proxy_agent.py +0 -471
  36. autogen/agentchat/contrib/multimodal_conversable_agent.py +0 -128
  37. autogen/agentchat/contrib/qdrant_retrieve_user_proxy_agent.py +0 -325
  38. autogen/agentchat/contrib/retrieve_assistant_agent.py +0 -56
  39. autogen/agentchat/contrib/retrieve_user_proxy_agent.py +0 -701
  40. autogen/agentchat/contrib/society_of_mind_agent.py +0 -203
  41. autogen/agentchat/contrib/swarm_agent.py +0 -414
  42. autogen/agentchat/contrib/text_analyzer_agent.py +0 -76
  43. autogen/agentchat/contrib/tool_retriever.py +0 -114
  44. autogen/agentchat/contrib/vectordb/__init__.py +0 -0
  45. autogen/agentchat/contrib/vectordb/base.py +0 -243
  46. autogen/agentchat/contrib/vectordb/chromadb.py +0 -326
  47. autogen/agentchat/contrib/vectordb/mongodb.py +0 -559
  48. autogen/agentchat/contrib/vectordb/pgvectordb.py +0 -958
  49. autogen/agentchat/contrib/vectordb/qdrant.py +0 -334
  50. autogen/agentchat/contrib/vectordb/utils.py +0 -126
  51. autogen/agentchat/contrib/web_surfer.py +0 -305
  52. autogen/agentchat/conversable_agent.py +0 -2908
  53. autogen/agentchat/groupchat.py +0 -1668
  54. autogen/agentchat/user_proxy_agent.py +0 -109
  55. autogen/agentchat/utils.py +0 -207
  56. autogen/browser_utils.py +0 -291
  57. autogen/cache/__init__.py +0 -10
  58. autogen/cache/abstract_cache_base.py +0 -78
  59. autogen/cache/cache.py +0 -182
  60. autogen/cache/cache_factory.py +0 -85
  61. autogen/cache/cosmos_db_cache.py +0 -150
  62. autogen/cache/disk_cache.py +0 -109
  63. autogen/cache/in_memory_cache.py +0 -61
  64. autogen/cache/redis_cache.py +0 -128
  65. autogen/code_utils.py +0 -745
  66. autogen/coding/__init__.py +0 -22
  67. autogen/coding/base.py +0 -113
  68. autogen/coding/docker_commandline_code_executor.py +0 -262
  69. autogen/coding/factory.py +0 -45
  70. autogen/coding/func_with_reqs.py +0 -203
  71. autogen/coding/jupyter/__init__.py +0 -22
  72. autogen/coding/jupyter/base.py +0 -32
  73. autogen/coding/jupyter/docker_jupyter_server.py +0 -164
  74. autogen/coding/jupyter/embedded_ipython_code_executor.py +0 -182
  75. autogen/coding/jupyter/jupyter_client.py +0 -224
  76. autogen/coding/jupyter/jupyter_code_executor.py +0 -161
  77. autogen/coding/jupyter/local_jupyter_server.py +0 -168
  78. autogen/coding/local_commandline_code_executor.py +0 -410
  79. autogen/coding/markdown_code_extractor.py +0 -44
  80. autogen/coding/utils.py +0 -57
  81. autogen/exception_utils.py +0 -46
  82. autogen/extensions/__init__.py +0 -0
  83. autogen/formatting_utils.py +0 -76
  84. autogen/function_utils.py +0 -362
  85. autogen/graph_utils.py +0 -148
  86. autogen/io/__init__.py +0 -15
  87. autogen/io/base.py +0 -105
  88. autogen/io/console.py +0 -43
  89. autogen/io/websockets.py +0 -213
  90. autogen/logger/__init__.py +0 -11
  91. autogen/logger/base_logger.py +0 -140
  92. autogen/logger/file_logger.py +0 -287
  93. autogen/logger/logger_factory.py +0 -29
  94. autogen/logger/logger_utils.py +0 -42
  95. autogen/logger/sqlite_logger.py +0 -459
  96. autogen/math_utils.py +0 -356
  97. autogen/oai/__init__.py +0 -33
  98. autogen/oai/anthropic.py +0 -428
  99. autogen/oai/bedrock.py +0 -600
  100. autogen/oai/cerebras.py +0 -264
  101. autogen/oai/client.py +0 -1148
  102. autogen/oai/client_utils.py +0 -167
  103. autogen/oai/cohere.py +0 -453
  104. autogen/oai/completion.py +0 -1216
  105. autogen/oai/gemini.py +0 -469
  106. autogen/oai/groq.py +0 -281
  107. autogen/oai/mistral.py +0 -279
  108. autogen/oai/ollama.py +0 -576
  109. autogen/oai/openai_utils.py +0 -810
  110. autogen/oai/together.py +0 -343
  111. autogen/retrieve_utils.py +0 -487
  112. autogen/runtime_logging.py +0 -163
  113. autogen/token_count_utils.py +0 -257
  114. autogen/types.py +0 -20
  115. autogen/version.py +0 -7
  116. {ag2-0.4b1.dist-info → ag2-0.4.2b1.dist-info}/LICENSE +0 -0
  117. {ag2-0.4b1.dist-info → ag2-0.4.2b1.dist-info}/NOTICE.md +0 -0
  118. {ag2-0.4b1.dist-info → ag2-0.4.2b1.dist-info}/WHEEL +0 -0
@@ -1,203 +0,0 @@
1
- # Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
- #
5
- # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
- # SPDX-License-Identifier: MIT
7
- # ruff: noqa: E722
8
- import copy
9
- import traceback
10
- from typing import Callable, Dict, List, Literal, Optional, Tuple, Union
11
-
12
- from autogen import Agent, ConversableAgent, GroupChat, GroupChatManager, OpenAIWrapper
13
-
14
-
15
- class SocietyOfMindAgent(ConversableAgent):
16
- """(In preview) A single agent that runs a Group Chat as an inner monologue.
17
- At the end of the conversation (termination for any reason), the SocietyOfMindAgent
18
- applies the response_preparer method on the entire inner monologue message history to
19
- extract a final answer for the reply.
20
-
21
- Most arguments are inherited from ConversableAgent. New arguments are:
22
- chat_manager (GroupChatManager): the group chat manager that will be running the inner monologue
23
- response_preparer (Optional, Callable or String): If response_preparer is a callable function, then
24
- it should have the signature:
25
- f( self: SocietyOfMindAgent, messages: List[Dict])
26
- where `self` is this SocietyOfMindAgent, and `messages` is a list of inner-monologue messages.
27
- The function should return a string representing the final response (extracted or prepared)
28
- from that history.
29
- If response_preparer is a string, then it should be the LLM prompt used to extract the final
30
- message from the inner chat transcript.
31
- The default response_preparer depends on if an llm_config is provided. If llm_config is False,
32
- then the response_preparer deterministically returns the last message in the inner-monolgue. If
33
- llm_config is set to anything else, then a default LLM prompt is used.
34
- """
35
-
36
- def __init__(
37
- self,
38
- name: str,
39
- chat_manager: GroupChatManager,
40
- response_preparer: Optional[Union[str, Callable]] = None,
41
- is_termination_msg: Optional[Callable[[Dict], bool]] = None,
42
- max_consecutive_auto_reply: Optional[int] = None,
43
- human_input_mode: Literal["ALWAYS", "NEVER", "TERMINATE"] = "TERMINATE",
44
- function_map: Optional[Dict[str, Callable]] = None,
45
- code_execution_config: Union[Dict, Literal[False]] = False,
46
- llm_config: Optional[Union[Dict, Literal[False]]] = False,
47
- default_auto_reply: Optional[Union[str, Dict, None]] = "",
48
- **kwargs,
49
- ):
50
- super().__init__(
51
- name=name,
52
- system_message="",
53
- is_termination_msg=is_termination_msg,
54
- max_consecutive_auto_reply=max_consecutive_auto_reply,
55
- human_input_mode=human_input_mode,
56
- function_map=function_map,
57
- code_execution_config=code_execution_config,
58
- llm_config=llm_config,
59
- default_auto_reply=default_auto_reply,
60
- **kwargs,
61
- )
62
-
63
- self.update_chat_manager(chat_manager)
64
-
65
- # response_preparer default depends on if the llm_config is set, and if a client was created
66
- if response_preparer is None:
67
- if self.client is not None:
68
- response_preparer = "Output a standalone response to the original request, without mentioning any of the intermediate discussion."
69
- else:
70
-
71
- def response_preparer(agent, messages):
72
- return messages[-1]["content"].replace("TERMINATE", "").strip()
73
-
74
- # Create the response_preparer callable, if given only a prompt string
75
- if isinstance(response_preparer, str):
76
- self.response_preparer = lambda agent, messages: agent._llm_response_preparer(response_preparer, messages)
77
- else:
78
- self.response_preparer = response_preparer
79
-
80
- # NOTE: Async reply functions are not yet supported with this contrib agent
81
- self._reply_func_list = []
82
- self.register_reply([Agent, None], SocietyOfMindAgent.generate_inner_monologue_reply)
83
- self.register_reply([Agent, None], ConversableAgent.generate_code_execution_reply)
84
- self.register_reply([Agent, None], ConversableAgent.generate_function_call_reply)
85
- self.register_reply([Agent, None], ConversableAgent.check_termination_and_human_reply)
86
-
87
- def _llm_response_preparer(self, prompt, messages):
88
- """Default response_preparer when provided with a string prompt, rather than a callable.
89
-
90
- Args:
91
- prompt (str): The prompt used to extract the final response from the transcript.
92
- messages (list): The messages generated as part of the inner monologue group chat.
93
- """
94
-
95
- _messages = [
96
- {
97
- "role": "system",
98
- "content": """Earlier you were asked to fulfill a request. You and your team worked diligently to address that request. Here is a transcript of that conversation:""",
99
- }
100
- ]
101
-
102
- for message in messages:
103
- message = copy.deepcopy(message)
104
- message["role"] = "user"
105
-
106
- # Convert tool and function calls to basic messages to avoid an error on the LLM call
107
- if "content" not in message:
108
- message["content"] = ""
109
-
110
- if "tool_calls" in message:
111
- del message["tool_calls"]
112
- if "tool_responses" in message:
113
- del message["tool_responses"]
114
- if "function_call" in message:
115
- if message["content"] == "":
116
- try:
117
- message["content"] = (
118
- message["function_call"]["name"] + "(" + message["function_call"]["arguments"] + ")"
119
- )
120
- except KeyError:
121
- pass
122
- del message["function_call"]
123
-
124
- # Add the modified message to the transcript
125
- _messages.append(message)
126
-
127
- _messages.append(
128
- {
129
- "role": "system",
130
- "content": prompt,
131
- }
132
- )
133
-
134
- response = self.client.create(context=None, messages=_messages, cache=self.client_cache, agent=self.name)
135
- extracted_response = self.client.extract_text_or_completion_object(response)[0]
136
- if not isinstance(extracted_response, str):
137
- return str(extracted_response.model_dump(mode="dict"))
138
- else:
139
- return extracted_response
140
-
141
- @property
142
- def chat_manager(self) -> Union[GroupChatManager, None]:
143
- """Return the group chat manager."""
144
- return self._chat_manager
145
-
146
- def update_chat_manager(self, chat_manager: Union[GroupChatManager, None]):
147
- """Update the chat manager.
148
-
149
- Args:
150
- chat_manager (GroupChatManager): the group chat manager
151
- """
152
- self._chat_manager = chat_manager
153
-
154
- # Awkward, but due to object cloning, there's no better way to do this
155
- # Read the GroupChat object from the callback
156
- self._group_chat = None
157
- if self._chat_manager is not None:
158
- for item in self._chat_manager._reply_func_list:
159
- if isinstance(item["config"], GroupChat):
160
- self._group_chat = item["config"]
161
- break
162
-
163
- def generate_inner_monologue_reply(
164
- self,
165
- messages: Optional[List[Dict]] = None,
166
- sender: Optional[Agent] = None,
167
- config: Optional[OpenAIWrapper] = None,
168
- ) -> Tuple[bool, Union[str, Dict, None]]:
169
- """Generate a reply by running the group chat"""
170
- if self.chat_manager is None:
171
- return False, None
172
- if messages is None:
173
- messages = self._oai_messages[sender]
174
-
175
- # We want to clear the inner monolgue, keeping only the exteranl chat for context.
176
- # Reset all the counters and histories, then populate agents with necessary context from the external chat
177
- self.chat_manager.reset()
178
- self.update_chat_manager(self.chat_manager)
179
-
180
- external_history = []
181
- if len(messages) > 1:
182
- external_history = messages[0 : len(messages) - 1] # All but the current message
183
-
184
- for agent in self._group_chat.agents:
185
- agent.reset()
186
- for message in external_history:
187
- # Assign each message a name
188
- attributed_message = message.copy()
189
- if "name" not in attributed_message:
190
- if attributed_message["role"] == "assistant":
191
- attributed_message["name"] = self.name
192
- else:
193
- attributed_message["name"] = sender.name
194
-
195
- self.chat_manager.send(attributed_message, agent, request_reply=False, silent=True)
196
-
197
- try:
198
- self.initiate_chat(self.chat_manager, message=messages[-1], clear_history=False)
199
- except:
200
- traceback.print_exc()
201
-
202
- response_preparer = self.response_preparer
203
- return True, response_preparer(self, self._group_chat.messages)
@@ -1,414 +0,0 @@
1
- # Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
- import json
5
- from dataclasses import dataclass
6
- from enum import Enum
7
- from inspect import signature
8
- from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union
9
-
10
- from pydantic import BaseModel
11
-
12
- from autogen.function_utils import get_function_schema
13
- from autogen.oai import OpenAIWrapper
14
-
15
- from ..agent import Agent
16
- from ..chat import ChatResult
17
- from ..conversable_agent import ConversableAgent
18
- from ..groupchat import GroupChat, GroupChatManager
19
- from ..user_proxy_agent import UserProxyAgent
20
-
21
- # Parameter name for context variables
22
- # Use the value in functions and they will be substituted with the context variables:
23
- # e.g. def my_function(context_variables: Dict[str, Any], my_other_parameters: Any) -> Any:
24
- __CONTEXT_VARIABLES_PARAM_NAME__ = "context_variables"
25
-
26
-
27
- class AfterWorkOption(Enum):
28
- TERMINATE = "TERMINATE"
29
- REVERT_TO_USER = "REVERT_TO_USER"
30
- STAY = "STAY"
31
-
32
-
33
- @dataclass
34
- class AFTER_WORK:
35
- agent: Union[AfterWorkOption, "SwarmAgent", str, Callable]
36
-
37
- def __post_init__(self):
38
- if isinstance(self.agent, str):
39
- self.agent = AfterWorkOption(self.agent.upper())
40
-
41
-
42
- @dataclass
43
- class ON_CONDITION:
44
- agent: "SwarmAgent"
45
- condition: str = ""
46
-
47
-
48
- def initiate_swarm_chat(
49
- initial_agent: "SwarmAgent",
50
- messages: Union[List[Dict[str, Any]], str],
51
- agents: List["SwarmAgent"],
52
- user_agent: Optional[UserProxyAgent] = None,
53
- max_rounds: int = 20,
54
- context_variables: Optional[Dict[str, Any]] = None,
55
- after_work: Optional[Union[AFTER_WORK, Callable]] = AFTER_WORK(AfterWorkOption.TERMINATE),
56
- ) -> Tuple[ChatResult, Dict[str, Any], "SwarmAgent"]:
57
- """Initialize and run a swarm chat
58
-
59
- Args:
60
- initial_agent: The first receiving agent of the conversation.
61
- messages: Initial message(s).
62
- agents: List of swarm agents.
63
- user_agent: Optional user proxy agent for falling back to.
64
- max_rounds: Maximum number of conversation rounds.
65
- context_variables: Starting context variables.
66
- after_work: Method to handle conversation continuation when an agent doesn't select the next agent. If no agent is selected and no tool calls are output, we will use this method to determine the next agent.
67
- Must be a AFTER_WORK instance (which is a dataclass accepting a SwarmAgent, AfterWorkOption, A str (of the AfterWorkOption)) or a callable.
68
- AfterWorkOption:
69
- - TERMINATE (Default): Terminate the conversation.
70
- - REVERT_TO_USER : Revert to the user agent if a user agent is provided. If not provided, terminate the conversation.
71
- - STAY : Stay with the last speaker.
72
-
73
- Callable: A custom function that takes the current agent, messages, groupchat, and context_variables as arguments and returns the next agent. The function should return None to terminate.
74
- ```python
75
- def custom_afterwork_func(last_speaker: SwarmAgent, messages: List[Dict[str, Any]], groupchat: GroupChat, context_variables: Optional[Dict[str, Any]]) -> Optional[SwarmAgent]:
76
- ```
77
- Returns:
78
- ChatResult: Conversations chat history.
79
- Dict[str, Any]: Updated Context variables.
80
- SwarmAgent: Last speaker.
81
- """
82
- assert isinstance(initial_agent, SwarmAgent), "initial_agent must be a SwarmAgent"
83
- assert all(isinstance(agent, SwarmAgent) for agent in agents), "agents must be a list of SwarmAgents"
84
-
85
- context_variables = context_variables or {}
86
- if isinstance(messages, str):
87
- messages = [{"role": "user", "content": messages}]
88
-
89
- swarm_agent_names = [agent.name for agent in agents]
90
-
91
- tool_execution = SwarmAgent(
92
- name="Tool_Execution",
93
- system_message="Tool Execution",
94
- )
95
- tool_execution._set_to_tool_execution(context_variables=context_variables)
96
-
97
- # Update tool execution agent with all the functions from all the agents
98
- for agent in agents:
99
- tool_execution._function_map.update(agent._function_map)
100
-
101
- INIT_AGENT_USED = False
102
-
103
- def swarm_transition(last_speaker: SwarmAgent, groupchat: GroupChat):
104
- """Swarm transition function to determine the next agent in the conversation"""
105
- nonlocal INIT_AGENT_USED
106
- if not INIT_AGENT_USED:
107
- INIT_AGENT_USED = True
108
- return initial_agent
109
-
110
- if "tool_calls" in groupchat.messages[-1]:
111
- return tool_execution
112
- if tool_execution._next_agent is not None:
113
- next_agent = tool_execution._next_agent
114
- tool_execution._next_agent = None
115
- return next_agent
116
-
117
- # get the last swarm agent
118
- last_swarm_speaker = None
119
- for message in reversed(groupchat.messages):
120
- if "name" in message and message["name"] in swarm_agent_names:
121
- agent = groupchat.agent_by_name(name=message["name"])
122
- if isinstance(agent, SwarmAgent):
123
- last_swarm_speaker = agent
124
- break
125
- if last_swarm_speaker is None:
126
- raise ValueError("No swarm agent found in the message history")
127
-
128
- # If the user last spoke, return to the agent prior
129
- if (user_agent and last_speaker == user_agent) or groupchat.messages[-1]["role"] == "tool":
130
- return last_swarm_speaker
131
-
132
- # No agent selected via hand-offs (tool calls)
133
- # Assume the work is Done
134
- # override if agent-level after_work is defined, else use the global after_work
135
- tmp_after_work = last_swarm_speaker.after_work if last_swarm_speaker.after_work is not None else after_work
136
- if isinstance(tmp_after_work, AFTER_WORK):
137
- tmp_after_work = tmp_after_work.agent
138
-
139
- if isinstance(tmp_after_work, SwarmAgent):
140
- return tmp_after_work
141
- elif isinstance(tmp_after_work, AfterWorkOption):
142
- if tmp_after_work == AfterWorkOption.TERMINATE or (
143
- user_agent is None and tmp_after_work == AfterWorkOption.REVERT_TO_USER
144
- ):
145
- return None
146
- elif tmp_after_work == AfterWorkOption.REVERT_TO_USER:
147
- return user_agent
148
- elif tmp_after_work == AfterWorkOption.STAY:
149
- return last_speaker
150
- elif isinstance(tmp_after_work, Callable):
151
- return tmp_after_work(last_speaker, groupchat.messages, groupchat, context_variables)
152
- else:
153
- raise ValueError("Invalid After Work condition")
154
-
155
- # If there's only one message and there's no identified swarm agent
156
- # Start with a user proxy agent, creating one if they haven't passed one in
157
- if len(messages) == 1 and "name" not in messages[0] and not user_agent:
158
- temp_user_proxy = [UserProxyAgent(name="_User")]
159
- else:
160
- temp_user_proxy = []
161
-
162
- groupchat = GroupChat(
163
- agents=[tool_execution] + agents + ([user_agent] if user_agent is not None else temp_user_proxy),
164
- messages=[], # Set to empty. We will resume the conversation with the messages
165
- max_round=max_rounds,
166
- speaker_selection_method=swarm_transition,
167
- )
168
- manager = GroupChatManager(groupchat)
169
- clear_history = True
170
-
171
- if len(messages) > 1:
172
- last_agent, last_message = manager.resume(messages=messages)
173
- clear_history = False
174
- else:
175
- last_message = messages[0]
176
-
177
- if "name" in last_message:
178
- if "name" in swarm_agent_names:
179
- # If there's a name in the message and it's a swarm agent, use that
180
- last_agent = groupchat.agent_by_name(name=last_message["name"])
181
- else:
182
- raise ValueError(f"Invalid swarm agent name in last message: {last_message['name']}")
183
- else:
184
- # No name, so we're using the user proxy to start the conversation
185
- if user_agent:
186
- last_agent = user_agent
187
- else:
188
- # If no user agent passed in, use our temporary user proxy
189
- last_agent = temp_user_proxy[0]
190
-
191
- chat_result = last_agent.initiate_chat(
192
- manager,
193
- message=last_message,
194
- clear_history=clear_history,
195
- )
196
-
197
- # Clear the temporary user proxy's name from messages
198
- if len(temp_user_proxy) == 1:
199
- for message in chat_result.chat_history:
200
- if "name" in message and message["name"] == "_User":
201
- # delete the name key from the message
202
- del message["name"]
203
-
204
- return chat_result, context_variables, manager.last_speaker
205
-
206
-
207
- class SwarmResult(BaseModel):
208
- """
209
- Encapsulates the possible return values for a swarm agent function.
210
-
211
- Args:
212
- values (str): The result values as a string.
213
- agent (SwarmAgent): The swarm agent instance, if applicable.
214
- context_variables (dict): A dictionary of context variables.
215
- """
216
-
217
- values: str = ""
218
- agent: Optional["SwarmAgent"] = None
219
- context_variables: Dict[str, Any] = {}
220
-
221
- class Config: # Add this inner class
222
- arbitrary_types_allowed = True
223
-
224
- def __str__(self):
225
- return self.values
226
-
227
-
228
- class SwarmAgent(ConversableAgent):
229
- """Swarm agent for participating in a swarm.
230
-
231
- SwarmAgent is a subclass of ConversableAgent.
232
-
233
- Additional args:
234
- functions (List[Callable]): A list of functions to register with the agent.
235
- """
236
-
237
- def __init__(
238
- self,
239
- name: str,
240
- system_message: Optional[str] = "You are a helpful AI Assistant.",
241
- llm_config: Optional[Union[Dict, Literal[False]]] = None,
242
- functions: Union[List[Callable], Callable] = None,
243
- is_termination_msg: Optional[Callable[[Dict], bool]] = None,
244
- max_consecutive_auto_reply: Optional[int] = None,
245
- human_input_mode: Literal["ALWAYS", "NEVER", "TERMINATE"] = "NEVER",
246
- description: Optional[str] = None,
247
- code_execution_config=False,
248
- **kwargs,
249
- ) -> None:
250
- super().__init__(
251
- name,
252
- system_message,
253
- is_termination_msg,
254
- max_consecutive_auto_reply,
255
- human_input_mode,
256
- llm_config=llm_config,
257
- description=description,
258
- code_execution_config=code_execution_config,
259
- **kwargs,
260
- )
261
-
262
- if isinstance(functions, list):
263
- self.add_functions(functions)
264
- elif isinstance(functions, Callable):
265
- self.add_single_function(functions)
266
-
267
- self.after_work = None
268
-
269
- # use in the tool execution agent to transfer to the next agent
270
- self._context_variables = {}
271
- self._next_agent = None
272
-
273
- def _set_to_tool_execution(self, context_variables: Optional[Dict[str, Any]] = None):
274
- """Set to a special instance of SwarmAgent that is responsible for executing tool calls from other swarm agents.
275
- This agent will be used internally and should not be visible to the user.
276
-
277
- It will execute the tool calls and update the context_variables and next_agent accordingly.
278
- """
279
- self._next_agent = None
280
- self._context_variables = context_variables or {}
281
- self._reply_func_list.clear()
282
- self.register_reply([Agent, None], SwarmAgent.generate_swarm_tool_reply)
283
-
284
- def __str__(self):
285
- return f"SwarmAgent --> {self.name}"
286
-
287
- def register_hand_off(
288
- self,
289
- hand_to: Union[List[Union[ON_CONDITION, AFTER_WORK]], ON_CONDITION, AFTER_WORK],
290
- ):
291
- """Register a function to hand off to another agent.
292
-
293
- Args:
294
- hand_to: A list of ON_CONDITIONs and an, optional, AFTER_WORK condition
295
-
296
- Hand off template:
297
- def transfer_to_agent_name() -> SwarmAgent:
298
- return agent_name
299
- 1. register the function with the agent
300
- 2. register the schema with the agent, description set to the condition
301
- """
302
- if isinstance(hand_to, (ON_CONDITION, AFTER_WORK)):
303
- hand_to = [hand_to]
304
-
305
- for transit in hand_to:
306
- if isinstance(transit, AFTER_WORK):
307
- self.after_work = transit
308
- elif isinstance(transit, ON_CONDITION):
309
-
310
- # Create closure with current loop transit value
311
- # to ensure the condition matches the one in the loop
312
- def make_transfer_function(current_transit):
313
- def transfer_to_agent() -> "SwarmAgent":
314
- return current_transit.agent
315
-
316
- return transfer_to_agent
317
-
318
- transfer_func = make_transfer_function(transit)
319
- self.add_single_function(transfer_func, f"transfer_to_{transit.agent.name}", transit.condition)
320
- else:
321
- raise ValueError("Invalid hand off condition, must be either ON_CONDITION or AFTER_WORK")
322
-
323
- def generate_swarm_tool_reply(
324
- self,
325
- messages: Optional[List[Dict]] = None,
326
- sender: Optional[Agent] = None,
327
- config: Optional[OpenAIWrapper] = None,
328
- ) -> Tuple[bool, dict]:
329
- """Pre-processes and generates tool call replies.
330
-
331
- This function:
332
- 1. Adds context_variables back to the tool call for the function, if necessary.
333
- 2. Generates the tool calls reply.
334
- 3. Updates context_variables and next_agent based on the tool call response."""
335
-
336
- if config is None:
337
- config = self
338
- if messages is None:
339
- messages = self._oai_messages[sender]
340
-
341
- message = messages[-1]
342
- if "tool_calls" in message:
343
- # 1. add context_variables to the tool call arguments
344
- for tool_call in message["tool_calls"]:
345
- if tool_call["type"] == "function":
346
- function_name = tool_call["function"]["name"]
347
-
348
- # Check if this function exists in our function map
349
- if function_name in self._function_map:
350
- func = self._function_map[function_name] # Get the original function
351
-
352
- # Check if function has context_variables parameter
353
- sig = signature(func)
354
- if __CONTEXT_VARIABLES_PARAM_NAME__ in sig.parameters:
355
- current_args = json.loads(tool_call["function"]["arguments"])
356
- current_args[__CONTEXT_VARIABLES_PARAM_NAME__] = self._context_variables
357
- # Update the tool call with new arguments
358
- tool_call["function"]["arguments"] = json.dumps(current_args)
359
-
360
- # 2. generate tool calls reply
361
- _, tool_message = self.generate_tool_calls_reply([message])
362
-
363
- # 3. update context_variables and next_agent, convert content to string
364
- for tool_response in tool_message["tool_responses"]:
365
- content = tool_response.get("content")
366
- if isinstance(content, SwarmResult):
367
- if content.context_variables != {}:
368
- self._context_variables.update(content.context_variables)
369
- if content.agent is not None:
370
- self._next_agent = content.agent
371
- elif isinstance(content, Agent):
372
- self._next_agent = content
373
- tool_response["content"] = str(tool_response["content"])
374
-
375
- return True, tool_message
376
- return False, None
377
-
378
- def add_single_function(self, func: Callable, name=None, description=""):
379
- if name:
380
- func._name = name
381
- else:
382
- func._name = func.__name__
383
-
384
- if description:
385
- func._description = description
386
- else:
387
- # Use function's docstring, strip whitespace, fall back to empty string
388
- func._description = (func.__doc__ or "").strip()
389
-
390
- f = get_function_schema(func, name=func._name, description=func._description)
391
-
392
- # Remove context_variables parameter from function schema
393
- f_no_context = f.copy()
394
- if __CONTEXT_VARIABLES_PARAM_NAME__ in f_no_context["function"]["parameters"]["properties"]:
395
- del f_no_context["function"]["parameters"]["properties"][__CONTEXT_VARIABLES_PARAM_NAME__]
396
- if "required" in f_no_context["function"]["parameters"]:
397
- required = f_no_context["function"]["parameters"]["required"]
398
- f_no_context["function"]["parameters"]["required"] = [
399
- param for param in required if param != __CONTEXT_VARIABLES_PARAM_NAME__
400
- ]
401
- # If required list is empty, remove it
402
- if not f_no_context["function"]["parameters"]["required"]:
403
- del f_no_context["function"]["parameters"]["required"]
404
-
405
- self.update_tool_signature(f_no_context, is_remove=False)
406
- self.register_function({func._name: func})
407
-
408
- def add_functions(self, func_list: List[Callable]):
409
- for func in func_list:
410
- self.add_single_function(func)
411
-
412
-
413
- # Forward references for SwarmAgent in SwarmResult
414
- SwarmResult.update_forward_refs()
@@ -1,76 +0,0 @@
1
- # Copyright (c) 2023 - 2024, Owners of https://github.com/ag2ai
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
- #
5
- # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
- # SPDX-License-Identifier: MIT
7
- from typing import Any, Dict, List, Literal, Optional, Tuple, Union
8
-
9
- from autogen.agentchat.agent import Agent
10
- from autogen.agentchat.assistant_agent import ConversableAgent
11
-
12
- system_message = """You are an expert in text analysis.
13
- The user will give you TEXT to analyze.
14
- The user will give you analysis INSTRUCTIONS copied twice, at both the beginning and the end.
15
- You will follow these INSTRUCTIONS in analyzing the TEXT, then give the results of your expert analysis in the format requested."""
16
-
17
-
18
- class TextAnalyzerAgent(ConversableAgent):
19
- """(Experimental) Text Analysis agent, a subclass of ConversableAgent designed to analyze text as instructed."""
20
-
21
- def __init__(
22
- self,
23
- name="analyzer",
24
- system_message: Optional[str] = system_message,
25
- human_input_mode: Literal["ALWAYS", "NEVER", "TERMINATE"] = "NEVER",
26
- llm_config: Optional[Union[Dict, bool]] = None,
27
- **kwargs,
28
- ):
29
- """
30
- Args:
31
- name (str): name of the agent.
32
- system_message (str): system message for the ChatCompletion inference.
33
- human_input_mode (str): This agent should NEVER prompt the human for input.
34
- llm_config (dict or False): llm inference configuration.
35
- Please refer to [OpenAIWrapper.create](/docs/reference/oai/client#create)
36
- for available options.
37
- To disable llm-based auto reply, set to False.
38
- **kwargs (dict): other kwargs in [ConversableAgent](../conversable_agent#__init__).
39
- """
40
- super().__init__(
41
- name=name,
42
- system_message=system_message,
43
- human_input_mode=human_input_mode,
44
- llm_config=llm_config,
45
- **kwargs,
46
- )
47
- self.register_reply(Agent, TextAnalyzerAgent._analyze_in_reply, position=2)
48
-
49
- def _analyze_in_reply(
50
- self,
51
- messages: Optional[List[Dict]] = None,
52
- sender: Optional[Agent] = None,
53
- config: Optional[Any] = None,
54
- ) -> Tuple[bool, Union[str, Dict, None]]:
55
- """Analyzes the given text as instructed, and returns the analysis as a message.
56
- Assumes exactly two messages containing the text to analyze and the analysis instructions.
57
- See Teachability.analyze for an example of how to use this method."""
58
- if self.llm_config is False:
59
- raise ValueError("TextAnalyzerAgent requires self.llm_config to be set in its base class.")
60
- if messages is None:
61
- messages = self._oai_messages[sender] # In case of a direct call.
62
- assert len(messages) == 2
63
-
64
- # Delegate to the analysis method.
65
- return True, self.analyze_text(messages[0]["content"], messages[1]["content"])
66
-
67
- def analyze_text(self, text_to_analyze, analysis_instructions):
68
- """Analyzes the given text as instructed, and returns the analysis."""
69
- # Assemble the message.
70
- text_to_analyze = "# TEXT\n" + text_to_analyze + "\n"
71
- analysis_instructions = "# INSTRUCTIONS\n" + analysis_instructions + "\n"
72
- msg_text = "\n".join(
73
- [analysis_instructions, text_to_analyze, analysis_instructions]
74
- ) # Repeat the instructions.
75
- # Generate and return the analysis string.
76
- return self.generate_oai_reply([{"role": "user", "content": msg_text}], None, None)[1]