// 二期第2段端到端:真实调脚本 agent,验「总时长 = 各镜相加」「单镜 4–15 秒」「组合回写」 const API = process.env.API || "http://127.0.0.1:8010"; const login = await (await fetch(`${API}/api/auth/login/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "airshelf", password: "Restraint2026" }) })).json(); const auth = { Authorization: `Token ${login.token}`, "Content-Type": "application/json" }; const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || []; const project = await (await fetch(`${API}/api/projects/`, { method: "POST", headers: auth, body: JSON.stringify({ name: `二期第2段e2e-${Date.now() % 100000}`, product: prods[0].id }) })).json(); const CASES = [ { total_duration: 40, presentation_format: "drama", video_structure: "pain" }, { total_duration: 15, presentation_format: "oral", video_structure: "contrast" }, ]; for (const params of CASES) { const res = await fetch(`${API}/api/projects/${project.id}/script-agent-stream/`, { method: "POST", headers: auth, body: JSON.stringify({ mode: "auto", aspect_ratio: "9:16", ...params }) }); let saved = null; const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buf += dec.decode(value, { stream: true }); // SSE 帧只有 data: 行,事件类型在 JSON 的 type 字段里 for (const chunk of buf.split("\n\n").slice(0, -1)) { const data = /^data:\s*([\s\S]+)$/m.exec(chunk)?.[1]; if (!data) continue; let ev; try { ev = JSON.parse(data); } catch { continue; } if (ev.type === "draft") saved = ev.draft; if (ev.type === "error") console.error(" error:", data); } buf = buf.slice(buf.lastIndexOf("\n\n") + 2); } const draft = saved || {}; const segs = draft.segments || []; const durs = segs.map((s) => s.duration); const sum = durs.reduce((a, b) => a + b, 0); console.log( `请求 ${params.total_duration}s ${params.presentation_format}×${params.video_structure}`, "→ 出稿", draft?.total_duration, "s ·", segs.length, "镜 ·", JSON.stringify(durs), "加总", sum, "|", draft?.presentation_format, draft?.video_structure, sum === params.total_duration && durs.every((d) => d >= 4 && d <= 15) ? "✅" : "❌" ); } await fetch(`${API}/api/projects/${project.id}/`, { method: "DELETE", headers: auth });