Spaces:
Running
Running
File size: 3,369 Bytes
bb59211 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /** 训练页的后端调用 —— 全页只有这里出现 `fetch`。
*
* **这里刻意没有**提交训练 / 停止 / 删除 / 下载。本期不接后端执行,与其放一个
* 点了会 404 的函数,不如等端点真正存在时再加进来 —— 视图层也就不会误以为
* 它们可用。将来接执行时,新端点加在本文件顶部即可,形状保持一致。
*
* 对应后端:`app/routes/training.py`(只读,不执行任何训练)。
*/
const BASE = '/api/v1/training';
async function getJSON(url) {
const res = await fetch(url, { headers: { Accept: 'application/json' } });
if (!res.ok) {
// 后端的错误体是 {detail: ...}。取不到就退回状态码 —— 不要吞成空消息,
// 否则页面上只剩一个没有原因的空白。
let detail = '';
try {
detail = (await res.json()).detail || '';
} catch {
/* 非 JSON 响应(网关/代理错误页),用状态码兜底 */
}
throw new Error(detail || `${res.status} ${res.statusText}`);
}
return res.json();
}
/**
* 列出训练产物。
*
* @returns {Promise<{runs: Array, root: string, exists: boolean}>}
* `exists: false` 表示训练目录不存在(新机器、或本机从未跑过训练)。
* 这是**正常情况**,由调用方渲染空态,不要当错误抛。
*/
export function listRuns() {
return getJSON(`${BASE}/runs`);
}
/**
* 单个 checkpoint 文件的下载地址 —— 给 `<a download>` 用的**字符串**,不是请求。
*
* 路由是 ``/runs/{run}/checkpoints/{step}/files/{name:path}``,``{name:path}`` 要收
* 子目录路径(``pretrained_model/model.safetensors``),所以**整体保留斜杠、只逐段
* 编码**;整串 encodeURIComponent 会把斜杠也编掉,后端就找不到文件。
*/
export function checkpointFileUrl(runName, step, name) {
const seg = encodeURIComponent;
const path = String(name).split('/').map(seg).join('/');
return `${BASE}/runs/${seg(runName)}/checkpoints/${seg(step)}/files/${path}`;
}
/**
* 本机 GPU 列表(读 nvidia-smi)。
*
* @returns {Promise<{available: boolean, error: string, gpus: Array}>}
* `available: false` 表示**问不到显卡**(驱动没起来 / 没装 nvidia-smi),
* 与"显卡闲着"完全是两回事 —— 调用方必须区别显示,不能当 0 用。
*/
export function listGpus() {
return getJSON(`${BASE}/gpus`);
}
/**
* 可训练的数据集 = `sessions/<项目>` 下的多集 LeRobot 树。
*
* 不是 `state/exports/*`:那里每条都是单集导出(total_episodes=1),凑不成训练集。
*/
export function listDatasets() {
return getJSON(`${BASE}/datasets`);
}
/**
* 各项目离「可以导出」还有多远 —— 训练页的「到点提醒」用。
*
* 判据是**已通过集数达到项目设置的采集上限**(`params.target_episodes`),
* 到点只提醒、不自动导出 —— 理由见 `app/training/projects.py` 的 docstring。
*
* @returns {Promise<{projects: Array, ready_count: number}>}
*/
export function listReadyProjects() {
return getJSON(`${BASE}/ready`);
}
// 注:这里曾经有一个 listEpisodes()(走 /api/v1/ingestion/episodes 逐个勾选
// episode)。改成"按数据集(项目)选"之后就没人用了,已删除 —— 等真要做
// "从已审核的集组装训练集"那一步时再加回来,那时它多半还要带上筛选参数。
|