feat(render3d): 三渲二接进编排 - #277
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
引擎契约与 server 编排的后半段。路线选择由 server 读 DB 决定:造型上有 model_3d_url 就调 CharacterGeneratorPort.generate_rendered,没有则照旧走 i2v。 三渲二不进 ROUTE_MATRIX —— 那张表的隐含前提是"路线由动作物理性质唯一决定", 而这条还取决于该造型有没有 3D 资产。 引擎侧新增 generate_rendered 而不另立 port,并补一项分区动量读数:整幅的 motion_scale 与死帧判据看不见"腿在迈、手臂僵成柱子",而那正是自动绑骨漏认 肢体的典型产物。 出帧台 provider 走函数内延迟 import,没装它时 i2v / 逐帧两条路线仍可用。 Refs 1024XEngineer#192 1024XEngineer#122 1024XEngineer#121
这两条只用 PIL 和 slicing.quality,与三渲二 provider 无关。留在 test_render3d_route_and_assets.py 里会被那个文件的整体 skip 一并带走, 于是本 PR 新增的这项读数在 provider 合入前一条断言都不跑。
1024XEngineer#241 合入后 rebase 撞出来的:它给装配表加了一条断言,要求每个 GenRoute 成员都被装配, 而本 PR 新增的 RENDER_3D 依赖 1024XEngineer#270 的 provider 模块——那个模块在这条分支上还不存在, 装配期就 import 会让本来走 i2v 的任务也起不来。 改成装配表里放一个惰性策略:真走到三渲二那条路线时才 import 出帧台依赖。 断言因此仍然成立(表里确实有这个键),而缺 provider 也不影响其余路线。 两个状态都验过:不带 1024XEngineer#270 时 496 passed / 2 skipped;把 1024XEngineer#270 的 provider 层铺进工作区 再跑,591 passed / 14 skipped,惰性策略能真的实例化出 RenderFrameStrategy。 Refs 1024XEngineer#192
b6ded67 to
204f238
Compare
|
/review |
There was a problem hiding this comment.
审查结论
这组改动已经串起了 DB 选路、模型下载、引擎渲帧和质量读数,但与 #192 已定契约仍有两处关键偏差:多朝向产物被压成单朝向,渲染浏览器仍运行在 FastAPI 进程内。另外,人工驳回模型后当前缓存流程无法真正重新生成。
验证
- 按固定范围
12793606d44bf3a089d4540c5e1c307cc94e6d25...9b509f9cfab91d9dba6ae889bca28879d17c3751审阅全部 18 个变更文件。 git diff --check与变更模块compileall通过。- 当前环境缺少
uv/pytest,未能本地复跑测试;GitHub 的后端、前端与 patch coverage 检查均为通过。
| f"出帧台没有产出朝向 {want}(动作 {action.action.value}、facing " | ||
| f"{action.facing.value});实际产出 {available}。" | ||
| ) | ||
| frames = list(chosen.frames) |
There was a problem hiding this comment.
[P1] 保留全部渲染朝向而不是只返回一个
这里从 sheet.sequences 只取请求朝向的 frames,随后明确丢弃其余序列;同时 server 的 ProjectConstraints.directions(1/4/8)没有传入该策略。结果是四向/八向项目即使 provider 已零成本渲出全部朝向,任务结果仍只有一组平铺帧,无法形成前端 sequences[].direction 契约。这直接违背 #192 的 D18 和验收标准 3,也让三渲二的核心多朝向能力对调用方不可用。请让出参/上传层承载每个方向的独立序列,并按项目方向数请求和持久化;单向调用再保持现有平铺形状。
| from windup_ai_engine.strategy.concrete import RenderFrameStrategy | ||
| from windup_framework.providers.render3d import LocalSpriteRenderProvider | ||
|
|
||
| self._inner = RenderFrameStrategy(LocalSpriteRenderProvider()) |
There was a problem hiding this comment.
[P1] 将浏览器渲染移出 FastAPI 进程
LocalSpriteRenderProvider 在 ActionTaskExecutor 的惰性策略里直接实例化并执行,而该 executor 由 app.state.run_action_task 提交给进程内 ThreadPoolExecutor。因此即使队列并发为 1,常驻浏览器、约 1GB 峰值内存和高 CPU 负载仍与 FastAPI 共享进程,浏览器崩溃或资源耗尽会直接影响 API 服务。#192 的 D11 已明确要求部署服务器上的独立 worker、不得与 FastAPI 同进程;请把渲染任务投递到独立 worker/进程边界,再由编排层取回结果。
|
|
||
| # 图生 3D 的产物单独存一份。**这不是冗余** —— 待审期间会有第二次、第三次调用走到 | ||
| # 这里,若不存,每次都要重付一遍图生 3D 的钱,而停点的本意恰恰是省钱。 | ||
| model = self._store.get(raw_key) |
There was a problem hiding this comment.
[P2] 驳回模型时清除原始模型缓存
待审模型被判定不合格后,异常文案要求删除待审 .glb 以重新生成,但这里会一直从 raw:{key} 读取首次生成的 bytes。删除 review 目录里的文件后,下一次 ensure() 只会把同一份缓存重新提交,image_to_3d() 永远不会再调用,造型因此无法从一次坏模型中恢复;残留的 .approved 标记还可能放行后续替换的模型。请提供显式 reject/invalidate 操作,同时清除 raw 缓存、待审文件和批准标记,或用模型版本/摘要绑定审批状态。
三渲二的后半段:引擎契约 + 编排接线 + 分区动量。前半段是 #270(provider 层)。
合并顺序:本 PR 依赖 #270,请在它之后合
本 PR 的代码要用
windup_framework.providers.render3d,那个包由 #270 引入。为了不让这条依赖把整个后端拖下水,出帧台 provider 的 import 全部改成
函数内延迟 import /
TYPE_CHECKING:strategy/concrete.pyTYPE_CHECKING;RENDER_SIZE原是类定义期求值的默认参数,改成size=None+__init__内取orchestrator/render3d_assets.pyTYPE_CHECKINGorchestrator/executor.py结果:没装 provider 时,i2v / 逐帧两条路线的 import 与运行完全不受影响,
本 PR 自身 CI 也是绿的。真需要 provider 的 2 条用例显式 skip 并打印缺的模块名
(
pytest.skip(allow_module_level=True)/importorskip),不静默通过。核心设计
character_data.outfits[].model_3d_url有没有值。有 → 三渲二;没有 → 照旧 i2v。引擎侧因此不提供"能不能用"的预查询 —— 那份数据在 DB 里,让引擎回答它,
要么引擎去反查存储(破它"只吃 bytes"的分层),要么它只能猜。
CharacterGeneratorPort加generate_rendered方法,而不是另立一个 port。server 调 ai_engine 只该有一个入口;
generate对应视频截帧,generate_rendered对应三渲二,两者共用同一段尾段(帧数对账 → 脚线对齐 → 量成色)。
ROUTE_MATRIX。 那张表的隐含前提是"路线由动作的物理性质唯一决定",对 i2v / 逐帧成立;三渲二还取决于"该造型有没有 3D 资产",硬塞进去会破坏前提。
所以表的形状不动,这条路线由 server 直接选方法。
model_3d_url却下载/渲染失败就报错,不改走 i2v ——两条路线的画风、成本、多朝向能力都不同,悄悄换一条会让调用方拿着错误前提做后续决定,
而帧数、时长、成色全部正常,没有任何一道会红。
limb_motion)。 整幅的motion_scale与死帧判据看不见"腿在迈、手臂僵成柱子",而那正是自动绑骨漏认一条肢体的典型产物(那块网格没有骨骼驱动,
每帧同姿势)。只报各区占比 + 最静区名,不给合格线 —— 几何分区区分不了
"该动没动"和"本来就不该动",判决交给调用方。
本地验证(均在
backend/下)uv run ruff check .uv run lint-importsuv run pytest -q -p no:cacheprovider另外把 #270 的 provider 层临时铺进工作区跑了一遍合并后的状态,确认延迟 import
在 provider 真的存在时也正确、两条 skip 会变成真跑:
(临时文件已全部清除,本分支 diff 不含 #270 的任何文件。)
codecov/patch 会红,原因已定位
48.79% of diff hit (target 86.21%)。缺口全部来自那两条被 skip 的用例。同样两个状态各跑一次覆盖率,差异只落在三渲二相关模块:
orchestrator/render3d_assets.pystrategy/concrete.pyweb/api/generation.py即 #270 合入后这项自然转绿,不需要为它补测试。
两处测试改动的说明
tests/test_orchestrator_hardening.py:本 PR 给GenRoute加了RENDER_3D,而 main 上那条
test_real_generator_assembly_covers_every_declared_route断言每个
GenRoute成员都被装配。缺 provider 时它必然红 —— 那是缺件不是漏装,所以加了一条
importorskip显式跳过,没有放宽断言。feat(providers): 三渲二三段能力的 provider 层 #270 合入后它照常跑。tests/test_master_check_and_quality.py:两条limb_motion用例从test_render3d_route_and_assets.py挪了过来。它们只用 PIL 和slicing.quality,与 provider 无关,留在那个文件里会被整体 skip 一并带走 —— 那样本 PR 新增的这项读数
在 feat(providers): 三渲二三段能力的 provider 层 #270 合入前一条断言都不跑。
Refs #192 #122 #121