Add /models fetch timeout and health-derived default fallback for model dropdowns.
This commit is contained in:
+24
-8
@@ -171,6 +171,7 @@ const drop=document.getElementById('drop'), fileInput=document.getElementById('f
|
||||
logBox=document.getElementById('logBox'),
|
||||
logHint=document.getElementById('logHint');
|
||||
let chosen=null, polling=null, currentJobId=null, sheetPage={}, viewerZoom=1, reviewDirty=false;
|
||||
let defaultVisionModel='', defaultTextModel='';
|
||||
|
||||
function modelLabel(m){
|
||||
// Include per-1M-token pricing when the catalog provides it.
|
||||
@@ -212,6 +213,12 @@ function saveModelsCache(data){
|
||||
try{ localStorage.setItem(MODELS_CACHE_KEY, JSON.stringify({ts:Date.now(), data})); }
|
||||
catch(e){}
|
||||
}
|
||||
function fetchWithTimeout(url, ms){
|
||||
return Promise.race([
|
||||
fetch(url, {cache:'no-store'}),
|
||||
new Promise((_,reject)=>setTimeout(()=>reject(new Error('timeout')), ms))
|
||||
]);
|
||||
}
|
||||
|
||||
async function loadModels(){
|
||||
const note=document.getElementById('modelNote');
|
||||
@@ -225,7 +232,8 @@ async function loadModels(){
|
||||
return;
|
||||
}
|
||||
try{
|
||||
const res=await fetch('/models');
|
||||
note.textContent='fetching models...';
|
||||
const res=await fetchWithTimeout('/models', 5000);
|
||||
if(!res.ok) throw new Error('models HTTP '+res.status);
|
||||
const data=await res.json();
|
||||
saveModelsCache(data);
|
||||
@@ -235,10 +243,12 @@ async function loadModels(){
|
||||
modelsLoaded=true;
|
||||
note.textContent='('+(data.text||[]).length+' text / '+(data.vision||[]).length+' vision available)';
|
||||
}catch(err){
|
||||
visionSel.innerHTML='<option value="">(default)</option>';
|
||||
textSel.innerHTML='<option value="">(default)</option>';
|
||||
const fallback=[{id:defaultVisionModel||'', name:defaultVisionModel||'(default)', prompt_usd_per_mtok:null, completion_usd_per_mtok:null}];
|
||||
fillSelect(visionSel, fallback.filter(m=>m.id), defaultVisionModel);
|
||||
const fallbackText=[{id:defaultTextModel||'', name:defaultTextModel||'(default)', prompt_usd_per_mtok:null, completion_usd_per_mtok:null}];
|
||||
fillSelect(textSel, fallbackText.filter(m=>m.id), defaultTextModel);
|
||||
visionSel.disabled=false; textSel.disabled=false;
|
||||
note.textContent='using configured defaults (list unavailable)';
|
||||
note.textContent='using configured defaults ('+(err.message==='timeout'?'fetch timed out':'list unavailable')+')';
|
||||
console.warn('Could not load models:', err);
|
||||
}
|
||||
}
|
||||
@@ -674,11 +684,17 @@ async function finalizeReview(){
|
||||
|
||||
// If opened from an email link (/?job=<id>), load that job's results directly.
|
||||
(function init(){
|
||||
// Show the deployed build in the header so it's obvious which version is up.
|
||||
fetch('/health').then(r=>r.ok?r.json():null).then(h=>{
|
||||
if(h&&h.build) document.getElementById('buildTag').textContent=' · build '+h.build;
|
||||
}).catch(()=>{});
|
||||
// Fetch /health first: build tag for the header and default models as a
|
||||
// fallback if the larger /models catalog fails or times out.
|
||||
fetch('/health', {cache:'no-store'}).then(r=>r.ok?r.json():null).then(h=>{
|
||||
if(h){
|
||||
if(h.build) document.getElementById('buildTag').textContent=' · build '+h.build;
|
||||
if(h.model) defaultVisionModel=h.model;
|
||||
if(h.text_model) defaultTextModel=h.text_model;
|
||||
}
|
||||
}).catch(()=>{}).finally(()=>{
|
||||
loadModels();
|
||||
});
|
||||
const jobId=new URLSearchParams(location.search).get('job');
|
||||
if(jobId){ statusEl.innerHTML='<span class="spinner"></span>Loading job '+esc(jobId)+'...'; poll(jobId); }
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user