Files
ai-video-fullstack/backend/routes/conversations.py
Xin Wang ba59ea447c Add conversation deletion functionality and update HistoryPage
- Implement a new API endpoint for deleting conversations in the backend.
- Enhance the HistoryPage component to include a dropdown menu for conversation actions, allowing users to delete conversations.
- Update the pagination logic to handle conversation removal and improve user experience during deletion.
- Adjust the loading state and error handling for the delete operation, ensuring smooth interaction.
2026-07-10 16:38:59 +08:00

131 lines
4.0 KiB
Python

"""对话历史查询 API。"""
from db.models import ConversationMessage, ConversationSession
from db.session import get_session
from fastapi import APIRouter, Depends, HTTPException, Query
from schemas import (
ConversationDetailOut,
ConversationListOut,
ConversationMessageOut,
ConversationOut,
)
from services.auth import require_admin
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
router = APIRouter(
prefix="/api/conversations",
tags=["conversations"],
dependencies=[Depends(require_admin)],
)
def _session_out(row: ConversationSession) -> ConversationOut:
return ConversationOut(
id=row.id,
assistant_id=row.assistant_id,
assistant_name=row.assistant_name,
channel=row.channel,
runtime_mode=row.runtime_mode,
status=row.status,
message_count=row.message_count,
started_at=row.started_at,
ended_at=row.ended_at,
)
@router.get("", response_model=ConversationListOut)
async def list_conversations(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
assistant_id: str | None = None,
search: str | None = Query(None, max_length=128),
channel: str | None = None,
status: str | None = None,
sort_order: str = Query("newest", pattern="^(newest|oldest)$"),
session: AsyncSession = Depends(get_session),
):
filters = []
if assistant_id:
filters.append(ConversationSession.assistant_id == assistant_id)
if search and search.strip():
keyword = f"%{search.strip()}%"
filters.append(
or_(
ConversationSession.assistant_name.ilike(keyword),
ConversationSession.id.ilike(keyword),
)
)
if channel:
filters.append(ConversationSession.channel == channel)
if status:
filters.append(ConversationSession.status == status)
total = await session.scalar(
select(func.count()).select_from(ConversationSession).where(*filters)
)
rows = (
await session.execute(
select(ConversationSession)
.where(*filters)
.order_by(
ConversationSession.started_at.asc()
if sort_order == "oldest"
else ConversationSession.started_at.desc()
)
.offset((page - 1) * page_size)
.limit(page_size)
)
).scalars().all()
return ConversationListOut(
items=[_session_out(row) for row in rows],
total=int(total or 0),
page=page,
page_size=page_size,
)
@router.get("/{conversation_id}", response_model=ConversationDetailOut)
async def get_conversation(
conversation_id: str,
session: AsyncSession = Depends(get_session),
):
conversation = await session.get(ConversationSession, conversation_id)
if not conversation:
raise HTTPException(404, "对话记录不存在")
messages = (
await session.execute(
select(ConversationMessage)
.where(ConversationMessage.session_id == conversation_id)
.order_by(ConversationMessage.sequence)
)
).scalars().all()
return ConversationDetailOut(
**_session_out(conversation).model_dump(),
messages=[
ConversationMessageOut(
id=message.id,
sequence=message.sequence,
role=message.role,
content_type=message.content_type,
content=message.content,
occurred_at=message.occurred_at,
extra=message.extra or {},
)
for message in messages
],
)
@router.delete("/{conversation_id}")
async def delete_conversation(
conversation_id: str,
session: AsyncSession = Depends(get_session),
):
conversation = await session.get(ConversationSession, conversation_id)
if not conversation:
raise HTTPException(404, "对话记录不存在")
await session.delete(conversation)
await session.commit()
return {"ok": True}