Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/videogenx-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ are fixed. New share/export URLs omit these keys. Both GPU cost bases
remain available as evidence, not selectable chart settings.
`r_workload` uses a compact source point identifier;
canonical workload JSON stays inside the projection. Share includes the `r_`
registry scope. `?unofficialrun=<id>` reads only that run through
registry scope. `?unofficialrun=<id>` (the key matches in any case, so the PR visualizer's
`?unofficialRun=` link works) reads only that run through
`namedRunPoints`. A stored run keeps its actual approval status. A run absent from
the History pages is read live from its GitHub artifacts through
`/api/video-runs?run=<id>&format=live`: the newest attempt of each native
Expand Down Expand Up @@ -581,7 +582,7 @@ Y 轴可选每 1 美元 TCO 生成的视频秒数(默认)、每 GPU 小时
已存储的运行保留其发布批准状态;不在 History 中的运行,通过 `/api/video-runs?run=<id>&format=live` 从其 GitHub 产物实时读取:
只取每个原生 `h3-results` 配置的最新一次尝试,按显式字节区间读取 JSON 文档,并用 `SHA256SUMS` 和 manifest 身份校验。
这类条目未经批准,没有 `publishedAt`,也不附带媒体,因此视频面板不显示它们,横幅会注明该运行尚未发布。
指定运行单独放入 overlayCells 和 overlayDeploymentCells;选中期间,即使已批准也不进入正式结果、部署或工作负载构成。两者均保留所有匹配的 GPU 数,不再按 GPU 数量筛选。
`?unofficialrun=` 参数名不区分大小写,PR 可视化评论中的 `?unofficialRun=` 链接同样有效。指定运行单独放入 overlayCells 和 overlayDeploymentCells;选中期间,即使已批准也不进入正式结果、部署或工作负载构成。两者均保留所有匹配的 GPU 数,不再按 GPU 数量筛选。
默认优先选择有已批准结果的工作负载;仅由指定运行提供的工作负载须明确选择,
或在没有匹配的已批准结果时作为回退。
支持隐藏硬件及移除;清除叠加运行后不会将未批准数据转为正式结果。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import type { VideoPoint } from './metrics';
import type { VideoInitialData } from './initial-data';
import {
DEFAULT_RESULTS_STATE,
isUnofficialRunParam,
MAX_CHIP_CONFIGS,
parseResultsState,
resultProjection,
Expand Down Expand Up @@ -369,10 +370,7 @@ export default function VideoDashboard({
latest.current = next;
const params = new URLSearchParams(
[...new URLSearchParams(location.search)].filter(
([key]) =>
(!key.startsWith('r_') || isStageParam(key)) &&
key !== 'unofficialrun' &&
key !== 'unofficialruns',
([key]) => (!key.startsWith('r_') || isStageParam(key)) && !isUnofficialRunParam(key),
),
);
for (const [key, value] of new URLSearchParams(resultsSearch(next))) params.set(key, value);
Expand Down
12 changes: 12 additions & 0 deletions packages/app/src/components/video-benchmark/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { join } from 'node:path';
import { describe, expect, it } from 'vitest';
import {
at,
generationDuration,
estimateEconomics,
httpReader,
loadBundle,
Expand Down Expand Up @@ -183,3 +184,14 @@ it.skipIf(!process.env.H3_ARTIFACT_DIR)(
}
},
);

describe('generationDuration', () => {
it('prefers the nominal duration and falls back to frame_count / fps', () => {
expect(generationDuration({ duration_seconds: 5, frame_count: 124, fps: 24 })).toBe(5);
expect(generationDuration({ frame_count: 81, fps: 16 })).toBe(5.0625);
expect(generationDuration({ duration_seconds: 0, frame_count: 81, fps: 16 })).toBeNull();
expect(generationDuration({ frame_count: 81 })).toBeNull();
expect(generationDuration({ frame_count: 81, fps: 0 })).toBeNull();
expect(generationDuration(null)).toBeNull();
});
});
11 changes: 11 additions & 0 deletions packages/app/src/components/video-benchmark/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ export function entries(value: Json): [string, Json][] {
export function number(value: Json): number | null {
return typeof value === 'number' && Number.isFinite(value) ? value : null;
}
/**
* Clip length a plan requests: its `duration_seconds`, or `frame_count / fps` for plans that
* request frames (Wan-style specs carry no separate nominal duration).
*/
export function generationDuration(generation: Json): number | null {
const seconds = number(at(generation, 'duration_seconds'));
if (seconds !== null) return seconds > 0 ? seconds : null;
const frames = number(at(generation, 'frame_count')),
fps = number(at(generation, 'fps'));
return frames !== null && fps !== null && frames > 0 && fps > 0 ? frames / fps : null;
}
export function text(value: Json): string {
return typeof value === 'string' ? value : '';
}
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/video-benchmark/evidence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ const SPEC_NAME: Readonly<Record<string, string>> = {
h100: 'H100 SXM',
h200: 'H200 SXM',
b200: 'B200 SXM',
b300: 'B300 SXM',
mi355x: 'MI355X',
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
const VIDEO_HARDWARE_COLORS: Record<string, { light: string; dark: string }> = {
b200: { light: '#5f9e45', dark: '#7fc45f' },
b300: { light: '#6d28d9', dark: '#a78bfa' },
mi355x: { light: '#c2410c', dark: '#f07a3e' },
h200: { light: '#1d6fb8', dark: '#5aa9ec' },
h100: { light: '#0b5c3c', dark: '#2fb58a' },
Expand Down
21 changes: 21 additions & 0 deletions packages/app/src/components/video-benchmark/history.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,27 @@ describe('video history projection', () => {
]);
});

it('keys frame-count plans that request no nominal duration (Wan-style specs)', () => {
const artifact = saved();
changePlans(artifact, (plan) => {
const generation = at(plan, 'generation') as Record<string, Json>;
delete generation.duration_seconds;
Object.assign(generation, { width: 832, height: 480, frame_count: 81, fps: 16 });
});
const source = videoHistoryEntry(artifact, null).sources[0];
expect(source.error).toBeNull();
const [observation] = source.observations;
expect(observation.workloadKey).toEqual(expect.any(String));
expect(observation.durationSeconds).toBe(81 / 16);
expect(observation.frameCount).toBe(81);
// Without frames or a duration the workload is still incomplete.
const incomplete = structuredClone(artifact);
changePlans(incomplete, (plan) => {
delete (at(plan, 'generation') as Record<string, Json>).frame_count;
});
expect(videoHistoryEntry(incomplete, null).sources[0].observations[0].workloadKey).toBeNull();
});

it('groups concurrency cells under their original source without promoting smoke to qualification', () => {
const entry = videoHistoryEntry(saved(), '2026-09-12T00:00:00Z');
expect(entry.id).toBe('123.40');
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/components/video-benchmark/history.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { at, type Json, number, text } from './bundle';
import { at, generationDuration, type Json, number, text } from './bundle';
import type { CIArtifact } from './archive';
import { nativeHardware } from './hardware';
import { storedBundle, storedFidelityBundle, type StoredArtifact } from './stored';
Expand Down Expand Up @@ -168,7 +168,7 @@ export function videoHistoryEntry(
replicas: point.replicas,
wallSeconds: point.wall,
videoSecondsPerHour: point.secondsRate,
durationSeconds: number(at(point.workload, 'generation', 'duration_seconds')),
durationSeconds: generationDuration(at(point.workload, 'generation')),
frameCount: number(at(point.workload, 'generation', 'frame_count')),
avgPowerW: point.power,
enforcedLimitW: point.powerLimit?.watts ?? null,
Expand Down
3 changes: 3 additions & 0 deletions packages/app/src/components/video-benchmark/results.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ describe('VideoGenX results contract', () => {
expect(shared.get('r_price_i2v')).toBe('0.1');
expect(shared.has('r_price_r2v')).toBe(false);
for (const key of ['r_gpus', 'r_basis']) expect(shared.has(key)).toBe(false);
// PR visualizer links spell the overlay `unofficialRun`, as the other dashboards accept.
expect(parseResultsState('unofficialRun=43653414377').unofficialRun).toBe('43653414377');
expect(parseResultsState('UnofficialRuns=7').unofficialRun).toBe('7');
// Retired engine comparison: an old link still plots the best engine per GPU.
expect(parseResultsState('r_compare=1&r_lossy=1')).toEqual({ ...state(), lossy: true });
expect(
Expand Down
17 changes: 12 additions & 5 deletions packages/app/src/components/video-benchmark/results.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { at, number, rows, text, type Json } from './bundle';
import { at, generationDuration, number, rows, text, type Json } from './bundle';
import { costPerGpuHour, type CostTier } from './hardware';
import { metricValue, type VideoPoint } from './metrics';
import { chipConfigKey, compareDeployments } from './deployment';
Expand All @@ -14,7 +14,7 @@ import {
type ResultsYMetric,
} from './results-chart';

export const RESULTS_HARDWARE = ['gb200', 'b200', 'mi355x', 'h200', 'h100'] as const;
export const RESULTS_HARDWARE = ['gb200', 'b300', 'b200', 'mi355x', 'h200', 'h100'] as const;

/** Listed before its first matched run so readers see it is coming; other hardware appears with data. */
const PLANNED_HARDWARE: readonly string[] = ['mi355x'];
Expand Down Expand Up @@ -125,9 +125,16 @@ export const RESULTS_QUERY_PARAMS = [
'unofficialruns',
] as const;

/** Matches the overlay param in any case, as the other dashboards do (`?unofficialRun=` in PR links). */
export const isUnofficialRunParam = (key: string) => /^unofficialruns?$/iu.test(key);

export function parseResultsState(search: string | URLSearchParams): ResultsState {
const s = typeof search === 'string' ? new URLSearchParams(search) : search;
const overlay = s.get('unofficialruns') ?? s.get('unofficialrun');
const overlay =
s.get('unofficialruns') ??
s.get('unofficialrun') ??
[...s].find(([key, value]) => isUnofficialRunParam(key) && value)?.[1] ??
null;
const x = s.get('r_x'),
y = s.get('r_y');
const numeric = (key: string, fallback: number | null, allowZero = false) => {
Expand Down Expand Up @@ -224,7 +231,7 @@ export function resultMetadata(point: VideoPoint) {
text(at(plan, 'model_revision')) &&
positive(width) &&
positive(height) &&
positive(number(at(generation, 'duration_seconds'))) &&
positive(generationDuration(generation)) &&
positive(number(at(generation, 'fps'))) &&
positive(number(at(generation, 'num_inference_steps'))) &&
rows(at(plan, 'cases')).length > 0 &&
Expand All @@ -234,7 +241,7 @@ export function resultMetadata(point: VideoPoint) {
),
model: text(at(plan, 'model_id')) || point.model,
resolution: positive(width) && positive(height) ? `${width}×${height}` : null,
duration: number(at(generation, 'duration_seconds')) ?? point.durationSeconds,
duration: generationDuration(generation) ?? point.durationSeconds,
steps: number(at(generation, 'num_inference_steps')),
fps: number(at(generation, 'fps')),
measurementBoundary: text(at(identity, 'semantics', 'boundary')) || null,
Expand Down
6 changes: 4 additions & 2 deletions packages/app/src/components/video-benchmark/tradeoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
at,
canonical,
entries,
generationDuration,
number,
ROLES,
rows,
Expand Down Expand Up @@ -49,15 +50,16 @@ function workloadInfo(plan: Json, identity: string, semantics: Json) {
const completeWorkload =
text(at(plan, 'model_id')) &&
text(at(plan, 'model_revision')) &&
['width', 'height', 'duration_seconds', 'fps', 'num_inference_steps'].every(
['width', 'height', 'fps', 'num_inference_steps'].every(
(key) => positive(at(generation, key)) !== null,
) &&
generationDuration(generation) !== null &&
cases.length > 0 &&
cases.every((item) => text(at(item, 'prompt')) && Number.isInteger(number(at(item, 'seed'))));
const group = completeWorkload ? canonical({ plan: workload, semantics }) : `unknown:${identity}`;
const workloadLabel = [
`${number(at(generation, 'width')) ?? '?'} × ${number(at(generation, 'height')) ?? '?'}`,
`${number(at(generation, 'duration_seconds')) ?? '?'} s`,
`${generationDuration(generation) ?? '?'} s`,
`${number(at(generation, 'fps')) ?? '?'} fps`,
`${number(at(generation, 'num_inference_steps')) ?? '?'} steps`,
`${text(at(plan, 'model_id'))} @ ${text(at(plan, 'model_revision')).slice(0, 12)}`,
Expand Down
8 changes: 4 additions & 4 deletions packages/app/src/lib/api/api-route-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1568,7 +1568,7 @@ export const apiContractSourceDigests = [
},
{
source: 'src/components/video-benchmark/results.ts',
sourceSha256: '0e92ce3863fa78900eb99b21debf3ee83f3c3bbbd7786ec76d9f1d17f5d2665d',
sourceSha256: '6434392fd34c1fe2ecff965a1cdad9b2c04d9bbcd8c8de15b6510fed41cc94d2',
reviewArea: {
en: 'Shared VideoGenX results selectors, sample admission, dual GPU cost bases and modelled profit.',
zh: 'VideoGenX 结果页共用的筛选、样本准入、两种 GPU 成本口径及估算利润。',
Expand Down Expand Up @@ -1680,15 +1680,15 @@ export const apiContractSourceDigests = [
},
{
source: 'src/components/video-benchmark/tradeoff.ts',
sourceSha256: 'f7ec9e081fa5bb279d896ddc7e9dc176a338e29da5f6dcd40bf50e53727d542f',
sourceSha256: '7d8ea4904eae396109f64644beea847f49166dac202019d95e9559761ffb57f7',
reviewArea: {
en: 'Video source metrics and GPU allocation evidence shared by history and API projections.',
zh: '历史与 API 投影共用的视频来源指标及 GPU 分配证据。',
},
},
{
source: 'src/components/video-benchmark/history.ts',
sourceSha256: '49932cb03796c2e4e8f1ced73418cbe0ad690e42bd73596690f8f393cb6df7bd',
sourceSha256: '6d066cf0c77a15452141c1801b7a9680d3077d0d09fc7b211e2bbf15d774d953',
reviewArea: {
en: 'VideoGenX public dashboard selectors, source identity and shared numerical projection.',
zh: 'VideoGenX 公开仪表板的选择项、来源身份与共享数值投影。',
Expand Down Expand Up @@ -1728,7 +1728,7 @@ export const apiContractSourceDigests = [
},
{
source: 'src/components/video-benchmark/evidence.ts',
sourceSha256: '6337852b266d377873567964a2709bf64f0ed1adfea3c237bde8521dc3cd4886',
sourceSha256: 'ca0a142887e0490130ba5ef5f29d42befb4c20eaba56784ff11146e8ac2ba123',
reviewArea: {
en: 'VideoGenX public dashboard selectors, source identity and shared numerical projection.',
zh: 'VideoGenX 公开仪表板的选择项、来源身份与共享数值投影。',
Expand Down
6 changes: 3 additions & 3 deletions packages/app/src/lib/video/video-history-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,9 @@ describe('history projection version', () => {
for (const file of [...seen].toSorted())
hash.update(relative(root, file)).update(readFileSync(file));
expect({ projection: VIDEO_HISTORY_PROJECTION, sources: hash.digest('hex') }).toEqual({
projection: 4,
// GLM-5.2 8K/1K left the model sequence retirement maps; recorded video entries are unchanged.
sources: 'b028e29c68277a2e6d1c0730306a0796984d62293624824713f924bf67666ab9',
projection: 5,
// Frame-count plans (no `duration_seconds`) now project a workload key and duration.
sources: '261cb8b7390cbcc50b02048b3e3f471fad2a24b6d551be57b590c0f618f075cd',
});
});
});
2 changes: 1 addition & 1 deletion packages/app/src/lib/video/video-history-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from './video-storage';

/** Bump when projected entries can change, so snapshots written by older code are ignored. */
export const VIDEO_HISTORY_PROJECTION = 4;
export const VIDEO_HISTORY_PROJECTION = 5;
const BUILD_BATCH = 8;
const HISTORY = `${PREFIX}/runs/history/`;
const SNAPSHOTS = `${HISTORY}v${VIDEO_HISTORY_PROJECTION}/`;
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/lib/views-api/docs/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ const PARAMETER_NOTES: Record<string, [string, string]> = {
'封印计划中的完整宽×高,例如 1344×768。',
],
r_duration: [
'Positive requested clip duration from the plan, in seconds; not decoded output duration.',
'计划中的请求视频时长,单位秒,必须为正数;不是解码输出时长。',
'Positive requested clip duration from the plan, in seconds: duration_seconds, or frame_count ÷ fps for plans that request frames; not decoded output duration.',
'计划中的请求视频时长,单位秒,必须为正数:取 duration_seconds,按帧数请求的计划取 frame_count ÷ fps;不是解码输出时长。',
],
r_mode: [
'Explicit generation mode; unknown modes are never inferred from model names.',
Expand Down
2 changes: 1 addition & 1 deletion packages/skills/skills/inferencex-api/integrity.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"references/cli-contract.md": "4f070877eeefffad2860c58f79f68432fbe27aa21fc578a1c696f0309a3d7d7b",
"references/cli.md": "11e1183f7ed70d669019e8435ac16a6a9f873de027507b8850a9943f6659bfd9",
"references/collectivex.md": "0399e680c715ab1b6474a42742a59d7862fe8a82d4beff93b79c3c2a70f685de",
"references/dashboard-views.md": "31b15bd46cab91e95116c49c29721947c9a1c9adf6e61cf9094b6cef35f2ed2a",
"references/dashboard-views.md": "d2cef1a80d163ba11853d25ae53ddb10d1417a43c2989de1c775fdecdde11d8e",
"references/offline-exports.md": "95aa565dcd4c9e592159baa560a9a58217bc61e395de1c6daf0576795c7f4ec9",
"references/pareto.md": "1b4d2d163f982e3f2d97addce310789eae9c1e5badd82dd7501708c9e0385027",
"references/powerx.md": "5af5527d54a7e6191a5769080c122638b333b1ceb59e19fdee7df662a5cd3c8f",
Expand Down
Loading
Loading