diff --git a/packages/app/cypress/e2e/navigation.cy.ts b/packages/app/cypress/e2e/navigation.cy.ts index 3e5ee3e0c..b46326952 100644 --- a/packages/app/cypress/e2e/navigation.cy.ts +++ b/packages/app/cypress/e2e/navigation.cy.ts @@ -321,3 +321,32 @@ describe('TPUv7 launch banner', { testIsolation: true }, () => { cy.get('[data-testid="tco-basis-toggle"]').should('not.exist'); }); }); + +describe('H3 video artifact viewer', () => { + it('uses the shared unlock for navigation and keeps an empty viewer free of sample results', () => { + cy.viewport(1440, 1000); + cy.visit('/video', { + onBeforeLoad(win) { + win.localStorage.removeItem('inferencex-feature-gate'); + }, + }); + cy.get('[data-testid="video-benchmark"]').should('contain', 'Open a real CI result to begin'); + cy.get('video[data-role]').should('not.exist'); + cy.get('[data-testid="tab-trigger-hidden"]').should('not.exist'); + cy.get('body').type('{upArrow}{upArrow}{downArrow}{downArrow}'); + cy.get('[data-testid="tab-trigger-hidden"]').click(); + cy.contains('a', 'Video').should('have.attr', 'href', '/video'); + cy.get('head meta[name="robots"]').should('have.attr', 'content', 'noindex, nofollow'); + }); + it('shows a recoverable load error and the Chinese empty state', () => { + cy.visit('/video'); + cy.get('[data-testid="video-benchmark"]').contains('summary', 'Manifest URL').click(); + cy.get('input[aria-label="Manifest URL"]').type('https://example.com/wrong.json'); + cy.contains('button', 'Load manifest').click(); + cy.get('[role="alert"]').should('contain', 'Could not load this bundle'); + cy.get('video[data-role]').should('not.exist'); + cy.visit('/zh/video'); + cy.get('[data-testid="video-benchmark"]').should('contain', '打开真实 CI 结果开始查看'); + cy.get('head meta[name="robots"]').should('have.attr', 'content', 'noindex, nofollow'); + }); +}); diff --git a/packages/app/src/app/(dashboard)/video/page.tsx b/packages/app/src/app/(dashboard)/video/page.tsx new file mode 100644 index 000000000..0624873c2 --- /dev/null +++ b/packages/app/src/app/(dashboard)/video/page.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next'; +import VideoBenchmark from '@/components/video-benchmark/VideoBenchmark'; +import { tabMetadata } from '@/lib/tab-meta'; + +export const metadata: Metadata = { + ...tabMetadata('video'), + robots: { index: false, follow: false }, +}; +export default function VideoPage() { + return ; +} diff --git a/packages/app/src/app/zh/(dashboard)/video/page.tsx b/packages/app/src/app/zh/(dashboard)/video/page.tsx new file mode 100644 index 000000000..3550a1019 --- /dev/null +++ b/packages/app/src/app/zh/(dashboard)/video/page.tsx @@ -0,0 +1,11 @@ +import type { Metadata } from 'next'; +import VideoBenchmark from '@/components/video-benchmark/VideoBenchmark'; +import { tabMetadataZh } from '@/lib/tab-meta-zh'; + +export const metadata: Metadata = { + ...tabMetadataZh('video'), + robots: { index: false, follow: false }, +}; +export default function VideoPage() { + return ; +} diff --git a/packages/app/src/components/tab-nav.tsx b/packages/app/src/components/tab-nav.tsx index 4732315eb..d93012ea0 100644 --- a/packages/app/src/components/tab-nav.tsx +++ b/packages/app/src/components/tab-nav.tsx @@ -56,6 +56,7 @@ const TAB_LABELS_EN: Record = { 'ai-chart': 'AI Chart', 'gpu-metrics': 'PowerX', 'current-inferencex-image': 'Images', + video: 'Video', feedback: 'Feedback', }; diff --git a/packages/app/src/components/video-benchmark/VideoBenchmark.tsx b/packages/app/src/components/video-benchmark/VideoBenchmark.tsx new file mode 100644 index 000000000..c5a4e8317 --- /dev/null +++ b/packages/app/src/components/video-benchmark/VideoBenchmark.tsx @@ -0,0 +1,939 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { Card } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Heading } from '@/components/ui/heading'; +import { useLocale } from '@/lib/use-locale'; +import { track } from '@/lib/analytics'; +import { + at, + entries, + estimateEconomics, + folderReader, + httpReader, + loadBundle, + number, + ROLES, + rows, + sampledPower, + text, + type Bundle, + type Json, +} from './bundle'; + +const STRINGS = { + en: { + title: 'H3 Video Benchmark', + subtitle: 'Original media. Measured execution. Traceable comparisons.', + aa: 'Same-build A/A', + sameWorkload: 'Same workload', + statsNote: + 'The metrics below summarize the measured block. Selected warmup clips are excluded.', + open: 'Open artifact folder', + load: 'Load manifest', + source: 'Manifest URL', + clear: 'Clear results', + loading: 'Loading and verifying checksums…', + empty: 'Open a real CI result to begin', + instructions: + 'Download the H3 artifact from GitHub Actions, extract it, then choose the folder containing manifest.json. Files stay in this browser; nothing is uploaded. Reloading clears imported files.', + sample: 'Download reference CI artifact', + remote: + 'Alternatively, load an HTTPS artifact directory with CORS enabled. Private bundles should use the local folder option; hidden navigation is not access control.', + error: 'Could not load this bundle', + retry: + 'Choose a complete artifact folder or correct the manifest URL and retry. Existing results have been cleared.', + verified: 'File checksums verified', + trust: + 'Checksums establish bundle consistency, not independent source or hardware attestation. Confirm the CI run and manifest hash before sharing conclusions.', + execution: 'Execution', + comparison: 'Regression', + calibration: 'Calibration', + qualification: 'Release qualified', + unavailable: 'Unavailable', + yes: 'Yes', + no: 'No', + baseline: 'Baseline', + candidate: 'Candidate', + slot: 'Clip / request', + prompt: 'Prompt', + settings: 'Generation settings', + seed: 'Seed', + warmup: 'Warmup', + measured: 'Measured', + noMedia: 'No generated media for this request', + mediaError: + 'This browser could not decode the video/audio. Download the original media to inspect it.', + download: 'Download original MP4', + audio: 'Audio is enabled. Play one side at a time to compare the sound.', + latency: 'Median end-to-end latency', + throughput: 'Valid clips / second', + counts: 'Completion accounting', + memory: 'Sampled GPU memory', + timing: + 'Latency: submission through downloaded and validated media, including polling, transfer and analysis. Throughput: valid measured clips ÷ serial measured-block seconds; warmup excluded. These are point estimates, not saturated serving capacity.', + window: 'Measured block', + completed: 'Completed', + valid: 'Valid', + scheduled: 'Scheduled', + failed: 'Failed / invalid', + power: 'Measured power & sampled energy', + powerNote: + 'Board power summed across participating GPUs. Time-weighted power and trapezoidal energy cover only the first-to-last complete telemetry samples tagged measurement, including warmup. No endpoint extrapolation; gaps over 3× the requested interval invalidate the estimate. This is not facility energy or energy per measured clip.', + meanPower: 'Time-weighted mean', + energy: 'Integrated sampled energy (estimate)', + coverage: 'Covered telemetry window', + samples: 'Samples', + memoryNote: + 'Maximum observed device-used VRAM per GPU during the client workload, including warmup. MiB = 2²⁰ bytes. Sampling can miss peaks; these are not allocator peaks.', + integrity: 'Video & audio integrity', + fidelity: 'Paired fidelity', + fidelityNote: + 'Pixel and waveform similarity detect implementation drift. They do not establish prompt adherence, perceptual quality, lip sync or human preference. Thresholds remain uncalibrated unless the backend says otherwise.', + identical: 'Identical frames; finite PSNR undefined', + checks: 'Recorded checks', + hardware: 'Hardware & revisions', + videoPsnr: 'Video PSNR (dB)', + videoMae: 'Video MAE (normalized 0–1)', + audioSpectral: 'Audio spectral cosine', + audioRms: 'Audio RMS ratio (candidate / baseline)', + audioMae: 'Audio waveform MAE (PCM amplitude)', + videoCoverage: 'Video coverage (fraction)', + audioCoverage: 'Audio coverage (fraction)', + node: 'Node', + model: 'Model', + modelRevision: 'Model revision', + runtimeRevision: 'Runtime revision', + sourceHash: 'Source SHA256', + participating: 'Participating GPUs', + allocated: 'Allocated GPUs', + provenance: 'Provenance & downloads', + ciRun: 'Exact CI run', + commit: 'Backend commit', + manifest: 'Manifest SHA256', + report: 'Open original report', + reportNote: + 'Original report rendered in a sandbox, with verified local media. Scripts and external requests are disabled.', + raw: 'Raw artifacts', + economics: 'Revenue & profit scenario', + economicsNote: + 'User-entered USD assumptions. Revenue extrapolates measured serial throughput to one hour at 100% demand; it excludes startup/warmup and is not demonstrated capacity. Profit requires all-in cost per billed GPU-hour, including rental or amortization, electricity, cooling, host/network, storage, labor, licensing and other overhead. Allocation count defaults from Slurm; change billed count only if your contract differs.', + price: 'Selling price (USD / generated clip)', + cost: 'All-in cost (USD / billed GPU-hour)', + billed: 'Billed GPUs', + assumption: 'Price and cost source / assumptions', + date: 'Assumptions as of', + revenueParticipant: 'Revenue / participating GPU-hour', + revenueBilled: 'Revenue / billed GPU-hour', + profitParticipant: 'Profit / participating GPU-hour', + profitBilled: 'Profit / billed GPU-hour', + currency: 'USD', + estimate: 'Estimate', + needAssumptions: + 'Enter a price, source, date and billed GPU count. Leave cost blank to keep profit unavailable.', + missing: 'Backend fields & limits', + missingNote: + 'Missing values stay unavailable. This contract has no measured-only per-request power boundaries, facility energy, pricing or full cost model. Sensor calibration and independent hardware attestation are not supplied. No private object-storage access is configured by this viewer.', + }, + zh: { + title: 'H3 视频基准测试', + subtitle: '查看原始媒体、实测执行数据与可追溯的比较结果。', + aa: '同构建 A/A', + sameWorkload: '相同工作负载', + statsNote: '以下指标汇总正式测量时段;所选 warmup 视频不计入其中。', + open: '打开产物文件夹', + load: '加载 manifest', + source: 'Manifest URL', + clear: '清除结果', + loading: '正在加载并校验文件…', + empty: '打开真实 CI 结果开始查看', + instructions: + '从 GitHub Actions 下载 H3 产物并解压,选择包含 manifest.json 的文件夹。文件仅在当前浏览器中读取,不会上传;刷新页面后需要重新导入。', + sample: '下载参考 CI 产物', + remote: + '也可加载启用 CORS 的 HTTPS 产物目录。私有产物包请通过本地文件夹打开;隐藏导航不等于访问控制。', + error: '无法加载此产物包', + retry: '请选择完整的产物文件夹,或修正 manifest URL 后重试。此前的结果已清除。', + verified: '文件校验和通过', + trust: + '校验和仅确认产物包内部一致,不构成独立的来源或硬件认证。分享结论前请核对 CI run 与 manifest 哈希。', + execution: '执行状态', + comparison: '回归结论', + calibration: '校准状态', + qualification: '发布验收通过', + unavailable: '无数据', + yes: '是', + no: '否', + baseline: '基线', + candidate: '候选', + slot: '视频 / 请求', + prompt: 'Prompt', + settings: '生成配置', + seed: 'Seed', + warmup: 'Warmup', + measured: '正式测量', + noMedia: '此请求没有生成媒体', + mediaError: '浏览器无法解码此视频或音频,请下载原始媒体检查。', + download: '下载原始 MP4', + audio: '音频已启用。建议逐个播放,对比两侧声音。', + latency: '端到端延迟中位数', + throughput: '每秒有效视频数', + counts: '完成情况', + memory: 'GPU 显存采样峰值', + timing: + '延迟从提交请求计时,直到媒体下载并验证完成,包含轮询、传输与分析。吞吐量 = 有效测量视频数 ÷ 串行测量时段秒数,不含 warmup。这些是点估计,不代表饱和服务容量。', + window: '正式测量时段', + completed: '已完成', + valid: '有效', + scheduled: '计划请求', + failed: '失败 / 无效', + power: '实测功耗与采样能耗', + powerNote: + '功率为参与计算的 GPU 板卡功率之和。时间加权平均功率和梯形积分能耗仅覆盖 measurement 阶段首末完整采样之间的时段,包含 warmup。端点不外推;间隔超过请求采样周期的 3 倍时不提供估算。这不是设施能耗,也不是每个正式测量视频的能耗。', + meanPower: '时间加权平均功率', + energy: '采样积分能耗(估算)', + coverage: '遥测覆盖时段', + samples: '采样数', + memoryNote: + '各 GPU 在客户端工作负载期间采样到的显存占用最大值,包含 warmup。MiB = 2²⁰ 字节。采样可能漏掉峰值,不等同于分配器峰值。', + integrity: '视频与音频完整性', + fidelity: '配对保真度', + fidelityNote: + '像素与波形相似度用于发现实现变化造成的输出漂移,不能证明 prompt 遵循度、感知质量、口型同步或人类偏好。除非后端明确标注,否则阈值仍未经校准。', + identical: '帧完全一致,有限 PSNR 无定义', + checks: '已记录的检查', + hardware: '硬件与版本', + videoPsnr: '视频 PSNR(dB)', + videoMae: '视频 MAE(归一化至 0–1)', + audioSpectral: '音频频谱余弦相似度', + audioRms: '音频 RMS 比值(候选 / 基线)', + audioMae: '音频波形 MAE(PCM 振幅)', + videoCoverage: '视频覆盖比例', + audioCoverage: '音频覆盖比例', + node: '节点', + model: '模型', + modelRevision: '模型版本', + runtimeRevision: '运行时版本', + sourceHash: '源码 SHA256', + participating: '参与计算的 GPU', + allocated: '分配的 GPU', + provenance: '来源记录与下载', + ciRun: '对应 CI run', + commit: '后端 commit', + manifest: 'Manifest SHA256', + report: '打开原始报告', + reportNote: '报告在沙盒中显示并引用已校验的本地媒体,脚本与外部请求均禁用。', + raw: '原始产物', + economics: '收入与利润情景估算', + economicsNote: + '金额采用用户输入的美元假设。收入按实测串行吞吐量外推至满需求的一小时,不含启动与 warmup,不代表已验证的服务容量。利润需要每个计费 GPU 小时的完整成本,涵盖租赁或折旧、电力、冷却、主机与网络、存储、人力、许可费及其他开销。分配数量默认来自 Slurm;仅在合同计费方式不同时修改计费数量。', + price: '售价(USD / 生成视频)', + cost: '完整成本(USD / 计费 GPU 小时)', + billed: '计费 GPU 数', + assumption: '价格与成本来源 / 假设', + date: '假设基准日期', + revenueParticipant: '每 GPU 小时收入(参与计算)', + revenueBilled: '每 GPU 小时收入(计费)', + profitParticipant: '每 GPU 小时利润(参与计算)', + profitBilled: '每 GPU 小时利润(计费)', + currency: 'USD', + estimate: '估算', + needAssumptions: '请填写售价、来源、日期与计费 GPU 数。成本留空时,利润显示为无数据。', + missing: '后端字段与局限', + missingNote: + '缺失值显示为无数据。当前后端数据契约不提供逐请求且排除 warmup 的功耗边界、设施能耗、售价或完整成本模型,也未提供传感器校准与独立硬件认证。此查看器未配置私有对象存储访问。', + }, +}; + +interface Loaded { + bundle: Bundle; + urls: Map; + power: Record>; + html: string; +} +const REF_RUN = 'https://github.com/SemiAnalysisAI/InferenceX/actions/runs/34293342829'; +const REF_ARTIFACT = `${REF_RUN}/artifacts/10082823150`; +const parseInput = (value: string) => (value.trim() === '' ? null : Number(value)); + +const field = (label: string, value: string, change: (value: string) => void, type = 'number') => ( + +); + +export default function VideoBenchmark() { + const s = STRINGS[useLocale()]; + const [loaded, setLoaded] = useState(null); + const [loading, setLoading] = useState(false); + const [loadError, setError] = useState(''); + const [source, setSource] = useState(''); + const [slot, setSlot] = useState(''); + const [price, setPrice] = useState(''); + const [cost, setCost] = useState(''); + const [billed, setBilled] = useState(''); + const [assumption, setAssumption] = useState(''); + const [date, setDate] = useState(''); + const generation = useRef(0); + const input = useRef(null); + const activeUrls = useRef([]); + const fmt = (value: Json | undefined, digits = 2): string => { + if (typeof value === 'number') + return Number.isFinite(value) + ? value.toLocaleString('en-US', { maximumFractionDigits: digits }) + : s.unavailable; + if (value === null || value === undefined || value === '') return s.unavailable; + if (typeof value === 'boolean') return value ? s.yes : s.no; + return typeof value === 'string' ? value : JSON.stringify(value); + }; + function clear() { + generation.current++; + activeUrls.current.forEach(URL.revokeObjectURL); + activeUrls.current = []; + setLoaded(null); + setError(''); + setLoading(false); + setSlot(''); + } + async function open(read: () => (path: string) => Promise) { + clear(); + const current = generation.current; + setLoading(true); + const urls = new Map(); + try { + const bundle = await loadBundle(read()); + const power: Loaded['power'] = {}; + for (const role of ROLES) { + const file = bundle.files.get(`gpu/supervisor/${role}/telemetry.jsonl`); + const telemetry = at(bundle.job, 'roles', role, 'telemetry_summary'); + const uuids = rows(at(telemetry, 'gpu_identity')).map((g) => text(at(g, 'uuid'))); + const interval = number(at(telemetry, 'requested_interval_seconds')); + const telemetryText = file ? await file.text() : ''; + power[role] = + file && interval !== null && at(telemetry, 'qualified') === true + ? sampledPower( + telemetryText + .trim() + .split('\n') + .filter(Boolean) + .map((line) => JSON.parse(line)), + uuids, + interval * 3, + ) + : null; + } + if (current !== generation.current) return; + for (const [path, file] of bundle.files) { + urls.set( + path, + URL.createObjectURL( + new Blob([file], { + type: path.endsWith('.mp4') ? 'video/mp4' : 'application/octet-stream', + }), + ), + ); + } + const rawHtml = await bundle.files.get('report/index.html')?.text(); + let html = ''; + if (rawHtml) { + const doc = new DOMParser().parseFromString(rawHtml, 'text/html'); + doc + .querySelectorAll('script,base,iframe,object,embed,link,meta[http-equiv],form') + .forEach((n) => n.remove()); + doc.querySelectorAll('[src], [href]').forEach((n) => { + for (const attr of ['src', 'href']) { + const value = n.getAttribute(attr); + if (value) { + const url = urls.get(`report/${value}`); + if (url) n.setAttribute(attr, url); + else n.removeAttribute(attr); + } + } + }); + const csp = doc.createElement('meta'); + csp.httpEquiv = 'Content-Security-Policy'; + csp.content = + "default-src 'none'; media-src blob:; img-src blob: data:; style-src 'unsafe-inline'; form-action 'none'; base-uri 'none'"; + doc.head.prepend(csp); + html = doc.documentElement.outerHTML; + } + if (current !== generation.current) { + urls.forEach(URL.revokeObjectURL); + return; + } + activeUrls.current = [...urls.values()]; + setLoaded({ bundle, urls, power, html }); + const first = + rows(at(bundle.report, 'roles', 'baseline', 'observations'))[0] ?? + rows(at(bundle.report, 'roles', 'candidate', 'observations'))[0]; + setSlot(text(at(first, 'slot_id'))); + setBilled( + /(?:^|,)gres\/gpu=(?\d+)(?:,|$)/u.exec(text(at(bundle.ci, 'slurm_job', 'AllocTRES'))) + ?.groups?.count ?? '', + ); + track('video_bundle_loaded'); + } catch (error) { + urls.forEach(URL.revokeObjectURL); + if (current === generation.current) + setError(error instanceof Error ? error.message : String(error)); + } finally { + if (current === generation.current) setLoading(false); + } + } + useEffect(() => { + // A shared preview link must not trigger requests to a URL supplied by its sender. + if (['127.0.0.1', 'localhost'].includes(location.hostname)) { + const initial = new URLSearchParams(location.search).get('manifest'); + if (initial) { + setSource(initial); + void open(() => httpReader(initial)); + } + } + return () => { + generation.current++; + activeUrls.current.forEach(URL.revokeObjectURL); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const b = loaded?.bundle; + const participating = rows( + at(b?.job, 'roles', 'baseline', 'telemetry_summary', 'gpu_identity'), + ).length; + const observations = (role: string) => [ + ...rows(at(b?.report, 'roles', role, 'observations')), + ...rows(at(b?.report, 'roles', role, 'warmups')), + ]; + const selected = (role: string) => observations(role).find((o) => at(o, 'slot_id') === slot); + const clip = selected('baseline') ?? selected('candidate'); + const slots = [ + ...new Map( + ROLES.flatMap((role) => observations(role)).map((o) => [text(at(o, 'slot_id')), o]), + ).values(), + ]; + const ciUrl = /^\d+$/u.test(text(at(b?.manifest, 'run_id'))) + ? `https://github.com/SemiAnalysisAI/InferenceX/actions/runs/${text(at(b?.manifest, 'run_id'))}` + : null; + const pair = at(b?.report, 'slot_comparisons', slot); + const rawPair = rows(at(b?.comparison, 'slots')).find((o) => at(o, 'slot_id') === slot); + const table = (data: [string, Json | undefined][]) => ( +
+ {data.map(([label, value]) => ( +
+
{label}
+
{fmt(value, 4)}
+
+ ))} +
+ ); + + return ( +
+
+
+ + {s.title} + +

{s.subtitle}

+
+
+ + {b && ( + + )} +
+
+ { + const files = [...(e.target.files ?? [])]; + e.target.value = ''; + if (files.length > 0) void open(() => folderReader(files)); + }} + /> +
+ {s.source} +
{ + e.preventDefault(); + void open(() => httpReader(source)); + }} + > + setSource(e.target.value)} + placeholder="https://…/manifest.json" + required + /> + +
+

{s.remote}

+
+ {loading && {s.loading}} + {loadError && ( + + {s.error} +

{loadError}

+

{s.retry}

+
+ )} + {!b && !loading && !loadError && ( + + {s.empty} +

{s.instructions}

+ + {s.sample} · #34293342829 + +
+ )} + {b && loaded && ( + <> + +
+ + GitHub #{text(at(b.manifest, 'run_id'))} · {fmt(at(b.manifest, 'run_attempt'))} ·{' '} + {fmt(at(b.ci, 'finished_at'))} + + + {s.verified} · {b.checksums.size} + +
+ {at(b.report, 'same_workload') === true && + at(b.report, 'same_gpu_uuid_set') === true && + at( + b.documents.get('gpu/baseline/run.json'), + 'configuration', + 'configuration_sha256', + ) !== null && + at( + b.documents.get('gpu/baseline/run.json'), + 'configuration', + 'configuration_sha256', + ) === + at( + b.documents.get('gpu/candidate/run.json'), + 'configuration', + 'configuration_sha256', + ) && + at(b.report, 'roles', 'baseline', 'source_identity', 'source_sha256') !== null && + at(b.report, 'roles', 'baseline', 'source_identity', 'source_sha256') === + at(b.report, 'roles', 'candidate', 'source_identity', 'source_sha256') && ( +

{s.aa}

+ )} +
+ {[ + [s.execution, at(b.ci, 'phase')], + [s.comparison, at(b.ci, 'regression_status')], + [s.calibration, at(b.report, 'policy', 'calibration_status')], + [s.qualification, at(b.ci, 'release_qualified')], + ].map(([label, value]) => ( +
+

{fmt(label)}

+

{fmt(value)}

+
+ ))} +
+

+ {s.sameWorkload}: {fmt(at(b.report, 'same_workload'))}. {s.trust} +

+
+ {slots.length > 0 && ( + + +
+ + {s.prompt} · {s.seed} {fmt(at(clip, 'seed'))} + +

{fmt(at(clip, 'prompt'))}

+
+
+ {s.settings} +
+ {table(entries(at(b.manifest, 'workload_plan', 'generation')))} +
+
+
+ )} +
+ {ROLES.map((role) => { + const o = selected(role); + const r = at(b.report, 'roles', role); + const summary = at(r, 'summary'); + const mediaUrl = loaded.urls.get(`report/${text(at(o, 'artifact_path'))}`); + const run = b.documents.get(`gpu/${role}/run.json`); + const rawSummary = at(run, 'summary'); + return ( + +
+ {s[role]} + {fmt(at(o, 'status'))} +
+ {mediaUrl ? ( + <> +
+ ); + })} +
+

{s.timing}

+ + {s.power} +

{s.powerNote}

+
+ {ROLES.map((role) => { + const power = loaded.power[role]; + return ( +
+ {s[role]} + {table([ + [`${s.meanPower} (W)`, power?.watts], + [`${s.energy} (kJ)`, power ? power.joules / 1000 : null], + [`${s.coverage} (s)`, power?.seconds], + [s.samples, power?.sampleCount], + ])} + {power && ( +

+ {power.start} → {power.end} +

+ )} +
+ ); + })} +
+ {s.memory} +

{s.memoryNote}

+
+ {ROLES.map((role) => ( +
+ {s[role]} + {entries( + at( + b.job, + 'roles', + role, + 'telemetry_summary', + 'measurement_observed_memory_peak_mib_by_gpu', + ), + ).length > 0 + ? table( + entries( + at( + b.job, + 'roles', + role, + 'telemetry_summary', + 'measurement_observed_memory_peak_mib_by_gpu', + ), + ).map(([gpu, value]) => [gpu, `${fmt(value)} MiB`]), + ) + : s.unavailable} +
+ ))} +
+
+ + {s.fidelity} +

{s.fidelityNote}

+ {table([ + [ + s.videoPsnr, + at(pair, 'metrics', 'video_identical') === true + ? s.identical + : at(pair, 'metrics', 'video_psnr_db'), + ], + [s.videoMae, at(pair, 'metrics', 'video_mae')], + [s.audioSpectral, at(pair, 'metrics', 'audio_spectral_cosine')], + [s.audioRms, at(pair, 'metrics', 'audio_rms_ratio')], + [s.audioMae, at(rawPair, 'metrics', 'audio_waveform_mae')], + [s.videoCoverage, at(rawPair, 'metrics', 'video_sample_coverage_fraction')], + [s.audioCoverage, at(rawPair, 'metrics', 'audio_sample_coverage_fraction')], + ])} +
+ {s.checks} + {rows(at(pair, 'checks')).map((c, i) => ( +

+ {text(at(c, 'name'))} · {fmt(at(c, 'status'))} · {text(at(c, 'reason'))} +

+ ))} +
+
+ + {s.hardware} + {table([ + [s.participating, participating > 0 ? participating : null], + [ + s.allocated, + /(?:^|,)gres\/gpu=(?\d+)(?:,|$)/u.exec( + text(at(b.ci, 'slurm_job', 'AllocTRES')), + )?.groups?.count, + ], + ['Slurm', at(b.manifest, 'slurm_allocation', 'identity', 'JobId')], + [s.node, at(b.ci, 'slurm_job', 'NodeList')], + [s.model, at(b.manifest, 'workload_plan', 'model_id')], + [s.modelRevision, at(b.manifest, 'workload_plan', 'model_revision')], + ])} +
+ {ROLES.map((role) => ( +
+ {s[role]} + {table([ + [ + s.runtimeRevision, + at(b.report, 'roles', role, 'configuration', 'runtime_revision'), + ], + [s.sourceHash, at(b.report, 'roles', role, 'source_identity', 'source_sha256')], + [ + 'GPU', + rows(at(b.job, 'roles', role, 'telemetry_summary', 'gpu_identity')) + .map((g) => text(at(g, 'name'))) + .join(', '), + ], + ])} +
+ {s.settings} +
+                      {JSON.stringify(at(b.job, 'roles', role, 'launch_argv'), null, 2)}
+                    
+
+
+ ))} +
+
+ + {s.economics} +

{s.economicsNote}

+
+ {field(s.price, price, setPrice)} + {field(s.cost, cost, setCost)} + {field(s.billed, billed, setBilled)} + {field(s.assumption, assumption, setAssumption, 'text')} + {field(s.date, date, setDate, 'date')} +
+

{s.needAssumptions}

+
+ {ROLES.map((role) => { + const summary = at(b.report, 'roles', role, 'summary'); + const count = rows( + at(b.job, 'roles', role, 'telemetry_summary', 'gpu_identity'), + ).length; + const e = + assumption.trim() && date + ? estimateEconomics( + number(at(summary, 'valid_clips_per_second')), + count || null, + parseInput(billed), + parseInput(price), + parseInput(cost), + ) + : null; + return ( +
+ + {s[role]} · {s.estimate} (USD) + + {table([ + [s.revenueParticipant, e?.revenuePerParticipating], + [s.revenueBilled, e?.revenuePerBilled], + [s.profitParticipant, e?.profitPerParticipating], + [s.profitBilled, e?.profitPerBilled], + ])} +
+ ); + })} +
+
+ + {s.provenance} + {ciUrl && ( + + {s.ciRun} · #{text(at(b.manifest, 'run_id'))} + + )} + + {s.commit} · {text(at(b.manifest, 'git_commit'))} + + {table([[s.manifest, b.manifestSha256]])} + {loaded.html && ( +
+ {s.report} +

{s.reportNote}

+