Skip to content

Commit 0790da0

Browse files
Grigorij-Dudnikdavorrunjesonichi
authored
functionality of manual history cleaning by user proxy added (#1230)
* functionality of manual history cleaning by admin added * formatting improved * formatting improved 2 * formatting improved 3 * test function added * test code formatting * test code formatting 2 * more advanced logging. Now user can see nr of messages to preserve as confirmation * test_invalid_allow_repeat_speaker uncommented * warning when providing recepient agent and nr messages to preserve added, changed variables names * code formatting * code formatting * code formatting * added 'enable_clear_history' variable to GroupChat * 'enable_clear_history' added, better descripted * clearing groupchat history added * clearing groupchat history added * two ifs merged into one, formatting improved * two ifs merged into one, formatting improved * two ifs merged into one, formatting improved * formatting * formatting --------- Co-authored-by: Davor Runje <[email protected]> Co-authored-by: Chi Wang <[email protected]>
1 parent 9434cd4 commit 0790da0

File tree

3 files changed

+189
-6
lines changed

3 files changed

+189
-6
lines changed

autogen/agentchat/conversable_agent.py

+19-5
Original file line numberDiff line numberDiff line change
@@ -759,16 +759,30 @@ def reset_consecutive_auto_reply_counter(self, sender: Optional[Agent] = None):
759759
else:
760760
self._consecutive_auto_reply_counter[sender] = 0
761761

762-
def clear_history(self, agent: Optional[Agent] = None):
762+
def clear_history(self, recipient: Optional[Agent] = None, nr_messages_to_preserve: Optional[int] = None):
763763
"""Clear the chat history of the agent.
764764
765765
Args:
766-
agent: the agent with whom the chat history to clear. If None, clear the chat history with all agents.
766+
recipient: the agent with whom the chat history to clear. If None, clear the chat history with all agents.
767+
nr_messages_to_preserve: the number of newest messages to preserve in the chat history.
767768
"""
768-
if agent is None:
769-
self._oai_messages.clear()
769+
if recipient is None:
770+
if nr_messages_to_preserve:
771+
for key in self._oai_messages:
772+
# Remove messages from history except last `nr_messages_to_preserve` messages.
773+
self._oai_messages[key] = self._oai_messages[key][-nr_messages_to_preserve:]
774+
else:
775+
self._oai_messages.clear()
770776
else:
771-
self._oai_messages[agent].clear()
777+
self._oai_messages[recipient].clear()
778+
if nr_messages_to_preserve:
779+
print(
780+
colored(
781+
"WARNING: `nr_preserved_messages` is ignored when clearing chat history with a specific agent.",
782+
"yellow",
783+
),
784+
flush=True,
785+
)
772786

773787
def generate_oai_reply(
774788
self,

autogen/agentchat/groupchat.py

+79
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class GroupChat:
3131
- "random": the next speaker is selected randomly.
3232
- "round_robin": the next speaker is selected in a round robin fashion, i.e., iterating in the same order as provided in `agents`.
3333
- 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.
34+
- enable_clear_history: enable possibility to clear history of messages for agents manually by providing
35+
"clear history" phrase in user prompt. This is experimental feature.
36+
See description of GroupChatManager.clear_agents_history function for more info.
3437
"""
3538

3639
agents: List[Agent]
@@ -40,6 +43,7 @@ class GroupChat:
4043
func_call_filter: Optional[bool] = True
4144
speaker_selection_method: Optional[str] = "auto"
4245
allow_repeat_speaker: Optional[Union[bool, List[Agent]]] = True
46+
enable_clear_history: Optional[bool] = False
4347

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

@@ -388,6 +392,14 @@ def run_chat(
388392
raise
389393
if reply is None:
390394
break
395+
396+
# check for "clear history" phrase in reply and activate clear history function if found
397+
if (
398+
groupchat.enable_clear_history
399+
and isinstance(reply, dict)
400+
and "CLEAR HISTORY" in reply["content"].upper()
401+
):
402+
reply["content"] = self.clear_agents_history(reply["content"], groupchat)
391403
# The speaker sends the message without requesting a reply
392404
speaker.send(reply, self, request_reply=False)
393405
message = self.last_message(speaker)
@@ -464,3 +476,70 @@ def _raise_exception_on_async_reply_functions(self) -> None:
464476

465477
for agent in self._groupchat.agents:
466478
agent._raise_exception_on_async_reply_functions()
479+
480+
def clear_agents_history(self, reply: str, groupchat: GroupChat) -> str:
481+
"""Clears history of messages for all agents or selected one. Can preserve selected number of last messages.
482+
That function is called when user manually provide "clear history" phrase in his reply.
483+
When "clear history" is provided, the history of messages for all agents is cleared.
484+
When "clear history <agent_name>" is provided, the history of messages for selected agent is cleared.
485+
When "clear history <nr_of_messages_to_preserve>" is provided, the history of messages for all agents is cleared
486+
except last <nr_of_messages_to_preserve> messages.
487+
When "clear history <agent_name> <nr_of_messages_to_preserve>" is provided, the history of messages for selected
488+
agent is cleared except last <nr_of_messages_to_preserve> messages.
489+
Phrase "clear history" and optional arguments are cut out from the reply before it passed to the chat.
490+
491+
Args:
492+
reply (str): Admin reply to analyse.
493+
groupchat (GroupChat): GroupChat object.
494+
"""
495+
# Split the reply into words
496+
words = reply.split()
497+
# Find the position of "clear" to determine where to start processing
498+
clear_word_index = next(i for i in reversed(range(len(words))) if words[i].upper() == "CLEAR")
499+
# Extract potential agent name and steps
500+
words_to_check = words[clear_word_index + 2 : clear_word_index + 4]
501+
nr_messages_to_preserve = None
502+
agent_to_memory_clear = None
503+
504+
for word in words_to_check:
505+
if word.isdigit():
506+
nr_messages_to_preserve = int(word)
507+
elif word[:-1].isdigit(): # for the case when number of messages is followed by dot or other sign
508+
nr_messages_to_preserve = int(word[:-1])
509+
else:
510+
for agent in groupchat.agents:
511+
if agent.name == word:
512+
agent_to_memory_clear = agent
513+
break
514+
elif agent.name == word[:-1]: # for the case when agent name is followed by dot or other sign
515+
agent_to_memory_clear = agent
516+
break
517+
# clear history
518+
if agent_to_memory_clear:
519+
if nr_messages_to_preserve:
520+
print(
521+
f"Clearing history for {agent_to_memory_clear.name} except last {nr_messages_to_preserve} messages."
522+
)
523+
else:
524+
print(f"Clearing history for {agent_to_memory_clear.name}.")
525+
agent_to_memory_clear.clear_history(nr_messages_to_preserve=nr_messages_to_preserve)
526+
else:
527+
if nr_messages_to_preserve:
528+
print(f"Clearing history for all agents except last {nr_messages_to_preserve} messages.")
529+
# clearing history for groupchat here
530+
temp = groupchat.messages[-nr_messages_to_preserve:]
531+
groupchat.messages.clear()
532+
groupchat.messages.extend(temp)
533+
else:
534+
print("Clearing history for all agents.")
535+
# clearing history for groupchat here
536+
groupchat.messages.clear()
537+
# clearing history for agents
538+
for agent in groupchat.agents:
539+
agent.clear_history(nr_messages_to_preserve=nr_messages_to_preserve)
540+
541+
# Reconstruct the reply without the "clear history" command and parameters
542+
skip_words_number = 2 + int(bool(agent_to_memory_clear)) + int(bool(nr_messages_to_preserve))
543+
reply = " ".join(words[:clear_word_index] + words[clear_word_index + skip_words_number :])
544+
545+
return reply

test/agentchat/test_groupchat.py

+91-1
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,95 @@ def test_selection_helpers():
504504
groupchat.manual_select_speaker()
505505

506506

507+
def test_clear_agents_history():
508+
agent1 = autogen.ConversableAgent(
509+
"alice",
510+
max_consecutive_auto_reply=10,
511+
human_input_mode="NEVER",
512+
llm_config=False,
513+
default_auto_reply="This is alice speaking.",
514+
)
515+
agent2 = autogen.ConversableAgent(
516+
"bob",
517+
max_consecutive_auto_reply=10,
518+
human_input_mode="NEVER",
519+
llm_config=False,
520+
default_auto_reply="This is bob speaking.",
521+
)
522+
agent3 = autogen.ConversableAgent(
523+
"sam",
524+
max_consecutive_auto_reply=10,
525+
human_input_mode="ALWAYS",
526+
llm_config=False,
527+
)
528+
groupchat = autogen.GroupChat(agents=[agent1, agent2, agent3], messages=[], max_round=3, enable_clear_history=True)
529+
group_chat_manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=False)
530+
531+
# testing pure "clear history" statement
532+
with mock.patch.object(builtins, "input", lambda _: "clear history. How you doing?"):
533+
agent1.initiate_chat(group_chat_manager, message="hello")
534+
agent1_history = list(agent1._oai_messages.values())[0]
535+
agent2_history = list(agent2._oai_messages.values())[0]
536+
assert agent1_history == [{"content": "How you doing?", "name": "sam", "role": "user"}]
537+
assert agent2_history == [{"content": "How you doing?", "name": "sam", "role": "user"}]
538+
assert groupchat.messages == [{"content": "How you doing?", "name": "sam", "role": "user"}]
539+
540+
# testing clear history for defined agent
541+
with mock.patch.object(builtins, "input", lambda _: "clear history bob. How you doing?"):
542+
agent1.initiate_chat(group_chat_manager, message="hello")
543+
agent1_history = list(agent1._oai_messages.values())[0]
544+
agent2_history = list(agent2._oai_messages.values())[0]
545+
assert agent1_history == [
546+
{"content": "hello", "role": "assistant"},
547+
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
548+
{"content": "How you doing?", "name": "sam", "role": "user"},
549+
]
550+
assert agent2_history == [{"content": "How you doing?", "name": "sam", "role": "user"}]
551+
assert groupchat.messages == [
552+
{"content": "hello", "role": "user", "name": "alice"},
553+
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
554+
{"content": "How you doing?", "name": "sam", "role": "user"},
555+
]
556+
557+
# testing clear history with defined nr of messages to preserve
558+
with mock.patch.object(builtins, "input", lambda _: "clear history 1. How you doing?"):
559+
agent1.initiate_chat(group_chat_manager, message="hello")
560+
agent1_history = list(agent1._oai_messages.values())[0]
561+
agent2_history = list(agent2._oai_messages.values())[0]
562+
assert agent1_history == [
563+
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
564+
{"content": "How you doing?", "name": "sam", "role": "user"},
565+
]
566+
assert agent2_history == [
567+
{"content": "This is bob speaking.", "role": "assistant"},
568+
{"content": "How you doing?", "name": "sam", "role": "user"},
569+
]
570+
assert groupchat.messages == [
571+
{"content": "This is bob speaking.", "role": "user", "name": "bob"},
572+
{"content": "How you doing?", "role": "user", "name": "sam"},
573+
]
574+
575+
# testing clear history with defined agent and nr of messages to preserve
576+
with mock.patch.object(builtins, "input", lambda _: "clear history bob 1. How you doing?"):
577+
agent1.initiate_chat(group_chat_manager, message="hello")
578+
agent1_history = list(agent1._oai_messages.values())[0]
579+
agent2_history = list(agent2._oai_messages.values())[0]
580+
assert agent1_history == [
581+
{"content": "hello", "role": "assistant"},
582+
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
583+
{"content": "How you doing?", "name": "sam", "role": "user"},
584+
]
585+
assert agent2_history == [
586+
{"content": "This is bob speaking.", "role": "assistant"},
587+
{"content": "How you doing?", "name": "sam", "role": "user"},
588+
]
589+
assert groupchat.messages == [
590+
{"content": "hello", "name": "alice", "role": "user"},
591+
{"content": "This is bob speaking.", "name": "bob", "role": "user"},
592+
{"content": "How you doing?", "name": "sam", "role": "user"},
593+
]
594+
595+
507596
if __name__ == "__main__":
508597
# test_func_call_groupchat()
509598
# test_broadcast()
@@ -514,4 +603,5 @@ def test_selection_helpers():
514603
# test_agent_mentions()
515604
# test_termination()
516605
# test_next_agent()
517-
test_invalid_allow_repeat_speaker()
606+
# test_invalid_allow_repeat_speaker()
607+
test_clear_agents_history()

0 commit comments

Comments
 (0)