| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>Cosmos3 Latent Reasoning Probe</title> |
| <style> |
| :root { |
| color-scheme: light; |
| --bg: #f7f7f4; |
| --panel: #ffffff; |
| --text: #222222; |
| --muted: #6f6f67; |
| --line: #d8d8ce; |
| --accent: #0b6b57; |
| --soft: #eef3ef; |
| } |
| * { box-sizing: border-box; } |
| body { |
| margin: 0; |
| font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; |
| background: var(--bg); |
| color: var(--text); |
| } |
| header { |
| padding: 28px 32px 18px; |
| border-bottom: 1px solid var(--line); |
| background: var(--panel); |
| } |
| h1 { |
| margin: 0 0 8px; |
| font-size: 28px; |
| line-height: 1.2; |
| letter-spacing: 0; |
| } |
| .subtitle { |
| margin: 0; |
| color: var(--muted); |
| max-width: 920px; |
| line-height: 1.45; |
| } |
| main { |
| display: grid; |
| grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); |
| gap: 18px; |
| padding: 18px; |
| } |
| aside, section { |
| background: var(--panel); |
| border: 1px solid var(--line); |
| border-radius: 8px; |
| } |
| aside { |
| padding: 16px; |
| align-self: start; |
| position: sticky; |
| top: 12px; |
| max-height: calc(100vh - 24px); |
| overflow: auto; |
| } |
| section { |
| padding: 16px; |
| min-width: 0; |
| } |
| label { |
| display: block; |
| color: var(--muted); |
| font-size: 12px; |
| margin: 12px 0 6px; |
| text-transform: uppercase; |
| } |
| select, input, textarea, button { |
| width: 100%; |
| min-height: 36px; |
| border: 1px solid var(--line); |
| border-radius: 6px; |
| background: #fff; |
| color: var(--text); |
| padding: 6px 8px; |
| font-size: 14px; |
| } |
| textarea { |
| min-height: 84px; |
| resize: vertical; |
| font-family: inherit; |
| line-height: 1.4; |
| } |
| button { |
| cursor: pointer; |
| background: var(--accent); |
| color: #fff; |
| border-color: var(--accent); |
| font-weight: 650; |
| } |
| button.secondary { |
| background: #fff; |
| color: var(--accent); |
| } |
| .manual-grid { |
| display: grid; |
| grid-template-columns: repeat(3, minmax(0, 1fr)); |
| gap: 10px; |
| } |
| .manual-actions { |
| display: grid; |
| grid-template-columns: repeat(4, minmax(0, 1fr)); |
| gap: 10px; |
| margin-top: 12px; |
| } |
| .status { |
| color: var(--muted); |
| font-size: 12px; |
| margin-top: 8px; |
| min-height: 18px; |
| } |
| #videoList { |
| min-height: 240px; |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
| font-size: 12px; |
| } |
| video { |
| width: 100%; |
| max-height: 62vh; |
| background: #111; |
| border-radius: 6px; |
| border: 1px solid var(--line); |
| } |
| .badges { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 8px; |
| margin: 12px 0; |
| } |
| .badge { |
| background: var(--soft); |
| color: #173b32; |
| border: 1px solid #c9d8d0; |
| border-radius: 999px; |
| padding: 5px 9px; |
| font-size: 12px; |
| } |
| .grid { |
| display: grid; |
| grid-template-columns: repeat(2, minmax(0, 1fr)); |
| gap: 12px; |
| margin-top: 12px; |
| } |
| .block { |
| border-top: 1px solid var(--line); |
| padding-top: 12px; |
| min-width: 0; |
| } |
| .block.full { grid-column: 1 / -1; } |
| h2 { |
| margin: 0 0 6px; |
| font-size: 15px; |
| line-height: 1.35; |
| } |
| pre { |
| margin: 0; |
| white-space: pre-wrap; |
| overflow-wrap: anywhere; |
| background: #fbfbf9; |
| border: 1px solid var(--line); |
| border-radius: 6px; |
| padding: 10px; |
| max-height: 360px; |
| overflow: auto; |
| font-size: 12px; |
| line-height: 1.45; |
| } |
| .scores { |
| display: grid; |
| grid-template-columns: repeat(4, minmax(0, 1fr)); |
| gap: 8px; |
| margin: 12px 0; |
| } |
| .score { |
| background: #fbfbf9; |
| border: 1px solid var(--line); |
| border-radius: 6px; |
| padding: 8px; |
| } |
| .score span { |
| display: block; |
| color: var(--muted); |
| font-size: 11px; |
| margin-bottom: 3px; |
| } |
| .score strong { font-size: 15px; } |
| @media (max-width: 900px) { |
| main { grid-template-columns: 1fr; } |
| aside { position: static; max-height: none; } |
| .grid, .scores, .manual-grid, .manual-actions { grid-template-columns: 1fr; } |
| } |
| </style> |
| </head> |
| <body> |
| <header> |
| <h1>Cosmos3 Latent Reasoning Probe</h1> |
| <p class="subtitle">Clean, corrupted, and corrective prompt variants for inspecting whether Cosmos3 video generation recovers from language-space physical errors.</p> |
| </header> |
| <main> |
| <aside> |
| <label for="benchmarkFilter">Benchmark</label> |
| <select id="benchmarkFilter"></select> |
| <label for="conditionFilter">Condition</label> |
| <select id="conditionFilter"></select> |
| <label for="errorFilter">Error type</label> |
| <select id="errorFilter"></select> |
| <label for="videoList">Video</label> |
| <select id="videoList" size="14"></select> |
| </aside> |
| <section> |
| <video id="video" controls playsinline></video> |
| <div class="badges" id="badges"></div> |
| <div class="scores" id="scores"></div> |
| <div class="grid"> |
| <div class="block full"> |
| <h2>Original Prompt</h2> |
| <pre id="original"></pre> |
| </div> |
| <div class="block"> |
| <h2>Automatic Evaluation</h2> |
| <pre id="evaluation"></pre> |
| </div> |
| <div class="block full"> |
| <h2>Manual Evaluation</h2> |
| <p class="subtitle">Mark only whether this video is a world-prior success. Saved in this browser; export JSONL/CSV after labeling.</p> |
| <div class="manual-grid"> |
| <div> |
| <label for="manualWorldPrior">World-prior success?</label> |
| <select id="manualWorldPrior"></select> |
| </div> |
| </div> |
| <div class="manual-actions"> |
| <button id="saveManual">Save Current</button> |
| <button id="clearManual" class="secondary">Clear Current</button> |
| <button id="exportJsonl" class="secondary">Export JSONL</button> |
| <button id="exportCsv" class="secondary">Export CSV</button> |
| </div> |
| <div id="manualStatus" class="status"></div> |
| </div> |
| <div class="block"> |
| <h2>Metadata</h2> |
| <pre id="metadata"></pre> |
| </div> |
| <div class="block"> |
| <h2>Clean Upsampled Prompt</h2> |
| <pre id="clean"></pre> |
| </div> |
| <div class="block"> |
| <h2>Generation Prompt</h2> |
| <pre id="generation"></pre> |
| </div> |
| </div> |
| </section> |
| </main> |
| <script> |
| let items = []; |
| let filtered = []; |
| let manualReviews = {}; |
| const REVIEW_STORAGE_KEY = "cosmos3_reasoning_probe_manual_reviews_v1"; |
| |
| const ids = [ |
| "benchmarkFilter", "conditionFilter", "errorFilter", "videoList", "video", |
| "badges", "scores", "original", "evaluation", "metadata", "clean", "generation", |
| "manualWorldPrior", "saveManual", "clearManual", "exportJsonl", "exportCsv", |
| "manualStatus" |
| ]; |
| const el = Object.fromEntries(ids.map((id) => [id, document.getElementById(id)])); |
| |
| function pretty(value) { |
| if (value === null || value === undefined || value === "") return ""; |
| if (typeof value === "string") { |
| try { value = JSON.parse(value); } catch (_) { return value; } |
| } |
| return JSON.stringify(value, null, 2); |
| } |
| |
| function unique(field) { |
| const values = [...new Set(items.map((item) => item[field]).filter(Boolean).map(String))].sort(); |
| return ["All", ...values]; |
| } |
| |
| function fillSelect(select, values) { |
| select.innerHTML = ""; |
| for (const value of values) { |
| const option = document.createElement("option"); |
| option.value = value; |
| option.textContent = value; |
| select.appendChild(option); |
| } |
| } |
| |
| function fillManualControls() { |
| const tri = ["", "true", "false", "unclear"]; |
| fillSelect(el.manualWorldPrior, tri); |
| } |
| |
| function parseTriState(value) { |
| if (value === "true") return true; |
| if (value === "false") return false; |
| if (value === "unclear") return "unclear"; |
| return null; |
| } |
| |
| function triStateToValue(value) { |
| if (value === true) return "true"; |
| if (value === false) return "false"; |
| if (value === "unclear") return "unclear"; |
| return ""; |
| } |
| |
| function itemKey(item) { |
| return [ |
| item.file || "", |
| item.output_stem || "", |
| item.seed ?? "", |
| item.num_inference_steps ?? "", |
| ].join("::"); |
| } |
| |
| function loadManualReviews() { |
| try { |
| manualReviews = JSON.parse(localStorage.getItem(REVIEW_STORAGE_KEY) || "{}"); |
| } catch (_) { |
| manualReviews = {}; |
| } |
| } |
| |
| function persistManualReviews() { |
| localStorage.setItem(REVIEW_STORAGE_KEY, JSON.stringify(manualReviews)); |
| } |
| |
| function currentItem() { |
| if (!filtered.length) return null; |
| return filtered[Number(el.videoList.value || 0)] || null; |
| } |
| |
| function setManualStatus(text) { |
| el.manualStatus.textContent = text; |
| } |
| |
| function loadManualForm(item) { |
| const review = item ? manualReviews[itemKey(item)] : null; |
| const manual = review?.manual || {}; |
| el.manualWorldPrior.value = triStateToValue(manual.world_prior_success_signal); |
| setManualStatus(review ? `Loaded saved manual review from ${review.updated_at}.` : "No manual review saved for this video."); |
| } |
| |
| function buildManualReview(item) { |
| const evaluation = item.evaluation || {}; |
| return { |
| review_key: itemKey(item), |
| updated_at: new Date().toISOString(), |
| row_number: item.row_number, |
| benchmark: item.benchmark, |
| source_index: item.source_index, |
| condition: item.condition, |
| error_type: item.error_type, |
| seed: item.seed ?? null, |
| num_inference_steps: item.num_inference_steps ?? null, |
| file: item.file, |
| output_stem: item.output_stem, |
| original_prompt: item.original_prompt || "", |
| expected_physical_outcome: item.expected_physical_outcome || "", |
| withheld_physical_law: item.withheld_physical_law || "", |
| target_violation: item.target_violation || "", |
| auto_evaluation: evaluation, |
| manual: { |
| world_prior_success_signal: parseTriState(el.manualWorldPrior.value), |
| }, |
| }; |
| } |
| |
| function saveCurrentManualReview() { |
| const item = currentItem(); |
| if (!item) return; |
| const review = buildManualReview(item); |
| manualReviews[review.review_key] = review; |
| persistManualReviews(); |
| setManualStatus(`Saved manual review at ${review.updated_at}. Total saved: ${Object.keys(manualReviews).length}.`); |
| renderItem(item); |
| } |
| |
| function clearCurrentManualReview() { |
| const item = currentItem(); |
| if (!item) return; |
| delete manualReviews[itemKey(item)]; |
| persistManualReviews(); |
| loadManualForm(item); |
| renderItem(item); |
| } |
| |
| function downloadText(filename, text, mime) { |
| const blob = new Blob([text], {type: mime}); |
| const url = URL.createObjectURL(blob); |
| const link = document.createElement("a"); |
| link.href = url; |
| link.download = filename; |
| document.body.appendChild(link); |
| link.click(); |
| link.remove(); |
| URL.revokeObjectURL(url); |
| } |
| |
| function exportManualJsonl() { |
| const rows = Object.values(manualReviews); |
| const text = rows.map((row) => JSON.stringify(row)).join("\n") + (rows.length ? "\n" : ""); |
| downloadText("cosmos3_manual_reviews.jsonl", text, "application/jsonl"); |
| setManualStatus(`Exported ${rows.length} manual reviews as JSONL.`); |
| } |
| |
| function csvCell(value) { |
| if (value === null || value === undefined) return ""; |
| const text = typeof value === "string" ? value : JSON.stringify(value); |
| return `"${text.replaceAll('"', '""')}"`; |
| } |
| |
| function exportManualCsv() { |
| const rows = Object.values(manualReviews); |
| const header = [ |
| "updated_at", "source_index", "condition", "error_type", "seed", |
| "num_inference_steps", "manual_world_prior_success_signal", |
| "auto_world_prior_success_signal", "auto_target_law_violation_present", "file" |
| ]; |
| const lines = [header.join(",")]; |
| for (const row of rows) { |
| const manual = row.manual || {}; |
| const auto = row.auto_evaluation || {}; |
| lines.push([ |
| row.updated_at, |
| row.source_index, |
| row.condition, |
| row.error_type, |
| row.seed, |
| row.num_inference_steps, |
| manual.world_prior_success_signal, |
| auto.world_prior_success_signal, |
| auto.target_law_violation_present, |
| row.file, |
| ].map(csvCell).join(",")); |
| } |
| downloadText("cosmos3_manual_reviews.csv", lines.join("\n") + "\n", "text/csv"); |
| setManualStatus(`Exported ${rows.length} manual reviews as CSV.`); |
| } |
| |
| function label(item) { |
| const parts = [ |
| String(item.source_index).padStart(3, "0"), |
| item.condition, |
| item.error_type, |
| ]; |
| if (item.seed !== undefined && item.seed !== null) parts.push(`seed ${item.seed}`); |
| if (item.num_inference_steps !== undefined && item.num_inference_steps !== null) { |
| parts.push(`step ${item.num_inference_steps}`); |
| } |
| return parts.join(" | "); |
| } |
| |
| function applyFilters() { |
| const benchmark = el.benchmarkFilter.value; |
| const condition = el.conditionFilter.value; |
| const errorType = el.errorFilter.value; |
| filtered = items.filter((item) => { |
| if (benchmark !== "All" && item.benchmark !== benchmark) return false; |
| if (condition !== "All" && item.condition !== condition) return false; |
| if (errorType !== "All" && item.error_type !== errorType) return false; |
| return true; |
| }); |
| el.videoList.innerHTML = ""; |
| filtered.forEach((item, index) => { |
| const option = document.createElement("option"); |
| option.value = String(index); |
| option.textContent = label(item); |
| el.videoList.appendChild(option); |
| }); |
| if (filtered.length) { |
| el.videoList.value = "0"; |
| renderItem(filtered[0]); |
| } else { |
| renderEmpty(); |
| } |
| } |
| |
| function scoreCard(labelText, value) { |
| return `<div class="score"><span>${labelText}</span><strong>${value ?? "NA"}</strong></div>`; |
| } |
| |
| function renderItem(item) { |
| const evaluation = item.evaluation || {}; |
| const hasManual = Boolean(manualReviews[itemKey(item)]); |
| const relativeVideoUrl = `${item.file}?v=${encodeURIComponent(item.asset_version || "1")}`; |
| el.video.src = item.file_url || relativeVideoUrl; |
| el.video.load(); |
| el.badges.innerHTML = [ |
| ["Benchmark", item.benchmark], |
| ["Source", item.source_index], |
| ["Condition", item.condition], |
| ["Error", item.error_type], |
| ["Seed", item.seed], |
| ["Steps", item.num_inference_steps], |
| ["Injected error", evaluation.injected_error_present], |
| ["Target violation", evaluation.target_law_violation_present], |
| ["World-prior success", evaluation.world_prior_success_signal], |
| ["Manual saved", hasManual], |
| ["Corrected", evaluation.corrected_in_video], |
| ["Black-box signal", evaluation.black_box_correction_signal], |
| ].map(([k, v]) => `<span class="badge">${k}: ${v ?? ""}</span>`).join(""); |
| el.scores.innerHTML = [ |
| scoreCard("Physical plausibility", evaluation.physical_plausibility), |
| scoreCard("Task completion", evaluation.task_completion), |
| scoreCard("Object permanence", evaluation.object_permanence), |
| scoreCard("Contact / support", evaluation.contact_support), |
| scoreCard("Causal order", evaluation.causal_order), |
| scoreCard("Confidence", evaluation.confidence), |
| ].join(""); |
| el.original.textContent = item.original_prompt || ""; |
| el.evaluation.textContent = pretty(evaluation); |
| el.metadata.textContent = pretty({ |
| source_path: item.source_path, |
| file_url: item.file_url, |
| seed: item.seed, |
| num_inference_steps: item.num_inference_steps, |
| expected_physical_outcome: item.expected_physical_outcome, |
| target_violation: item.target_violation, |
| withheld_physical_law: item.withheld_physical_law, |
| injected_error: item.injected_error, |
| trajectory_probe: item.trajectory_probe, |
| seed_sweep: item.seed_sweep, |
| causal_intervention: item.causal_intervention, |
| benchmark_metadata: item.metadata, |
| }); |
| el.clean.textContent = pretty(item.clean_upsampled_prompt); |
| el.generation.textContent = pretty(item.generation_prompt); |
| loadManualForm(item); |
| } |
| |
| function renderEmpty() { |
| el.video.removeAttribute("src"); |
| el.badges.textContent = "No videos match the selected filters."; |
| el.scores.innerHTML = ""; |
| for (const key of ["original", "evaluation", "metadata", "clean", "generation"]) { |
| el[key].textContent = ""; |
| } |
| loadManualForm(null); |
| } |
| |
| async function main() { |
| const response = await fetch("metadata.json"); |
| items = await response.json(); |
| loadManualReviews(); |
| fillManualControls(); |
| fillSelect(el.benchmarkFilter, unique("benchmark")); |
| fillSelect(el.conditionFilter, unique("condition")); |
| fillSelect(el.errorFilter, unique("error_type")); |
| for (const select of [el.benchmarkFilter, el.conditionFilter, el.errorFilter]) { |
| select.addEventListener("change", applyFilters); |
| } |
| el.videoList.addEventListener("change", () => { |
| renderItem(filtered[Number(el.videoList.value)]); |
| }); |
| el.saveManual.addEventListener("click", saveCurrentManualReview); |
| el.clearManual.addEventListener("click", clearCurrentManualReview); |
| el.exportJsonl.addEventListener("click", exportManualJsonl); |
| el.exportCsv.addEventListener("click", exportManualCsv); |
| applyFilters(); |
| } |
| |
| main().catch((error) => { |
| console.error(error); |
| el.badges.textContent = `Failed to load metadata: ${error}`; |
| }); |
| </script> |
| </body> |
| </html> |
|
|