fix(core): 交叉验证修复 — 单镜改稿按 DB 真实镜数(防截镜误扣)+ 模型框点外关闭选择器
critical:regenerate_segment_via_agent 改用 _draft_from_version 从 DB ScriptSegment 行重建基准稿(原读 stale content,用户增删镜后 normalize 会按 stale total_duration 截掉真实镜还照扣费)。低危:模型框容器补 chat-model-pick class,点外关闭选择器生效。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
03e7949307
commit
c3306bcac3
@@ -675,6 +675,35 @@ def _load_base_draft(project, base_version_id: str) -> dict | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _draft_from_version(version) -> dict:
|
||||||
|
"""从 ScriptVersion 的 DB 行(segments + metadata)重建 ScriptDraft —— 比解析可能已 stale 的 content 可靠
|
||||||
|
(用户增删/改镜后 content 不一定同步)。total_duration 按真实镜数算,避免 normalize 按 stale 值截/补镜。"""
|
||||||
|
meta = version.metadata or {}
|
||||||
|
segs = list(version.segments.order_by("sort_order"))
|
||||||
|
return {
|
||||||
|
"hook": meta.get("hook", ""),
|
||||||
|
"tone": meta.get("tone", ""),
|
||||||
|
"aspect_ratio": meta.get("aspect_ratio", "9:16"),
|
||||||
|
"total_duration": max(int(meta.get("total_duration") or 0), 15 * len(segs)) or 60,
|
||||||
|
"segment_count": len(segs),
|
||||||
|
"entities": meta.get("entities", []),
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"index": s.sort_order,
|
||||||
|
"duration": s.duration_seconds or 15,
|
||||||
|
"role": s.role or "",
|
||||||
|
"narration": s.narration or "",
|
||||||
|
"speaker": s.speaker or None,
|
||||||
|
"visual": s.visual_prompt or "",
|
||||||
|
"product_exposure": s.product_exposure or "",
|
||||||
|
"entity_refs": s.entity_refs or [],
|
||||||
|
"dialogue": s.dialogue or [],
|
||||||
|
}
|
||||||
|
for s in segs
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def regenerate_segment_via_agent(*, project, user, model_config: ModelConfig, segment, instruction: str = ""):
|
def regenerate_segment_via_agent(*, project, user, model_config: ModelConfig, segment, instruction: str = ""):
|
||||||
"""非流式·精准改一镜(「场次刷新」按钮复用 agent 单镜逻辑):读全脚本上下文,只重写该镜,落新 ScriptVersion。
|
"""非流式·精准改一镜(「场次刷新」按钮复用 agent 单镜逻辑):读全脚本上下文,只重写该镜,落新 ScriptVersion。
|
||||||
与 stream_script_agent 的 target_index 分支同源,但同步返回(不走 SSE)。计费 reserve→charge/release 闭环。"""
|
与 stream_script_agent 的 target_index 分支同源,但同步返回(不走 SSE)。计费 reserve→charge/release 闭环。"""
|
||||||
@@ -683,16 +712,13 @@ def regenerate_segment_via_agent(*, project, user, model_config: ModelConfig, se
|
|||||||
from apps.ai.services import build_provider, create_ai_task
|
from apps.ai.services import build_provider, create_ai_task
|
||||||
from apps.billing.services.ledger import charge_reserved_credit
|
from apps.billing.services.ledger import charge_reserved_credit
|
||||||
|
|
||||||
base_version = segment.script_version
|
base_draft = _draft_from_version(segment.script_version) # 用 DB 行重建基准,别用可能 stale 的 content
|
||||||
base_draft = _load_base_draft(project, str(base_version.id))
|
|
||||||
if base_draft is None:
|
|
||||||
raise ValueError("基准脚本无法解析,无法精准改镜")
|
|
||||||
target_index = segment.sort_order
|
target_index = segment.sort_order
|
||||||
aspect_ratio = (base_draft.get("aspect_ratio") or "9:16").strip()
|
aspect_ratio = (base_draft.get("aspect_ratio") or "9:16").strip()
|
||||||
total_duration = base_draft.get("total_duration") or 60
|
seg_n = len(base_draft.get("segments") or [])
|
||||||
seg_n = len(base_draft.get("segments", []))
|
|
||||||
if not (0 <= target_index < seg_n):
|
if not (0 <= target_index < seg_n):
|
||||||
raise ValueError(f"镜号越界:第 {target_index + 1} 镜(共 {seg_n} 镜)")
|
raise ValueError(f"镜号越界:第 {target_index + 1} 镜(共 {seg_n} 镜)")
|
||||||
|
total_duration = base_draft["total_duration"] # 已按真实镜数算,normalize 不会截掉用户增删后的镜
|
||||||
|
|
||||||
messages = build_agent_messages(
|
messages = build_agent_messages(
|
||||||
project=project,
|
project=project,
|
||||||
|
|||||||
@@ -2279,7 +2279,7 @@ export function PipelinePage(props: {
|
|||||||
</button>
|
</button>
|
||||||
{/* 模型选择小按钮(输入框下方,对齐 ChatGPT/Lovart)· 复用 restraint chip 下拉,向上展开 */}
|
{/* 模型选择小按钮(输入框下方,对齐 ChatGPT/Lovart)· 复用 restraint chip 下拉,向上展开 */}
|
||||||
{textModels && textModels.length > 0 ? (
|
{textModels && textModels.length > 0 ? (
|
||||||
<div className={`chip-wrap${modelMenuOpen ? " open" : ""}`}>
|
<div className={`chip-wrap chat-model-pick${modelMenuOpen ? " open" : ""}`}>
|
||||||
<button type="button" className="chip" title="选择脚本生成模型(豆包 / GPT / Gemini)" onClick={() => setModelMenuOpen((v) => !v)}>
|
<button type="button" className="chip" title="选择脚本生成模型(豆包 / GPT / Gemini)" onClick={() => setModelMenuOpen((v) => !v)}>
|
||||||
{activeModelName}
|
{activeModelName}
|
||||||
<svg className="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6" /></svg>
|
<svg className="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6" /></svg>
|
||||||
|
|||||||
Reference in New Issue
Block a user