Download app/chatkit/server.py from umar-30/AI-todo: direct link, hf CLI and curl.
- Browser
- Download file 6.99 kB
-
https://huggingface.co/spaces/umar-30/AI-todo/resolve/main/app/chatkit/server.py
- Command line
-
hf download hf://spaces/umar-30/AI-todo/app/chatkit/server.py
-
curl -L -o server.py https://huggingface.co/spaces/umar-30/AI-todo/resolve/main/app/chatkit/server.py
6.99 kB
| """ | |
| ChatKit Server implementation for Todo AI Chatbot. | |
| Wraps the TaskAgent to provide ChatKit-compatible responses. | |
| """ | |
| import json | |
| import logging | |
| from typing import Any, AsyncGenerator | |
| from fastapi import APIRouter, Request | |
| from fastapi.responses import StreamingResponse | |
| from pydantic import BaseModel | |
| from ..agent import create_task_agent | |
| from .store import ChatKitStore | |
| logger = logging.getLogger(__name__) | |
| class ChatKitRequest(BaseModel): | |
| """ChatKit request format.""" | |
| type: str # e.g., "message", "thread.create", "thread.messages" | |
| thread_id: str | None = None | |
| message: dict[str, Any] | None = None | |
| metadata: dict[str, Any] | None = None | |
| class TodoChatKitServer: | |
| """ | |
| ChatKit server implementation that delegates to TaskAgent. | |
| Handles ChatKit protocol requests and streams responses using SSE. | |
| """ | |
| def __init__(self, user_id: str = "demo-user"): | |
| """ | |
| Initialize the ChatKit server. | |
| Args: | |
| user_id: Default user ID for operations | |
| """ | |
| self.user_id = user_id | |
| self.store = ChatKitStore(user_id=user_id) | |
| self.agent = create_task_agent() | |
| async def handle_request(self, request: ChatKitRequest) -> AsyncGenerator[str, None]: | |
| """ | |
| Handle a ChatKit request and yield SSE events. | |
| Args: | |
| request: ChatKit request object | |
| Yields: | |
| SSE event strings | |
| """ | |
| request_type = request.type | |
| thread_id = request.thread_id | |
| message = request.message | |
| metadata = request.metadata or {} | |
| # Extract user_id from metadata if provided | |
| user_id = metadata.get("user_id", self.user_id) | |
| try: | |
| if request_type == "thread.create": | |
| # Create a new thread | |
| thread = self.store.create_thread(user_id=user_id) | |
| yield self._format_event("thread.created", {"thread": thread}) | |
| elif request_type == "thread.messages": | |
| # Get messages for a thread | |
| if not thread_id: | |
| yield self._format_event("error", {"message": "thread_id required"}) | |
| return | |
| messages = self.store.get_messages(thread_id) | |
| yield self._format_event("thread.messages", { | |
| "thread_id": thread_id, | |
| "messages": messages, | |
| }) | |
| elif request_type == "message": | |
| # Process a message through the agent | |
| if not message or not message.get("content"): | |
| yield self._format_event("error", {"message": "message content required"}) | |
| return | |
| content = message["content"] | |
| # Create thread if not provided | |
| if not thread_id: | |
| thread = self.store.create_thread(user_id=user_id) | |
| thread_id = thread["id"] | |
| yield self._format_event("thread.created", {"thread": thread}) | |
| # Store user message | |
| user_msg = self.store.add_message(thread_id, "user", content) | |
| yield self._format_event("message.created", { | |
| "message": user_msg, | |
| "thread_id": thread_id, | |
| }) | |
| # Get context from conversation history | |
| history = self.store.get_messages(thread_id) | |
| context = { | |
| "conversation_history": history[:-1], # Exclude the just-added user message | |
| } | |
| # Run the agent | |
| try: | |
| result = await self.agent.run( | |
| message=content, | |
| user_id=user_id, | |
| context=context, | |
| ) | |
| response_text = result.get("response", "") | |
| tool_calls = result.get("tool_calls", []) | |
| # Emit tool events if any | |
| for tc in tool_calls: | |
| yield self._format_event("tool.started", { | |
| "name": tc.get("name"), | |
| "arguments": tc.get("arguments"), | |
| }) | |
| yield self._format_event("tool.completed", { | |
| "name": tc.get("name"), | |
| "result": tc.get("result"), | |
| }) | |
| # Store assistant message | |
| assistant_msg = self.store.add_message( | |
| thread_id, "assistant", response_text | |
| ) | |
| # Emit response | |
| yield self._format_event("message.created", { | |
| "message": assistant_msg, | |
| "thread_id": thread_id, | |
| }) | |
| yield self._format_event("message.completed", { | |
| "thread_id": thread_id, | |
| }) | |
| except Exception as e: | |
| logger.error(f"Agent error: {e}") | |
| yield self._format_event("error", { | |
| "message": f"Agent error: {str(e)}", | |
| }) | |
| else: | |
| yield self._format_event("error", { | |
| "message": f"Unknown request type: {request_type}", | |
| }) | |
| except Exception as e: | |
| logger.error(f"ChatKit server error: {e}") | |
| yield self._format_event("error", {"message": str(e)}) | |
| def _format_event(self, event: str, data: dict[str, Any]) -> str: | |
| """ | |
| Format an SSE event. | |
| Args: | |
| event: Event type | |
| data: Event data | |
| Returns: | |
| SSE-formatted string | |
| """ | |
| return f"event: {event}\ndata: {json.dumps(data)}\n\n" | |
| def create_chatkit_endpoint() -> APIRouter: | |
| """ | |
| Create the ChatKit API router. | |
| Returns: | |
| FastAPI APIRouter with /chatkit endpoint | |
| """ | |
| router = APIRouter(tags=["ChatKit"]) | |
| async def chatkit_endpoint(request: Request): | |
| """ | |
| ChatKit message endpoint. | |
| Receives messages from ChatKit frontend and returns streamed responses. | |
| """ | |
| try: | |
| body = await request.json() | |
| chatkit_request = ChatKitRequest(**body) | |
| except Exception as e: | |
| logger.error(f"Invalid request: {e}") | |
| return StreamingResponse( | |
| iter([f"event: error\ndata: {json.dumps({'message': str(e)})}\n\n"]), | |
| media_type="text/event-stream", | |
| ) | |
| # Extract user_id from headers or use default | |
| user_id = request.headers.get("X-User-Id", "demo-user") | |
| server = TodoChatKitServer(user_id=user_id) | |
| return StreamingResponse( | |
| server.handle_request(chatkit_request), | |
| media_type="text/event-stream", | |
| ) | |
| return router | |