- 新增审核卡片和确认卡片模型,支持Agent推送审核任务和用户确认 - 实现审核卡片API服务,支持创建、更新、批准、驳回等操作 - 扩展审核维度配置,新增角色一致性、剧情连贯性等维度 - 优化前端审核配置页面,修复API路径错误和状态枚举问题 - 改进剧集创作平台布局,新增左侧边栏用于剧集管理和上下文查看 - 增强Skill管理,支持从审核系统跳转创建/编辑Skill - 修复episodes.json数据问题,清理聊天历史记录 - 更新Agent提示词,明确Skill引用加载流程 - 统一前端主题配置,优化整体UI体验
432 lines
14 KiB
Python
432 lines
14 KiB
Python
"""
|
||
Review Task API Routes
|
||
|
||
审核任务API路由 - 提供过程中审核和生成后审核的管理接口
|
||
"""
|
||
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
||
from typing import List, Optional
|
||
import asyncio
|
||
|
||
from app.models.review_task import (
|
||
ReviewTask,
|
||
ReviewMode,
|
||
ReviewAction,
|
||
ProcessReviewRequest,
|
||
UserReviewActionRequest,
|
||
PostCreationReviewRequest,
|
||
PostCreationReviewResult,
|
||
ReviewTaskStatus
|
||
)
|
||
from app.core.review.review_task_manager import (
|
||
ReviewTaskManager,
|
||
get_review_task_manager,
|
||
StreamReviewHelper
|
||
)
|
||
from app.db.repositories import project_repo, episode_repo
|
||
from app.models.project import SeriesProject, Episode
|
||
from app.utils.logger import get_logger
|
||
|
||
|
||
logger = get_logger(__name__)
|
||
|
||
router = APIRouter(prefix="/review-task", tags=["审核任务"])
|
||
|
||
|
||
# ============================================
|
||
# 依赖注入
|
||
# ============================================
|
||
|
||
async def get_task_manager() -> ReviewTaskManager:
|
||
"""获取审核任务管理器"""
|
||
return get_review_task_manager()
|
||
|
||
|
||
async def get_stream_helper(project_id: str) -> StreamReviewHelper:
|
||
"""获取流式审核助手"""
|
||
task_manager = get_review_task_manager()
|
||
return StreamReviewHelper(project_id, task_manager)
|
||
|
||
|
||
# ============================================
|
||
# 过程中审核 API
|
||
# ============================================
|
||
|
||
@router.post("/process")
|
||
async def create_process_review_task(
|
||
request: ProcessReviewRequest,
|
||
task_manager: ReviewTaskManager = Depends(get_task_manager)
|
||
):
|
||
"""
|
||
创建过程中审核任务
|
||
|
||
由Agent在流式输出过程中调用,推送审核任务到前端。
|
||
前端通过WebSocket接收通知并等待用户确认。
|
||
"""
|
||
try:
|
||
# 创建审核任务
|
||
task = await task_manager.create_process_review_task(request)
|
||
|
||
# 通过WebSocket推送到前端
|
||
from app.api.v1.websocket import manager
|
||
await manager.send_to_project(request.project_id, {
|
||
"type": "review_task_created",
|
||
"data": {
|
||
"task_id": task.id,
|
||
"mode": task.mode.value,
|
||
"issue_type": task.issue_type,
|
||
"issue_description": task.issue_description,
|
||
"severity": task.severity,
|
||
"location": task.location.dict(),
|
||
"available_actions": [action.value for action in task.available_actions],
|
||
"suggestion": task.suggestion,
|
||
"trigger_source": task.trigger_source.value,
|
||
"created_at": task.created_at.isoformat()
|
||
}
|
||
})
|
||
|
||
logger.info(f"过程中审核任务已创建并推送: {task.id}")
|
||
|
||
return {
|
||
"success": True,
|
||
"task_id": task.id,
|
||
"message": "审核任务已创建"
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.error(f"创建过程中审核任务失败: {str(e)}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=f"创建审核任务失败: {str(e)}"
|
||
)
|
||
|
||
|
||
@router.post("/process/{task_id}/action")
|
||
async def handle_process_review_action(
|
||
task_id: str,
|
||
request: UserReviewActionRequest,
|
||
task_manager: ReviewTaskManager = Depends(get_task_manager)
|
||
):
|
||
"""
|
||
处理用户对过程中审核任务的响应
|
||
|
||
Args:
|
||
task_id: 任务ID
|
||
request: 用户操作请求
|
||
|
||
Returns:
|
||
处理结果
|
||
"""
|
||
try:
|
||
# 处理用户操作
|
||
task = await task_manager.handle_user_review_action(request)
|
||
|
||
# 通过WebSocket通知前端任务已完成
|
||
from app.api.v1.websocket import manager
|
||
await manager.send_to_project(task.project_id or "unknown", {
|
||
"type": "review_task_completed",
|
||
"data": {
|
||
"task_id": task_id,
|
||
"status": task.status.value,
|
||
"user_response": task.user_response,
|
||
"completed_at": task.completed_at.isoformat() if task.completed_at else None
|
||
}
|
||
})
|
||
|
||
logger.info(f"用户操作已处理: {task_id}, 操作: {request.action.value}")
|
||
|
||
return {
|
||
"success": True,
|
||
"task_id": task_id,
|
||
"status": task.status.value,
|
||
"message": "操作已处理"
|
||
}
|
||
|
||
except ValueError as e:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail=str(e)
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"处理用户操作失败: {str(e)}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=f"处理失败: {str(e)}"
|
||
)
|
||
|
||
|
||
@router.get("/process/pending/{project_id}")
|
||
async def get_pending_process_tasks(
|
||
project_id: str,
|
||
task_manager: ReviewTaskManager = Depends(get_task_manager)
|
||
) -> List[ReviewTask]:
|
||
"""
|
||
获取项目所有待处理的过程审核任务
|
||
|
||
Args:
|
||
project_id: 项目ID
|
||
|
||
Returns:
|
||
List[ReviewTask]: 待处理任务列表
|
||
"""
|
||
return task_manager.get_pending_process_tasks(project_id)
|
||
|
||
|
||
# ============================================
|
||
# 生成后审核 API
|
||
# ============================================
|
||
|
||
@router.post("/post-creation")
|
||
async def execute_post_creation_review(
|
||
request: PostCreationReviewRequest,
|
||
task_manager: ReviewTaskManager = Depends(get_task_manager),
|
||
background_tasks: BackgroundTasks = None
|
||
):
|
||
"""
|
||
执行生成后审核
|
||
|
||
根据用户配置的规则/Skill进行自动检查,根据阈值报告问题。
|
||
|
||
Args:
|
||
request: 生成后审核请求
|
||
background_tasks: FastAPI后台任务管理器
|
||
|
||
Returns:
|
||
PostCreationReviewResult: 审核结果
|
||
"""
|
||
# 获取项目
|
||
project = await project_repo.get(request.project_id)
|
||
if not project:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail=f"项目不存在: {request.project_id}"
|
||
)
|
||
|
||
# 获取剧集
|
||
episodes = await episode_repo.list_by_project(request.project_id)
|
||
episode = next((ep for ep in episodes if ep.id == request.episode_id), None)
|
||
|
||
if not episode:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail=f"剧集不存在: {request.episode_id}"
|
||
)
|
||
|
||
try:
|
||
# 获取审核配置
|
||
from app.models.review import ReviewConfig
|
||
raw_config = getattr(project, 'reviewConfig', None)
|
||
|
||
if isinstance(raw_config, dict):
|
||
config = ReviewConfig(**raw_config)
|
||
elif raw_config is None:
|
||
config = ReviewConfig()
|
||
else:
|
||
config = raw_config
|
||
|
||
# 确保dimension_settings是dict
|
||
if not hasattr(config, 'dimension_settings') or not isinstance(config.dimension_settings, dict):
|
||
config.dimension_settings = {}
|
||
|
||
# 执行审核
|
||
result = await task_manager.execute_post_creation_review(
|
||
request=request,
|
||
project_config=config
|
||
)
|
||
|
||
# 更新剧集审核结果
|
||
from app.models.review import ReviewResult
|
||
review_result = ReviewResult(
|
||
id=f"review_{episode.id}",
|
||
episode_id=episode.id,
|
||
episode_number=episode.number,
|
||
project_id=project.id,
|
||
overall_score=result.overall_score or 75.0,
|
||
passed=result.passed,
|
||
passed_dimensions=0,
|
||
total_dimensions=1,
|
||
dimension_scores=[],
|
||
issues=[],
|
||
issue_count=result.task_count,
|
||
high_severity_count=sum(1 for t in result.tasks if t.severity == "high"),
|
||
medium_severity_count=sum(1 for t in result.tasks if t.severity == "medium"),
|
||
low_severity_count=sum(1 for t in result.tasks if t.severity == "low")
|
||
)
|
||
|
||
# 保存审核结果到剧集
|
||
await episode_repo.update(episode.id, {
|
||
"reviewResult": review_result.dict()
|
||
})
|
||
|
||
# 通过WebSocket推送审核结果
|
||
from app.api.v1.websocket import manager
|
||
await manager.send_to_project(request.project_id, {
|
||
"type": "post_creation_review_completed",
|
||
"data": {
|
||
"episode_id": episode.id,
|
||
"episode_number": episode.number,
|
||
"overall_score": result.overall_score,
|
||
"passed": result.passed,
|
||
"task_count": result.task_count,
|
||
"tasks": [
|
||
{
|
||
"id": task.id,
|
||
"issue_type": task.issue_type,
|
||
"issue_description": task.issue_description,
|
||
"severity": task.severity,
|
||
"location": task.location.dict(),
|
||
"available_actions": [action.value for action in task.available_actions],
|
||
"status": task.status.value,
|
||
"suggestion": task.suggestion
|
||
}
|
||
for task in result.tasks
|
||
]
|
||
}
|
||
})
|
||
|
||
logger.info(f"生成后审核完成: {episode.id}, 分数: {result.overall_score}")
|
||
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.error(f"生成后审核失败: {str(e)}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=f"审核失败: {str(e)}"
|
||
)
|
||
|
||
|
||
@router.post("/post-creation/{task_id}/action")
|
||
async def handle_post_creation_task_action(
|
||
task_id: str,
|
||
request: UserReviewActionRequest,
|
||
task_manager: ReviewTaskManager = Depends(get_task_manager)
|
||
):
|
||
"""
|
||
处理用户对生成后审核任务的响应
|
||
|
||
Args:
|
||
task_id: 任务ID
|
||
request: 用户操作请求
|
||
|
||
Returns:
|
||
处理结果
|
||
"""
|
||
try:
|
||
# 处理用户操作
|
||
task = await task_manager.handle_user_review_action(request)
|
||
|
||
# 如果修改了内容,更新剧集内容
|
||
if request.action == ReviewAction.MODIFY and request.modified_content:
|
||
# 获取剧集
|
||
episodes = await episode_repo.list_by_project(task.project_id or "")
|
||
episode = next((ep for ep in episodes if ep.id == task.episode_id), None)
|
||
|
||
if episode:
|
||
# 更新内容
|
||
await episode_repo.update(episode.id, {
|
||
"content": request.modified_content
|
||
})
|
||
|
||
# 通过WebSocket通知内容已更新
|
||
from app.api.v1.websocket import manager
|
||
await manager.send_to_project(task.project_id or "", {
|
||
"type": "episode_content_updated",
|
||
"data": {
|
||
"episode_id": episode.id,
|
||
"episode_number": episode.number
|
||
}
|
||
})
|
||
|
||
# 通过WebSocket通知任务已完成
|
||
from app.api.v1.websocket import manager
|
||
await manager.send_to_project(task.project_id or "", {
|
||
"type": "review_task_completed",
|
||
"data": {
|
||
"task_id": task_id,
|
||
"status": task.status.value,
|
||
"user_response": task.user_response,
|
||
"completed_at": task.completed_at.isoformat() if task.completed_at else None
|
||
}
|
||
})
|
||
|
||
logger.info(f"生成后审核任务已处理: {task_id}, 操作: {request.action.value}")
|
||
|
||
return {
|
||
"success": True,
|
||
"task_id": task_id,
|
||
"status": task.status.value,
|
||
"message": "操作已处理"
|
||
}
|
||
|
||
except ValueError as e:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_400_BAD_REQUEST,
|
||
detail=str(e)
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"处理生成后审核任务操作失败: {str(e)}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=f"处理失败: {str(e)}"
|
||
)
|
||
|
||
|
||
# ============================================
|
||
# Agent流式审核辅助 API
|
||
# ============================================
|
||
|
||
@router.post("/agent/push")
|
||
async def agent_push_review_task(
|
||
project_id: str,
|
||
issue_type: str,
|
||
issue_description: str,
|
||
severity: str = "medium",
|
||
skill_id: Optional[str] = None,
|
||
suggestion: Optional[str] = None,
|
||
task_manager: ReviewTaskManager = Depends(get_task_manager)
|
||
):
|
||
"""
|
||
Agent推送审核任务(简化版)
|
||
|
||
由Agent在流式输出过程中调用,推送审核任务。
|
||
|
||
Args:
|
||
project_id: 项目ID
|
||
issue_type: 问题类型
|
||
issue_description: 问题描述
|
||
severity: 严重程度
|
||
skill_id: 使用的Skill ID
|
||
suggestion: 修改建议
|
||
|
||
Returns:
|
||
创建的任务ID
|
||
"""
|
||
try:
|
||
from app.models.review_task import ReviewTaskLocation
|
||
|
||
task = await task_manager.create_process_review_task(
|
||
ProcessReviewRequest(
|
||
project_id=project_id,
|
||
issue_type=issue_type,
|
||
issue_description=issue_description,
|
||
severity=severity,
|
||
location=ReviewTaskLocation(),
|
||
trigger_source="agent_auto",
|
||
skill_id=skill_id,
|
||
suggestion=suggestion
|
||
)
|
||
)
|
||
|
||
return {
|
||
"success": True,
|
||
"task_id": task.id,
|
||
"message": "审核任务已推送"
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.error(f"Agent推送审核任务失败: {str(e)}")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=f"推送审核任务失败: {str(e)}"
|
||
)
|