Spaces:
Starting on CPU Upgrade
Starting on CPU Upgrade
Upload 7 files
Browse files- web/static/app.css +28 -0
- web/static/app.js +76 -1
web/static/app.css
CHANGED
|
@@ -2093,3 +2093,31 @@ strong,p,h1,h2,h3,h4,span,a,button,div,li{overflow-wrap:break-word;word-break:no
|
|
| 2093 |
/* v180: direct eval archive activation */
|
| 2094 |
.eval-enable-button[hidden]{display:none!important;}
|
| 2095 |
.eval-enable-button{white-space:nowrap;}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2093 |
/* v180: direct eval archive activation */
|
| 2094 |
.eval-enable-button[hidden]{display:none!important;}
|
| 2095 |
.eval-enable-button{white-space:nowrap;}
|
| 2096 |
+
|
| 2097 |
+
/* v182: Run Storage / Eval Archive split section */
|
| 2098 |
+
.compact-storage{gap:12px!important;}
|
| 2099 |
+
.compact-storage .bucket-status-card{margin-bottom:0!important;}
|
| 2100 |
+
.eval-archive-card{
|
| 2101 |
+
display:grid;
|
| 2102 |
+
grid-template-columns:minmax(0,1fr) auto;
|
| 2103 |
+
align-items:center;
|
| 2104 |
+
gap:12px;
|
| 2105 |
+
margin-top:4px;
|
| 2106 |
+
padding:12px 12px;
|
| 2107 |
+
border-radius:18px;
|
| 2108 |
+
background:linear-gradient(180deg,rgba(255,255,255,.78),rgba(248,250,252,.7));
|
| 2109 |
+
border:1px solid rgba(226,232,240,.72);
|
| 2110 |
+
box-shadow:0 1px 0 rgba(255,255,255,.9) inset;
|
| 2111 |
+
}
|
| 2112 |
+
.eval-archive-card[hidden]{display:none!important;}
|
| 2113 |
+
.eval-archive-card.on{background:linear-gradient(180deg,rgba(236,253,245,.66),rgba(255,255,255,.78));border-color:rgba(34,197,94,.18)}
|
| 2114 |
+
.eval-archive-card.off{background:linear-gradient(180deg,rgba(248,250,252,.78),rgba(255,255,255,.8));}
|
| 2115 |
+
.eval-archive-copy{min-width:0;display:grid;gap:3px;}
|
| 2116 |
+
.eval-archive-copy strong{font-size:.88rem;line-height:1.15;color:var(--ink);letter-spacing:-.01em;}
|
| 2117 |
+
.eval-archive-copy p{margin:0;color:var(--muted);font-size:.72rem;line-height:1.25;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
| 2118 |
+
.eval-archive-card.on .eval-archive-copy p{color:#047857;}
|
| 2119 |
+
.eval-archive-actions{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap;}
|
| 2120 |
+
.eval-archive-actions button{min-height:34px;padding:8px 10px!important;border-radius:12px!important;white-space:nowrap;}
|
| 2121 |
+
.eval-archive-actions .danger{color:#be123c!important;background:rgba(255,241,242,.82)!important;border-color:rgba(244,63,94,.2)!important;}
|
| 2122 |
+
.eval-feedback{display:block!important;width:auto!important;max-width:100%;padding:0!important;border:0!important;background:transparent!important;border-radius:0!important;font-size:.72rem!important;font-weight:650!important;line-height:1.25!important;letter-spacing:0!important;}
|
| 2123 |
+
@media(max-width:760px){.eval-archive-card{grid-template-columns:1fr}.eval-archive-actions{justify-content:flex-start}}
|
web/static/app.js
CHANGED
|
@@ -222,7 +222,57 @@ function setBucketFeedback(mode='checking',message='Checking private bucket acce
|
|
| 222 |
text.title=String(message||'');
|
| 223 |
}
|
| 224 |
}
|
| 225 |
-
function renderAnonymousEvalStatus(me){
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
function setBucketReady(ok,message){state.bucketReady=Boolean(ok);setBuildLaunchEnabled();const feedback=state.bucketReady?'Ready for builds, validations and artifacts.':(message||'Create your private run bucket before launching builds.');setText('buildGateText',state.bucketReady?'Bucket ready. You can launch builds.':bucketFeedbackSummary(feedback));setText('bucketInlineStatus',state.bucketReady?'Ready':'Setup needed');setBucketFeedback(state.bucketReady?'ready':'error',feedback);for(const id of ['bucketInlineDot','sidebarBucketDot']){const dot=$(id);if(dot)dot.className='status-dot '+(state.bucketReady?'green':'amber-dot')}const inline=$('bucketInlineStatus');if(inline)inline.className='badge '+(state.bucketReady?'success':'warn');document.querySelectorAll('[data-requires-bucket="true"]').forEach(btn=>btn.disabled=state.busy||!state.bucketReady);setBuildLaunchEnabled()}
|
| 227 |
function effectivePreferredHardware(){const preferred=formValue('preferredHardware')||'zero-a10g';const fallback=formValue('fallbackHardware')||'a10g-large';const tryZero=$('tryZeroGpu')?.checked??true;if(tryZero)return preferred;if(preferred==='zero-a10g'||preferred.startsWith('zero'))return fallback;return preferred}
|
| 228 |
function hardwarePlanLabel(){const tryZero=$('tryZeroGpu')?.checked??true;const preferred=effectivePreferredHardware();const fallback=formValue('fallbackHardware')||'a10g-large';const allow=$('allowFallback')?.checked??true;if(tryZero)return `ZeroGPU → ${allow?fallback:'no fixed fallback'}`;return `${preferred}${allow&&fallback!==preferred?' → '+fallback+' fallback':''}`}
|
|
@@ -384,6 +434,29 @@ async function enableEvalArchive(){
|
|
| 384 |
}catch(e){showMessage(e.message,true)}
|
| 385 |
finally{setButtonBusy('enableEvalArchive',false)}
|
| 386 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
|
| 388 |
async function checkBucket(noisy=true){setButtonBusy('checkBucket',true,'Checking…');setBucketFeedback('checking','Checking private bucket access…');try{const data=await apiGet(`/api/bucket/status?bucket_name=${encodeURIComponent(state.bucketName)}`);const source=data.bucket_source||state.bucketName;state.bucketSource=source;setText('bucketInlineLabel',source);setBucketReady(Boolean(data.ok),data.error||'Create your private run bucket before launching builds.');if(noisy)showMessage(data.ok?`Bucket ready: ${source}`:`Bucket not ready: ${data.error||'not found'}`,!data.ok);return data}catch(e){setBucketReady(false,e.message);if(noisy)showMessage(e.message,true);throw e}finally{setButtonBusy('checkBucket',false)}}
|
| 389 |
async function createBucket(){setButtonBusy('createBucket',true,'Creating…');setBucketFeedback('creating','Creating private bucket…');try{const data=await apiPost('/api/bucket/create',{bucket_name:state.bucketName});const source=data.bucket_source||state.bucketName;state.bucketSource=source;setText('bucketInlineLabel',source);setBucketReady(Boolean(data.ok),data.error||'Could not create bucket.');showMessage(data.ok?`Created/ready: ${source}`:`Could not create bucket: ${data.error}`,!data.ok);if(data.ok)loadRuns(true)}catch(e){setBucketReady(false,e.message);showMessage(e.message,true)}finally{setButtonBusy('createBucket',false)}}
|
|
@@ -728,6 +801,8 @@ function bindActions(){
|
|
| 728 |
checkBucket:()=>checkBucket(true),
|
| 729 |
createBucket:createBucket,
|
| 730 |
enableEvalArchive:enableEvalArchive,
|
|
|
|
|
|
|
| 731 |
refreshRuns:()=>loadRuns(true),
|
| 732 |
refreshRunsHome:()=>loadRuns(true),
|
| 733 |
cancelRun:cancelActiveRun,
|
|
|
|
| 222 |
text.title=String(message||'');
|
| 223 |
}
|
| 224 |
}
|
| 225 |
+
function renderAnonymousEvalStatus(me){
|
| 226 |
+
const card=$('evalArchiveCard');
|
| 227 |
+
const title=$('evalArchiveTitle');
|
| 228 |
+
const text=$('anonymousEvalText');
|
| 229 |
+
const enable=$('enableEvalArchive');
|
| 230 |
+
const disable=$('disableEvalArchive');
|
| 231 |
+
const verify=$('verifyEvalArchive');
|
| 232 |
+
const cfg=me?.anonymous_eval||{};
|
| 233 |
+
const enabled=Boolean(cfg.enabled);
|
| 234 |
+
const canManage=Boolean(cfg.can_manage)&&cfg.env_override!==true;
|
| 235 |
+
const readonly=Boolean(cfg.readonly)||Boolean(enabled&&!canManage);
|
| 236 |
+
const bucket=cfg.bucket_source||cfg.proposed_bucket_source||'configured bucket';
|
| 237 |
+
const path=cfg.bucket_path?'/'+cfg.bucket_path:(cfg.default_bucket_path?'/'+cfg.default_bucket_path:'');
|
| 238 |
+
const mount=cfg.job_mount_path||cfg.default_mount_path||'/evals';
|
| 239 |
+
if(card){
|
| 240 |
+
card.hidden=false;
|
| 241 |
+
card.className='eval-archive-card '+(enabled?'on':'off')+(readonly?' readonly':'')+(canManage?' manageable':'');
|
| 242 |
+
}
|
| 243 |
+
if(title){
|
| 244 |
+
title.textContent=enabled?'Anonymous eval archive is on':'Anonymous eval archive is off';
|
| 245 |
+
}
|
| 246 |
+
if(text){
|
| 247 |
+
text.className='eval-feedback '+(enabled?'on':'off');
|
| 248 |
+
if(enabled){
|
| 249 |
+
const manager=cfg.managed_by==='you'?'You manage this instance setting.':`Managed by ${cfg.managed_by||'instance owner'}.`;
|
| 250 |
+
text.textContent=`${bucket}${path} → ${mount}`;
|
| 251 |
+
text.title=`Anonymous project eval archive enabled from ${cfg.source||'config'}: ${bucket}${path} mounted at ${mount}. ${manager} ASF backend publishes anonymized eval records after user Jobs write local run evals.`;
|
| 252 |
+
}else if(me?.username){
|
| 253 |
+
text.textContent='Mount an operator eval bucket at /evals, then enable publishing.';
|
| 254 |
+
text.title='Recommended: private operator bucket, bucket path evals, mounted read-write at /evals on this ASF Space. Env vars still work as an admin override.';
|
| 255 |
+
}else{
|
| 256 |
+
text.textContent='Sign in as the instance owner to enable it.';
|
| 257 |
+
text.title='Anonymous eval archive can be enabled by the instance owner or configured eval admins.';
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
if(enable){
|
| 261 |
+
enable.hidden=enabled||!me?.username||cfg.env_override===true;
|
| 262 |
+
enable.disabled=!me?.username||state.busy;
|
| 263 |
+
enable.title=`Enable anonymous eval archive using ${cfg.proposed_bucket_source||'your namespace bucket'} mounted at ${cfg.default_mount_path||'/evals'}.`;
|
| 264 |
+
}
|
| 265 |
+
if(disable){
|
| 266 |
+
disable.hidden=!enabled||!canManage;
|
| 267 |
+
disable.disabled=state.busy;
|
| 268 |
+
disable.title='Disable anonymous eval publishing for this ASF instance. Existing archived eval records are not deleted.';
|
| 269 |
+
}
|
| 270 |
+
if(verify){
|
| 271 |
+
verify.hidden=!enabled||!canManage;
|
| 272 |
+
verify.disabled=state.busy;
|
| 273 |
+
verify.title='Refresh and verify the eval archive configuration.';
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
function setBucketReady(ok,message){state.bucketReady=Boolean(ok);setBuildLaunchEnabled();const feedback=state.bucketReady?'Ready for builds, validations and artifacts.':(message||'Create your private run bucket before launching builds.');setText('buildGateText',state.bucketReady?'Bucket ready. You can launch builds.':bucketFeedbackSummary(feedback));setText('bucketInlineStatus',state.bucketReady?'Ready':'Setup needed');setBucketFeedback(state.bucketReady?'ready':'error',feedback);for(const id of ['bucketInlineDot','sidebarBucketDot']){const dot=$(id);if(dot)dot.className='status-dot '+(state.bucketReady?'green':'amber-dot')}const inline=$('bucketInlineStatus');if(inline)inline.className='badge '+(state.bucketReady?'success':'warn');document.querySelectorAll('[data-requires-bucket="true"]').forEach(btn=>btn.disabled=state.busy||!state.bucketReady);setBuildLaunchEnabled()}
|
| 277 |
function effectivePreferredHardware(){const preferred=formValue('preferredHardware')||'zero-a10g';const fallback=formValue('fallbackHardware')||'a10g-large';const tryZero=$('tryZeroGpu')?.checked??true;if(tryZero)return preferred;if(preferred==='zero-a10g'||preferred.startsWith('zero'))return fallback;return preferred}
|
| 278 |
function hardwarePlanLabel(){const tryZero=$('tryZeroGpu')?.checked??true;const preferred=effectivePreferredHardware();const fallback=formValue('fallbackHardware')||'a10g-large';const allow=$('allowFallback')?.checked??true;if(tryZero)return `ZeroGPU → ${allow?fallback:'no fixed fallback'}`;return `${preferred}${allow&&fallback!==preferred?' → '+fallback+' fallback':''}`}
|
|
|
|
| 434 |
}catch(e){showMessage(e.message,true)}
|
| 435 |
finally{setButtonBusy('enableEvalArchive',false)}
|
| 436 |
}
|
| 437 |
+
async function verifyEvalArchive(){
|
| 438 |
+
setButtonBusy('verifyEvalArchive',true,'Verifying…');
|
| 439 |
+
try{
|
| 440 |
+
const result=await apiGet('/api/eval-archive/status');
|
| 441 |
+
state.user={...(state.user||{}),anonymous_eval:result};
|
| 442 |
+
renderAnonymousEvalStatus(state.user);
|
| 443 |
+
showMessage(result.enabled?'Eval archive configuration verified.':'Eval archive is not enabled.',result.enabled?'success':'warning');
|
| 444 |
+
}catch(e){showMessage(e.message,true)}
|
| 445 |
+
finally{setButtonBusy('verifyEvalArchive',false)}
|
| 446 |
+
}
|
| 447 |
+
async function disableEvalArchive(){
|
| 448 |
+
const ok=window.confirm('Disable anonymous eval publishing for this ASF instance? Existing archived records will not be deleted.');
|
| 449 |
+
if(!ok)return;
|
| 450 |
+
setButtonBusy('disableEvalArchive',true,'Disabling…');
|
| 451 |
+
try{
|
| 452 |
+
const result=await apiPost('/api/eval-archive/disable',{});
|
| 453 |
+
state.user={...(state.user||{}),anonymous_eval:result};
|
| 454 |
+
renderAnonymousEvalStatus(state.user);
|
| 455 |
+
showMessage(result.message||'Eval archive disabled.','success');
|
| 456 |
+
}catch(e){showMessage(e.message,true)}
|
| 457 |
+
finally{setButtonBusy('disableEvalArchive',false)}
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
|
| 461 |
async function checkBucket(noisy=true){setButtonBusy('checkBucket',true,'Checking…');setBucketFeedback('checking','Checking private bucket access…');try{const data=await apiGet(`/api/bucket/status?bucket_name=${encodeURIComponent(state.bucketName)}`);const source=data.bucket_source||state.bucketName;state.bucketSource=source;setText('bucketInlineLabel',source);setBucketReady(Boolean(data.ok),data.error||'Create your private run bucket before launching builds.');if(noisy)showMessage(data.ok?`Bucket ready: ${source}`:`Bucket not ready: ${data.error||'not found'}`,!data.ok);return data}catch(e){setBucketReady(false,e.message);if(noisy)showMessage(e.message,true);throw e}finally{setButtonBusy('checkBucket',false)}}
|
| 462 |
async function createBucket(){setButtonBusy('createBucket',true,'Creating…');setBucketFeedback('creating','Creating private bucket…');try{const data=await apiPost('/api/bucket/create',{bucket_name:state.bucketName});const source=data.bucket_source||state.bucketName;state.bucketSource=source;setText('bucketInlineLabel',source);setBucketReady(Boolean(data.ok),data.error||'Could not create bucket.');showMessage(data.ok?`Created/ready: ${source}`:`Could not create bucket: ${data.error}`,!data.ok);if(data.ok)loadRuns(true)}catch(e){setBucketReady(false,e.message);showMessage(e.message,true)}finally{setButtonBusy('createBucket',false)}}
|
|
|
|
| 801 |
checkBucket:()=>checkBucket(true),
|
| 802 |
createBucket:createBucket,
|
| 803 |
enableEvalArchive:enableEvalArchive,
|
| 804 |
+
verifyEvalArchive:verifyEvalArchive,
|
| 805 |
+
disableEvalArchive:disableEvalArchive,
|
| 806 |
refreshRuns:()=>loadRuns(true),
|
| 807 |
refreshRunsHome:()=>loadRuns(true),
|
| 808 |
cancelRun:cancelActiveRun,
|