Spaces:
Running
Running
Match Carrie landing typography and palette; clarify experiment workflow
Browse files- index.html +33 -20
index.html
CHANGED
|
@@ -1,36 +1,49 @@
|
|
| 1 |
-
<!doctype html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
<style>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
</style>
|
| 6 |
-
<
|
| 7 |
-
<
|
| 8 |
-
<
|
| 9 |
-
<
|
| 10 |
-
<div class="
|
| 11 |
-
<div class="
|
| 12 |
-
<div class="
|
| 13 |
-
<
|
| 14 |
-
<
|
| 15 |
-
<
|
| 16 |
-
<
|
| 17 |
-
<
|
| 18 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
<dialog id="agent-dialog"><div class="top"><h2>Connect your agent</h2><button class="quiet" id="agent-close" aria-label="Close agent instructions">✕</button></div><p>Sign in to Hugging Face with access to this private Space, then give your agent the instructions below. It can use the API directly; browser clicking is optional.</p><pre id="agent-prompt">Read https://benchflow-posttrain-submission-lab-20260920.hf.space/AGENTS.md using my existing Hugging Face login. Follow its API instructions to inspect the available models, choose SFT parameters, download a complete recipe package, and submit one CPU workflow test. Wait for completion and report the job and artifacts. Do not expose tokens, allocate GPUs, or claim training results.</pre><button class="quiet" id="copy-agent">Copy instructions</button><p id="copy-status" role="status"></p></dialog>
|
| 20 |
<script>
|
| 21 |
const $=id=>document.getElementById(id);let submitting=false;let recipeText='';let recipeVersion=0;let refreshing=false;
|
| 22 |
const selection=()=>({model:$('model').value,steps:Number($('steps').value),rate:Number($('rate').value),rank:Number($('rank').value)});
|
| 23 |
async function api(path,body){const r=await fetch(path,body?{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}:{});if(!r.ok){let e;try{e=await r.json()}catch{}throw Error(typeof e?.detail==='string'?e.detail:e?.detail?.blockers?.join(' ')||'The request failed. Please try again.')}return r}
|
| 24 |
-
async function update(){const v=++recipeVersion;const s=selection();$('chosen-model').textContent=$('model').selectedOptions[0].textContent;$('chosen-recipe').textContent=`${s.steps} steps · rank ${s.rank}`;$('model-link').href='https://huggingface.co/'+s.model;$('model-help').textContent=s.model.includes('3.6')?'
|
| 25 |
function el(tag,text,cls){const n=document.createElement(tag);if(text!==undefined)n.textContent=text;if(cls)n.className=cls;return n}
|
| 26 |
function link(text,url){const a=el('a',text);if(url&&url.startsWith('https://huggingface.co/')){a.href=url;a.target='_blank';a.rel='noopener'}return a}
|
| 27 |
async function refresh(){if(refreshing)return;refreshing=true;$('refresh').disabled=true;try{const rows=await(await api('/api/runs')).json();$('runs').replaceChildren();$('run-status').textContent='';if(!rows.length)$('runs').append(el('div','Your first run will appear here. Choose a recipe and test the submission.','empty'));for(const r of rows){const card=el('article',undefined,'run');const top=el('div',undefined,'run-top');top.append(el('div',r.model?.split('/')[1]||'Submitted experiment','run-title'));const stage=r.job_stage;top.append(el('span',stage==='COMPLETED'?(r.status==='dry-run'?'CPU test complete':r.status==='completed'?'Training complete':'Job finished — inspect results'):stage==='RUNNING'?'Running':stage==='SCHEDULING'?'Queued':stage==='UNKNOWN'?'Status unavailable':stage,'badge'+(['ERROR','CANCELED','CANCELLED'].includes(stage)?' error':'')));card.append(top);const date=r.updated_at?new Date(r.updated_at).toLocaleString():'';card.append(el('div',`${r.run_id} · ${date}`,'run-sub'));const bottom=el('div',undefined,'run-bottom');bottom.append(el('span',r.status==='dry-run'?'Workflow checked · no training score':`Pipeline: ${r.status}`));const links=el('div',undefined,'links');if(r.job_url)links.append(link('View job ↗',r.job_url));if(r.artifact_url)links.append(link('Artifacts ↗',r.artifact_url));bottom.append(links);card.append(bottom);$('runs').append(card)}}catch(e){$('run-status').textContent=e.message;$('run-status').className='error';if(!$('runs').querySelector('.run'))$('runs').replaceChildren(el('div','Run history is unavailable. Your selected recipe is preserved.','empty'))}finally{refreshing=false;$('refresh').disabled=false}}
|
| 28 |
-
$('launch').onclick=async()=>{if(submitting)return;submitting=true;$('launch').disabled=true;$('launch').textContent='Submitting…';$('launch-status').textContent='Preparing your recipe and scheduling an HF Job…';try{const r=await(await api('/api/submit',selection())).json();$('launch-status').replaceChildren(el('span','Submitted. '),link('Open HF Job ↗',r.job_url));await refresh()}catch(e){$('launch-status').textContent=e.message}finally{submitting=false;$('launch').disabled=false;$('launch').textContent='Run CPU
|
| 29 |
for(const id of ['model','steps','rate','rank'])$(id).onchange=update;
|
| 30 |
$('refresh').onclick=refresh;$('recipe-download').onclick=async()=>{try{const response=await api('/api/bundle',selection());const u=URL.createObjectURL(await response.blob());const a=document.createElement('a');a.href=u;a.download='posttrain-recipe.zip';a.click();setTimeout(()=>URL.revokeObjectURL(u),1000)}catch(e){$('recipe').textContent=e.message}};
|
| 31 |
$('agent-open').onclick=()=>$('agent-dialog').showModal();$('agent-close').onclick=()=>$('agent-dialog').close();$('copy-agent').onclick=async()=>{try{await navigator.clipboard.writeText($('agent-prompt').textContent);$('copy-status').textContent='Copied. Paste this into your agent.'}catch{$('copy-status').textContent='Select the instructions above and copy them manually.'}};
|
| 32 |
let trainingState=null;
|
| 33 |
-
async function checkTraining(){try{trainingState=await(await api('/api/training-readiness')).json();$('training-blockers').replaceChildren(...trainingState.blockers.map(x=>el('li',x)));const eligible=trainingState.enabled&&$('model').value===trainingState.model;$('train-launch').disabled=!eligible;$('train-launch').textContent=eligible?'Start GPU training →':'Training
|
| 34 |
$('model').addEventListener('change',checkTraining);
|
| 35 |
$('train-launch').onclick=async()=>{if(submitting)return;submitting=true;$('train-launch').disabled=true;$('launch').disabled=true;$('training-status').textContent='Submitting a real GPU training job…';try{const r=await(await api('/api/submit',{...selection(),mode:'train'})).json();$('training-status').replaceChildren(el('span','Training submitted. '),link('Open HF Job ↗',r.job_url));await refresh()}catch(e){$('training-status').textContent=e.message}finally{submitting=false;$('launch').disabled=false;await checkTraining()}};
|
| 36 |
checkTraining();update();refresh();setInterval(()=>{if(!document.hidden)refresh()},15000);
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 5 |
+
<title>PostTrain Arena / Experiments</title>
|
| 6 |
+
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 7 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet">
|
| 8 |
<style>
|
| 9 |
+
:root{--paper:#fff;--ink:#080808;--muted:#5b666b;--blue:#0006fc;--wash:#e6e7ff;--line:#dfe3e4;--panel:#f4f5f6;--red:#ae2727}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);color:var(--ink);font:14px/1.55 Inter,system-ui,sans-serif}button,select{font:inherit}button,a,summary,select{-webkit-tap-highlight-color:transparent}button{cursor:pointer}a{color:inherit;text-underline-offset:4px}button:focus-visible,a:focus-visible,select:focus-visible,summary:focus-visible{outline:2px solid var(--blue);outline-offset:4px}button{transition:background .16s,color .16s}button:disabled{cursor:not-allowed;background:var(--panel);color:var(--muted);border-color:var(--line)}.skip{position:absolute;top:-60px;left:20px;background:white;padding:10px;z-index:10}.skip:focus{top:8px}header{padding:22px 4%;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:24px}.brand{font-size:18px;text-decoration:none;letter-spacing:-.6px;white-space:nowrap}.brand span{color:var(--muted);font-size:13px;letter-spacing:0;margin-left:10px}.header-right{display:flex;gap:28px;align-items:center}.nav-link{font-size:11px;text-transform:uppercase;letter-spacing:1px;text-decoration:none}.nav-link:hover{color:var(--blue)}.quiet{background:white;border:1px solid var(--line);padding:10px 16px;color:var(--ink);font-size:12px}.quiet:hover{background:var(--panel)}.agent-button{border-color:var(--ink)}main{max-width:1280px;margin:auto;padding:38px 40px 24px}.eyebrow,.section-label{font-size:10px;font-weight:500;letter-spacing:1.5px;text-transform:uppercase}.eyebrow{color:var(--blue)}.masthead{display:flex;justify-content:space-between;align-items:center;gap:32px;margin-bottom:28px}h1{font:400 clamp(38px,4.5vw,60px)/1.05 'Instrument Serif',Georgia,serif;letter-spacing:-1.2px;margin:10px 0 12px}h2{font:400 30px/1.15 'Instrument Serif',Georgia,serif;margin:0}p{margin:0}.intro{color:var(--muted);max-width:550px;font-size:13px}.equation{display:flex;align-items:center;gap:12px;white-space:nowrap;font:400 30px 'Instrument Serif',Georgia,serif;align-self:center;color:var(--blue)}.equation .term{border:1px solid var(--blue);padding:11px 16px}.equation .plain{color:var(--ink)}.notice{border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 0;display:flex;align-items:center;gap:13px;font-size:12px;color:var(--muted);margin-bottom:30px}.notice strong{color:var(--ink);font-weight:500}.status-dot{width:6px;height:6px;border:1px solid var(--blue);background:var(--blue);flex-shrink:0;border-radius:50%}.layout{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(310px,1fr);gap:48px;align-items:start}.section-title{display:flex;align-items:center;justify-content:space-between;padding-bottom:20px;border-bottom:1px solid var(--line)}.section-label{color:var(--muted)}.field-row{display:grid;grid-template-columns:32px minmax(0,1fr);gap:12px;padding:24px 0;border-bottom:1px solid var(--line)}.step{font-size:11px;color:var(--blue);font-variant-numeric:tabular-nums;padding-top:3px}.field-title{font-weight:500;display:block;margin-bottom:12px;font-size:13px}.help{color:var(--muted);font-size:11px;line-height:1.6;margin:8px 0 0}.model-info{display:flex;align-items:start;gap:15px;justify-content:space-between}.model-info a{white-space:nowrap}select{width:100%;min-height:44px;padding:10px 12px;border:1px solid #a3aeb2;border-radius:0;background:white;color:var(--ink)}.env{display:flex;justify-content:space-between;align-items:center;gap:12px}.env strong{font-size:14px;font-weight:500}.env p{color:var(--muted);font-size:12px;margin-top:5px}.tag{font-size:10px;padding:4px 8px;background:var(--panel);white-space:nowrap}.parameters{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.parameters label{font-size:11px;color:var(--muted);display:block;margin-bottom:7px}.parameters select{font-size:12px;padding:10px 8px}.method{display:flex;justify-content:space-between;gap:8px;margin-bottom:15px;font-size:12px}.method span{color:var(--muted)}details{margin-top:18px}summary{cursor:pointer;color:var(--muted);font-size:12px}pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:300px;overflow:auto;background:var(--panel);border:1px solid var(--line);padding:16px;font:11px/1.65 ui-monospace,monospace}.recipe-footer{display:flex;justify-content:space-between;align-items:center;gap:15px;padding-top:20px}.recipe-footer details{margin:0;flex:1}.recipe-body{margin-top:18px}.panel{min-width:0}.summary{background:var(--panel);padding:24px}.summary-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.summary h2{margin-bottom:18px}.summary dl{margin:0}.summary dl>div{display:flex;justify-content:space-between;gap:16px;padding:9px 0;font-size:12px;border-bottom:1px solid var(--line)}.summary dt{color:var(--muted)}.summary dd{margin:0;text-align:right}.primary{display:block;width:100%;border:1px solid var(--blue);background:var(--blue);color:white;font-size:13px;font-weight:500;padding:13px 16px;margin-top:20px}.primary:hover:not(:disabled){background:#0003c7}.footnote{font-size:11px;color:var(--muted);margin-top:10px;line-height:1.6}.training-panel{margin-top:24px;border-top:1px solid var(--line);padding-top:20px}.training-title{display:flex;justify-content:space-between;gap:14px;align-items:center}.training-title h2{font:500 13px Inter,system-ui,sans-serif}.training-panel .primary{margin-top:12px}.training-panel details{margin-top:12px}.training-panel ul{padding-left:18px}.training-panel li{margin-top:6px}.status{font-size:12px;overflow-wrap:anywhere;margin-top:12px}.status:empty{display:none}.status a{color:var(--blue)}.budget{display:flex;justify-content:space-between;align-items:baseline;margin:16px 0 0;font-size:11px;color:var(--muted)}.budget strong{color:var(--ink);font-weight:500}.runs-header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding-top:36px;margin:30px 0 18px;border-top:1px solid var(--line)}.runs-header .eyebrow{margin-bottom:8px}.run{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 24px;border-top:1px solid var(--line);padding:18px 0}.run-top{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.run-title{font-weight:500;font-size:13px}.badge{font-size:10px;background:var(--panel);padding:4px 8px;color:var(--muted);white-space:nowrap}.badge.error{background:#fff0ef;color:var(--red)}.run-sub{grid-column:1;font-size:10px;overflow-wrap:anywhere;color:var(--muted)}.run-bottom{grid-column:2;grid-row:1 / 3;display:flex;flex-direction:column;justify-content:center;align-items:flex-end;gap:7px;font-size:11px;color:var(--muted)}.links{display:flex;gap:20px;color:var(--ink)}.empty{padding:25px 0;color:var(--muted);border-top:1px solid var(--line)}.error{color:var(--red)}.metric{color:var(--muted);font-size:11px}.metric strong{font-weight:500;color:var(--ink)}footer{margin-top:26px;padding-top:20px;border-top:1px solid var(--line);display:flex;justify-content:space-between;gap:16px;font-size:10px;color:var(--muted)}dialog{width:calc(100% - 32px);max-width:620px;border:1px solid var(--ink);padding:28px;color:var(--ink)}dialog::backdrop{background:#08080866}dialog .top{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:16px}dialog p{font-size:13px;color:var(--muted)}
|
| 10 |
+
@media(min-width:1500px){main{padding-top:46px}}@media(max-width:900px){main{padding:28px 24px}.layout{gap:26px;grid-template-columns:minmax(0,1.25fr) minmax(280px,1fr)}.equation{font-size:24px;gap:8px}.equation .term{padding:8px 11px}.header-right{gap:16px}.brand span{display:none}.model-info{display:block}}@media(max-width:700px){header{padding:17px 20px}.header-right .nav-link{display:none}.agent-button{padding:8px 10px;font-size:11px}.brand{font-size:16px}main{padding:26px 20px}.masthead{display:block;margin-bottom:22px}.equation{display:none}h1{font-size:42px}.notice{align-items:flex-start;font-size:11px}.status-dot{margin-top:5px}.layout{grid-template-columns:1fr;gap:28px}.section-title{padding-bottom:16px}.field-row{padding:20px 0;grid-template-columns:24px minmax(0,1fr)}.summary{padding:22px}.model-info{display:flex}.run{display:block}.run-sub{margin:6px 0 12px}.run-bottom{align-items:flex-start;gap:10px}.runs-header{margin-top:24px;padding-top:28px}.parameters{gap:8px}footer{flex-direction:column;gap:8px}.recipe-footer{align-items:flex-start}.env{align-items:flex-start}.env .tag{white-space:normal;text-align:right}}@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{transition:none!important}}
|
| 11 |
+
</style>
|
| 12 |
+
</head><body>
|
| 13 |
+
<a class="skip" href="#experiment">Skip to experiment</a>
|
| 14 |
+
<header><a class="brand" href="#experiment">PostTrain Arena<span>/ Experiments</span></a><nav class="header-right" aria-label="Workspace"><a class="nav-link" href="#runs-heading">Run history</a><a class="nav-link" href="https://github.com/benchflow-ai/posttrainarena" target="_blank" rel="noopener">Documentation ↗</a><button class="quiet agent-button" id="agent-open">Connect your agent ↗</button></nav></header>
|
| 15 |
+
<main id="experiment">
|
| 16 |
+
<div class="masthead"><div><div class="eyebrow">BenchFlow × Hugging Face / Private workspace</div><h1>Better models start here.</h1><p class="intro">Choose your model, shape the recipe, and follow every run.</p></div><div class="equation" aria-label="Model plus data yields improvement"><span class="plain">Model</span><span>+</span><span class="term">your data</span><span>→ Δ</span></div></div>
|
| 17 |
+
<div class="notice"><span class="status-dot" aria-hidden="true"></span><p><strong>CPU submission checks are live.</strong> Training is not connected to this workspace yet. Completed checks do not produce trained weights or scores.</p></div>
|
| 18 |
+
<div class="layout">
|
| 19 |
+
<section class="panel" aria-labelledby="setup-heading"><div class="section-title"><h2 id="setup-heading">Your experiment</h2><span class="section-label">Configure</span></div>
|
| 20 |
+
<div class="field-row"><span class="step">01</span><div><label class="field-title" for="model">Start with a model</label><select id="model"><option value="Qwen/Qwen3.6-27B">Qwen3.6 · 27B</option><option value="Qwen/Qwen3.5-9B">Qwen3.5 · 9B</option></select><div class="model-info"><p class="help" id="model-help">Training compatibility in this workspace is unverified.</p><a class="help" id="model-link" href="https://huggingface.co/Qwen/Qwen3.6-27B" target="_blank" rel="noopener">Model card ↗</a></div></div></div>
|
| 21 |
+
<div class="field-row"><span class="step">02</span><div><div class="field-title">Choose the task set</div><div class="env"><div><strong>Data agent / Red wine</strong><p>16 train · 14 evaluation tasks</p></div><span class="tag">Diagnostic slice</span></div><p class="help">Pinned task packages. Training trajectories have not been generated.</p></div></div>
|
| 22 |
+
<div class="field-row"><span class="step">03</span><div><div class="field-title">Shape the training recipe</div><div class="method">Supervised fine-tuning <span>LoRA · GRPO off</span></div><div class="parameters"><div><label for="steps">Steps</label><select id="steps"><option>1</option><option selected>5</option><option>10</option></select></div><div><label for="rate">Learning rate</label><select id="rate"><option value="0.00001">1 × 10⁻⁵</option><option selected value="0.00002">2 × 10⁻⁵</option><option value="0.00005">5 × 10⁻⁵</option></select></div><div><label for="rank">LoRA rank</label><select id="rank"><option>8</option><option selected>16</option><option>32</option></select></div></div><p class="help">Saved with your recipe. Applied only when training runs.</p></div></div>
|
| 23 |
+
<div class="recipe-footer"><span class="help">Pinned revisions. Reproducible settings.</span><button class="quiet" id="recipe-download">Download recipe ↓</button></div><details id="recipe-details"><summary>Inspect recipe</summary><pre id="recipe">Loading recipe…</pre></details>
|
| 24 |
+
</section>
|
| 25 |
+
<aside>
|
| 26 |
+
<section class="panel summary"><div class="summary-head"><span class="eyebrow">Run preview</span><span class="tag">CPU check</span></div><h2>A small check.<br>A clear starting point.</h2><dl><div><dt>Model</dt><dd id="chosen-model">Qwen3.6 · 27B</dd></div><div><dt>Recipe</dt><dd id="chosen-recipe">5 steps · rank 16</dd></div><div><dt>Compute</dt><dd>CPU Basic · 15 min limit</dd></div><div><dt>You’ll receive</dt><dd>Recipe, logs & status</dd></div></dl><button class="primary" id="launch">Run CPU check →</button><p class="footnote">Checks the submission path. No GPU, model training, or evaluation score.</p><div id="launch-status" class="status" role="status" aria-live="polite"></div></section>
|
| 27 |
+
<section class="training-panel" aria-labelledby="training-heading"><div class="training-title"><h2 id="training-heading">Model training</h2><span class="tag" id="training-badge">Not connected</span></div><p class="help">The next step: train, evaluate, and compare your model.</p><button class="primary" id="train-launch" disabled>Training not connected</button><details><summary>What’s needed to enable training?</summary><ul id="training-blockers" class="help"><li>Checking training connection…</li></ul></details><p id="training-status" role="status" aria-live="polite" class="status"></p><div class="budget"><span>Approved experiment budget</span><strong>$200 total</strong></div><p class="help">Includes training, serving and evaluation. Spend tracking is not connected.</p></section>
|
| 28 |
+
</aside></div>
|
| 29 |
+
<section aria-labelledby="runs-heading"><div class="runs-header"><div><div class="eyebrow">Your experiment log</div><h2 id="runs-heading">Run history</h2></div><button class="quiet" id="refresh">Refresh ↻</button></div><div id="run-status" role="status" aria-live="polite"></div><div id="runs"><div class="empty">Loading runs…</div></div></section>
|
| 30 |
+
<footer><span>PostTrain Arena / BenchFlow</span><span class="metric"><strong>Δ = score after − score before.</strong> Measured only after training and evaluation.</span></footer>
|
| 31 |
+
</main>
|
| 32 |
<dialog id="agent-dialog"><div class="top"><h2>Connect your agent</h2><button class="quiet" id="agent-close" aria-label="Close agent instructions">✕</button></div><p>Sign in to Hugging Face with access to this private Space, then give your agent the instructions below. It can use the API directly; browser clicking is optional.</p><pre id="agent-prompt">Read https://benchflow-posttrain-submission-lab-20260920.hf.space/AGENTS.md using my existing Hugging Face login. Follow its API instructions to inspect the available models, choose SFT parameters, download a complete recipe package, and submit one CPU workflow test. Wait for completion and report the job and artifacts. Do not expose tokens, allocate GPUs, or claim training results.</pre><button class="quiet" id="copy-agent">Copy instructions</button><p id="copy-status" role="status"></p></dialog>
|
| 33 |
<script>
|
| 34 |
const $=id=>document.getElementById(id);let submitting=false;let recipeText='';let recipeVersion=0;let refreshing=false;
|
| 35 |
const selection=()=>({model:$('model').value,steps:Number($('steps').value),rate:Number($('rate').value),rank:Number($('rank').value)});
|
| 36 |
async function api(path,body){const r=await fetch(path,body?{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}:{});if(!r.ok){let e;try{e=await r.json()}catch{}throw Error(typeof e?.detail==='string'?e.detail:e?.detail?.blockers?.join(' ')||'The request failed. Please try again.')}return r}
|
| 37 |
+
async function update(){const v=++recipeVersion;const s=selection();$('chosen-model').textContent=$('model').selectedOptions[0].textContent;$('chosen-recipe').textContent=`${s.steps} steps · rank ${s.rank}`;$('model-link').href='https://huggingface.co/'+s.model;$('model-help').textContent=s.model.includes('3.6')?'27B parameters · training in this workspace unverified':'9B parameters · training in this workspace unverified';try{const text=await(await api('/api/recipe',s)).text();if(v!==recipeVersion)return;recipeText=text;$('recipe').textContent=text}catch(e){$('recipe').textContent=e.message}}
|
| 38 |
function el(tag,text,cls){const n=document.createElement(tag);if(text!==undefined)n.textContent=text;if(cls)n.className=cls;return n}
|
| 39 |
function link(text,url){const a=el('a',text);if(url&&url.startsWith('https://huggingface.co/')){a.href=url;a.target='_blank';a.rel='noopener'}return a}
|
| 40 |
async function refresh(){if(refreshing)return;refreshing=true;$('refresh').disabled=true;try{const rows=await(await api('/api/runs')).json();$('runs').replaceChildren();$('run-status').textContent='';if(!rows.length)$('runs').append(el('div','Your first run will appear here. Choose a recipe and test the submission.','empty'));for(const r of rows){const card=el('article',undefined,'run');const top=el('div',undefined,'run-top');top.append(el('div',r.model?.split('/')[1]||'Submitted experiment','run-title'));const stage=r.job_stage;top.append(el('span',stage==='COMPLETED'?(r.status==='dry-run'?'CPU test complete':r.status==='completed'?'Training complete':'Job finished — inspect results'):stage==='RUNNING'?'Running':stage==='SCHEDULING'?'Queued':stage==='UNKNOWN'?'Status unavailable':stage,'badge'+(['ERROR','CANCELED','CANCELLED'].includes(stage)?' error':'')));card.append(top);const date=r.updated_at?new Date(r.updated_at).toLocaleString():'';card.append(el('div',`${r.run_id} · ${date}`,'run-sub'));const bottom=el('div',undefined,'run-bottom');bottom.append(el('span',r.status==='dry-run'?'Workflow checked · no training score':`Pipeline: ${r.status}`));const links=el('div',undefined,'links');if(r.job_url)links.append(link('View job ↗',r.job_url));if(r.artifact_url)links.append(link('Artifacts ↗',r.artifact_url));bottom.append(links);card.append(bottom);$('runs').append(card)}}catch(e){$('run-status').textContent=e.message;$('run-status').className='error';if(!$('runs').querySelector('.run'))$('runs').replaceChildren(el('div','Run history is unavailable. Your selected recipe is preserved.','empty'))}finally{refreshing=false;$('refresh').disabled=false}}
|
| 41 |
+
$('launch').onclick=async()=>{if(submitting)return;submitting=true;$('launch').disabled=true;$('launch').textContent='Submitting…';$('launch-status').textContent='Preparing your recipe and scheduling an HF Job…';try{const r=await(await api('/api/submit',selection())).json();$('launch-status').replaceChildren(el('span','Submitted. '),link('Open HF Job ↗',r.job_url));await refresh()}catch(e){$('launch-status').textContent=e.message}finally{submitting=false;$('launch').disabled=false;$('launch').textContent='Run CPU check →'}};
|
| 42 |
for(const id of ['model','steps','rate','rank'])$(id).onchange=update;
|
| 43 |
$('refresh').onclick=refresh;$('recipe-download').onclick=async()=>{try{const response=await api('/api/bundle',selection());const u=URL.createObjectURL(await response.blob());const a=document.createElement('a');a.href=u;a.download='posttrain-recipe.zip';a.click();setTimeout(()=>URL.revokeObjectURL(u),1000)}catch(e){$('recipe').textContent=e.message}};
|
| 44 |
$('agent-open').onclick=()=>$('agent-dialog').showModal();$('agent-close').onclick=()=>$('agent-dialog').close();$('copy-agent').onclick=async()=>{try{await navigator.clipboard.writeText($('agent-prompt').textContent);$('copy-status').textContent='Copied. Paste this into your agent.'}catch{$('copy-status').textContent='Select the instructions above and copy them manually.'}};
|
| 45 |
let trainingState=null;
|
| 46 |
+
async function checkTraining(){try{trainingState=await(await api('/api/training-readiness')).json();$('training-blockers').replaceChildren(...trainingState.blockers.map(x=>el('li',x)));const eligible=trainingState.enabled&&$('model').value===trainingState.model;$('train-launch').disabled=!eligible;$('train-launch').textContent=eligible?'Start GPU training →':'Training not connected';$('training-heading').textContent='Model training';$('training-badge').textContent=trainingState.enabled?'Connected':'Not connected';if(trainingState.enabled&&!eligible)$('training-blockers').append(el('li','Select Qwen3.5 · 9B for the configured training target.'))}catch(e){$('training-status').textContent='Cannot check training readiness. '+e.message;$('train-launch').disabled=true}}
|
| 47 |
$('model').addEventListener('change',checkTraining);
|
| 48 |
$('train-launch').onclick=async()=>{if(submitting)return;submitting=true;$('train-launch').disabled=true;$('launch').disabled=true;$('training-status').textContent='Submitting a real GPU training job…';try{const r=await(await api('/api/submit',{...selection(),mode:'train'})).json();$('training-status').replaceChildren(el('span','Training submitted. '),link('Open HF Job ↗',r.job_url));await refresh()}catch(e){$('training-status').textContent=e.message}finally{submitting=false;$('launch').disabled=false;await checkTraining()}};
|
| 49 |
checkTraining();update();refresh();setInterval(()=>{if(!document.hidden)refresh()},15000);
|