fix(core): 对抗式交叉验证发现的 4 个 critical + 关键 major

- 额度泄漏(critical):流式 agent 客户端断连抛 GeneratorExit(BaseException)逃过 except,
  预扣额度冻结。改 try/finally + settled 标志兜底释放。
- 默认图像模型(critical):0006 只停用 yunqi,没停火山 Seedream(bootstrap 种入 active 且更早),
  get_default_model 仍选 Seedream → 参考图分镜/模特库被架空。改为停用所有非 tokenssr 图像模型 +
  provider down→up 自愈置 active。
- 故事板参考提示词(critical):image_edit 分支复用 request_payload['prompt'](恒真)→ refs 版
  锁脸锁商品提示词成死代码,参考图白传。改为强制用 refs 版提示词。
- 凭证解析(major):build_provider 官方直连分支丢了 DB api_key,与中转站路径优先级不一致。
  统一走 resolve_provider_credentials。
- JSON 抽取(major):_extract_json 取首个围栏块(易抓示例块)+ rfind('}') 越界。改为取末个
  合法草稿块 + 字符串感知的括号配平扫描。
- 前端兜底(major):流式已成功(saved)但收尾抖动时 catch 仍回退旧端点 → 重复生成+扣费。
  改为按 ok/收到事件 判定,只在零事件时回退。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-17 03:21:06 +08:00
co-authored by Claude Opus 4.8
parent 6464001f84
commit a625f86b38
4 changed files with 155 additions and 83 deletions
+7 -7
View File
@@ -60,11 +60,12 @@ def resolve_provider_credentials(provider) -> tuple[str | None, str | None]:
def build_provider(model_config: ModelConfig):
"""按 provider.name 分流:火山官方直连 → VolcanoArkProvider;其余 → 通用 OpenAICompatibleProvider。"""
"""按 provider.name 分流:火山官方直连 → VolcanoArkProvider;其余 → 通用 OpenAICompatibleProvider。
两条路都走 resolve_provider_credentials,统一 DB→.env 优先级(官方直连的 None 再由 __post_init__ 回退 settings.VOLCANO)。"""
provider = model_config.provider
if provider.name in OFFICIAL_DIRECT_PROVIDERS:
return VolcanoArkProvider(base_url=provider.base_url or None)
base_url, api_key = resolve_provider_credentials(provider)
if provider.name in OFFICIAL_DIRECT_PROVIDERS:
return VolcanoArkProvider(base_url=base_url, api_key=api_key)
return OpenAICompatibleProvider(base_url=base_url, api_key=api_key)
@@ -768,10 +769,9 @@ def _storyboard_frame_worker(task_id, version_id, segment_id, user_id) -> None:
refs = _storyboard_reference_images(project, segment)
ref_urls = [r["url"] for r in refs]
if ref_urls and hasattr(provider, "image_edit"):
# gpt-image-2 多图参考:把角色/场景/商品合成进本镜(@图1@图2@图3),锁脸锁外观保一致
frame_prompt = task.request_payload.get("prompt") or build_storyboard_frame_prompt_refs(
project, version, segment, refs
)
# gpt-image-2 多图参考:必须用 refs 版提示词(点名「参考图N=角色/场景/商品」+锁脸锁商品),
# 不能复用 request_payload['prompt'](那是建任务时写死的基础提示词,恒为真值会架空一致性约束)。
frame_prompt = build_storyboard_frame_prompt_refs(project, version, segment, refs)
response = provider.image_edit(
model=model_config.name,
prompt=frame_prompt,