You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

app_team.py 8.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. from typing import List, cast
  2. import chainlit as cl
  3. import yaml
  4. import uuid
  5. import string
  6. import asyncio
  7. from autogen_core import (
  8. ClosureAgent,
  9. ClosureContext,
  10. DefaultTopicId,
  11. MessageContext,
  12. message_handler,
  13. RoutedAgent,
  14. SingleThreadedAgentRuntime,
  15. TopicId,
  16. TypeSubscription,
  17. )
  18. from autogen_core.models import (
  19. AssistantMessage,
  20. ChatCompletionClient,
  21. CreateResult,
  22. #LLMMessage,
  23. UserMessage,
  24. )
  25. from SimpleAssistantAgent import SimpleAssistantAgent, StreamResult, GroupChatMessage, RequestToSpeak
  26. assistant_topic_type = "assistant"
  27. critic_topic_type = "critic"
  28. group_chat_topic_type = "group_chat"
  29. TASK_RESULTS_TOPIC_TYPE = "task-results"
  30. task_results_topic_id = TopicId(type=TASK_RESULTS_TOPIC_TYPE, source="default")
  31. CLOSURE_AGENT_TYPE = "collect_result_agent"
  32. class GroupChatManager(RoutedAgent):
  33. def __init__(
  34. self,
  35. participant_topic_types: List[str],
  36. model_client: ChatCompletionClient,
  37. ) -> None:
  38. super().__init__("Group chat manager")
  39. self._participant_topic_types = participant_topic_types
  40. self._model_client = model_client
  41. self._chat_history: List[UserMessage] = []
  42. self._previous_participant_idx = -1
  43. @message_handler
  44. async def handle_message(self, message: GroupChatMessage, ctx: MessageContext) -> None:
  45. assert isinstance(message.body, UserMessage)
  46. self._chat_history.append(message.body)
  47. # If the message is an approval message from the user, stop the chat.
  48. if message.body.source == "User":
  49. assert isinstance(message.body.content, str)
  50. if message.body.content.lower().strip(string.punctuation).endswith("approve"): # type: ignore
  51. await self.runtime.publish_message(StreamResult(content="stop", source=self.id.type), topic_id=task_results_topic_id)
  52. return
  53. if message.body.source == "Critic":
  54. #if ("approve" in message.body.content.lower().strip(string.punctuation)):
  55. if message.body.content.lower().strip(string.punctuation).endswith("approve"): # type: ignore
  56. stop_msg = AssistantMessage(content="Task Finished", source=self.id.type)
  57. await self.runtime.publish_message(StreamResult(content=stop_msg, source=self.id.type), topic_id=task_results_topic_id)
  58. return
  59. # Simple round robin algorithm to call next client to speak
  60. selected_topic_type: str
  61. idx = self._previous_participant_idx +1
  62. if (idx == len(self._participant_topic_types)):
  63. idx = 0
  64. selected_topic_type = self._participant_topic_types[idx]
  65. self._previous_participant_idx = idx
  66. # Send the RequestToSpeak message to next agent
  67. await self.publish_message(RequestToSpeak(), DefaultTopicId(type=selected_topic_type))
  68. # Function called when closure agent receives message. It put the messages to the output queue
  69. async def output_result(_agent: ClosureContext, message: StreamResult, ctx: MessageContext) -> None:
  70. queue = cast(asyncio.Queue[StreamResult], cl.user_session.get("queue_stream")) # type: ignore
  71. await queue.put(message)
  72. @cl.on_chat_start # type: ignore
  73. async def start_chat() -> None:
  74. # Load model configuration and create the model client.
  75. with open("model_config.yaml", "r") as f:
  76. model_config = yaml.safe_load(f)
  77. model_client = ChatCompletionClient.load_component(model_config)
  78. runtime = SingleThreadedAgentRuntime()
  79. cl.user_session.set("run_time", runtime) # type: ignore
  80. queue = asyncio.Queue[StreamResult]()
  81. cl.user_session.set("queue_stream", queue) # type: ignore
  82. # Create the assistant agent.
  83. assistant_agent_type = await SimpleAssistantAgent.register(runtime, "Assistant", lambda: SimpleAssistantAgent(
  84. name="Assistant",
  85. group_chat_topic_type=group_chat_topic_type,
  86. model_client=model_client,
  87. system_message="You are a helpful assistant",
  88. model_client_stream=True, # Enable model client streaming.
  89. ))
  90. # Assistant agent listen to assistant topic and group chat topic
  91. await runtime.add_subscription(TypeSubscription(topic_type=assistant_topic_type, agent_type=assistant_agent_type.type))
  92. await runtime.add_subscription(TypeSubscription(topic_type=group_chat_topic_type, agent_type=assistant_agent_type.type))
  93. # Create the critic agent.
  94. critic_agent_type = await SimpleAssistantAgent.register(runtime, "Critic", lambda: SimpleAssistantAgent(
  95. name="Critic",
  96. group_chat_topic_type=group_chat_topic_type,
  97. model_client=model_client,
  98. system_message="You are a critic. Provide constructive feedback. Respond with 'APPROVE' if your feedback has been addressed.",
  99. model_client_stream=True, # Enable model client streaming.
  100. ))
  101. # Critic agent listen to critic topic and group chat topic
  102. await runtime.add_subscription(TypeSubscription(topic_type=critic_topic_type, agent_type=critic_agent_type.type))
  103. await runtime.add_subscription(TypeSubscription(topic_type=group_chat_topic_type, agent_type=critic_agent_type.type))
  104. # Chain the assistant and critic agents using group_chat_manager.
  105. group_chat_manager_type = await GroupChatManager.register(
  106. runtime,
  107. "group_chat_manager",
  108. lambda: GroupChatManager(
  109. participant_topic_types=[assistant_topic_type, critic_topic_type],
  110. model_client=model_client,
  111. ),
  112. )
  113. await runtime.add_subscription(
  114. TypeSubscription(topic_type=group_chat_topic_type, agent_type=group_chat_manager_type.type)
  115. )
  116. # Register the Closure Agent, it will place streamed response into the output queue by calling output_result function
  117. await ClosureAgent.register_closure(
  118. runtime, CLOSURE_AGENT_TYPE, output_result, subscriptions=lambda:[TypeSubscription(topic_type=TASK_RESULTS_TOPIC_TYPE, agent_type=CLOSURE_AGENT_TYPE)]
  119. )
  120. runtime.start() # Start processing messages in the background.
  121. cl.user_session.set("prompt_history", "") # type: ignore
  122. @cl.set_starters # type: ignore
  123. async def set_starts() -> List[cl.Starter]:
  124. return [
  125. cl.Starter(
  126. label="Poem Writing",
  127. message="Write a poem about the ocean.",
  128. ),
  129. cl.Starter(
  130. label="Story Writing",
  131. message="Write a story about a detective solving a mystery.",
  132. ),
  133. cl.Starter(
  134. label="Write Code",
  135. message="Write a function that merge two list of numbers into single sorted list.",
  136. ),
  137. ]
  138. async def pass_msg_to_ui() -> None:
  139. queue = cast(asyncio.Queue[StreamResult], cl.user_session.get("queue_stream")) # type: ignore
  140. ui_resp = cl.Message("")
  141. first_message = True
  142. while True:
  143. stream_msg = await queue.get()
  144. if (isinstance(stream_msg.content, str)):
  145. if (first_message):
  146. ui_resp = cl.Message(content= stream_msg.source + ": ")
  147. first_message = False
  148. await ui_resp.stream_token(stream_msg.content)
  149. elif (isinstance(stream_msg.content, CreateResult)):
  150. await ui_resp.send()
  151. ui_resp = cl.Message("")
  152. first_message = True
  153. else:
  154. # This is a stop meesage
  155. if (stream_msg.content.content == "stop"):
  156. break
  157. break
  158. @cl.on_message # type: ignore
  159. async def chat(message: cl.Message) -> None:
  160. # Construct the response message.
  161. # Get the runtime and queue from the session
  162. runtime = cast(SingleThreadedAgentRuntime, cl.user_session.get("run_time")) # type: ignore
  163. queue = cast(asyncio.Queue[StreamResult], cl.user_session.get("queue_stream")) # type: ignore
  164. output_msg = cl.Message(content="")
  165. cl.user_session.set("output_msg", output_msg) # type: ignore
  166. # Publish the user message to the Group Chat
  167. session_id = str(uuid.uuid4())
  168. await runtime.publish_message( GroupChatMessage( body=UserMessage(
  169. content=message.content,
  170. source="User",
  171. )
  172. ),
  173. TopicId(type=group_chat_topic_type, source=session_id),)
  174. task1 = asyncio.create_task( pass_msg_to_ui())
  175. await task1