""" Agent 会话管理 API 提供会话重命名、置顶、删除、导出功能 """ from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import PlainTextResponse from pydantic import BaseModel, Field from typing import Optional from sqlalchemy.orm import Session import json from app.core.database import get_db from app.api.auth import get_current_user from app.api.deps import get_current_workspace_id from app.models.user import User from app.models.agent import Agent from app.models.agent_session import AgentSession from app.models.chat_message import ChatMessage router = APIRouter(prefix="/api/v1/agent-chat", tags=["agent-sessions"]) class UpdateSessionRequest(BaseModel): title: Optional[str] = Field(default=None, description="会话标题") is_pinned: Optional[bool] = Field(default=None, description="是否置顶") class SessionResponse(BaseModel): id: str title: Optional[str] = None is_pinned: bool = False created_at: Optional[str] = None updated_at: Optional[str] = None @router.patch("/{agent_id}/sessions/{session_id}", response_model=SessionResponse) async def update_session( agent_id: str, session_id: str, req: UpdateSessionRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), workspace_id: str = Depends(get_current_workspace_id), ): """更新会话信息(重命名 / 置顶)""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise HTTPException(status_code=404, detail="Agent 不存在") session = db.query(AgentSession).filter( AgentSession.id == session_id, AgentSession.agent_id == agent_id, ).first() if not session: session = AgentSession( id=session_id, user_id=current_user.id, agent_id=agent_id, workspace_id=workspace_id or None, ) db.add(session) if req.title is not None: session.title = req.title if req.is_pinned is not None: session.is_pinned = req.is_pinned db.commit() db.refresh(session) return SessionResponse( id=session.id, title=session.title, is_pinned=session.is_pinned, created_at=session.created_at.isoformat() if session.created_at else None, updated_at=session.updated_at.isoformat() if session.updated_at else None, ) @router.delete("/{agent_id}/sessions/{session_id}") async def delete_session( agent_id: str, session_id: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db), workspace_id: str = Depends(get_current_workspace_id), ): """删除会话及所有关联消息""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise HTTPException(status_code=404, detail="Agent 不存在") session_filter = [ AgentSession.id == session_id, AgentSession.agent_id == agent_id, ] if workspace_id: session_filter.append(AgentSession.workspace_id == workspace_id) db.query(AgentSession).filter(*session_filter).delete() msg_filter = [ ChatMessage.agent_id == agent_id, ChatMessage.session_id == session_id, ] if workspace_id: msg_filter.append(ChatMessage.workspace_id == workspace_id) deleted_count = db.query(ChatMessage).filter(*msg_filter).delete() db.commit() return {"deleted": True, "messages_deleted": deleted_count} @router.get("/{agent_id}/sessions/{session_id}/export") async def export_session( agent_id: str, session_id: str, format: str = Query("json", pattern="^(json|markdown)$"), current_user: User = Depends(get_current_user), db: Session = Depends(get_db), workspace_id: str = Depends(get_current_workspace_id), ): """导出会话为 JSON 或 Markdown 格式""" agent = db.query(Agent).filter(Agent.id == agent_id).first() if not agent: raise HTTPException(status_code=404, detail="Agent 不存在") msg_query = db.query(ChatMessage).filter( ChatMessage.agent_id == agent_id, ChatMessage.session_id == session_id, ) if workspace_id: msg_query = msg_query.filter(ChatMessage.workspace_id == workspace_id) messages = msg_query.order_by(ChatMessage.created_at.asc()).all() if not messages: raise HTTPException(status_code=404, detail="会话无消息") # 获取会话标题 session = db.query(AgentSession).filter(AgentSession.id == session_id).first() title = session.title if session and session.title else "对话" if format == "markdown": lines = [f"# {title}", "", f"**Agent**: {agent.name}", f"**时间**: {messages[0].created_at.isoformat() if messages[0].created_at else 'N/A'}", "", "---", ""] for m in messages: role_label = "用户" if m.role == "user" else "AI 助手" if m.role == "assistant" else m.role lines.append(f"### {role_label}") if m.content: lines.append(m.content) if m.tool_name: lines.append(f"\n> 工具调用: `{m.tool_name}`") if m.tool_output: lines.append(f"\n
工具输出\n\n```\n{m.tool_output[:500]}\n```\n
") lines.append("") return PlainTextResponse("\n".join(lines), media_type="text/markdown") else: # JSON export_data = { "title": title, "agent_name": agent.name, "agent_id": agent_id, "session_id": session_id, "exported_at": messages[0].created_at.isoformat() if messages[0].created_at else None, "messages": [ { "role": m.role, "content": m.content, "tool_name": m.tool_name, "tool_input": m.tool_input, "tool_output": m.tool_output, "created_at": m.created_at.isoformat() if m.created_at else None, } for m in messages ], } return export_data