Spaces:
Running on Zero
Running on Zero
Record selected policy explicitly and reject stale playback
Browse files
client.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
| 2 |
const $ = id => document.getElementById(id);
|
| 3 |
let job = null;
|
| 4 |
let busy = false;
|
|
|
|
|
|
|
|
|
|
| 5 |
let localResult = null;
|
| 6 |
let localAnalysis = null;
|
| 7 |
let pairedAnalysisPending = false;
|
|
@@ -58,7 +61,7 @@ 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.';
|
|
@@ -70,7 +73,7 @@ function batchRunContext(){
|
|
| 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;
|
| 75 |
trackLibrary?.setBusy(unavailable);
|
| 76 |
window.HFAuth?.setBusy(unavailable);
|
|
@@ -87,6 +90,13 @@ function buttons() {
|
|
| 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);
|
|
@@ -438,7 +448,7 @@ function saveLocal() {
|
|
| 438 |
setTimeout(()=>URL.revokeObjectURL(url),60000);
|
| 439 |
}
|
| 440 |
$('saveLocal').onclick=saveLocal;
|
| 441 |
-
$('forgetLocal').onclick=()=>{localResult=null;localAnalysis=null;pairedAnalysisPending=false;renderAnalysisPanel();buttons();say('Browser result discarded. Downloaded files are unaffected.');};
|
| 442 |
async function request(path, options = {}) {
|
| 443 |
const response = await fetch(path, {...options, cache:'no-store', credentials:'same-origin'});
|
| 444 |
if (!response.ok) {
|
|
@@ -455,19 +465,71 @@ async function encoded(file) {
|
|
| 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);window.TrackReplay?.setStagedNetwork?.(null);return;}
|
| 462 |
try{const network=await validateNetworkBytes(new Uint8Array(await file.arrayBuffer()));if(networkSource()===file){window.setFreezeNetwork?.(network);window.TrackReplay?.setStagedNetwork?.(network);}}
|
| 463 |
catch(error){if(networkSource()===file){window.setFreezeNetwork?.(null);window.TrackReplay?.setStagedNetwork?.(null);if($('freeze_status'))$('freeze_status').textContent='Network metadata unavailable: '+error.message;}}
|
| 464 |
}
|
| 465 |
$('network').onchange=()=>{stagedNetwork=null;selectedWinner=null;if($('networkChoice'))$('networkChoice').textContent='Uploaded file selected.';refreshFreezeNetwork(networkSource());buttons();};
|
| 466 |
window.TrainingClient={
|
| 467 |
-
isBusy:()=>busy||!!job||bundleRestoring,
|
| 468 |
hasUnsavedState:()=>!!(networkSource()||localResult||$('track').files.length||trackLibrary?.info?.()?.source==='builtin'||$('editor_file')?.files.length||window.hasUnsavedNetworkDraft?.()),
|
| 469 |
useNetwork(file){
|
| 470 |
-
if(busy||job)throw Error('Finish
|
| 471 |
stagedNetwork=file;$('network').value='';
|
| 472 |
if($('networkChoice'))$('networkChoice').textContent='Browser editor input: '+file.name+'. Not uploaded yet.';
|
| 473 |
refreshFreezeNetwork(file);
|
|
@@ -961,6 +1023,8 @@ function showDetails(r){
|
|
| 961 |
window.TrackReplay?.setRunAnalysis?.(localAnalysis,runReceipt||portableReceipt||(r?{report:r}:null));
|
| 962 |
}
|
| 963 |
$('run').onclick = async () => {
|
|
|
|
|
|
|
| 964 |
const comparisonAttempt=!!comparisonPlan,comparisonRole=comparisonAttempt?comparisonPlan.roles[comparisonPlan.index]:null;
|
| 965 |
const previousRunResultContext=runResultContext;
|
| 966 |
cancelSteamLeaderboard();
|
|
|
|
| 2 |
const $ = id => document.getElementById(id);
|
| 3 |
let job = null;
|
| 4 |
let busy = false;
|
| 5 |
+
let replayBusy = false;
|
| 6 |
+
let localReplay = null;
|
| 7 |
+
let replayRequestId = 0;
|
| 8 |
let localResult = null;
|
| 9 |
let localAnalysis = null;
|
| 10 |
let pairedAnalysisPending = false;
|
|
|
|
| 61 |
const trackInfo=trackLibrary?.info()??{source:'upload'},trackFile=$('track').files[0],network=networkSource(),backend=$('backend').value;
|
| 62 |
const hasTrack=trackInfo.source==='builtin'||!!trackFile;
|
| 63 |
let reason='';
|
| 64 |
+
if(busy||replayBusy||bundleRestoring||bucketOperation)reason='Finish the current operation before starting a batch.';
|
| 65 |
else if(job)reason='Finish or delete the current result before starting a batch.';
|
| 66 |
else if(!capabilitiesReady)reason='Connect to the training service before running a batch.';
|
| 67 |
else if($('run_mode')?.value==='evaluate')reason='Under Action in Run & results, choose Train a population before running a batch.';
|
|
|
|
| 73 |
network:network?.name||(network?'Selected network':null),backend:backend.toUpperCase()};
|
| 74 |
}
|
| 75 |
function buttons() {
|
| 76 |
+
const settingsUnavailable=busy||replayBusy||bundleRestoring||bucketOperation;
|
| 77 |
const unavailable=settingsUnavailable||!!job;
|
| 78 |
trackLibrary?.setBusy(unavailable);
|
| 79 |
window.HFAuth?.setBusy(unavailable);
|
|
|
|
| 90 |
$('forgetLocal').disabled = busy || !localResult;
|
| 91 |
$('track').disabled=unavailable;
|
| 92 |
$('network').disabled=unavailable;
|
| 93 |
+
if($('replay_record'))$('replay_record').disabled=unavailable||batchActive||!capabilitiesReady||!replayNetworkSource();
|
| 94 |
+
if($('replay_duration'))$('replay_duration').disabled=settingsUnavailable;
|
| 95 |
+
if($('replay_download'))$('replay_download').disabled=replayBusy||!localReplay;
|
| 96 |
+
if($('replay_policy')){
|
| 97 |
+
const selected=networkSource();
|
| 98 |
+
$('replay_policy').textContent=selected?'Policy: '+(selected.name||'Selected network'):localResult?'Policy: latest received result. Choose a winner below to record a different policy.':'Choose or create a network to record a drive.';
|
| 99 |
+
}
|
| 100 |
if($('batch_start')){
|
| 101 |
const context=batchRunContext();
|
| 102 |
window.setBatchRunContext?.(context);
|
|
|
|
| 448 |
setTimeout(()=>URL.revokeObjectURL(url),60000);
|
| 449 |
}
|
| 450 |
$('saveLocal').onclick=saveLocal;
|
| 451 |
+
$('forgetLocal').onclick=()=>{invalidateReplay();localResult=null;localAnalysis=null;pairedAnalysisPending=false;renderAnalysisPanel();buttons();say('Browser result discarded. Downloaded files are unaffected.');};
|
| 452 |
async function request(path, options = {}) {
|
| 453 |
const response = await fetch(path, {...options, cache:'no-store', credentials:'same-origin'});
|
| 454 |
if (!response.ok) {
|
|
|
|
| 465 |
}
|
| 466 |
const headers = () => ({'X-Job-Key':job.key});
|
| 467 |
const networkSource=()=>stagedNetwork || $('network').files[0];
|
| 468 |
+
function replayNetworkSource(){return networkSource()||localResult;}
|
| 469 |
+
function invalidateReplay(reason){
|
| 470 |
+
replayRequestId++;
|
| 471 |
+
localReplay=null;
|
| 472 |
+
window.TrackReplay?.clearRecording?.(reason);
|
| 473 |
+
if($('replay_download'))$('replay_download').disabled=true;
|
| 474 |
+
}
|
| 475 |
window.addEventListener('ald:batch-draft-change',buttons);
|
| 476 |
document.addEventListener('track-library-change',buttons);
|
| 477 |
+
document.addEventListener('track-library-change',()=>invalidateReplay('Track changed. Record a new drive for this track.'));
|
| 478 |
+
if($('replay_record'))$('replay_record').onclick=async()=>{
|
| 479 |
+
if(busy||replayBusy||job||batchActive||bundleRestoring||bucketOperation)return;
|
| 480 |
+
replayBusy=true;busy=true;
|
| 481 |
+
invalidateReplay('Recording a fresh CPU drive…');
|
| 482 |
+
const attempt=replayRequestId;
|
| 483 |
+
const controller=new AbortController();
|
| 484 |
+
const timeout=setTimeout(()=>controller.abort(),40000);
|
| 485 |
+
buttons();
|
| 486 |
+
try{
|
| 487 |
+
const track=await trackSource(),network=replayNetworkSource();
|
| 488 |
+
if(!track||!network)throw Error('Choose a track and a network before recording.');
|
| 489 |
+
const ticks=Number($('replay_duration').value);
|
| 490 |
+
if(![300,900].includes(ticks))throw Error('Choose a 5-second or 15-second recording.');
|
| 491 |
+
const [trackBytes,networkBytes]=await Promise.all([track.arrayBuffer(),network.arrayBuffer()]);
|
| 492 |
+
await validateNetworkBytes(new Uint8Array(networkBytes));
|
| 493 |
+
const [trackSha,networkSha,track64,network64]=await Promise.all([
|
| 494 |
+
sha(trackBytes),sha(networkBytes),encoded(track),encoded(network)
|
| 495 |
+
]);
|
| 496 |
+
const response=await request('/api/replay',{method:'POST',signal:controller.signal,
|
| 497 |
+
headers:{'Content-Type':'application/json'},body:JSON.stringify({track:track64,network:network64,ticks})});
|
| 498 |
+
const raw=await response.text();
|
| 499 |
+
if(raw.length>2*1024*1024)throw Error('Recording exceeds the browser limit.');
|
| 500 |
+
const recording=JSON.parse(raw);
|
| 501 |
+
const currentTrack=await trackSource(),currentNetwork=replayNetworkSource();
|
| 502 |
+
if(attempt!==replayRequestId||!currentTrack||!currentNetwork)throw Error('Selection changed during recording. Record the current selection again.');
|
| 503 |
+
const [currentTrackSha,currentNetworkSha]=await Promise.all([
|
| 504 |
+
currentTrack.arrayBuffer().then(sha),currentNetwork.arrayBuffer().then(sha)
|
| 505 |
+
]);
|
| 506 |
+
if(currentTrackSha!==trackSha||currentNetworkSha!==networkSha)throw Error('Selection changed during recording. Record the current selection again.');
|
| 507 |
+
if(!window.TrackReplay?.setRecording)throw Error('The recorded-drive viewer is unavailable. Reload this page.');
|
| 508 |
+
await window.TrackReplay.setRecording(recording,trackSha,networkSha);
|
| 509 |
+
if(attempt!==replayRequestId){window.TrackReplay.clearRecording();throw Error('Selection changed during recording. Record the current selection again.');}
|
| 510 |
+
localReplay=recording;
|
| 511 |
+
}catch(error){
|
| 512 |
+
if(attempt===replayRequestId&&$('replay_status'))$('replay_status').textContent=
|
| 513 |
+
error?.name==='AbortError'?'Recording timed out. Try a 5-second drive.':'Recording unavailable: '+error.message;
|
| 514 |
+
}finally{
|
| 515 |
+
clearTimeout(timeout);replayBusy=false;busy=false;buttons();
|
| 516 |
+
}
|
| 517 |
+
};
|
| 518 |
+
if($('replay_download'))$('replay_download').onclick=()=>{
|
| 519 |
+
if(localReplay)downloadTextFile(JSON.stringify(localReplay),'ald-recorded-drive.json','application/json');
|
| 520 |
+
};
|
| 521 |
async function refreshFreezeNetwork(file){
|
| 522 |
+
invalidateReplay('Network changed. Record a new drive for this policy.');
|
| 523 |
if(!file){window.setFreezeNetwork?.(null);window.TrackReplay?.setStagedNetwork?.(null);return;}
|
| 524 |
try{const network=await validateNetworkBytes(new Uint8Array(await file.arrayBuffer()));if(networkSource()===file){window.setFreezeNetwork?.(network);window.TrackReplay?.setStagedNetwork?.(network);}}
|
| 525 |
catch(error){if(networkSource()===file){window.setFreezeNetwork?.(null);window.TrackReplay?.setStagedNetwork?.(null);if($('freeze_status'))$('freeze_status').textContent='Network metadata unavailable: '+error.message;}}
|
| 526 |
}
|
| 527 |
$('network').onchange=()=>{stagedNetwork=null;selectedWinner=null;if($('networkChoice'))$('networkChoice').textContent='Uploaded file selected.';refreshFreezeNetwork(networkSource());buttons();};
|
| 528 |
window.TrainingClient={
|
| 529 |
+
isBusy:()=>busy||replayBusy||!!job||bundleRestoring,
|
| 530 |
hasUnsavedState:()=>!!(networkSource()||localResult||$('track').files.length||trackLibrary?.info?.()?.source==='builtin'||$('editor_file')?.files.length||window.hasUnsavedNetworkDraft?.()),
|
| 531 |
useNetwork(file){
|
| 532 |
+
if(busy||replayBusy||job)throw Error('Finish the current operation before changing the network');
|
| 533 |
stagedNetwork=file;$('network').value='';
|
| 534 |
if($('networkChoice'))$('networkChoice').textContent='Browser editor input: '+file.name+'. Not uploaded yet.';
|
| 535 |
refreshFreezeNetwork(file);
|
|
|
|
| 1023 |
window.TrackReplay?.setRunAnalysis?.(localAnalysis,runReceipt||portableReceipt||(r?{report:r}:null));
|
| 1024 |
}
|
| 1025 |
$('run').onclick = async () => {
|
| 1026 |
+
if(replayBusy)return;
|
| 1027 |
+
invalidateReplay();
|
| 1028 |
const comparisonAttempt=!!comparisonPlan,comparisonRole=comparisonAttempt?comparisonPlan.roles[comparisonPlan.index]:null;
|
| 1029 |
const previousRunResultContext=runResultContext;
|
| 1030 |
cancelSteamLeaderboard();
|