Skip to content

Commit 5db07e3

Browse files
committed
Added user proxy agent
1 parent bcaae3d commit 5db07e3

File tree

3 files changed

+93
-16
lines changed

3 files changed

+93
-16
lines changed

nextpy/ai/agent/assistant_agent.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from typing import Any, Dict, Union
1+
from typing import Any, Callable, Tuple
22
from nextpy.ai.agent.base_agent import BaseAgent
33
import logging
44
from pathlib import Path
55
from nextpy.ai import engine
6-
from typing import Callable, Tuple
76
import inspect
87
import asyncio
8+
import logging
99

1010

1111
def _call_functions(functions):
@@ -59,7 +59,7 @@ class AssistantAgent(BaseAgent):
5959
{{#user~}}
6060
Read the following CONVERSATION :
6161
{{messages}}
62-
Respond. Do not thank any team member or show appreciation."
62+
Respond as {{name}}. Do not thank any team member or show appreciation."
6363
{{~/user}}
6464
6565
{{#assistant~}}
@@ -78,6 +78,7 @@ def __init__(self,
7878
Tuple[Any], Tuple[Any]] | None = None,
7979
functions_after_call: Tuple[Callable,
8080
Tuple[Any], Tuple[Any]] | None = None,
81+
description: str = "Helpful AI Assistant Agent",
8182
**kwargs):
8283
"""
8384
Initializes an instance of the AssistantAgent class.
@@ -101,6 +102,7 @@ def __init__(self,
101102
:param kwargs: Additional keyword arguments.
102103
"""
103104
super().__init__(llm=llm, **kwargs)
105+
self.name = name
104106
self.prompt = self.DEFAULT_PROMPT
105107
self.system_message = system_message
106108
# This is used by multiagent manager to determine whether to use receive or a_receive
@@ -111,18 +113,19 @@ def __init__(self,
111113
system_message = Path(system_message).read_text()
112114
except Exception:
113115
pass
114-
self.prompt = self.prompt[:self.DEFAULT_PROMPT.find(
115-
'{{~/system}}')] + system_message + self.prompt[self.DEFAULT_PROMPT.find('{{~/system}}'):]
116+
self.prompt = self.prompt[:self.prompt.find(
117+
'{{~/system}}')] + system_message + self.prompt[self.prompt.find('{{~/system}}'):]
116118

117119
# Either llm or engine must be provided
118-
assert llm is not None or engine is not None, "Either llm or engine must be provided."
120+
if llm is not None or engine is not None:
121+
logging.debug("Warning! Either llm or engine must be provided.")
119122

120123
self.engine = custom_engine if custom_engine is not None else engine(
121124
template=self.prompt, llm=llm, memory=memory, async_mode=async_mode, **kwargs)
122125
self.output_key = 'answer'
123-
self.name = name
124126
self.functions_before_call = functions_before_call
125127
self.functions_after_call = functions_after_call
128+
self.description = description
126129

127130
@staticmethod
128131
def function_call_decorator(func):

nextpy/ai/agent/multiagent_manager.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
from typing import Tuple, List, Optional, Any
2-
3-
from nextpy.ai.agent.base_agent import BaseAgent
1+
from typing import Tuple, List, Any
42
from nextpy.ai.agent.assistant_agent import AssistantAgent
53
from nextpy.ai import engine
64

@@ -25,12 +23,12 @@ class MultiAgentManager:
2523
debug_mode (bool): A flag indicating whether to enable debug mode.
2624
"""
2725
DEFAULT_PROMPT = '''
28-
{{#system~}} You are playing a role playing game with the following participants : {{agents}}{{~/system}}
26+
{{#system~}} You are playing a role playing game with the following participants : \n{{agents}}{{~/system}}
2927
3028
{{#user~}}
3129
Read the following conversation and choose who the next speaker will be:
3230
{{messages}}
33-
Simply respond with the NAME of the next speaker. Do not include any numbers. Note, User is not a participant, you cannot choose User.
31+
Simply respond with the NAME of the next speaker without any other characters such as numbers or punctuations.
3432
{{~/user}}
3533
3634
{{#assistant~}}
@@ -70,6 +68,13 @@ def __init__(self,
7068

7169
self.debug_mode = debug_mode
7270

71+
if not any([isinstance(agent, AssistantAgent)
72+
for agent in agents]):
73+
self.DEFAULT_PROMPT = self.DEFAULT_PROMPT[:self.DEFAULT_PROMPT.find(
74+
'{{~/system}}')] + '\nNote, User is also a participant, you can choose User.' + self.DEFAULT_PROMPT[self.DEFAULT_PROMPT.find('{{~/system}}'):]
75+
else:
76+
self.DEFAULT_PROMPT = self.DEFAULT_PROMPT[:self.DEFAULT_PROMPT.find(
77+
'{{~/system}}')] + '\nNote, User is not a participant, you cannot choose User.' + self.DEFAULT_PROMPT[self.DEFAULT_PROMPT.find('{{~/system}}'):]
7378
self.engine = engine(
7479
self.DEFAULT_PROMPT, llm=llm, memory=memory, async_mode=async_mode)
7580
self.solution_summarizer = engine(
@@ -90,7 +95,7 @@ def agent_string(self):
9095
"""
9196
Returns a string representation of all the agent names separated by commas.
9297
"""
93-
return ','.join([agent.name for agent in self.agents])
98+
return '\n\n'.join([f'NAME: {agent.name}\n DESC: {agent.description}' for agent in self.agents])
9499

95100
def run_sequence(self, context):
96101
"""
@@ -103,7 +108,7 @@ def run_sequence(self, context):
103108
A list of messages exchanged between agents during the sequence.
104109
"""
105110
self.messages.append(['User', context])
106-
while self.rounds > 0 and not self._termination_message_received():
111+
while self.rounds != 0 and not self._termination_message_received():
107112
if self.debug_mode:
108113
print(
109114
f'{"-"*5}Messaging next agent : {self.agents[self.current_agent].name}{"-"*5}\n\n')
@@ -130,7 +135,7 @@ async def a_run_sequence(self, context):
130135
A list of messages exchanged between agents during the sequence.
131136
"""
132137
self.messages.append(['User', context])
133-
while self.rounds > 0 and not self._termination_message_received():
138+
while self.rounds != 0 and not self._termination_message_received():
134139
if self.debug_mode:
135140
print(
136141
f'{"-"*5}Messaging next agent : {self.agents[self.current_agent].name}{"-"*5}\n\n')
@@ -157,7 +162,7 @@ def run_auto(self, context):
157162
A list containing the messages exchanged between agents and the final solution.
158163
"""
159164
self.messages.append(['User', context])
160-
while self.rounds > 0 and not self._termination_message_received():
165+
while self.rounds != 0 and not self._termination_message_received():
161166
next_agent = self._choose_next_agent()
162167
if self.debug_mode:
163168
print(

nextpy/ai/agent/userproxy_agent.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from nextpy.ai.agent.assistant_agent import AssistantAgent
2+
from typing import Any, Tuple, Callable
3+
4+
5+
class UserProxyAgent(AssistantAgent):
6+
7+
def __init__(self,
8+
async_mode: bool = False,
9+
functions_before_call: Tuple[Callable,
10+
Tuple[Any], Tuple[Any]] | None = None,
11+
functions_after_call: Tuple[Callable,
12+
Tuple[Any], Tuple[Any]] | None = None,
13+
description: str = "User Proxy Agent capable of receiving user input.",
14+
**kwargs):
15+
self.name = 'User'
16+
self.description = description
17+
self.async_mode = async_mode
18+
self.functions_before_call = functions_before_call
19+
self.functions_after_call = functions_after_call
20+
21+
@AssistantAgent.function_call_decorator
22+
def receive(self, *args, **kwargs):
23+
"""
24+
Receives messages from other agents and generates a response.
25+
26+
:param agents: The list of agents involved in the conversation.
27+
:type agents: List[str]
28+
:param messages: The list of messages in the conversation.
29+
:type messages: List[str]
30+
:param termination_message: The termination message for the conversation.
31+
:type termination_message: str
32+
:return: The generated response.
33+
:rtype: str
34+
"""
35+
return self._receive_user_input()
36+
37+
@AssistantAgent.function_call_decorator
38+
async def a_receive(self, *args, **kwargs):
39+
"""
40+
Asynchronously receives messages from other agents and generates a response.
41+
42+
:param agents: The list of agents involved in the conversation.
43+
:type agents: List[str]
44+
:param messages: The list of messages in the conversation.
45+
:type messages: List[str]
46+
:param termination_message: The termination message for the conversation.
47+
:type termination_message: str
48+
:return: The generated response.
49+
:rtype: str
50+
"""
51+
return await self.a_receive_user_input()
52+
53+
def _receive_user_input(self):
54+
"""
55+
Receives user input and returns it as the response.
56+
57+
:return: The user input.
58+
:rtype: str
59+
"""
60+
return input('Provide feedback to chat_manager:')
61+
62+
async def a_receive_user_input(self):
63+
"""
64+
Asynchronously receives user input and returns it as the response.
65+
66+
:return: The user input.
67+
:rtype: str
68+
"""
69+
return input('Provide feedback to chat_manager:')

0 commit comments

Comments
 (0)