Spaces:
Running
Running
stouchinglhh
Claude
sync processor from GitLab c697b6e: 版本 1.19.4 / 前端 1.12.4 —— 服务端 i18n(首屏不再闪英文)、推理模块并入模型页、前端抽出 shared/、缓存键改用前端号
bb59211 Download processor/web/static/js/train/params.js from Stouching-Robotics/DataFlowing: direct link, hf CLI and curl.
- Browser
- Download file 4.64 kB
-
https://huggingface.co/spaces/Stouching-Robotics/DataFlowing/resolve/main/processor/web/static/js/train/params.js
- Command line
-
hf download hf://spaces/Stouching-Robotics/DataFlowing/processor/web/static/js/train/params.js
-
curl -L -o params.js https://huggingface.co/spaces/Stouching-Robotics/DataFlowing/resolve/main/processor/web/static/js/train/params.js
4.64 kB
| /** 「训练参数」页签 —— 渲染 train_config.json 里的真实超参。 | |
| * | |
| * 数据是**训练当时实际用的那份配置**(lerobot-train 自己写进 checkpoint 的), | |
| * 不是我们事后补的默认值。 | |
| * | |
| * 缺字段就**整行不渲染**。不同 LeRobot 版本的键名会变(我们比参考实现少 | |
| * `scheduler_state.json`、多四个 pre/postprocessor 文件就是同一类差异),写死 | |
| * "缺了就显示 N/A"会让人误以为训练时那个参数是 null。 | |
| */ | |
| import { el, clear } from '../shared/dom.js'; | |
| import { thousands } from '../shared/format.js'; | |
| import { tr } from '../shared/text.js'; | |
| /** 分组 → 每行 [显示名, 取值路径, 可选格式化函数]。 | |
| * | |
| * 分组标题存的是 **i18n key** 而不是翻好的文字:它是模块级常量,在这里调 | |
| * `tr()` 会在模块加载那一刻定死,用户之后再切语言就不会跟着变。渲染时再取。 */ | |
| const GROUPS = [ | |
| { | |
| titleKey: 'train_group_train', | |
| titleFallback: 'Training', | |
| rows: [ | |
| ['steps', 'steps', thousands], | |
| ['batch_size', 'batch_size'], | |
| ['save_freq', 'save_freq', thousands], | |
| ['seed', 'seed'], | |
| ['num_workers', 'num_workers'], | |
| ], | |
| }, | |
| { | |
| titleKey: 'train_group_policy', | |
| titleFallback: 'Policy', | |
| rows: [ | |
| ['type', 'policy.type'], | |
| ['dim_model', 'policy.dim_model'], | |
| ['chunk_size', 'policy.chunk_size'], | |
| ['n_action_steps', 'policy.n_action_steps'], | |
| ['vision_backbone', 'policy.vision_backbone'], | |
| ['kl_weight', 'policy.kl_weight'], | |
| ], | |
| }, | |
| { | |
| titleKey: 'train_group_optimizer', | |
| titleFallback: 'Optimizer', | |
| rows: [ | |
| ['type', 'optimizer.type'], | |
| ['lr', 'optimizer.lr'], | |
| ['weight_decay', 'optimizer.weight_decay'], | |
| ['grad_clip_norm', 'optimizer.grad_clip_norm'], | |
| ], | |
| }, | |
| { | |
| titleKey: 'train_group_dataset', | |
| titleFallback: 'Dataset', | |
| rows: [ | |
| ['repo_id', 'dataset.repo_id'], | |
| ['root', 'dataset.root'], | |
| ['eval_split', 'dataset.eval_split'], | |
| ['episodes', 'dataset.episodes'], | |
| ], | |
| }, | |
| ]; | |
| /** 按点号路径取值;任一层缺失返回 undefined(不抛)。 */ | |
| function pick(obj, path) { | |
| return path.split('.').reduce( | |
| (acc, key) => (acc === null || acc === undefined ? undefined : acc[key]), | |
| obj, | |
| ); | |
| } | |
| /** 值 → 显示文本。null/undefined/空串统一成 '—',数组/对象转紧凑 JSON。 */ | |
| function display(value, format) { | |
| if (value === null || value === undefined || value === '') return '—'; | |
| if (format) return format(value); | |
| if (Array.isArray(value) || typeof value === 'object') { | |
| return JSON.stringify(value); | |
| } | |
| return String(value); | |
| } | |
| function renderGroup(group, config) { | |
| // 先算一遍哪些行有值 —— 全组都没值的分组直接不渲染,避免出现空卡片。 | |
| const present = group.rows | |
| .map(([label, path, format]) => [label, pick(config, path), format]) | |
| .filter(([, value]) => value !== undefined && value !== null); | |
| if (!present.length) return null; | |
| return el('div', { class: 'bg-gray-900 rounded-lg border border-gray-800 p-4' }, [ | |
| el('div', { | |
| class: 'text-xs font-semibold text-gray-400 mb-3', | |
| text: tr(group.titleKey, group.titleFallback), | |
| }), | |
| el('div', { class: 'space-y-1.5' }, present.map(([label, value, format]) => ( | |
| el('div', { class: 'flex items-baseline gap-3 text-xs' }, [ | |
| el('span', { class: 'text-gray-500 shrink-0 w-32', text: label }), | |
| el('span', { | |
| class: 'text-gray-200 font-mono break-all', | |
| text: display(value, format), | |
| }), | |
| ]) | |
| ))), | |
| ]); | |
| } | |
| /** | |
| * @param {HTMLElement} container | |
| * @param {object} config train_config.json 的内容 | |
| * @param {{step: number}} meta 这份配置取自哪个 checkpoint | |
| */ | |
| export function renderParams(container, config, meta = {}) { | |
| clear(container); | |
| if (!config || Object.keys(config).length === 0) { | |
| container.appendChild(emptyState( | |
| tr('train_params_empty', | |
| 'No train_config.json in this checkpoint; parameters unavailable.'), | |
| )); | |
| return; | |
| } | |
| container.appendChild(el('div', { | |
| class: 'text-xs text-gray-500 mb-4', | |
| text: `${tr('train_params_source', 'Source:')} ` | |
| + `checkpoints/${meta.step ?? '?'}/pretrained_model/train_config.json`, | |
| })); | |
| const cards = GROUPS.map((g) => renderGroup(g, config)).filter(Boolean); | |
| container.appendChild( | |
| el('div', { class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, cards), | |
| ); | |
| } | |
| function emptyState(message) { | |
| return el('div', { | |
| class: 'text-center text-gray-600 text-sm py-10', | |
| text: message, | |
| }); | |
| } | |