""" 脚本生成 API ============ 提供脚本生成、润色、模型健康检查等功能。 支持 SSE 流式响应。 """ from __future__ import annotations import logging from fastapi import APIRouter, Request from fastapi.responses import StreamingResponse from app.schemas.common import ApiResponse, success_response from app.schemas.script import ( GenerateScriptRequest, ModelHealthResponse, PolishRequest, ScriptGenerationEvent, ScriptShot, TestModelRequest, TestModelResponse, ) from app.services.script_service import get_script_service router = APIRouter() logger = logging.getLogger(__name__) @router.post("/generate", response_model=ApiResponse[list[ScriptShot]]) async def generate_script(request: GenerateScriptRequest): """ 同步生成脚本 直接返回生成的分镜列表,适合快速预览。 """ service = get_script_service() shots = await service.generate_script( topic=request.topic, duration=request.duration, script_type=request.script_type, model=request.model, ) return success_response( data=shots, message=f"成功生成 {len(shots)} 个分镜", ) @router.post("/generate/stream") async def generate_script_stream(request: Request, data: GenerateScriptRequest): """ 流式生成脚本(SSE) 返回 Server-Sent Events,包含进度更新和最终结果。 前端通过 EventSource 接收实时进度。 **SSE 事件类型:** - `start`: 开始生成 - `analyzing`: 分析主题 - `planning`: 规划结构 - `generating`: AI 生成中 - `parsing`: 解析结果 - `complete`: 完成,包含 result 字段 - `error`: 错误 **示例事件流:** ``` data: {"type": "start", "progress": 0, "message": "开始生成脚本"} data: {"type": "analyzing", "progress": 15, "message": "分析目标受众..."} data: {"type": "complete", "progress": 100, "message": "成功生成 5 个分镜", "result": [...]} ``` """ service = get_script_service() async def event_generator(): """SSE 事件生成器,带客户端断开检测""" try: async for event in service.generate_script_stream( topic=data.topic, duration=data.duration, script_type=data.script_type, model=data.model, ): # 检查客户端是否已断开 if await request.is_disconnected(): logger.info("[SSE] 客户端已断开连接,停止生成") break # SSE 格式:data: {...}\n\n try: yield f"data: {event.model_dump_json()}\n\n" except Exception as e: logger.error(f"[SSE] 序列化事件失败: {e}") continue # 发送结束标记(如果客户端还连接着) if not await request.is_disconnected(): yield "data: [DONE]\n\n" except Exception as e: logger.exception("[SSE] 事件生成器异常") # 尝试发送错误信息给客户端 try: error_event = ScriptGenerationEvent( type="error", progress=0, message=f"服务器错误: {str(e)}", ) yield f"data: {error_event.model_dump_json()}\n\n" yield "data: [DONE]\n\n" except: pass return StreamingResponse( event_generator(), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no", # 禁用 Nginx 缓冲 }, ) @router.post("/polish", response_model=ApiResponse[str]) async def polish_content(request: PolishRequest): """ AI 润色文案/画面描述 - `polishType=scene`: 润色画面描述(根据 shot_type 自动区分分镜/空镜) - `polishType=voiceover`: 润色配音文案 参数: - `shot_type`: "segment"(分镜)或 "empty_shot"(空镜),画面润色时必填 """ service = get_script_service() polished = await service.polish_content( content=request.content, polish_type=request.polish_type, shot_type=request.shot_type or "segment", ) type_name = "画面" if request.polish_type == "scene" else "文案" return success_response( data=polished, message=f"{type_name}润色完成", ) @router.get("/model-health", response_model=ApiResponse[ModelHealthResponse]) async def check_model_health(): """ 检查 AI 模型健康状态 返回所有配置的模型及其可用性状态。 """ service = get_script_service() health_data = await service.check_model_health() return success_response( data=ModelHealthResponse(**health_data), message="模型健康检查完成", ) @router.post("/test-model", response_model=ApiResponse[TestModelResponse]) async def test_model(request: TestModelRequest): """ 测试指定模型连接 发送一个简单的测试请求,验证模型是否可用。 """ service = get_script_service() result = await service.test_model(request.model_id) return success_response( data=TestModelResponse(**result), message="模型测试完成" if result["success"] else f"模型测试失败: {result.get('error')}", )