← back to Dw Photo Capture
batch mode: OCR falls back to manual after a timeout (no more 30s hang)
70ed167afa2b7886a0d5d7d2e52ceacd83e68db8 · 2026-09-18 13:20:49 -0700 · Steve Abrams
Local dev defaulted OCR to macvision → local Ollama, now retired, so /api/identify
hung ~30s per sample. Add a 7s AbortController timeout on the auto-SKU call; on
timeout/failure it degrades to manual SKU entry instead of freezing the UI. Backend-
agnostic — when a real vision backend is wired, OCR just starts succeeding in-window.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uns3k4K15WBcFJNr3GcUd3
Files touched
Diff
commit 70ed167afa2b7886a0d5d7d2e52ceacd83e68db8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Sep 18 13:20:49 2026 -0700
batch mode: OCR falls back to manual after a timeout (no more 30s hang)
Local dev defaulted OCR to macvision → local Ollama, now retired, so /api/identify
hung ~30s per sample. Add a 7s AbortController timeout on the auto-SKU call; on
timeout/failure it degrades to manual SKU entry instead of freezing the UI. Backend-
agnostic — when a real vision backend is wired, OCR just starts succeeding in-window.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uns3k4K15WBcFJNr3GcUd3
---
public/batch.html | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/public/batch.html b/public/batch.html
index 0129262..e2edce3 100644
--- a/public/batch.html
+++ b/public/batch.html
@@ -202,7 +202,8 @@ const CFG = {
driftLumaMax: 38, // Δluma vs cal that forces re-cal
driftWBMax: 0.16, // per-channel WB ratio drift vs cal that forces re-cal
skuConfMin: 0.55, // OCR confidence below this → keep, but flag for manual confirm
- maxTries: 6 // upload attempts before a shot is PARKED as failed (never dropped)
+ maxTries: 6, // upload attempts before a shot is PARKED as failed (never dropped)
+ ocrTimeoutMs: 7000 // auto-SKU OCR: give up after this and fall back to manual entry (never hang)
};
// ── Session + calibration state (persisted) ──
@@ -444,13 +445,17 @@ async function maybeOcr(g){
const small=document.createElement('canvas'); small.width=Math.round(cnv.width*scale); small.height=Math.round(cnv.height*scale);
small.getContext('2d').drawImage(cnv,0,0,small.width,small.height);
const dataUrl=small.toDataURL('image/jpeg',0.8);
- const r=await fetch('/api/identify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({dataUrl})});
- const j=await r.json();
- batchCost += (j.cost_usd||0.0006); // $ cost tracking (Gemini ≈ $0.0006/call)
+ const ac=new AbortController(); const to=setTimeout(()=>ac.abort(), CFG.ocrTimeoutMs);
+ let j;
+ try{
+ const r=await fetch('/api/identify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({dataUrl}),signal:ac.signal});
+ j=await r.json();
+ } finally { clearTimeout(to); }
+ batchCost += (j.cost_usd||0); // $ cost (0 = local; external engines report their own)
if(j.code){ curSku=j.code; skuConf=(j.confidence??0.6); $('#skuInput').value=curSku; }
else { skuConf=0; }
setSkuChip();
- }catch(e){ setChip('#dSku','err'); $('#skuTxt').textContent='err'; }
+ }catch(e){ setChip('#dSku','warn'); $('#skuTxt').textContent='type SKU'; } // OCR slow/unavailable → manual entry
ocrPending=false;
}
function setSkuChip(){
← e43af57 auto-data-snapshot: 2026-09-18T11:01:55 (1 data files) — dat
·
back to Dw Photo Capture
·
Photo capture: vendor-first new-SKU creation across Shopify 0a65a03 →