Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functionality of manual history cleaning by user proxy added #1230

Merged
merged 34 commits into from
Jan 27, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
56b5fe5
functionality of manual history cleaning by admin added
Grigorij-Dudnik Jan 13, 2024
42cb928
formatting improved
Grigorij-Dudnik Jan 13, 2024
f1a9187
formatting improved 2
Grigorij-Dudnik Jan 13, 2024
1f40a73
formatting improved 3
Grigorij-Dudnik Jan 13, 2024
4df97a3
Merge branch 'main' into manual_history_cleaning
Grigorij-Dudnik Jan 13, 2024
4671d70
Merge branch 'main' into manual_history_cleaning
davorrunje Jan 15, 2024
4083ad4
Merge branch 'main' into manual_history_cleaning
Grigorij-Dudnik Jan 16, 2024
0d4f77d
test function added
Grigorij-Dudnik Jan 17, 2024
ef0cafb
test code formatting
Grigorij-Dudnik Jan 17, 2024
62ca13e
test code formatting 2
Grigorij-Dudnik Jan 17, 2024
aa73fcf
more advanced logging. Now user can see nr of messages to preserve as…
Grigorij-Dudnik Jan 18, 2024
8d02c30
Merge branch 'main' into manual_history_cleaning
davorrunje Jan 18, 2024
e2958c9
test_invalid_allow_repeat_speaker uncommented
Grigorij-Dudnik Jan 19, 2024
4400c32
Merge branch 'manual_history_cleaning' of https://github.com/GregorD1…
Grigorij-Dudnik Jan 19, 2024
f28d7c1
Merge branch 'main' into manual_history_cleaning
davorrunje Jan 19, 2024
822fbf9
warning when providing recepient agent and nr messages to preserve ad…
Grigorij-Dudnik Jan 20, 2024
d67562e
Merge branch 'manual_history_cleaning' of https://github.com/GregorD1…
Grigorij-Dudnik Jan 20, 2024
a520d54
code formatting
Grigorij-Dudnik Jan 20, 2024
6ce9e97
code formatting
Grigorij-Dudnik Jan 20, 2024
403e6e6
code formatting
Grigorij-Dudnik Jan 20, 2024
db46def
Merge branch 'main' into manual_history_cleaning
Grigorij-Dudnik Jan 22, 2024
2fc78ab
added 'enable_clear_history' variable to GroupChat
Grigorij-Dudnik Jan 22, 2024
fca28ab
Merge branch 'manual_history_cleaning' of https://github.com/GregorD1…
Grigorij-Dudnik Jan 22, 2024
4a76e91
'enable_clear_history' added, better descripted
Grigorij-Dudnik Jan 23, 2024
6488d82
clearing groupchat history added
Grigorij-Dudnik Jan 23, 2024
ac58b55
clearing groupchat history added
Grigorij-Dudnik Jan 23, 2024
b0b42a7
two ifs merged into one, formatting improved
Grigorij-Dudnik Jan 24, 2024
f27400f
two ifs merged into one, formatting improved
Grigorij-Dudnik Jan 24, 2024
11dbee3
two ifs merged into one, formatting improved
Grigorij-Dudnik Jan 24, 2024
e9706b7
formatting
Grigorij-Dudnik Jan 24, 2024
ea1bc05
formatting
Grigorij-Dudnik Jan 24, 2024
dce124c
Merge branch 'main' into manual_history_cleaning
sonichi Jan 24, 2024
25f3432
Merge branch 'main' into manual_history_cleaning
Grigorij-Dudnik Jan 25, 2024
fe813da
Merge branch 'main' into manual_history_cleaning
sonichi Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions autogen/agentchat/conversable_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,16 +759,30 @@ def reset_consecutive_auto_reply_counter(self, sender: Optional[Agent] = None):
else:
self._consecutive_auto_reply_counter[sender] = 0

def clear_history(self, agent: Optional[Agent] = None):
def clear_history(self, recipient: Optional[Agent] = None, nr_messages_to_preserve: Optional[int] = None):
"""Clear the chat history of the agent.

Args:
agent: the agent with whom the chat history to clear. If None, clear the chat history with all agents.
recipient: the agent with whom the chat history to clear. If None, clear the chat history with all agents.
nr_messages_to_preserve: the number of newest messages to preserve in the chat history.
"""
if agent is None:
self._oai_messages.clear()
if recipient is None:
if nr_messages_to_preserve:
for key in self._oai_messages:
# Remove messages from history except last `nr_messages_to_preserve` messages.
self._oai_messages[key] = self._oai_messages[key][-nr_messages_to_preserve:]
else:
self._oai_messages.clear()
else:
self._oai_messages[agent].clear()
self._oai_messages[recipient].clear()
if nr_messages_to_preserve:
print(
colored(
"WARNING: `nr_preserved_messages` is ignored when clearing chat history with a specific agent.",
"yellow",
),
flush=True,
)

def generate_oai_reply(
self,
Expand Down
76 changes: 76 additions & 0 deletions autogen/agentchat/groupchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class GroupChat:
- "random": the next speaker is selected randomly.
- "round_robin": the next speaker is selected in a round robin fashion, i.e., iterating in the same order as provided in `agents`.
- allow_repeat_speaker: whether to allow the same speaker to speak consecutively. Default is True, in which case all speakers are allowed to speak consecutively. If allow_repeat_speaker is a list of Agents, then only those listed agents are allowed to repeat. If set to False, then no speakers are allowed to repeat.
- enable_clear_history: enable possibility to clear history of messages for agents manually by providing
"clear history" phrase in user prompt. This is experimental feature.
See description of GroupChatManager.clear_agents_history function for more info.
"""

agents: List[Agent]
Expand All @@ -40,6 +43,7 @@ class GroupChat:
func_call_filter: Optional[bool] = True
speaker_selection_method: Optional[str] = "auto"
allow_repeat_speaker: Optional[Union[bool, List[Agent]]] = True
enable_clear_history: Optional[bool] = False

_VALID_SPEAKER_SELECTION_METHODS = ["auto", "manual", "random", "round_robin"]

Expand Down Expand Up @@ -388,6 +392,11 @@ def run_chat(
raise
if reply is None:
break

# check for "clear history" phrase in reply and activate clear history function if found
sonichi marked this conversation as resolved.
Show resolved Hide resolved
if groupchat.enable_clear_history and isinstance(reply, dict):
if "CLEAR HISTORY" in reply["content"].upper():
reply["content"] = self.clear_agents_history(reply["content"], groupchat)
# The speaker sends the message without requesting a reply
speaker.send(reply, self, request_reply=False)
message = self.last_message(speaker)
Expand Down Expand Up @@ -464,3 +473,70 @@ def _raise_exception_on_async_reply_functions(self) -> None:

for agent in self._groupchat.agents:
agent._raise_exception_on_async_reply_functions()

def clear_agents_history(self, reply: str, groupchat: GroupChat) -> str:
"""Clears history of messages for all agents or selected one. Can preserve selected number of last messages.
That function is called when user manually provide "clear history" phrase in his reply.
When "clear history" is provided, the history of messages for all agents is cleared.
When "clear history <agent_name>" is provided, the history of messages for selected agent is cleared.
When "clear history <nr_of_messages_to_preserve>" is provided, the history of messages for all agents is cleared
except last <nr_of_messages_to_preserve> messages.
When "clear history <agent_name> <nr_of_messages_to_preserve>" is provided, the history of messages for selected
agent is cleared except last <nr_of_messages_to_preserve> messages.
Phrase "clear history" and optional arguments are cut out from the reply before it passed to the chat.

Args:
reply (str): Admin reply to analyse.
sonichi marked this conversation as resolved.
Show resolved Hide resolved
groupchat (GroupChat): GroupChat object.
"""
# Split the reply into words
words = reply.split()
# Find the position of "clear" to determine where to start processing
clear_word_index = next(i for i in reversed(range(len(words))) if words[i].upper() == "CLEAR")
# Extract potential agent name and steps
words_to_check = words[clear_word_index + 2 : clear_word_index + 4]
nr_messages_to_preserve = None
agent_to_memory_clear = None

for word in words_to_check:
if word.isdigit():
nr_messages_to_preserve = int(word)
elif word[:-1].isdigit(): # for the case when number of messages is followed by dot or other sign
nr_messages_to_preserve = int(word[:-1])
else:
for agent in groupchat.agents:
if agent.name == word:
agent_to_memory_clear = agent
break
elif agent.name == word[:-1]: # for the case when agent name is followed by dot or other sign
agent_to_memory_clear = agent
break
# clear history
if agent_to_memory_clear:
if nr_messages_to_preserve:
print(
f"Clearing history for {agent_to_memory_clear.name} except last {nr_messages_to_preserve} messages."
)
else:
print(f"Clearing history for {agent_to_memory_clear.name}.")
agent_to_memory_clear.clear_history(nr_messages_to_preserve=nr_messages_to_preserve)
else:
if nr_messages_to_preserve:
print(f"Clearing history for all agents except last {nr_messages_to_preserve} messages.")
# clearing history for groupchat here
temp = groupchat.messages[-nr_messages_to_preserve:]
groupchat.messages.clear()
groupchat.messages.extend(temp)
else:
print("Clearing history for all agents.")
# clearing history for groupchat here
groupchat.messages.clear()
# clearing history for agents
for agent in groupchat.agents:
agent.clear_history(nr_messages_to_preserve=nr_messages_to_preserve)

# Reconstruct the reply without the "clear history" command and parameters
skip_words_number = 2 + int(bool(agent_to_memory_clear)) + int(bool(nr_messages_to_preserve))
reply = " ".join(words[:clear_word_index] + words[clear_word_index + skip_words_number :])

return reply
92 changes: 91 additions & 1 deletion test/agentchat/test_groupchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,95 @@ def test_selection_helpers():
groupchat.manual_select_speaker()


def test_clear_agents_history():
agent1 = autogen.ConversableAgent(
"alice",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is alice speaking.",
)
agent2 = autogen.ConversableAgent(
"bob",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is bob speaking.",
)
agent3 = autogen.ConversableAgent(
"sam",
max_consecutive_auto_reply=10,
human_input_mode="ALWAYS",
llm_config=False,
)
groupchat = autogen.GroupChat(agents=[agent1, agent2, agent3], messages=[], max_round=3, enable_clear_history=True)
group_chat_manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=False)

# testing pure "clear history" statement
with mock.patch.object(builtins, "input", lambda _: "clear history. How you doing?"):
agent1.initiate_chat(group_chat_manager, message="hello")
agent1_history = list(agent1._oai_messages.values())[0]
agent2_history = list(agent2._oai_messages.values())[0]
assert agent1_history == [{"content": "How you doing?", "name": "sam", "role": "user"}]
assert agent2_history == [{"content": "How you doing?", "name": "sam", "role": "user"}]
assert groupchat.messages == [{"content": "How you doing?", "name": "sam", "role": "user"}]

# testing clear history for defined agent
with mock.patch.object(builtins, "input", lambda _: "clear history bob. How you doing?"):
agent1.initiate_chat(group_chat_manager, message="hello")
agent1_history = list(agent1._oai_messages.values())[0]
agent2_history = list(agent2._oai_messages.values())[0]
assert agent1_history == [
{"content": "hello", "role": "assistant"},
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]
assert agent2_history == [{"content": "How you doing?", "name": "sam", "role": "user"}]
assert groupchat.messages == [
{"content": "hello", "role": "user", "name": "alice"},
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]

# testing clear history with defined nr of messages to preserve
with mock.patch.object(builtins, "input", lambda _: "clear history 1. How you doing?"):
agent1.initiate_chat(group_chat_manager, message="hello")
agent1_history = list(agent1._oai_messages.values())[0]
agent2_history = list(agent2._oai_messages.values())[0]
assert agent1_history == [
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]
assert agent2_history == [
{"content": "This is bob speaking.", "role": "assistant"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]
assert groupchat.messages == [
{'content': 'This is bob speaking.', 'role': 'user', 'name': 'bob'},
{'content': 'How you doing?', 'role': 'user', 'name': 'sam'},
]

# testing clear history with defined agent and nr of messages to preserve
with mock.patch.object(builtins, "input", lambda _: "clear history bob 1. How you doing?"):
agent1.initiate_chat(group_chat_manager, message="hello")
agent1_history = list(agent1._oai_messages.values())[0]
agent2_history = list(agent2._oai_messages.values())[0]
assert agent1_history == [
{"content": "hello", "role": "assistant"},
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]
assert agent2_history == [
{"content": "This is bob speaking.", "role": "assistant"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]
assert groupchat.messages == [
{"content": "hello", "name": "alice", "role": "user"},
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
{"content": "How you doing?", "name": "sam", "role": "user"},
]


if __name__ == "__main__":
# test_func_call_groupchat()
# test_broadcast()
Expand All @@ -514,4 +603,5 @@ def test_selection_helpers():
# test_agent_mentions()
# test_termination()
# test_next_agent()
test_invalid_allow_repeat_speaker()
# test_invalid_allow_repeat_speaker()
davorrunje marked this conversation as resolved.
Show resolved Hide resolved
test_clear_agents_history()
Loading