Files
aiagent/backend/app/api/agent_sessions.py

181 lines
6.0 KiB
Python
Raw Normal View History

"""
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<details><summary>工具输出</summary>\n\n```\n{m.tool_output[:500]}\n```\n</details>")
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