TiGa-RCE commited on
Commit
cedfddf
·
verified ·
1 Parent(s): 62f13e5

Guard batch launches with the visible saved plan and current run setup

Browse files
Files changed (1) hide show
  1. client.js +40 -11
client.js CHANGED
@@ -54,6 +54,21 @@ async function runTrackSource(priorContext=null){
54
  }
55
  throw Error('Choose or select a track before starting this run.');
56
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  function buttons() {
58
  const settingsUnavailable=busy||bundleRestoring||bucketOperation;
59
  const unavailable=settingsUnavailable||!!job;
@@ -72,7 +87,11 @@ function buttons() {
72
  $('forgetLocal').disabled = busy || !localResult;
73
  $('track').disabled=unavailable;
74
  $('network').disabled=unavailable;
75
- if($('batch_start'))$('batch_start').disabled=unavailable || !capabilitiesReady || $('run_mode')?.value==='evaluate' || ($('backend').value==='gpu'&&!gpuAvailable);
 
 
 
 
76
  if($('batch_stop'))$('batch_stop').disabled=!batchActive || batchStopRequested;
77
  if($('run_export'))$('run_export').disabled=busy || !runReceipt || pairedAnalysisPending;
78
  const report=pairedAnalysisPending?null:(cleanRunReceipt(runReceipt)||cleanRunReceipt(portableReceipt));
@@ -114,17 +133,17 @@ function syncRunModeUI(unavailable=busy||bundleRestoring||bucketOperation){
114
  if($('run'))$('run').textContent=evaluation?'Evaluate policy (weights frozen)':'Run training';
115
  if($('run_mode_help'))$('run_mode_help').textContent=evaluation
116
  ?'Frozen evaluation uses the selected policy unchanged in a fresh car. Mutation, decay, crossover, reproduction and winner selection are disabled.'
117
- :'Training uses your selected recipe and records winner policies and per-car measurements.';
118
  }
119
  if($('run_mode'))$('run_mode').addEventListener('change',()=>{
120
  syncRunModeUI();buttons();
121
  if(selectedWinner)say($('run_mode').value==='train'
122
- ?'Selected policy is staged as the starting network for training. The next Run click starts a training run with the selected recipe.'
123
  :'Selected policy is ready for frozen evaluation. Its weights stay unchanged; the next Run click starts it.');
124
  });
125
  function showFirstRunTab(tab,focusId){
126
- document.querySelector('[data-tab="'+tab+'"]')?.click();
127
- requestAnimationFrame(()=>$(focusId)?.focus());
128
  }
129
  if($('first_run_track'))$('first_run_track').onclick=()=>showFirstRunTab('drive','trackPresetDrive');
130
  if($('first_run_settings'))$('first_run_settings').onclick=()=>showFirstRunTab('training','population');
@@ -436,6 +455,8 @@ async function encoded(file) {
436
  }
437
  const headers = () => ({'X-Job-Key':job.key});
438
  const networkSource=()=>stagedNetwork || $('network').files[0];
 
 
439
  async function refreshFreezeNetwork(file){
440
  if(!file){window.setFreezeNetwork?.(null);return;}
441
  try{const network=await validateNetworkBytes(new Uint8Array(await file.arrayBuffer()));if(networkSource()===file)window.setFreezeNetwork?.(network);}
@@ -1016,7 +1037,7 @@ $('run').onclick = async () => {
1016
  $('runState').textContent=workLimit?'SETTINGS NEED ADJUSTMENT — no run started':'ERROR — no successful result confirmed';
1017
  $('runState').style.color='#ffb5a2';
1018
  const quotaRejected=isGpuQuotaError(error)||String(error?.message??'').startsWith('GPU allowance is exhausted for this request.');
1019
- $('runSummary').textContent=workLimit?'No run was submitted. Change population, generations, or ticks on Training runs & presets, then try again.':
1020
  'Elapsed: '+((performance.now()-started)/1000).toFixed(1)+' s\n'+(quotaRejected
1021
  ? 'HF rejected this GPU request before a result was published.'
1022
  : 'No downloadable result was received. A connection error does not prove the server stopped.');
@@ -1050,13 +1071,18 @@ $('erase').onclick=async()=>{
1050
  if($('batch_start')){
1051
  $('batch_stop').onclick=()=>{batchStopRequested=true;buttons();$('batch_status').textContent='Stop requested — the current run will finish; no next run will start.';};
1052
  $('batch_start').onclick=async()=>{
1053
- if(busy||job||!capabilitiesReady)return;
 
 
 
 
1054
  let plan,failedCandidateStep=null;const history=[];const started=Date.now();
1055
  try{
1056
- if(busy||job||!capabilitiesReady)throw Error('Finish the current job and connect first');
1057
  plan=window.getBatchPlan();
1058
  busy=true;buttons(); // Freeze both selectors before an asynchronous track fetch.
1059
- const trackFile=await trackSource(),trackBytes=new Uint8Array(await trackFile.arrayBuffer()),track=await encoded(trackFile),trackInfo=trackLibrary?.info()??{source:'upload'},original=networkSource();
 
 
1060
  await encoded(original); // Reject missing/oversize inputs before dispatch.
1061
  const backend=$('backend').value,autosaveSnapshot=bucketToken&&$('bucket_autosave')?.checked?bucketSnapshot():null;
1062
  busy=true;batchActive=true;batchStopRequested=false;batchReceipt=null;buttons();
@@ -1067,7 +1093,7 @@ if($('batch_start')){
1067
  $('runState').style.color='#ffd073';
1068
  showDetails(null);
1069
  const result=await window.BatchTraining.execute(plan,original,async(source,settings,i)=>{
1070
- $('batch_status').textContent='RUNNING · '+plan.name+' · step '+(i+1)+' / '+plan.runs.length+' · '+plan.runs[i].slot;
1071
  const inputHash=await sha(await source.arrayBuffer());
1072
  job=await submit({track,network:await encoded(source),settings,backend,automatic:true});
1073
  const stepJob=job,report=stepJob.report,stepRunReceipt={version:1,state:'completed',build:report?.build??appBuild,backend,output_sha256:stepJob.sha256,report};
@@ -1095,7 +1121,7 @@ if($('batch_start')){
1095
  $('batch_history').append(line);
1096
  }});
1097
  batchReceipt={version:1,build:appBuild,state:result.state,plan,track:trackLibrary?.info()??{source:'upload'},history,elapsed_seconds:(Date.now()-started)/1000};
1098
- $('batch_status').textContent=result.state.toUpperCase()+' · '+history.length+' / '+plan.runs.length+' runs. '+(localResult?'Last received candidate is available on Drive & view.':'No candidate received.');
1099
  $('runState').textContent='BATCH '+result.state.toUpperCase()+' — '+history.length+' / '+plan.runs.length+' runs; server copies deleted';
1100
  $('runState').style.color=result.state==='completed'?'#62dfb1':'#ffd073';
1101
  $('runSummary').textContent='Backend: '+backend.toUpperCase()+'\nCompleted steps: '+history.length+'\nUse Save received result again for the last candidate. A completed batch does not establish improved driving.';
@@ -1166,6 +1192,7 @@ async function connect(){
1166
  $('retry').onclick=()=>{if(!busy&&!job)connect();};
1167
  connect();
1168
 
 
1169
  if($('run_export'))$('run_export').onclick=()=>{
1170
  if(!runReceipt)return;
1171
  const cleaned=cleanRunReceipt(runReceipt);if(!cleaned)return;
@@ -1448,3 +1475,5 @@ if($('bundle_receipt_download'))$('bundle_receipt_download').onclick=()=>{
1448
  const url=URL.createObjectURL(new Blob([JSON.stringify(portableReceipt,null,2)],{type:'application/json'}));
1449
  const a=document.createElement('a');a.href=url;a.download='ald-run-receipt.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);
1450
  };
 
 
 
54
  }
55
  throw Error('Choose or select a track before starting this run.');
56
  }
57
+ function batchRunContext(){
58
+ const trackInfo=trackLibrary?.info()??{source:'upload'},trackFile=$('track').files[0],network=networkSource(),backend=$('backend').value;
59
+ const hasTrack=trackInfo.source==='builtin'||!!trackFile;
60
+ let reason='';
61
+ if(busy||bundleRestoring||bucketOperation)reason='Finish the current operation before starting a batch.';
62
+ else if(job)reason='Finish or delete the current result before starting a batch.';
63
+ else if(!capabilitiesReady)reason='Connect to the training service before running a batch.';
64
+ else if($('run_mode')?.value==='evaluate')reason='Under Action in Run & results, choose Train a population before running a batch.';
65
+ else if(!network)reason='Choose or create a starting network in Run & results.';
66
+ else if(!hasTrack)reason='Choose a track in Run & results.';
67
+ else if(backend==='gpu'&&!gpuAvailable)reason='GPU is unavailable. Choose CPU in Run & results.';
68
+ return {available:!reason,reason,
69
+ track:trackInfo.source==='builtin'?trackInfo.display_name:trackFile?.name,
70
+ network:network?.name||(network?'Selected network':null),backend:backend.toUpperCase()};
71
+ }
72
  function buttons() {
73
  const settingsUnavailable=busy||bundleRestoring||bucketOperation;
74
  const unavailable=settingsUnavailable||!!job;
 
87
  $('forgetLocal').disabled = busy || !localResult;
88
  $('track').disabled=unavailable;
89
  $('network').disabled=unavailable;
90
+ if($('batch_start')){
91
+ const context=batchRunContext();
92
+ window.setBatchRunContext?.(context);
93
+ $('batch_start').disabled=!context.available||!window.getBatchReadiness?.()?.ready;
94
+ }
95
  if($('batch_stop'))$('batch_stop').disabled=!batchActive || batchStopRequested;
96
  if($('run_export'))$('run_export').disabled=busy || !runReceipt || pairedAnalysisPending;
97
  const report=pairedAnalysisPending?null:(cleanRunReceipt(runReceipt)||cleanRunReceipt(portableReceipt));
 
133
  if($('run'))$('run').textContent=evaluation?'Evaluate policy (weights frozen)':'Run training';
134
  if($('run_mode_help'))$('run_mode_help').textContent=evaluation
135
  ?'Frozen evaluation uses the selected policy unchanged in a fresh car. Mutation, decay, crossover, reproduction and winner selection are disabled.'
136
+ :'Training uses the current settings in Training & recipes and records winner policies and per-car measurements.';
137
  }
138
  if($('run_mode'))$('run_mode').addEventListener('change',()=>{
139
  syncRunModeUI();buttons();
140
  if(selectedWinner)say($('run_mode').value==='train'
141
+ ?'Selected policy is staged as the starting network for training. The next Run click starts a training run with the current settings.'
142
  :'Selected policy is ready for frozen evaluation. Its weights stay unchanged; the next Run click starts it.');
143
  });
144
  function showFirstRunTab(tab,focusId){
145
+ const section=document.querySelector('[data-tab="'+tab+'"]');section?.click();
146
+ requestAnimationFrame(()=>{const target=$(focusId);(target&&!target.matches(':disabled')&&!target.closest('[hidden]')?target:section)?.focus();});
147
  }
148
  if($('first_run_track'))$('first_run_track').onclick=()=>showFirstRunTab('drive','trackPresetDrive');
149
  if($('first_run_settings'))$('first_run_settings').onclick=()=>showFirstRunTab('training','population');
 
455
  }
456
  const headers = () => ({'X-Job-Key':job.key});
457
  const networkSource=()=>stagedNetwork || $('network').files[0];
458
+ window.addEventListener('ald:batch-draft-change',buttons);
459
+ document.addEventListener('track-library-change',buttons);
460
  async function refreshFreezeNetwork(file){
461
  if(!file){window.setFreezeNetwork?.(null);return;}
462
  try{const network=await validateNetworkBytes(new Uint8Array(await file.arrayBuffer()));if(networkSource()===file)window.setFreezeNetwork?.(network);}
 
1037
  $('runState').textContent=workLimit?'SETTINGS NEED ADJUSTMENT — no run started':'ERROR — no successful result confirmed';
1038
  $('runState').style.color='#ffb5a2';
1039
  const quotaRejected=isGpuQuotaError(error)||String(error?.message??'').startsWith('GPU allowance is exhausted for this request.');
1040
+ $('runSummary').textContent=workLimit?'No run was submitted. Change population, generations, or ticks on Training & recipes, then try again.':
1041
  'Elapsed: '+((performance.now()-started)/1000).toFixed(1)+' s\n'+(quotaRejected
1042
  ? 'HF rejected this GPU request before a result was published.'
1043
  : 'No downloadable result was received. A connection error does not prove the server stopped.');
 
1071
  if($('batch_start')){
1072
  $('batch_stop').onclick=()=>{batchStopRequested=true;buttons();$('batch_status').textContent='Stop requested — the current run will finish; no next run will start.';};
1073
  $('batch_start').onclick=async()=>{
1074
+ const context=batchRunContext(),readiness=window.getBatchReadiness?.();
1075
+ if(!context.available||!readiness?.ready){
1076
+ $('batch_status').textContent=!readiness?.ready?(readiness?.message||'Save a valid batch before running it.'):context.reason;
1077
+ buttons();return;
1078
+ }
1079
  let plan,failedCandidateStep=null;const history=[];const started=Date.now();
1080
  try{
 
1081
  plan=window.getBatchPlan();
1082
  busy=true;buttons(); // Freeze both selectors before an asynchronous track fetch.
1083
+ const trackFile=await trackSource();
1084
+ if(!trackFile)throw Error('Choose a track in Run & results before running a batch.');
1085
+ const trackBytes=new Uint8Array(await trackFile.arrayBuffer()),track=await encoded(trackFile),trackInfo=trackLibrary?.info()??{source:'upload'},original=networkSource();
1086
  await encoded(original); // Reject missing/oversize inputs before dispatch.
1087
  const backend=$('backend').value,autosaveSnapshot=bucketToken&&$('bucket_autosave')?.checked?bucketSnapshot():null;
1088
  busy=true;batchActive=true;batchStopRequested=false;batchReceipt=null;buttons();
 
1093
  $('runState').style.color='#ffd073';
1094
  showDetails(null);
1095
  const result=await window.BatchTraining.execute(plan,original,async(source,settings,i)=>{
1096
+ $('batch_status').textContent='RUNNING · '+plan.name+' · step '+(i+1)+' / '+plan.runs.length+' · '+plan.runs[i].slot+' · '+plan.runs[i].name;
1097
  const inputHash=await sha(await source.arrayBuffer());
1098
  job=await submit({track,network:await encoded(source),settings,backend,automatic:true});
1099
  const stepJob=job,report=stepJob.report,stepRunReceipt={version:1,state:'completed',build:report?.build??appBuild,backend,output_sha256:stepJob.sha256,report};
 
1121
  $('batch_history').append(line);
1122
  }});
1123
  batchReceipt={version:1,build:appBuild,state:result.state,plan,track:trackLibrary?.info()??{source:'upload'},history,elapsed_seconds:(Date.now()-started)/1000};
1124
+ $('batch_status').textContent=result.state.toUpperCase()+' · '+history.length+' / '+plan.runs.length+' runs. '+(localResult?'Last received candidate is available on Run & results.':'No candidate received.');
1125
  $('runState').textContent='BATCH '+result.state.toUpperCase()+' — '+history.length+' / '+plan.runs.length+' runs; server copies deleted';
1126
  $('runState').style.color=result.state==='completed'?'#62dfb1':'#ffd073';
1127
  $('runSummary').textContent='Backend: '+backend.toUpperCase()+'\nCompleted steps: '+history.length+'\nUse Save received result again for the last candidate. A completed batch does not establish improved driving.';
 
1192
  $('retry').onclick=()=>{if(!busy&&!job)connect();};
1193
  connect();
1194
 
1195
+
1196
  if($('run_export'))$('run_export').onclick=()=>{
1197
  if(!runReceipt)return;
1198
  const cleaned=cleanRunReceipt(runReceipt);if(!cleaned)return;
 
1475
  const url=URL.createObjectURL(new Blob([JSON.stringify(portableReceipt,null,2)],{type:'application/json'}));
1476
  const a=document.createElement('a');a.href=url;a.download='ald-run-receipt.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);
1477
  };
1478
+
1479
+