按原型图重构核心页面并完善生成链路
This commit is contained in:
+34
-89
@@ -1,5 +1,4 @@
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
@@ -7,6 +6,7 @@ from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
|
||||
from config import settings
|
||||
from database import get_db
|
||||
@@ -17,6 +17,13 @@ from models.paragraph import Paragraph
|
||||
from models.template import Template
|
||||
from schemas.schemas import GenerateFullRequest, GenerateTestRequest, Response
|
||||
from services.ai_service import call_ai
|
||||
from services.generation_runtime import (
|
||||
build_mock_content,
|
||||
generation_progress,
|
||||
request_cancel,
|
||||
run_generation,
|
||||
update_progress,
|
||||
)
|
||||
from services.minio_client import upload_bytes
|
||||
|
||||
router = APIRouter()
|
||||
@@ -36,47 +43,6 @@ def _serialize_document(document: Document) -> dict:
|
||||
"updated_at": document.updated_at,
|
||||
}
|
||||
|
||||
|
||||
def _build_mock_content(paragraph: Paragraph) -> dict:
|
||||
if paragraph.output_format == "table":
|
||||
return {
|
||||
"content": [
|
||||
{
|
||||
"type": "table",
|
||||
"title": paragraph.title,
|
||||
"headers": ["字段", "内容"],
|
||||
"rows": [
|
||||
["段落标题", paragraph.title],
|
||||
["生成说明", paragraph.prompt_text or "根据模板内容生成"],
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
blocks = [
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"这是“{paragraph.title}”的示例生成内容,可用于前端联调与流程验证。"
|
||||
}
|
||||
]
|
||||
if paragraph.content:
|
||||
blocks.append({"type": "text", "text": f"模板上下文:{paragraph.content[:200]}"})
|
||||
if paragraph.need_prompt and paragraph.prompt_text:
|
||||
blocks.append({"type": "text", "text": f"预设提示词:{paragraph.prompt_text[:200]}"})
|
||||
return {"content": blocks}
|
||||
|
||||
|
||||
async def _get_effective_model(db: AsyncSession, paragraph: Paragraph) -> AiModel | None:
|
||||
if paragraph.model_id:
|
||||
model = await db.get(AiModel, paragraph.model_id)
|
||||
if model is not None and model.status == "enabled":
|
||||
return model
|
||||
result = await db.execute(
|
||||
select(AiModel).where(AiModel.status == "enabled").order_by(AiModel.id.asc()).limit(1)
|
||||
)
|
||||
return result.scalars().first()
|
||||
|
||||
|
||||
@router.post("/test")
|
||||
async def generate_test(body: GenerateTestRequest, db: AsyncSession = Depends(get_db)):
|
||||
paragraph = await db.get(Paragraph, body.paragraph_id)
|
||||
@@ -90,7 +56,7 @@ async def generate_test(body: GenerateTestRequest, db: AsyncSession = Depends(ge
|
||||
model = await db.get(AiModel, paragraph.model_id)
|
||||
|
||||
if model is None or model.status != "enabled":
|
||||
content = _build_mock_content(paragraph)
|
||||
content = build_mock_content(paragraph)
|
||||
message = "当前未找到可用模型,返回本地模拟生成结果。"
|
||||
else:
|
||||
result = await call_ai(paragraph, model)
|
||||
@@ -162,54 +128,32 @@ async def generate_full(body: GenerateFullRequest, db: AsyncSession = Depends(ge
|
||||
)
|
||||
db.add(document)
|
||||
await db.flush()
|
||||
|
||||
done_count = 0
|
||||
failed_count = 0
|
||||
for paragraph in paragraphs:
|
||||
if paragraph.edit_mode == "manual":
|
||||
content = {"content": [{"type": "text", "text": paragraph.content or "该段落为人工编辑模式。"}]}
|
||||
status = "success"
|
||||
duration = 0
|
||||
error_message = ""
|
||||
else:
|
||||
start = time.perf_counter()
|
||||
model = await _get_effective_model(db, paragraph)
|
||||
try:
|
||||
if model is None:
|
||||
content = _build_mock_content(paragraph)
|
||||
else:
|
||||
result = await call_ai(paragraph, model)
|
||||
content = result.content
|
||||
status = "success"
|
||||
error_message = ""
|
||||
except Exception as error:
|
||||
content = _build_mock_content(paragraph)
|
||||
status = "failed"
|
||||
error_message = str(error)
|
||||
failed_count += 1
|
||||
duration = round(time.perf_counter() - start, 4)
|
||||
|
||||
log = GenerationLog(
|
||||
document_id=document.id,
|
||||
paragraph_id=paragraph.id,
|
||||
model_id=paragraph.model_id,
|
||||
status=status,
|
||||
content=json.dumps(content, ensure_ascii=False),
|
||||
duration=duration,
|
||||
error_msg=error_message,
|
||||
)
|
||||
db.add(log)
|
||||
done_count += 1
|
||||
|
||||
document.para_count_done = done_count
|
||||
document.status = "completed" if failed_count == 0 else "failed"
|
||||
document.error = "" if failed_count == 0 else f"{failed_count} 个段落生成失败,已回退为模拟结果。"
|
||||
document.file_path = f"mock://document/{document.id}"
|
||||
await db.commit()
|
||||
await db.refresh(document)
|
||||
update_progress(document.id, status="pending", percent=0, done=0, total=len(paragraphs), message="任务已创建")
|
||||
asyncio.create_task(run_generation(document.id, template.id))
|
||||
return Response(data=_serialize_document(document))
|
||||
|
||||
|
||||
@router.get("/progress/{document_id}")
|
||||
async def generate_progress(document_id: int):
|
||||
async def event_generator():
|
||||
while True:
|
||||
state = generation_progress.get(
|
||||
document_id,
|
||||
{"status": "pending", "percent": 0, "message": "等待中", "done": 0, "total": 0},
|
||||
)
|
||||
yield {
|
||||
"event": "progress",
|
||||
"data": json.dumps(state, ensure_ascii=False),
|
||||
}
|
||||
if state.get("status") in {"completed", "failed", "cancelled"}:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
return EventSourceResponse(event_generator())
|
||||
|
||||
|
||||
@router.get("/documents")
|
||||
async def list_documents(
|
||||
page: int = Query(1, ge=1),
|
||||
@@ -263,9 +207,10 @@ async def cancel_document(document_id: int, db: AsyncSession = Depends(get_db)):
|
||||
if document is None:
|
||||
raise HTTPException(status_code=404, detail="生成记录不存在")
|
||||
|
||||
document.status = "cancelled"
|
||||
await db.commit()
|
||||
await db.refresh(document)
|
||||
if document.status in {"completed", "failed", "cancelled"}:
|
||||
return Response(data=_serialize_document(document))
|
||||
|
||||
request_cancel(document_id)
|
||||
return Response(data=_serialize_document(document))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user