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 5.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import json
  2. import logging
  3. import os
  4. from typing import Any, Awaitable, Callable, Optional
  5. import aiofiles
  6. import yaml
  7. from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
  8. from autogen_agentchat.base import TaskResult
  9. from autogen_agentchat.messages import TextMessage, UserInputRequestedEvent
  10. from autogen_agentchat.teams import RoundRobinGroupChat
  11. from autogen_core import CancellationToken
  12. from autogen_core.models import ChatCompletionClient
  13. from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
  14. from fastapi.middleware.cors import CORSMiddleware
  15. from fastapi.responses import FileResponse
  16. from fastapi.staticfiles import StaticFiles
  17. logger = logging.getLogger(__name__)
  18. app = FastAPI()
  19. # Add CORS middleware
  20. app.add_middleware(
  21. CORSMiddleware,
  22. allow_origins=["*"], # Allows all origins
  23. allow_credentials=True,
  24. allow_methods=["*"], # Allows all methods
  25. allow_headers=["*"], # Allows all headers
  26. )
  27. model_config_path = "model_config.yaml"
  28. state_path = "team_state.json"
  29. history_path = "team_history.json"
  30. # Serve static files
  31. app.mount("/static", StaticFiles(directory="."), name="static")
  32. @app.get("/")
  33. async def root():
  34. """Serve the chat interface HTML file."""
  35. return FileResponse("app_team.html")
  36. async def get_team(
  37. user_input_func: Callable[[str, Optional[CancellationToken]], Awaitable[str]],
  38. ) -> RoundRobinGroupChat:
  39. # Get model client from config.
  40. async with aiofiles.open(model_config_path, "r") as file:
  41. model_config = yaml.safe_load(await file.read())
  42. model_client = ChatCompletionClient.load_component(model_config)
  43. # Create the team.
  44. agent = AssistantAgent(
  45. name="assistant",
  46. model_client=model_client,
  47. system_message="You are a helpful assistant.",
  48. )
  49. yoda = AssistantAgent(
  50. name="yoda",
  51. model_client=model_client,
  52. system_message="Repeat the same message in the tone of Yoda.",
  53. )
  54. user_proxy = UserProxyAgent(
  55. name="user",
  56. input_func=user_input_func, # Use the user input function.
  57. )
  58. team = RoundRobinGroupChat(
  59. [agent, yoda, user_proxy],
  60. )
  61. # Load state from file.
  62. if not os.path.exists(state_path):
  63. return team
  64. async with aiofiles.open(state_path, "r") as file:
  65. state = json.loads(await file.read())
  66. await team.load_state(state)
  67. return team
  68. async def get_history() -> list[dict[str, Any]]:
  69. """Get chat history from file."""
  70. if not os.path.exists(history_path):
  71. return []
  72. async with aiofiles.open(history_path, "r") as file:
  73. return json.loads(await file.read())
  74. @app.get("/history")
  75. async def history() -> list[dict[str, Any]]:
  76. try:
  77. return await get_history()
  78. except Exception as e:
  79. raise HTTPException(status_code=500, detail=str(e)) from e
  80. @app.websocket("/ws/chat")
  81. async def chat(websocket: WebSocket):
  82. await websocket.accept()
  83. # User input function used by the team.
  84. async def _user_input(prompt: str, cancellation_token: CancellationToken | None) -> str:
  85. data = await websocket.receive_json()
  86. message = TextMessage.model_validate(data)
  87. return message.content
  88. try:
  89. while True:
  90. # Get user message.
  91. data = await websocket.receive_json()
  92. request = TextMessage.model_validate(data)
  93. try:
  94. # Get the team and respond to the message.
  95. team = await get_team(_user_input)
  96. history = await get_history()
  97. stream = team.run_stream(task=request)
  98. async for message in stream:
  99. if isinstance(message, TaskResult):
  100. continue
  101. await websocket.send_json(message.model_dump())
  102. if not isinstance(message, UserInputRequestedEvent):
  103. # Don't save user input events to history.
  104. history.append(message.model_dump())
  105. # Save team state to file.
  106. async with aiofiles.open(state_path, "w") as file:
  107. state = await team.save_state()
  108. await file.write(json.dumps(state))
  109. # Save chat history to file.
  110. async with aiofiles.open(history_path, "w") as file:
  111. await file.write(json.dumps(history))
  112. except Exception as e:
  113. # Send error message to client
  114. error_message = {
  115. "type": "error",
  116. "content": f"Error: {str(e)}",
  117. "source": "system"
  118. }
  119. await websocket.send_json(error_message)
  120. # Re-enable input after error
  121. await websocket.send_json({
  122. "type": "UserInputRequestedEvent",
  123. "content": "An error occurred. Please try again.",
  124. "source": "system"
  125. })
  126. except WebSocketDisconnect:
  127. logger.info("Client disconnected")
  128. except Exception as e:
  129. logger.error(f"Unexpected error: {str(e)}")
  130. try:
  131. await websocket.send_json({
  132. "type": "error",
  133. "content": f"Unexpected error: {str(e)}",
  134. "source": "system"
  135. })
  136. except:
  137. pass
  138. # Example usage
  139. if __name__ == "__main__":
  140. import uvicorn
  141. uvicorn.run(app, host="0.0.0.0", port=8002)