xdotli commited on
Commit
589e433
·
verified ·
1 Parent(s): 07cf017

Match Carrie landing typography and palette; clarify experiment workflow

Browse files
Files changed (1) hide show
  1. index.html +33 -20
index.html CHANGED
@@ -1,36 +1,49 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>PostTrain · Experiment workspace</title>
 
 
 
 
 
 
2
  <style>
3
- @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
4
- :root{--ink:#20251e;--muted:#677060;--paper:#f5f6f0;--line:#dce1d3;--accent:#d2f28c;--green:#345725;--white:#fff;--red:#a73826}*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:15px/1.55 'DM Sans',sans-serif}button,input,select{font:inherit}button,a,select,summary{ -webkit-tap-highlight-color:transparent}button{cursor:pointer}a{color:var(--green);text-underline-offset:4px}button:focus-visible,a:focus-visible,select:focus-visible,summary:focus-visible{outline:3px solid #648b33;outline-offset:4px}header{border-bottom:1px solid var(--line);padding:22px 5%;display:flex;justify-content:space-between;align-items:center;gap:20px}.brand{font:700 22px 'Space Grotesk',sans-serif;letter-spacing:-1px}.brand span{color:#718158}.header-right{display:flex;align-items:center;gap:22px}.private{font-size:12px;color:var(--muted)}.quiet{border:1px solid var(--line);background:transparent;border-radius:7px;padding:9px 15px;font-weight:600}main{max-width:1160px;margin:auto;padding:44px 30px}.eyebrow{font:600 11px 'Space Grotesk',sans-serif;text-transform:uppercase;letter-spacing:1.8px;color:var(--green)}h1{font:500 clamp(32px,4vw,48px)/1.12 'Space Grotesk',sans-serif;letter-spacing:-2px;margin:12px 0 14px}h2{font:600 22px 'Space Grotesk',sans-serif;letter-spacing:-.7px;margin:0}h3{font:600 17px 'Space Grotesk',sans-serif;margin:0}.intro{color:var(--muted);max-width:660px;margin:0 0 28px}.notice{border-left:3px solid #94ad69;padding:10px 16px;background:#ebf0e2;font-size:13px;margin-bottom:30px}.notice strong{font-weight:700}.layout{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(290px,1fr);gap:24px;align-items:start}.panel{background:var(--white);border:1px solid var(--line);border-radius:12px;padding:28px}.section-title{display:flex;align-items:center;gap:12px;margin-bottom:24px}.step{background:var(--paper);border:1px solid var(--line);font-size:12px;font-weight:700;padding:2px 8px;border-radius:5px}.field{margin-top:22px}label,.label{display:block;font-size:12px;font-weight:700;letter-spacing:.25px;margin-bottom:8px}.help{font-size:12px;color:var(--muted);margin:7px 0 0}select{width:100%;background:#fff;border:1px solid #bcc6ae;border-radius:7px;padding:12px;color:var(--ink)}.model-info{display:flex;justify-content:space-between;gap:10px}.env{border:1px solid var(--line);border-radius:8px;padding:14px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px}.env p{margin:3px 0 0;color:var(--muted);font-size:12px}.tag{font-size:10px;letter-spacing:.8px;text-transform:uppercase;font-weight:700;border:1px solid var(--line);padding:3px 7px;border-radius:4px;white-space:nowrap}.parameters{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.parameters select{padding:10px 8px}.summary{background:#25321f;color:#f3f7ed;border:0}.summary .eyebrow{color:#bddb8b}.summary h2{margin:12px 0 22px}.summary dl{margin:0}.summary dl div{display:flex;justify-content:space-between;gap:16px;padding:12px 0;border-bottom:1px solid #46503f;font-size:13px}.summary dt{color:#c4ccbb}.summary dd{margin:0;text-align:right}.primary{border:0;background:var(--accent);color:#203118;width:100%;font-weight:700;border-radius:7px;padding:14px 18px;margin-top:24px;transition:background .15s,transform .15s}.primary:hover{background:#c0e871;transform:translateY(-1px)}button:disabled{cursor:wait;opacity:.6;transform:none}.footnote{font-size:12px;color:#c1cdb5;margin:12px 0 0}.metric{padding:22px 0 0;color:var(--muted);font-size:13px}.metric strong{display:block;font:500 18px 'Space Grotesk';color:var(--ink);margin:5px 0}.metric p{margin:8px 0 0;font-size:12px}details{margin-top:24px;border-top:1px solid var(--line);padding-top:16px}summary{cursor:pointer;font-size:13px;color:var(--muted)}pre{white-space:pre-wrap;word-break:break-word;max-height:300px;overflow:auto;background:var(--paper);border:1px solid var(--line);padding:15px;font:12px/1.6 monospace;border-radius:6px}.runs-header{display:flex;align-items:center;justify-content:space-between;gap:15px;margin:38px 0 16px}.run{border:1px solid var(--line);background:white;border-radius:10px;padding:20px 24px;margin-top:12px}.run-top{display:flex;justify-content:space-between;gap:14px;align-items:center}.run-title{font-weight:600}.run-sub{color:var(--muted);font-size:12px;margin:4px 0 14px}.badge{font-size:11px;font-weight:700;border-radius:30px;padding:5px 10px;background:#edf3e5;color:#41622e;white-space:nowrap}.badge.error{background:#fff0ec;color:var(--red)}.run-bottom{border-top:1px solid var(--line);padding-top:12px;display:flex;justify-content:space-between;gap:16px;font-size:12px}.links{display:flex;gap:16px}.empty{border:1px dashed #bfc9b0;border-radius:10px;padding:28px;color:var(--muted);font-size:14px}.status{margin-top:16px;font-size:13px;word-break:break-word}.status:empty{display:none}.error{color:var(--red)}.summary .status{color:#e6f4d4}.summary .status a{color:#d2f28c}dialog{max-width:620px;width:calc(100% - 32px);border:1px solid var(--line);border-radius:14px;padding:30px;color:var(--ink)}dialog::backdrop{background:#20251e88}dialog .top{display:flex;justify-content:space-between;align-items:center}dialog p{color:var(--muted);font-size:14px}footer{display:flex;justify-content:space-between;gap:15px;color:var(--muted);font-size:11px;margin-top:30px;padding-top:20px;border-top:1px solid var(--line)}@media(max-width:760px){main{padding:28px 18px}.layout{grid-template-columns:1fr}.panel{padding:22px}header{padding:16px 18px}.private{display:none}h1{letter-spacing:-1.3px}.run-top,.run-bottom{align-items:flex-start;flex-direction:column}.parameters{gap:8px}.links{flex-wrap:wrap}footer{flex-direction:column}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}
5
- </style></head><body>
6
- <header><div class="brand">posttrain<span> / lab</span></div><div class="header-right"><span class="private">Private workspace</span><button class="quiet" id="agent-open">Connect your agent ↗</button></div></header>
7
- <main><div class="eyebrow">PostTrain Arena × Hugging Face</div><h1>Build your next experiment.</h1><p class="intro">Pick a model, choose your training parameters, and test the submission path. Every run keeps a pinned recipe and traceable artifacts.</p>
8
- <div class="notice"><strong>Submission testing is live.</strong> Jobs validate the workflow on CPU. Model training and benchmark scores are not enabled yet.</div>
9
- <div class="layout"><section class="panel" aria-labelledby="setup-heading"><div class="section-title"><span class="step">01</span><h2 id="setup-heading">Configure experiment</h2></div>
10
- <div class="field"><label for="model">Base 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">Qwen3.6 · training on this runtime 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>
11
- <div class="field"><span class="label">Environment</span><div class="env"><div><strong>Data agent · red wine</strong><p>16 training tasks · 14 disjoint evaluation tasks</p></div><span class="tag">Same domain</span></div></div>
12
- <div class="field"><span class="label">Training recipe</span><div class="env"><div><strong>Supervised fine-tuning</strong><p>LoRA adapter · GRPO off</p></div><span class="tag">SFT</span></div></div>
13
- <div class="field parameters"><div><label for="steps">Training 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>
14
- <p class="help">Parameters are saved with your run. LoRA alpha is twice the rank.</p>
15
- <details id="recipe-details"><summary>View the complete recipe</summary><pre id="recipe">Loading recipe…</pre><button class="quiet" id="recipe-download">Download recipe package</button></details></section>
16
- <aside><section class="panel" style="margin-bottom:24px" aria-labelledby="training-heading"><div class="eyebrow">Real model training</div><h2 id="training-heading">Training setup required</h2><p class="help">Baseline → teacher rollouts → LoRA SFT → evaluation → saved weights.</p><p class="help">First supported target: Qwen3.5 · 9B. GPU trainer: H100, maximum 1 hour; serving and teacher costs are additional.</p><ul id="training-blockers" class="help"><li>Checking runner configuration…</li></ul><button class="primary" id="train-launch" disabled>Training unavailable</button><p id="training-status" role="status" aria-live="polite" class="help"></p></section><section class="panel summary"><div class="eyebrow">02 / Submission test</div><h2>Check the submission path.</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>Execution</dt><dd>CPU Basic</dd></div><div><dt>Time limit</dt><dd>15 minutes</dd></div><div><dt>Output</dt><dd>Recipe, logs & status</dd></div></dl><button class="primary" id="launch">Run CPU test →</button><p class="footnote">Runs a real HF Job. No GPU is allocated and no model weights are trained.</p><div id="launch-status" class="status" role="status" aria-live="polite"></div></section><div class="metric">Future evaluation metric<strong>Δ score = after − before</strong><p>Scores appear only after a real training and evaluation run. The task split is a same-domain diagnostic. A completed submission does not demonstrate benchmark improvement.</p></div></aside></div>
17
- <section aria-labelledby="runs-heading"><div class="runs-header"><h2 id="runs-heading">Recent runs</h2><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 your runs…</div></div></section>
18
- <footer><span>Reproducible recipes. Traceable runs.</span><span>HF-native submission prototype · training integration pending</span></footer></main>
 
 
 
 
 
 
 
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')?'Qwen3.6 · training on this runtime unverified':'Smaller model · existing pipeline recipe';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}}
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 test →'}};
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 unavailable';$('training-heading').textContent=trainingState.enabled?'Train your model':'Training setup required';if(trainingState.enabled&&!eligible)$('training-blockers').append(el('li','Select Qwen3.5 · 9B for the verified training target.'))}catch(e){$('training-status').textContent='Cannot check training readiness. '+e.message;$('train-launch').disabled=true}}
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);