|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- from typing import List, cast
- import chainlit as cl
- import yaml
- import uuid
- import string
- import asyncio
-
- from autogen_core import (
- ClosureAgent,
- ClosureContext,
- DefaultTopicId,
- MessageContext,
- message_handler,
- RoutedAgent,
- SingleThreadedAgentRuntime,
- TopicId,
- TypeSubscription,
- )
- from autogen_core.models import (
- AssistantMessage,
- ChatCompletionClient,
- CreateResult,
- #LLMMessage,
- UserMessage,
- )
-
- from SimpleAssistantAgent import SimpleAssistantAgent, StreamResult, GroupChatMessage, RequestToSpeak
-
- assistant_topic_type = "assistant"
- critic_topic_type = "critic"
- group_chat_topic_type = "group_chat"
-
- TASK_RESULTS_TOPIC_TYPE = "task-results"
- task_results_topic_id = TopicId(type=TASK_RESULTS_TOPIC_TYPE, source="default")
- CLOSURE_AGENT_TYPE = "collect_result_agent"
-
- class GroupChatManager(RoutedAgent):
- def __init__(
- self,
- participant_topic_types: List[str],
- model_client: ChatCompletionClient,
- ) -> None:
- super().__init__("Group chat manager")
- self._participant_topic_types = participant_topic_types
- self._model_client = model_client
- self._chat_history: List[UserMessage] = []
- self._previous_participant_idx = -1
-
- @message_handler
- async def handle_message(self, message: GroupChatMessage, ctx: MessageContext) -> None:
- assert isinstance(message.body, UserMessage)
- self._chat_history.append(message.body)
- # If the message is an approval message from the user, stop the chat.
- if message.body.source == "User":
- assert isinstance(message.body.content, str)
- if message.body.content.lower().strip(string.punctuation).endswith("approve"): # type: ignore
- await self.runtime.publish_message(StreamResult(content="stop", source=self.id.type), topic_id=task_results_topic_id)
- return
- if message.body.source == "Critic":
- #if ("approve" in message.body.content.lower().strip(string.punctuation)):
- if message.body.content.lower().strip(string.punctuation).endswith("approve"): # type: ignore
- stop_msg = AssistantMessage(content="Task Finished", source=self.id.type)
- await self.runtime.publish_message(StreamResult(content=stop_msg, source=self.id.type), topic_id=task_results_topic_id)
- return
-
- # Simple round robin algorithm to call next client to speak
- selected_topic_type: str
- idx = self._previous_participant_idx +1
- if (idx == len(self._participant_topic_types)):
- idx = 0
- selected_topic_type = self._participant_topic_types[idx]
- self._previous_participant_idx = idx
-
- # Send the RequestToSpeak message to next agent
- await self.publish_message(RequestToSpeak(), DefaultTopicId(type=selected_topic_type))
-
- # Function called when closure agent receives message. It put the messages to the output queue
- async def output_result(_agent: ClosureContext, message: StreamResult, ctx: MessageContext) -> None:
- queue = cast(asyncio.Queue[StreamResult], cl.user_session.get("queue_stream")) # type: ignore
- await queue.put(message)
-
- @cl.on_chat_start # type: ignore
- async def start_chat() -> None:
-
- # Load model configuration and create the model client.
- with open("model_config.yaml", "r") as f:
- model_config = yaml.safe_load(f)
- model_client = ChatCompletionClient.load_component(model_config)
-
- runtime = SingleThreadedAgentRuntime()
- cl.user_session.set("run_time", runtime) # type: ignore
- queue = asyncio.Queue[StreamResult]()
- cl.user_session.set("queue_stream", queue) # type: ignore
-
- # Create the assistant agent.
- assistant_agent_type = await SimpleAssistantAgent.register(runtime, "Assistant", lambda: SimpleAssistantAgent(
- name="Assistant",
- group_chat_topic_type=group_chat_topic_type,
- model_client=model_client,
- system_message="You are a helpful assistant",
- model_client_stream=True, # Enable model client streaming.
- ))
-
- # Assistant agent listen to assistant topic and group chat topic
- await runtime.add_subscription(TypeSubscription(topic_type=assistant_topic_type, agent_type=assistant_agent_type.type))
- await runtime.add_subscription(TypeSubscription(topic_type=group_chat_topic_type, agent_type=assistant_agent_type.type))
-
- # Create the critic agent.
- critic_agent_type = await SimpleAssistantAgent.register(runtime, "Critic", lambda: SimpleAssistantAgent(
- name="Critic",
- group_chat_topic_type=group_chat_topic_type,
- model_client=model_client,
- system_message="You are a critic. Provide constructive feedback. Respond with 'APPROVE' if your feedback has been addressed.",
- model_client_stream=True, # Enable model client streaming.
- ))
-
- # Critic agent listen to critic topic and group chat topic
- await runtime.add_subscription(TypeSubscription(topic_type=critic_topic_type, agent_type=critic_agent_type.type))
- await runtime.add_subscription(TypeSubscription(topic_type=group_chat_topic_type, agent_type=critic_agent_type.type))
-
- # Chain the assistant and critic agents using group_chat_manager.
- group_chat_manager_type = await GroupChatManager.register(
- runtime,
- "group_chat_manager",
- lambda: GroupChatManager(
- participant_topic_types=[assistant_topic_type, critic_topic_type],
- model_client=model_client,
- ),
- )
- await runtime.add_subscription(
- TypeSubscription(topic_type=group_chat_topic_type, agent_type=group_chat_manager_type.type)
- )
-
- # Register the Closure Agent, it will place streamed response into the output queue by calling output_result function
- await ClosureAgent.register_closure(
- runtime, CLOSURE_AGENT_TYPE, output_result, subscriptions=lambda:[TypeSubscription(topic_type=TASK_RESULTS_TOPIC_TYPE, agent_type=CLOSURE_AGENT_TYPE)]
- )
- runtime.start() # Start processing messages in the background.
-
- cl.user_session.set("prompt_history", "") # type: ignore
-
-
- @cl.set_starters # type: ignore
- async def set_starts() -> List[cl.Starter]:
- return [
- cl.Starter(
- label="Poem Writing",
- message="Write a poem about the ocean.",
- ),
- cl.Starter(
- label="Story Writing",
- message="Write a story about a detective solving a mystery.",
- ),
- cl.Starter(
- label="Write Code",
- message="Write a function that merge two list of numbers into single sorted list.",
- ),
- ]
-
- async def pass_msg_to_ui() -> None:
- queue = cast(asyncio.Queue[StreamResult], cl.user_session.get("queue_stream")) # type: ignore
- ui_resp = cl.Message("")
- first_message = True
- while True:
- stream_msg = await queue.get()
- if (isinstance(stream_msg.content, str)):
- if (first_message):
- ui_resp = cl.Message(content= stream_msg.source + ": ")
- first_message = False
- await ui_resp.stream_token(stream_msg.content)
- elif (isinstance(stream_msg.content, CreateResult)):
- await ui_resp.send()
- ui_resp = cl.Message("")
- first_message = True
- else:
- # This is a stop meesage
- if (stream_msg.content.content == "stop"):
- break
- break
-
-
- @cl.on_message # type: ignore
- async def chat(message: cl.Message) -> None:
- # Construct the response message.
-
- # Get the runtime and queue from the session
- runtime = cast(SingleThreadedAgentRuntime, cl.user_session.get("run_time")) # type: ignore
- queue = cast(asyncio.Queue[StreamResult], cl.user_session.get("queue_stream")) # type: ignore
- output_msg = cl.Message(content="")
- cl.user_session.set("output_msg", output_msg) # type: ignore
-
- # Publish the user message to the Group Chat
- session_id = str(uuid.uuid4())
- await runtime.publish_message( GroupChatMessage( body=UserMessage(
- content=message.content,
- source="User",
- )
- ),
- TopicId(type=group_chat_topic_type, source=session_id),)
- task1 = asyncio.create_task( pass_msg_to_ui())
- await task1
|