Files
agent_alpha/backend/routes/chat.py
T
furyhawk dc4ed7c48d feat: add user management API with CRUD operations
- Implemented SQLAlchemy ORM models for User with role-based access control.
- Created API routes for user creation, retrieval, updating, and listing.
- Added input and output schemas for user data handling.
- Included session management for users.
2026-06-12 21:08:28 +08:00

124 lines
3.6 KiB
Python

"""Chat API endpoint — sends prompts to the agent and returns replies.
Messages are persisted in Valkey so chat history survives server restarts.
Sessions can optionally be linked to a user for session/role management.
"""
from __future__ import annotations
import uuid
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from backend.core.agent import AgentService
from backend.core.database import (
get_session_messages,
get_session_user_id,
list_sessions,
save_message,
)
from backend.core.dependencies import get_agent_service
router = APIRouter(prefix="/api/chat", tags=["chat"])
class ChatRequest(BaseModel):
message: str
session_id: str | None = None
user_id: str | None = None
class ChatResponse(BaseModel):
reply: str
session_id: str
class MessageOut(BaseModel):
role: str
content: str
timestamp: str
class SessionOut(BaseModel):
session_id: str
message_count: int
user_id: str | None = None
# ── POST /api/chat ────────────────────────────────────────────────────────
@router.post("", response_model=ChatResponse)
async def chat_endpoint(
body: ChatRequest,
agent: AgentService = Depends(get_agent_service),
) -> ChatResponse:
"""Send a user message to the agent and return its reply.
Saves both the user message and the assistant reply to Valkey.
Assigns a new UUID ``session_id`` if none was provided.
If ``user_id`` is provided, the session is linked to that user.
"""
session_id = body.session_id or uuid.uuid4().hex
try:
# Persist the user message (linked to user if provided).
await save_message(
session_id,
"user",
body.message,
user_id=body.user_id,
)
# Ask the agent.
output = await agent.ask(body.message, session_id=session_id)
# Persist the assistant reply.
await save_message(session_id, "assistant", output)
return ChatResponse(reply=output, session_id=session_id)
except Exception as exc:
raise HTTPException(status_code=500, detail=str(exc))
# ── GET /api/chat/history ─────────────────────────────────────────────────
@router.get("/history", response_model=list[MessageOut])
async def get_history(session_id: str) -> list[MessageOut]:
"""Return all messages for a given session."""
messages = await get_session_messages(session_id)
return [MessageOut(**msg) for msg in messages]
# ── GET /api/chat/sessions ────────────────────────────────────────────────
@router.get("/sessions", response_model=list[SessionOut])
async def sessions_list(
user_id: str | None = Query(None, description="Filter by user ID"),
) -> list[SessionOut]:
"""Return all known session IDs with their message counts.
If ``user_id`` is provided, only sessions belonging to that user
are returned.
"""
if user_id is not None:
from backend.core.database import list_user_sessions
ids = await list_user_sessions(user_id)
else:
ids = await list_sessions()
result: list[SessionOut] = []
for sid in ids:
msgs = await get_session_messages(sid)
uid = await get_session_user_id(sid)
result.append(
SessionOut(
session_id=sid, message_count=len(msgs), user_id=uid
)
)
return result