第11环裂变重写:对齐上线版 split.js 一次LLM出N套完整笔记包
架构从"扇出N个GenerationTask各跑完整管道"改为"一次LLM调用直接出N套
完整笔记包(N=1~5)",落 FissionNote 表 + 独立展示页。
后端:
- 018迁移:fission_notes 表(文案JSON+score+passed+imagePlan+images+status)
- fission_prompt:FISSION_SYSTEM+三档参考度(low/mid/high)+normalize_tags+品类兜底
- fission_pipeline:一次LLM出N套→各评分(@80合格线)→排序→落库,不达标标
needs_optimization 非丢弃;apiports 503 回落 codeproxy gpt-5.5 强档兜底
- fission_images:每套串行调现有生图接口(零改动image_gen/storyboard)
- tasks.py:run_fission_pipeline Celery task,删旧扇出注入
- api/v1/fission:进度聚合FissionNote + GET /fission/{id}/notes(剥内部字段)
前端:FissionProgress对齐状态机 + N套独立展示页 + FissionNoteCard
测试:test_fission_engine(19)+test_fission_pipeline(5) 全过;104 全量回归绿
实测task5(fanout=2,mid)端到端跑通:一次出2套→seq0=85过/seq1=79标优化→
生图codeproxy/edits→1024×1536去AI化→task completed→notes端点返完整数据
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
81
backend/app/services/fission_images.py
Normal file
81
backend/app/services/fission_images.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
app/services/fission_images.py — 第11环裂变 逐套生图(从 fission_pipeline 拆出)
|
||||
|
||||
复用单篇 generate_storyboard_images(codeproxy edits 带产品参考图),结果存
|
||||
FissionNote.images_json。拆分原因:fission_pipeline.py 超 200 行红线上限。
|
||||
🔴 生图引擎零改动;串行跑各套(套内 storyboard 已 asyncio.gather 并发)避免打爆中转站。
|
||||
"""
|
||||
import logging
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate_fission_images(
|
||||
db: Session, clients, ft, product: dict, image_count: int, note_ids: list[int],
|
||||
) -> None:
|
||||
"""逐套生图,结果存 FissionNote.images_json。
|
||||
|
||||
每套用该套自己的 note_json(含定制 imagePlan)当 note 喂生图引擎。
|
||||
产品参考图沿用源产品同一张,与单篇一致。
|
||||
"""
|
||||
import asyncio
|
||||
import json as _json
|
||||
import os
|
||||
|
||||
from app.models.fission import FissionNote
|
||||
from app.core.config import get_settings
|
||||
from app.services.ai_engine.image_gen import generate_storyboard_images
|
||||
from app.services.ai_engine.image_postprocessor import process_image
|
||||
from app.workers.pipeline_io import _resolve_image_path
|
||||
|
||||
reference_images = None
|
||||
_img_path = _resolve_image_path(product.get("image_path", ""))
|
||||
if _img_path and os.path.isfile(_img_path):
|
||||
try:
|
||||
with open(_img_path, "rb") as f:
|
||||
reference_images = [f.read()]
|
||||
except Exception as e: # noqa: BLE001
|
||||
logger.warning("裂变参考图读取失败,无参考图模式: %s", e)
|
||||
|
||||
upload_base = get_settings().UPLOAD_ABS_ROOT
|
||||
for nid in note_ids:
|
||||
fn = db.query(FissionNote).filter(FissionNote.id == nid).first()
|
||||
if not fn:
|
||||
continue
|
||||
try:
|
||||
note = _json.loads(fn.note_json) if fn.note_json else {}
|
||||
except (ValueError, TypeError):
|
||||
note = {}
|
||||
try:
|
||||
results = asyncio.run(generate_storyboard_images(
|
||||
client=clients, note=note, product=product,
|
||||
image_count=image_count, reference_images=reference_images,
|
||||
))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error("裂变套 seq=%s 生图失败: %s", fn.seq, exc)
|
||||
fn.status = "failed"; db.commit()
|
||||
continue
|
||||
|
||||
img_dir = os.path.join(upload_base, str(ft.workspace_id), f"fission_{ft.id}", str(fn.seq))
|
||||
os.makedirs(img_dir, exist_ok=True)
|
||||
images: list[dict] = []
|
||||
for i, r in enumerate(results):
|
||||
if r.get("error") or not r.get("image_bytes"):
|
||||
images.append({"role": r.get("role", ""), "error": str(r.get("error", "生图失败"))[:64]})
|
||||
continue
|
||||
try:
|
||||
processed = process_image(r["image_bytes"], aspect_ratio="3:4", resample_strength=1)
|
||||
except Exception: # noqa: BLE001
|
||||
processed = r["image_bytes"]
|
||||
fname = f"{i + 1:02d}_{r['role']}.jpg"
|
||||
with open(os.path.join(img_dir, fname), "wb") as f:
|
||||
f.write(processed)
|
||||
images.append({
|
||||
"role": r["role"], "seq": i + 1,
|
||||
"url": f"/uploads/{ft.workspace_id}/fission_{ft.id}/{fn.seq}/{fname}",
|
||||
})
|
||||
fn.images_json = _json.dumps(images, ensure_ascii=False)
|
||||
fn.status = "image_done"
|
||||
db.commit()
|
||||
Reference in New Issue
Block a user