fffiloni commited on
Commit
5801a0b
·
verified ·
1 Parent(s): 4058cee

Upload 7 files

Browse files
Files changed (3) hide show
  1. web/static/app.css +1 -0
  2. web/static/app.js +14 -5
  3. web/static/runs.js +20 -2
web/static/app.css CHANGED
@@ -2599,6 +2599,7 @@ strong,p,h1,h2,h3,h4,span,a,button,div,li{overflow-wrap:break-word;word-break:no
2599
  }
2600
 
2601
 
 
2602
  /* v190.5 — Active Run timeline hotfix: horizontal canonical row + restored active pulse */
2603
  @media (min-width:521px){
2604
  .timeline.timeline-canonical{
 
2599
  }
2600
 
2601
 
2602
+ /* v190.6 — Active Run timeline hotfix (carried forward from v190.5) */
2603
  /* v190.5 — Active Run timeline hotfix: horizontal canonical row + restored active pulse */
2604
  @media (min-width:521px){
2605
  .timeline.timeline-canonical{
web/static/app.js CHANGED
@@ -496,9 +496,12 @@ function parseJsonField(id,fallback){const raw=formValue(id);if(!raw)return fall
496
  function prettyJson(value){return JSON.stringify(value,null,2)}
497
  function validateValidationForm(){if(!formValue('validateSpace'))return'Enter the target Space ID to validate.';try{const args=parseJsonField('testArgs',[]);const kwargs=parseJsonField('testKwargs',{});if(!Array.isArray(args))return'Test args must be a JSON array because they are passed as positional arguments to gradio_client.predict(...).';if(!kwargs||Array.isArray(kwargs)||typeof kwargs!=='object')return'Test kwargs must be a JSON object because they are passed as keyword arguments.'}catch(e){return`Invalid JSON payload: ${e.message}`}return''}
498
  function validationIsTerminalStatus(status){const canonical=String(status||'').toLowerCase();return ['full_inference_success','partial_validation','manual_hardware_required','failed','stale','stopped'].includes(canonical)}
 
499
  function validationDetailRunId(detail={}){return detail.run_id||detail.summary?.run_id||detail.state?.run_id||detail.launch?.run_id||''}
500
- function rememberTerminalValidation(detail={}){const runId=validationDetailRunId(detail)||state.validationRunId;const canonical=validationCanonicalStatus(detail);if(runId&&validationIsTerminalStatus(canonical))state.validationTerminalByRun[runId]={canonical,detail};return canonical}
501
- function shouldIgnoreValidationUpdate(incoming={},runId=state.validationRunId){const incomingCanonical=validationCanonicalStatus(incoming);const terminal=state.validationTerminalByRun?.[runId];if(terminal&&incomingCanonical==='running')return true;return false}
 
 
502
  function extractEffectiveValidationPayload(detail={}){const smoke=detail.generation_smoke||detail.summary?.generation_smoke||detail.state?.generation_smoke||{};const payload=detail.validation_payload||detail.tests?.validation_payload||detail.summary?.validation_payload||{};const args=firstArrayPath(detail.effective_args,smoke.effective_args,payload.effective_args,smoke.test_args,payload.test_args);const kwargs=firstObjectPath(detail.effective_kwargs,smoke.effective_kwargs,payload.effective_kwargs,smoke.test_kwargs,payload.test_kwargs);const source=detail.args_source||smoke.args_source||payload.args_source||'';return {args,kwargs,source,argsWereAutofilled:Boolean(detail.args_were_autofilled||smoke.args_were_autofilled||payload.args_were_autofilled||source==='gradio_schema_auto_fill')};}
503
  function applyEffectiveValidationArgs(detail={}){const payload=extractEffectiveValidationPayload(detail);if(!payload.argsWereAutofilled||!Array.isArray(payload.args))return;if(state.validationArgsDirty)return;setInputValue('testArgs',prettyJson(payload.args));if(payload.kwargs&&typeof payload.kwargs==='object'&&!Array.isArray(payload.kwargs))setInputValue('testKwargs',prettyJson(payload.kwargs));const note=$('spaceTestArgsNote');if(note){note.hidden=false;note.textContent='Arguments filled from discovered Gradio schema.';} }
504
  function setBuildModeNew(){const panel=$('buildModePanel');if(panel){panel.classList.remove('inspecting');panel.innerHTML='<strong>Preparing a new build</strong><p>This form launches new builds. Existing runs can be inspected in the Runs Explorer on the right.</p>'}const launch=$('launchResultPanel');if(launch)launch.hidden=true;const status=$('progressStatus');if(status){status.textContent='Idle';status.className='badge neutral'}setText('progressTitle','No run selected')}
@@ -732,6 +735,8 @@ function validationPrefillFromDetail(detail={}){
732
  return {target,api,expected,args,kwargs};
733
  }
734
  function renderValidationStatus(data={}){
 
 
735
  state.validationDetail=data||state.validationDetail;
736
  const summary=data.summary||{};
737
  const smoke=data.generation_smoke||data.summary?.generation_smoke||data.state?.generation_smoke||{};
@@ -753,7 +758,7 @@ function renderValidationStatus(data={}){
753
  setText('validationStartedAt',formatTime(data.updated_at||summary.updated_at||data.state?.updated_at||data.created_at||summary.created_at||new Date().toISOString()));
754
  const latency=firstPositiveNumber(smoke.latency_seconds,smoke.observed_latency_seconds,summary.latency_seconds,summary.observed_latency_seconds,data.latency_seconds,data.observed_latency_seconds);
755
  setText('spaceTestLatency',latency?formatLatencySeconds(latency):(canonical==='running'?'—':'Not recorded'));
756
- setText('spaceTestVerdict',badgeLabel(displayStatus));
757
  const launchBtn=$('launchValidate');
758
  if(launchBtn&&!launchBtn.classList.contains('is-busy'))launchBtn.textContent=runId&&runId!=='—'&&!isRunning?'Run validation again':'Launch validation';
759
  if(launchBtn)launchBtn.disabled=state.busy||isRunning;
@@ -784,7 +789,9 @@ function renderValidationTimeline(detail={}){
784
  }).join('')||'<div class="empty">No validation events yet.</div>';
785
  }
786
  function showValidationRunDetail(detail={}){
787
- state.validationRunId=detail.run_id||detail.summary?.run_id||state.validationRunId;
 
 
788
  state.validationMode='view';
789
  state.validationDetail=detail;
790
  state.validationPrefillDraft=null;
@@ -805,7 +812,7 @@ async function refreshValidationProgress(pollToken=state.validationPollToken){
805
  try{
806
  const p=await apiGet(`/api/runs/${encodeURIComponent(requestedRunId)}/progress?bucket_name=${encodeURIComponent(state.bucketName)}&include_job_logs=1`);
807
  if(pollToken!==state.validationPollToken||requestedRunId!==state.validationRunId)return;
808
- if(shouldIgnoreValidationUpdate(p,requestedRunId))return;
809
  state.validationPollErrors=0;
810
  renderValidationStatus(p);
811
  const canonical=validationCanonicalStatus(p);
@@ -818,6 +825,8 @@ async function refreshValidationProgress(pollToken=state.validationPollToken){
818
  }
819
  }
820
  function startValidationPolling(runId){
 
 
821
  stopValidationPolling();
822
  state.validationMode='running';
823
  state.validationRunId=runId;
 
496
  function prettyJson(value){return JSON.stringify(value,null,2)}
497
  function validateValidationForm(){if(!formValue('validateSpace'))return'Enter the target Space ID to validate.';try{const args=parseJsonField('testArgs',[]);const kwargs=parseJsonField('testKwargs',{});if(!Array.isArray(args))return'Test args must be a JSON array because they are passed as positional arguments to gradio_client.predict(...).';if(!kwargs||Array.isArray(kwargs)||typeof kwargs!=='object')return'Test kwargs must be a JSON object because they are passed as keyword arguments.'}catch(e){return`Invalid JSON payload: ${e.message}`}return''}
498
  function validationIsTerminalStatus(status){const canonical=String(status||'').toLowerCase();return ['full_inference_success','partial_validation','manual_hardware_required','failed','stale','stopped'].includes(canonical)}
499
+ function validationIsNonTerminalStatus(status){const canonical=String(status||'').toLowerCase();return !canonical||['running','pending','waiting','started','queued','building','unknown'].includes(canonical)}
500
  function validationDetailRunId(detail={}){return detail.run_id||detail.summary?.run_id||detail.state?.run_id||detail.launch?.run_id||''}
501
+ function rememberTerminalValidation(detail={}){const runId=validationDetailRunId(detail)||state.validationRunId;const canonical=validationCanonicalStatus(detail);if(runId&&validationIsTerminalStatus(canonical)){state.validationTerminalByRun=state.validationTerminalByRun||{};state.validationTerminalByRun[runId]={canonical,detail:{...detail,_terminalLocked:true}};}return canonical}
502
+ function terminalValidationForRun(runId=state.validationRunId){return runId&&state.validationTerminalByRun?state.validationTerminalByRun[runId]||null:null}
503
+ function mergeValidationWithTerminalLock(incoming={},runId=state.validationRunId){const terminal=terminalValidationForRun(runId);if(!terminal)return incoming;const incomingCanonical=validationCanonicalStatus(incoming);if(validationIsNonTerminalStatus(incomingCanonical)){return {...(terminal.detail||{}),_refreshing:true,_ignoredIncomingStatus:incomingCanonical};}return incoming;}
504
+ function shouldIgnoreValidationUpdate(incoming={},runId=state.validationRunId){const incomingCanonical=validationCanonicalStatus(incoming);const terminal=terminalValidationForRun(runId);if(terminal&&incomingCanonical==='running')return true;return Boolean(terminal&&validationIsNonTerminalStatus(incomingCanonical));}
505
  function extractEffectiveValidationPayload(detail={}){const smoke=detail.generation_smoke||detail.summary?.generation_smoke||detail.state?.generation_smoke||{};const payload=detail.validation_payload||detail.tests?.validation_payload||detail.summary?.validation_payload||{};const args=firstArrayPath(detail.effective_args,smoke.effective_args,payload.effective_args,smoke.test_args,payload.test_args);const kwargs=firstObjectPath(detail.effective_kwargs,smoke.effective_kwargs,payload.effective_kwargs,smoke.test_kwargs,payload.test_kwargs);const source=detail.args_source||smoke.args_source||payload.args_source||'';return {args,kwargs,source,argsWereAutofilled:Boolean(detail.args_were_autofilled||smoke.args_were_autofilled||payload.args_were_autofilled||source==='gradio_schema_auto_fill')};}
506
  function applyEffectiveValidationArgs(detail={}){const payload=extractEffectiveValidationPayload(detail);if(!payload.argsWereAutofilled||!Array.isArray(payload.args))return;if(state.validationArgsDirty)return;setInputValue('testArgs',prettyJson(payload.args));if(payload.kwargs&&typeof payload.kwargs==='object'&&!Array.isArray(payload.kwargs))setInputValue('testKwargs',prettyJson(payload.kwargs));const note=$('spaceTestArgsNote');if(note){note.hidden=false;note.textContent='Arguments filled from discovered Gradio schema.';} }
507
  function setBuildModeNew(){const panel=$('buildModePanel');if(panel){panel.classList.remove('inspecting');panel.innerHTML='<strong>Preparing a new build</strong><p>This form launches new builds. Existing runs can be inspected in the Runs Explorer on the right.</p>'}const launch=$('launchResultPanel');if(launch)launch.hidden=true;const status=$('progressStatus');if(status){status.textContent='Idle';status.className='badge neutral'}setText('progressTitle','No run selected')}
 
735
  return {target,api,expected,args,kwargs};
736
  }
737
  function renderValidationStatus(data={}){
738
+ const candidateRunId=validationDetailRunId(data)||state.validationRunId;
739
+ data=mergeValidationWithTerminalLock(data||state.validationDetail||{},candidateRunId);
740
  state.validationDetail=data||state.validationDetail;
741
  const summary=data.summary||{};
742
  const smoke=data.generation_smoke||data.summary?.generation_smoke||data.state?.generation_smoke||{};
 
758
  setText('validationStartedAt',formatTime(data.updated_at||summary.updated_at||data.state?.updated_at||data.created_at||summary.created_at||new Date().toISOString()));
759
  const latency=firstPositiveNumber(smoke.latency_seconds,smoke.observed_latency_seconds,summary.latency_seconds,summary.observed_latency_seconds,data.latency_seconds,data.observed_latency_seconds);
760
  setText('spaceTestLatency',latency?formatLatencySeconds(latency):(canonical==='running'?'—':'Not recorded'));
761
+ setText('spaceTestVerdict',data._refreshing?`${badgeLabel(displayStatus)} · Refreshing…`:badgeLabel(displayStatus));
762
  const launchBtn=$('launchValidate');
763
  if(launchBtn&&!launchBtn.classList.contains('is-busy'))launchBtn.textContent=runId&&runId!=='—'&&!isRunning?'Run validation again':'Launch validation';
764
  if(launchBtn)launchBtn.disabled=state.busy||isRunning;
 
789
  }).join('')||'<div class="empty">No validation events yet.</div>';
790
  }
791
  function showValidationRunDetail(detail={}){
792
+ const incomingRunId=detail.run_id||detail.summary?.run_id||state.validationRunId;
793
+ state.validationRunId=incomingRunId;
794
+ detail=mergeValidationWithTerminalLock(detail,incomingRunId);
795
  state.validationMode='view';
796
  state.validationDetail=detail;
797
  state.validationPrefillDraft=null;
 
812
  try{
813
  const p=await apiGet(`/api/runs/${encodeURIComponent(requestedRunId)}/progress?bucket_name=${encodeURIComponent(state.bucketName)}&include_job_logs=1`);
814
  if(pollToken!==state.validationPollToken||requestedRunId!==state.validationRunId)return;
815
+ if(shouldIgnoreValidationUpdate(p,requestedRunId)){const terminal=terminalValidationForRun(requestedRunId);if(terminal){renderValidationStatus({...terminal.detail,_refreshing:true});stopValidationPolling();loadRuns(false);}return;}
816
  state.validationPollErrors=0;
817
  renderValidationStatus(p);
818
  const canonical=validationCanonicalStatus(p);
 
825
  }
826
  }
827
  function startValidationPolling(runId){
828
+ const terminal=terminalValidationForRun(runId);
829
+ if(terminal){stopValidationPolling();state.validationMode='view';state.validationRunId=runId;renderValidationStatus({...terminal.detail,_refreshing:true});return;}
830
  stopValidationPolling();
831
  state.validationMode='running';
832
  state.validationRunId=runId;
web/static/runs.js CHANGED
@@ -8,8 +8,15 @@ function runValidationTerminalText(run={}){
8
  run.generation_smoke?.status,run.summary?.smoke_status,run.smoke_status,run.details?.error,run.error,run.message
9
  ].map(x=>String(x||'').toLowerCase()).filter(Boolean).join(' ');
10
  }
 
 
 
 
 
11
  function runCanonicalStatus(run={}){
12
  const validation=isValidationRunSummary(run);
 
 
13
  const s=String(run.status||run.summary?.status||run.state?.status||'unknown').toLowerCase();
14
  const gate=String(run.gate_status||run.inference_gate?.status||run.summary?.gate_status||'').toLowerCase();
15
  const smoke=String(run.smoke_status||run.generation_smoke?.status||run.summary?.smoke_status||'').toLowerCase();
@@ -323,8 +330,19 @@ function bindRunRowEvents(root){
323
  e.preventDefault();selectRun(row.dataset.run);
324
  });
325
  }
326
- function renderRunsFromCache(){state.runsCache=sortRunsNewestFirst(state.runsCache||[]);const runs=filteredRuns();const perPage=state.runsPerPage||5;state.runPage=Math.min(Math.max(0,state.runPage||0),Math.max(0,Math.ceil(runs.length/perPage)-1));const visible=runs.slice(state.runPage*perPage,state.runPage*perPage+perPage);const selectedId=state.centerTab==='validate'&&state.validationRunId?state.validationRunId:state.runId;const signature=JSON.stringify({ids:visible.map(r=>[r.run_id,r.status,r.updated_at,r.target_space,r.model_id,r.latency_seconds,r.observed_latency_seconds,r.recommended_zero_gpu_duration_seconds,r.recommended_zerogpu_duration_seconds]),filter:state.runStatusFilter,search:state.runSearch,page:state.runPage,selected:selectedId});for(const [id,compact] of [['runsTableHome',true],['runsTable',false]]){const root=$(id);if(!root)continue;bindRunRowEvents(root);if(root.dataset.signature!==signature){root.innerHTML=renderRunRows(visible,compact)||'<div class="empty">No runs match this filter.</div>';root.dataset.signature=signature}}renderRunStats();renderRunPagination(runs.length);renderRunAggregateStats();document.querySelectorAll('.run-row').forEach(row=>row.classList.toggle('selected',Boolean(selectedId)&&row.dataset.run===selectedId))}
327
- async function loadRuns(forceNetwork=false){const stale=Date.now()-(state.runsLastLoaded||0)>45000;const shouldFetch=forceNetwork||!state.runsCache.length||stale;if(!shouldFetch){renderRunsFromCache();return state.runsCache||[]}if(state.runsLoadPromise&&!forceNetwork)return state.runsLoadPromise;document.querySelectorAll('.runs-panel,.full-runs-panel').forEach(x=>x.classList.add('is-refreshing'));state.runsLoadPromise=apiGet(`/api/runs?limit=100&bucket_name=${encodeURIComponent(state.bucketName)}`).then(data=>{state.runsCache=sortRunsNewestFirst((data.runs||[]).filter(r=>!isRunLocallyDeleted(r.run_id)));state.runsLastLoaded=Date.now();if(data.bucket_source)state.bucketSource=data.bucket_source;renderRunsFromCache();return state.runsCache}).catch(e=>{for(const id of ['runsTable','runsTableHome']){const root=$(id);if(root)root.textContent='Could not load runs: '+e.message}if(forceNetwork)console.warn(e);return state.runsCache||[]}).finally(()=>{state.runsLoadPromise=null;document.querySelectorAll('.runs-panel,.full-runs-panel').forEach(x=>x.classList.remove('is-refreshing'))});return state.runsLoadPromise}
 
 
 
 
 
 
 
 
 
 
 
328
  function cachedRunById(runId){
329
  if(isRunLocallyDeleted(runId))return null;
330
  return (state.runsCache||[]).find(r=>r.run_id===runId)||null;
 
8
  run.generation_smoke?.status,run.summary?.smoke_status,run.smoke_status,run.details?.error,run.error,run.message
9
  ].map(x=>String(x||'').toLowerCase()).filter(Boolean).join(' ');
10
  }
11
+ function validationLockedStatus(run={}){
12
+ const runId=run.run_id||run.summary?.run_id||run.state?.run_id||'';
13
+ const terminal=runId&&typeof state!=='undefined'&&state.validationTerminalByRun?state.validationTerminalByRun[runId]:null;
14
+ return terminal?.canonical||'';
15
+ }
16
  function runCanonicalStatus(run={}){
17
  const validation=isValidationRunSummary(run);
18
+ const locked=validation?validationLockedStatus(run):'';
19
+ if(locked)return locked;
20
  const s=String(run.status||run.summary?.status||run.state?.status||'unknown').toLowerCase();
21
  const gate=String(run.gate_status||run.inference_gate?.status||run.summary?.gate_status||'').toLowerCase();
22
  const smoke=String(run.smoke_status||run.generation_smoke?.status||run.summary?.smoke_status||'').toLowerCase();
 
330
  e.preventDefault();selectRun(row.dataset.run);
331
  });
332
  }
333
+ function renderRunsFromCache(){state.runsCache=sortRunsNewestFirst(state.runsCache||[]);const runs=filteredRuns();const perPage=state.runsPerPage||5;state.runPage=Math.min(Math.max(0,state.runPage||0),Math.max(0,Math.ceil(runs.length/perPage)-1));const visible=runs.slice(state.runPage*perPage,state.runPage*perPage+perPage);const selectedId=state.centerTab==='validate'&&state.validationRunId?state.validationRunId:state.runId;const signature=JSON.stringify({ids:visible.map(r=>[r.run_id,runCanonicalStatus(r),r.status,r.updated_at,r.target_space,r.model_id,r.latency_seconds,r.observed_latency_seconds,r.recommended_zero_gpu_duration_seconds,r.recommended_zerogpu_duration_seconds]),filter:state.runStatusFilter,search:state.runSearch,page:state.runPage,selected:selectedId});for(const [id,compact] of [['runsTableHome',true],['runsTable',false]]){const root=$(id);if(!root)continue;bindRunRowEvents(root);if(root.dataset.signature!==signature){root.innerHTML=renderRunRows(visible,compact)||'<div class="empty">No runs match this filter.</div>';root.dataset.signature=signature}}renderRunStats();renderRunPagination(runs.length);renderRunAggregateStats();document.querySelectorAll('.run-row').forEach(row=>row.classList.toggle('selected',Boolean(selectedId)&&row.dataset.run===selectedId))}
334
+
335
+ function mergeRunExplorerTerminalLocks(runs=[]){
336
+ if(typeof state==='undefined'||!state.validationTerminalByRun)return runs||[];
337
+ return (runs||[]).map(run=>{
338
+ const validation=isValidationRunSummary(run);
339
+ const runId=run.run_id||run.summary?.run_id||'';
340
+ const terminal=validation&&runId?state.validationTerminalByRun[runId]:null;
341
+ if(!terminal)return run;
342
+ return {...run,status:terminal.canonical,validation_status:terminal.canonical,result_status:terminal.canonical,summary:{...(run.summary||{}),status:terminal.canonical,validation_status:terminal.canonical,result_status:terminal.canonical}};
343
+ });
344
+ }
345
+ async function loadRuns(forceNetwork=false){const stale=Date.now()-(state.runsLastLoaded||0)>45000;const shouldFetch=forceNetwork||!state.runsCache.length||stale;if(!shouldFetch){renderRunsFromCache();return state.runsCache||[]}if(state.runsLoadPromise&&!forceNetwork)return state.runsLoadPromise;document.querySelectorAll('.runs-panel,.full-runs-panel').forEach(x=>x.classList.add('is-refreshing'));state.runsLoadPromise=apiGet(`/api/runs?limit=100&bucket_name=${encodeURIComponent(state.bucketName)}`).then(data=>{state.runsCache=sortRunsNewestFirst(mergeRunExplorerTerminalLocks((data.runs||[]).filter(r=>!isRunLocallyDeleted(r.run_id))));state.runsLastLoaded=Date.now();if(data.bucket_source)state.bucketSource=data.bucket_source;renderRunsFromCache();return state.runsCache}).catch(e=>{for(const id of ['runsTable','runsTableHome']){const root=$(id);if(root)root.textContent='Could not load runs: '+e.message}if(forceNetwork)console.warn(e);return state.runsCache||[]}).finally(()=>{state.runsLoadPromise=null;document.querySelectorAll('.runs-panel,.full-runs-panel').forEach(x=>x.classList.remove('is-refreshing'))});return state.runsLoadPromise}
346
  function cachedRunById(runId){
347
  if(isRunLocallyDeleted(runId))return null;
348
  return (state.runsCache||[]).find(r=>r.run_id===runId)||null;