Wizzas commited on
Commit
3e1bf17
·
verified ·
1 Parent(s): b715fbf

DefectRAG phase 5: incremental real-model application migration

Browse files
README.md CHANGED
@@ -15,7 +15,9 @@ short_description: Live React dashboard and DINOv2 inspection on ZeroGPU
15
  # DefectRAG — migration phase 5
16
 
17
  Existing React UI, real frozen DINOv2 inspection across all 12 VisA categories,
18
- original normal-reference banks, calibration and heatmaps. No training rerun.
 
 
19
  Anonymous browser history is isolated and temporary. Sign in to the configured
20
  staff workspace for durable shared records when available. Do not treat anomaly
21
  scores as probabilities or diagnoses. The operator selects the product category.
 
15
  # DefectRAG — migration phase 5
16
 
17
  Existing React UI, real frozen DINOv2 inspection across all 12 VisA categories,
18
+ original normal-reference banks and heatmaps, with versioned normal-only decision
19
+ calibration. No training rerun. The q95 sensitivity policy trades more false
20
+ alarms for higher recall; historical inspection decisions are preserved.
21
  Anonymous browser history is isolated and temporary. Sign in to the configured
22
  staff workspace for durable shared records when available. Do not treat anomaly
23
  scores as probabilities or diagnoses. The operator selects the product category.
backend/calibration.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Versioned decision calibration; encoder, memory and old records stay unchanged."""
2
+ import json
3
+ import math
4
+ import os
5
+
6
+
7
+ POLICY = 'normal-q95-v1'
8
+ FILENAME = 'sensitive_deployment_evaluation.json'
9
+
10
+
11
+ def active_evaluation(root, baseline):
12
+ policy = os.getenv('DEFECTRAG_CALIBRATION', POLICY)
13
+ if policy == 'normal-max-v1':
14
+ return baseline
15
+ if policy != POLICY:
16
+ raise ValueError('Unknown decision calibration policy')
17
+ path = root / 'results' / FILENAME
18
+ if not path.exists():
19
+ return baseline
20
+ candidate = json.loads(path.read_text())
21
+ if candidate.get('decision_policy') != POLICY or set(candidate['categories']) != set(baseline['categories']):
22
+ raise ValueError('Invalid decision calibration manifest')
23
+ for category, original in baseline['categories'].items():
24
+ card = candidate['categories'][category]
25
+ for key in ('category', 'profile', 'encoder_sha256', 'bank_sha256', 'heatmap_scale', 'train_normal_count'):
26
+ if card[key] != original[key]:
27
+ raise ValueError('Calibration manifest does not match the original model: ' + category)
28
+ if (card['baseline_threshold'] != original['threshold'] or
29
+ card['model_id'] != original['model_id'] + '-q95-v1' or
30
+ card['calibration'].get('policy') != POLICY or
31
+ card['calibration']['normal'] != original['calibration']['normal'] or
32
+ card['calibration']['anomalous'] != 0 or
33
+ not math.isfinite(card['threshold']) or not 0 < card['threshold'] <= original['threshold'] or
34
+ not math.isclose(card['uncertainty_margin'], card['threshold'] * .05, abs_tol=1e-12)):
35
+ raise ValueError('Invalid calibrated threshold: ' + category)
36
+ return candidate
37
+
38
+
39
+ def calibrated_card(root, original):
40
+ # Both local and ZeroGPU inference use this exact manifest and guard logic.
41
+ baseline_path = root / 'results/highres_deployment_evaluation.json'
42
+ if not baseline_path.exists():
43
+ return original
44
+ baseline = json.loads(baseline_path.read_text())
45
+ for key in ('threshold', 'model_id', 'encoder_sha256', 'bank_sha256'):
46
+ if baseline['categories'][original['category']][key] != original[key]:
47
+ raise ValueError('Original model card and evaluation disagree')
48
+ return active_evaluation(root, baseline)['categories'][original['category']]
backend/highres.py CHANGED
@@ -17,6 +17,7 @@ import torch.nn.functional as F
17
  from transformers import AutoModel
18
  from .vision import distances,ModelUnavailable,sha256,display_map
19
  from .config import CATEGORIES
 
20
 
21
 
22
  class HighResolutionEncoder:
@@ -84,11 +85,11 @@ class HighResolutionDetector:
84
  self.banks={};self.lock=threading.Lock()
85
 
86
  def cards(self):
87
- return [json.loads((self.directory/f'{category}.json').read_text()) for category in CATEGORIES]
88
 
89
  def load(self,category):
90
  if category not in CATEGORIES:raise ValueError('Unsupported product category')
91
- card=json.loads((self.directory/f'{category}.json').read_text())
92
  encoder = self.encoder
93
  if encoder is None:
94
  source=self.settings.root/'models/encoder_dinov2'
 
17
  from transformers import AutoModel
18
  from .vision import distances,ModelUnavailable,sha256,display_map
19
  from .config import CATEGORIES
20
+ from .calibration import calibrated_card
21
 
22
 
23
  class HighResolutionEncoder:
 
85
  self.banks={};self.lock=threading.Lock()
86
 
87
  def cards(self):
88
+ return [calibrated_card(self.settings.root,json.loads((self.directory/f'{category}.json').read_text())) for category in CATEGORIES]
89
 
90
  def load(self,category):
91
  if category not in CATEGORIES:raise ValueError('Unsupported product category')
92
+ card=calibrated_card(self.settings.root,json.loads((self.directory/f'{category}.json').read_text()))
93
  encoder = self.encoder
94
  if encoder is None:
95
  source=self.settings.root/'models/encoder_dinov2'
backend/main.py CHANGED
@@ -115,6 +115,9 @@ def create_app(settings=None, detector=None, workflow=None, repository=None):
115
  active=getattr(detector,'profile_name','legacy')=='dinov2_highres_v1'
116
  path=settings.root/'results'/('highres_deployment_evaluation.json' if active else 'evaluation.json')
117
  result=json.loads(path.read_text()) if path.exists() else {'status':'unverified'}
 
 
 
118
  profile=settings.root/'results'/'serving_profile.json'
119
  if profile.exists() and not active:result['serving_profile']=json.loads(profile.read_text())
120
  return {'data':result}
 
115
  active=getattr(detector,'profile_name','legacy')=='dinov2_highres_v1'
116
  path=settings.root/'results'/('highres_deployment_evaluation.json' if active else 'evaluation.json')
117
  result=json.loads(path.read_text()) if path.exists() else {'status':'unverified'}
118
+ if active and 'categories' in result:
119
+ from .calibration import active_evaluation
120
+ result=active_evaluation(settings.root,result)
121
  profile=settings.root/'results'/'serving_profile.json'
122
  if profile.exists() and not active:result['serving_profile']=json.loads(profile.read_text())
123
  return {'data':result}
deployment/space/app.py CHANGED
@@ -25,6 +25,7 @@ from fastapi import HTTPException
25
  from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
26
  from fastapi.staticfiles import StaticFiles
27
  from backend.config import CATEGORIES, Settings
 
28
  from backend.highres import HighResolutionEncoder, foreground_mask, score_tokens, restore_map
29
  from backend.vision import sha256, display_map
30
  from backend.storage import now
@@ -32,7 +33,7 @@ from deployment.space.sessions import SessionRepository, identity, namespace, NA
32
 
33
  ZERO = os.getenv('SPACES_ZERO_GPU') == '1'
34
  DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
35
- CARDS = {category: json.loads((ROOT/f'models/highres/{category}.json').read_text()) for category in CATEGORIES}
36
  ENCODER = HighResolutionEncoder(ROOT/'models/encoder_dinov2', device='cpu')
37
  encoder_digest = sha256(ROOT/'models/encoder_dinov2/model.safetensors')
38
  assert all(card['encoder_sha256'] == encoder_digest for card in CARDS.values())
 
25
  from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
26
  from fastapi.staticfiles import StaticFiles
27
  from backend.config import CATEGORIES, Settings
28
+ from backend.calibration import calibrated_card
29
  from backend.highres import HighResolutionEncoder, foreground_mask, score_tokens, restore_map
30
  from backend.vision import sha256, display_map
31
  from backend.storage import now
 
33
 
34
  ZERO = os.getenv('SPACES_ZERO_GPU') == '1'
35
  DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
36
+ CARDS = {category: calibrated_card(ROOT,json.loads((ROOT/f'models/highres/{category}.json').read_text())) for category in CATEGORIES}
37
  ENCODER = HighResolutionEncoder(ROOT/'models/encoder_dinov2', device='cpu')
38
  encoder_digest = sha256(ROOT/'models/encoder_dinov2/model.safetensors')
39
  assert all(card['encoder_sha256'] == encoder_digest for card in CARDS.values())
deployment/space/publish.py CHANGED
@@ -23,6 +23,8 @@ def main():
23
  path=ROOT/relative
24
  if path.exists():
25
  files[relative]=path
 
 
26
  for directory in ('backend','deployment/space'):
27
  for path in (ROOT/directory).glob('*.py'):
28
  files[path.relative_to(ROOT).as_posix()] = path
@@ -52,7 +54,9 @@ def main():
52
  header = readme.split('---',2)[1].replace('DefectRAG - Phase 1 compatibility test','DefectRAG').replace('Existing React UI plus one genuine DINOv2 ZeroGPU inspection','Live React dashboard and DINOv2 inspection on ZeroGPU')
53
  body = f'''\n# DefectRAG — migration phase {args.phase}\n
54
  Existing React UI, real frozen DINOv2 inspection across all 12 VisA categories,
55
- original normal-reference banks, calibration and heatmaps. No training rerun.
 
 
56
  Anonymous browser history is isolated and temporary. Sign in to the configured
57
  staff workspace for durable shared records when available. Do not treat anomaly
58
  scores as probabilities or diagnoses. The operator selects the product category.
 
23
  path=ROOT/relative
24
  if path.exists():
25
  files[relative]=path
26
+ for relative in ('results/sensitive_deployment_evaluation.json',):
27
+ files[relative]=ROOT/relative
28
  for directory in ('backend','deployment/space'):
29
  for path in (ROOT/directory).glob('*.py'):
30
  files[path.relative_to(ROOT).as_posix()] = path
 
54
  header = readme.split('---',2)[1].replace('DefectRAG - Phase 1 compatibility test','DefectRAG').replace('Existing React UI plus one genuine DINOv2 ZeroGPU inspection','Live React dashboard and DINOv2 inspection on ZeroGPU')
55
  body = f'''\n# DefectRAG — migration phase {args.phase}\n
56
  Existing React UI, real frozen DINOv2 inspection across all 12 VisA categories,
57
+ original normal-reference banks and heatmaps, with versioned normal-only decision
58
+ calibration. No training rerun. The q95 sensitivity policy trades more false
59
+ alarms for higher recall; historical inspection decisions are preserved.
60
  Anonymous browser history is isolated and temporary. Sign in to the configured
61
  staff workspace for durable shared records when available. Do not treat anomaly
62
  scores as probabilities or diagnoses. The operator selects the product category.
frontend/dist/assets/TeamWorkspace-C-ePZ-kX.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import{c as h,r as n,a as Q,b as d,R as e,L as X,S as $,A as ee,d as te}from"./index-BdEpy7cy.js";/**
2
+ * @license lucide-react v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const ae=h("ArchiveRestore",[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h2",key:"tvwodi"}],["path",{d:"M20 8v11a2 2 0 0 1-2 2h-2",key:"1gkqxj"}],["path",{d:"m9 15 3-3 3 3",key:"1pd0qc"}],["path",{d:"M12 12v9",key:"192myk"}]]);/**
7
+ * @license lucide-react v0.468.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const _=h("Cloud",[["path",{d:"M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z",key:"p7xjir"}]]);/**
12
+ * @license lucide-react v0.468.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const re=h("ExternalLink",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);/**
17
+ * @license lucide-react v0.468.0 - ISC
18
+ *
19
+ * This source code is licensed under the ISC license.
20
+ * See the LICENSE file in the root directory of this source tree.
21
+ */const ne=h("LogIn",[["path",{d:"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4",key:"u53s6r"}],["polyline",{points:"10 17 15 12 10 7",key:"1ail0h"}],["line",{x1:"15",x2:"3",y1:"12",y2:"12",key:"v6grx8"}]]);/**
22
+ * @license lucide-react v0.468.0 - ISC
23
+ *
24
+ * This source code is licensed under the ISC license.
25
+ * See the LICENSE file in the root directory of this source tree.
26
+ */const le=h("LogOut",[["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}],["polyline",{points:"16 17 21 12 16 7",key:"1gabdz"}],["line",{x1:"21",x2:"9",y1:"12",y2:"12",key:"1uyos4"}]]);/**
27
+ * @license lucide-react v0.468.0 - ISC
28
+ *
29
+ * This source code is licensed under the ISC license.
30
+ * See the LICENSE file in the root directory of this source tree.
31
+ */const se=h("RefreshCw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);/**
32
+ * @license lucide-react v0.468.0 - ISC
33
+ *
34
+ * This source code is licensed under the ISC license.
35
+ * See the LICENSE file in the root directory of this source tree.
36
+ */const ce=h("Send",[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",key:"1ffxy3"}],["path",{d:"m21.854 2.147-10.94 10.939",key:"12cjpa"}]]);/**
37
+ * @license lucide-react v0.468.0 - ISC
38
+ *
39
+ * This source code is licensed under the ISC license.
40
+ * See the LICENSE file in the root directory of this source tree.
41
+ */const oe=h("Users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["path",{d:"M16 3.13a4 4 0 0 1 0 7.75",key:"1da9ce"}]]);function me(){var D;const[c,A]=n.useState(null),[l,x]=n.useState(null),[v,M]=n.useState([]),[S,H]=n.useState({}),[g,F]=n.useState(""),[R,L]=n.useState(""),[o,q]=n.useState(!1),[P,y]=n.useState(""),[E,W]=n.useState(!1),[s,w]=n.useState(null),[B,V]=n.useState([]),[K,G]=n.useState([]),[k,z]=n.useState(""),[Y,I]=n.useState(!1),[U,O]=n.useState(""),[Z,N]=n.useState(!1),C=Q();async function i(t){q(!0),y("");try{await t()}catch(r){y(r.message||"Team request failed.")}finally{q(!1)}}async function b(){const{client:t,staff:r}=await te();x(r);let a=t.from("shared_inspections").select("*").order("created_at",{ascending:!1}).limit(200);a=E?a.not("archived_at","is",null):a.is("archived_at",null);const[{data:m,error:f},{data:u,error:p}]=await Promise.all([a,t.from("staff_members").select("*")]);if(f)throw f;if(p)throw p;M(m||[]),H(Object.fromEntries((u||[]).map(T=>[T.user_id,T.display_name])))}n.useEffect(()=>{if(!C)return;let t=!1,r;return d().then(async a=>{r=a.auth.onAuthStateChange((f,u)=>{t||(A((u==null?void 0:u.user)||null),f==="PASSWORD_RECOVERY"&&I(!0))}).data.subscription;const{data:m}=await a.auth.getUser();t||A(m.user)}).catch(a=>{t||y(a.message)}),()=>{t=!0,r==null||r.unsubscribe()}},[C]),n.useEffect(()=>{if(!c)return;let t=!1;b().catch(a=>{t||y(a.message)});const r=setInterval(()=>b().catch(a=>{t||y(a.message)}),15e3);return()=>{t=!0,clearInterval(r)}},[c==null?void 0:c.id,E]);async function j(t){await i(async()=>{const r=await d(),a=r.storage.from("team-inspections"),m=await Promise.all([t.image_path,t.heatmap_path].map(p=>a.createSignedUrl(p,900)));if(m.some(p=>p.error))throw new Error("Private images could not be loaded. Check staff/storage policies.");const{data:f,error:u}=await r.from("staff_notes").select("*").eq("inspection_id",t.id).order("created_at");if(u)throw u;w(t),V(m.map(p=>p.data.signedUrl)),G(f||[])})}async function J(){await i(async()=>{const t=await d(),r=v.filter(a=>a.owner_id===c.id).map(a=>a.id);if(r.length){const{error:a}=await t.rpc("archive_team_inspections",{p_ids:r});if(a)throw a}w(null),N(!1),await b()})}return C?e.createElement(e.Fragment,null,e.createElement("section",{className:"card"},e.createElement("div",{className:"section-title"},e.createElement("div",null,e.createElement("h2",null,e.createElement(oe,{size:21})," Staff workspace"),e.createElement("p",null,"Signed-in staff share saved inspections and review notes. Live AI runs on the connected inspection backend.")),e.createElement("span",{className:"subtle-tag"},e.createElement(_,{size:13})," SUPABASE")),P&&e.createElement("div",{className:"error",role:"alert"},P),c?e.createElement(e.Fragment,null,e.createElement("div",{className:"team-toolbar"},e.createElement("div",null,e.createElement($,{size:18}),e.createElement("strong",null,(l==null?void 0:l.display_name)||c.email),e.createElement("span",{className:"subtle-tag"},(l==null?void 0:l.role)||"CHECKING MEMBERSHIP")),e.createElement("button",{className:"secondary",disabled:o,onClick:()=>i(b)},e.createElement(se,{size:16})," Refresh"),e.createElement("button",{className:"secondary",onClick:()=>i(async()=>{await(await d()).auth.signOut(),M([]),w(null),x(null)})},e.createElement(le,{size:16})," Sign out")),Y&&e.createElement("form",{className:"login-form",onSubmit:t=>{t.preventDefault(),i(async()=>{const r=await d(),{error:a}=await r.auth.updateUser({password:U});if(a)throw a;O(""),I(!1)})}},e.createElement("label",{className:"field"},"Set a new password",e.createElement("input",{type:"password",minLength:12,autoComplete:"new-password",value:U,onChange:t=>O(t.target.value),required:!0})),e.createElement("button",{className:"primary",disabled:o},"Save password")),e.createElement("div",{className:"team-toolbar"},e.createElement("span",null,"Latest ",v.length," shared records · refreshes every 15 seconds"),(l==null?void 0:l.role)==="developer"&&e.createElement("button",{className:"secondary",onClick:()=>{W(!E),w(null)}},e.createElement(ae,{size:16}),E?"Active history":"Developer recovery"),!E&&e.createElement("button",{className:"secondary danger",disabled:o||!v.some(t=>t.owner_id===c.id),onClick:()=>N(!0)},e.createElement(ee,{size:16})," Archive my visible history")),Z&&e.createElement("div",{className:"note"},e.createElement("div",null,e.createElement("strong",null,"Archive your visible records?"),e.createElement("p",null,"They will leave the shared active history. Original files and conversations remain recoverable by a developer. Other staff members’ records will stay active."),e.createElement("div",{className:"button-row"},e.createElement("button",{className:"primary",disabled:o,onClick:J},"Archive my records"),e.createElement("button",{className:"secondary",onClick:()=>N(!1)},"Cancel")))),e.createElement("div",{className:"table-wrap"},e.createElement("table",null,e.createElement("thead",null,e.createElement("tr",null,e.createElement("th",null,"Inspection"),e.createElement("th",null,"Product"),e.createElement("th",null,"Result"),e.createElement("th",null,"Shared by"),e.createElement("th",null,"Saved"),e.createElement("th",null))),e.createElement("tbody",null,v.map(t=>{var r;return e.createElement("tr",{key:t.id},e.createElement("td",null,e.createElement("strong",null,t.id.slice(0,8))),e.createElement("td",null,t.payload.category),e.createElement("td",null,e.createElement("span",{className:"badge "+((r=t.payload.verdict)==null?void 0:r.toLowerCase())},t.payload.verdict)),e.createElement("td",null,S[t.owner_id]||"Staff member"),e.createElement("td",null,new Date(t.created_at).toLocaleString()),e.createElement("td",null,e.createElement("button",{className:"text-button",onClick:()=>j(t)},"Review"),E&&(l==null?void 0:l.role)==="developer"&&e.createElement("button",{className:"text-button",disabled:o,onClick:()=>i(async()=>{const a=await d(),{error:m}=await a.rpc("restore_team_inspections",{p_ids:[t.id]});if(m)throw m;await b()})},"Restore")))}))),!v.length&&e.createElement("div",{className:"empty"},e.createElement(_,{size:30}),e.createElement("p",null,E?"No archived records to recover.":"No shared inspections yet. In the local app, open a result and select Share with staff.")))):e.createElement("form",{className:"login-form",onSubmit:t=>{t.preventDefault(),i(async()=>{const r=await d(),{error:a}=await r.auth.signInWithPassword({email:g,password:R});if(a)throw a;L("")})}},e.createElement("label",{className:"field"},"Work email",e.createElement("input",{type:"email",autoComplete:"username",value:g,onChange:t=>F(t.target.value),required:!0})),e.createElement("label",{className:"field"},"Password",e.createElement("input",{type:"password",autoComplete:"current-password",value:R,onChange:t=>L(t.target.value),required:!0})),e.createElement("button",{className:"primary",disabled:o},o?e.createElement(X,{className:"spin",size:17}):e.createElement(ne,{size:17})," Sign in"),e.createElement("button",{type:"button",className:"text-button",disabled:!g||o,onClick:()=>i(async()=>{const t=await d(),{error:r}=await t.auth.resetPasswordForEmail(g,{redirectTo:location.origin+location.pathname});if(r)throw r;y("Password reset requested. Check your email.")})},"Reset password"),e.createElement("p",{className:"caption"},"Accounts must be invited and approved by your workspace developer. Signing up alone does not grant staff access."))),c&&s&&e.createElement("section",{className:"card"},e.createElement("div",{className:"section-title"},e.createElement("div",null,e.createElement("h2",null,"Shared inspection · ",s.id.slice(0,8)),e.createElement("p",null,"Saved snapshot from ",S[s.owner_id]||"a colleague",". This page does not run a new prediction.")),e.createElement("button",{className:"text-button",onClick:()=>w(null)},"Close")),e.createElement("div",{className:"shared-images"},B.map((t,r)=>e.createElement("img",{key:t,src:t,alt:r?"Saved anomaly heatmap":"Saved inspected product"}))),e.createElement("div",{className:"observation"},e.createElement("strong",null,"SAVED MEASUREMENTS"),e.createElement("span",null,s.payload.verdict," · score ",Number(s.payload.score).toFixed(5)," · threshold ",Number(s.payload.threshold).toFixed(5))),e.createElement("h3",{className:"section-gap"},"Recorded evidence"),(D=s.conversation)!=null&&D.length?s.conversation.map((t,r)=>e.createElement("p",{className:"cloud-message",key:r},e.createElement("strong",null,t.role==="user"?"Inspector":"Evidence Assistant",": "),t.question||t.answer)):e.createElement("p",{className:"caption"},"No conversation was included when this inspection was shared."),e.createElement("h3",{className:"section-gap"},"Staff review notes"),K.map(t=>e.createElement("div",{className:"cloud-message",key:t.id},e.createElement("strong",null,S[t.author_id]||"Staff member"),e.createElement("p",null,t.body),e.createElement("small",null,new Date(t.created_at).toLocaleString()))),!s.archived_at&&e.createElement("form",{className:"question-form",onSubmit:t=>{t.preventDefault(),i(async()=>{const r=await d(),{error:a}=await r.from("staff_notes").insert({inspection_id:s.id,author_id:c.id,body:k.trim()});if(a)throw a;z(""),await j(s)})}},e.createElement("input",{"aria-label":"Staff review note",maxLength:2e3,placeholder:"Add a review note for your colleagues…",value:k,onChange:t=>z(t.target.value)}),e.createElement("button",{className:"primary",disabled:o||!k.trim()},e.createElement(ce,{size:16})," Add note")))):e.createElement("section",{className:"card cloud-setup"},e.createElement("div",{className:"setup-icon"},e.createElement(_,{size:32})),e.createElement("span",{className:"eyebrow"},"TEAM WORKSPACE"),e.createElement("h2",null,"One shared view of inspection."),e.createElement("p",null,"The staff workspace is ready to connect to your own Supabase project. No cloud project or account has been created for you."),e.createElement("div",{className:"setup-grid"},[["01","Create a free project","Use your Supabase account and select an eligible Free plan."],["02","Set up staff access","Run the supplied schema, add your developer account and invite colleagues."],["03","Connect this dashboard","Add the project URL and publishable key to config.js, then reload."]].map(([t,r,a])=>e.createElement("div",{key:t},e.createElement("b",null,t),e.createElement("h3",null,r),e.createElement("p",null,a)))),e.createElement("a",{className:"primary",href:"https://supabase.com/dashboard",target:"_blank",rel:"noreferrer"},"Open Supabase ",e.createElement(re,{size:16})),e.createElement("p",{className:"caption"},"Setup files: deployment/SUPABASE_SETUP.md and deployment/supabase.sql. Never place a service-role key in the browser. Local inspection works without cloud setup."))}export{me as default};
frontend/dist/assets/index-BdEpy7cy.js ADDED
The diff for this file is too large to render. See raw diff
 
frontend/dist/index.html CHANGED
@@ -1,3 +1,3 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><meta name="theme-color" content="#132e32"/><meta name="description" content="Local visual anomaly inspection with traceable evidence."/><title>DefectRAG · Inspection intelligence</title><link rel="icon" type="image/svg+xml" href="./logo.svg"><script src="./config.js"></script> <script type="module" crossorigin src="./assets/index-B1eq_NAw.js"></script>
2
  <link rel="stylesheet" crossorigin href="./assets/index-Bn4HF2vD.css">
3
  </head><body><div id="root"></div></body></html>
 
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><meta name="theme-color" content="#132e32"/><meta name="description" content="Local visual anomaly inspection with traceable evidence."/><title>DefectRAG · Inspection intelligence</title><link rel="icon" type="image/svg+xml" href="./logo.svg"><script src="./config.js"></script> <script type="module" crossorigin src="./assets/index-BdEpy7cy.js"></script>
2
  <link rel="stylesheet" crossorigin href="./assets/index-Bn4HF2vD.css">
3
  </head><body><div id="root"></div></body></html>
results/sensitive_deployment_evaluation.json ADDED
@@ -0,0 +1,710 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
3
+ "profile": "dinov2_highres_v1",
4
+ "protocol": "Official VisA test assignments; same frozen DINOv2 operational banks. Threshold is the fixed linear 95th percentile of the existing content-grouped held-out training-normal scores. All categories use the same quantile; no test-label optimization. Retrospective development evaluation, not independent validation.",
5
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
6
+ "device": "cuda",
7
+ "categories": {
8
+ "pcb1": {
9
+ "category": "pcb1",
10
+ "available": true,
11
+ "model_id": "dinov2-highres-pcb1-v1-q95-v1",
12
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
13
+ "profile": "dinov2_highres_v1",
14
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
15
+ "bank_sha256": "14e71cb6285f22e3d12d2b0d6357c552a26685a8f03a9b626a3e8999e8a89c65",
16
+ "threshold": 0.1414354294538498,
17
+ "uncertainty_margin": 0.00707177147269249,
18
+ "heatmap_scale": 0.10316987216472613,
19
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
20
+ "bank_size_bytes": 23237446,
21
+ "encoder_size_bytes": 88249960,
22
+ "calibration": {
23
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
24
+ "normal": 91,
25
+ "anomalous": 0,
26
+ "seed": 42,
27
+ "policy": "normal-q95-v1",
28
+ "quantile": 0.95,
29
+ "calibration_above_threshold": 5,
30
+ "future_false_positive_rate_guaranteed": false
31
+ },
32
+ "train_normal_count": 813,
33
+ "metrics": {
34
+ "image_auroc": 0.9597,
35
+ "average_precision": 0.9564653221826244,
36
+ "precision": 0.9148936170212766,
37
+ "recall": 0.86,
38
+ "f1": 0.8865979381443299,
39
+ "confusion_matrix": [
40
+ [
41
+ 92,
42
+ 8
43
+ ],
44
+ [
45
+ 14,
46
+ 86
47
+ ]
48
+ ],
49
+ "n_images": 200,
50
+ "pixel_auroc_224": 0.9946015614062951,
51
+ "pixel_average_precision_224": 0.8562862818777615,
52
+ "uncertain_fraction": 0.07,
53
+ "latency_median_ms": null,
54
+ "peak_gpu_mb": null,
55
+ "false_positive_rate": 0.08
56
+ },
57
+ "resolution_short_edge": 448,
58
+ "patch_size": 14,
59
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
60
+ "baseline_threshold": 0.16181933879852295,
61
+ "decision_policy": "normal-q95-v1"
62
+ },
63
+ "capsules": {
64
+ "category": "capsules",
65
+ "available": true,
66
+ "model_id": "dinov2-highres-capsules-v1-q95-v1",
67
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
68
+ "profile": "dinov2_highres_v1",
69
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
70
+ "bank_sha256": "fa34f8c1a203e609450e57156d5f75ca157140d88d07d0bf46947c3029675c55",
71
+ "threshold": 0.1439922600984573,
72
+ "uncertainty_margin": 0.007199613004922866,
73
+ "heatmap_scale": 0.1274158811569204,
74
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
75
+ "bank_size_bytes": 22108790,
76
+ "encoder_size_bytes": 88249960,
77
+ "calibration": {
78
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
79
+ "normal": 55,
80
+ "anomalous": 0,
81
+ "seed": 42,
82
+ "policy": "normal-q95-v1",
83
+ "quantile": 0.95,
84
+ "calibration_above_threshold": 3,
85
+ "future_false_positive_rate_guaranteed": false
86
+ },
87
+ "train_normal_count": 487,
88
+ "metrics": {
89
+ "image_auroc": 0.9683333333333333,
90
+ "average_precision": 0.9798232872953215,
91
+ "precision": 0.9425287356321839,
92
+ "recall": 0.82,
93
+ "f1": 0.877005347593583,
94
+ "confusion_matrix": [
95
+ [
96
+ 55,
97
+ 5
98
+ ],
99
+ [
100
+ 18,
101
+ 82
102
+ ]
103
+ ],
104
+ "n_images": 160,
105
+ "pixel_auroc_224": 0.9704973188279925,
106
+ "pixel_average_precision_224": 0.403655563796705,
107
+ "uncertain_fraction": 0.0875,
108
+ "latency_median_ms": null,
109
+ "peak_gpu_mb": null,
110
+ "false_positive_rate": 0.08333333333333333
111
+ },
112
+ "resolution_short_edge": 448,
113
+ "patch_size": 14,
114
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
115
+ "baseline_threshold": 0.19718621671199799,
116
+ "decision_policy": "normal-q95-v1"
117
+ },
118
+ "cashew": {
119
+ "category": "cashew",
120
+ "available": true,
121
+ "model_id": "dinov2-highres-cashew-v1-q95-v1",
122
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
123
+ "profile": "dinov2_highres_v1",
124
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
125
+ "bank_sha256": "4b908b22f79158fb46c0e591610d5c9470d588d4f0d017dd0bb493a58e64c693",
126
+ "threshold": 0.1289861112833023,
127
+ "uncertainty_margin": 0.006449305564165116,
128
+ "heatmap_scale": 0.10505769878625872,
129
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
130
+ "bank_size_bytes": 18366576,
131
+ "encoder_size_bytes": 88249960,
132
+ "calibration": {
133
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
134
+ "normal": 45,
135
+ "anomalous": 0,
136
+ "seed": 42,
137
+ "policy": "normal-q95-v1",
138
+ "quantile": 0.95,
139
+ "calibration_above_threshold": 3,
140
+ "future_false_positive_rate_guaranteed": false
141
+ },
142
+ "train_normal_count": 405,
143
+ "metrics": {
144
+ "image_auroc": 0.9608000000000001,
145
+ "average_precision": 0.9817707260519815,
146
+ "precision": 0.9090909090909091,
147
+ "recall": 0.9,
148
+ "f1": 0.9045226130653266,
149
+ "confusion_matrix": [
150
+ [
151
+ 41,
152
+ 9
153
+ ],
154
+ [
155
+ 10,
156
+ 90
157
+ ]
158
+ ],
159
+ "n_images": 150,
160
+ "pixel_auroc_224": 0.9960830910862268,
161
+ "pixel_average_precision_224": 0.7034966403298871,
162
+ "uncertain_fraction": 0.06,
163
+ "latency_median_ms": null,
164
+ "peak_gpu_mb": null,
165
+ "false_positive_rate": 0.18
166
+ },
167
+ "resolution_short_edge": 448,
168
+ "patch_size": 14,
169
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
170
+ "baseline_threshold": 0.13224798440933228,
171
+ "decision_policy": "normal-q95-v1"
172
+ },
173
+ "candle": {
174
+ "category": "candle",
175
+ "available": true,
176
+ "model_id": "dinov2-highres-candle-v1-q95-v1",
177
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
178
+ "profile": "dinov2_highres_v1",
179
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
180
+ "bank_sha256": "bb6767014719520899e8d2336ea2811ae8dadabb210a8855f7497f2ac48ab7ad",
181
+ "threshold": 0.15027016177773475,
182
+ "uncertainty_margin": 0.007513508088886738,
183
+ "heatmap_scale": 0.1193666505813595,
184
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
185
+ "bank_size_bytes": 23244689,
186
+ "encoder_size_bytes": 88249960,
187
+ "calibration": {
188
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
189
+ "normal": 90,
190
+ "anomalous": 0,
191
+ "seed": 42,
192
+ "policy": "normal-q95-v1",
193
+ "quantile": 0.95,
194
+ "calibration_above_threshold": 5,
195
+ "future_false_positive_rate_guaranteed": false
196
+ },
197
+ "train_normal_count": 810,
198
+ "metrics": {
199
+ "image_auroc": 0.9410000000000001,
200
+ "average_precision": 0.9459465742669609,
201
+ "precision": 0.8787878787878788,
202
+ "recall": 0.87,
203
+ "f1": 0.8743718592964824,
204
+ "confusion_matrix": [
205
+ [
206
+ 88,
207
+ 12
208
+ ],
209
+ [
210
+ 13,
211
+ 87
212
+ ]
213
+ ],
214
+ "n_images": 200,
215
+ "pixel_auroc_224": 0.9911336061961822,
216
+ "pixel_average_precision_224": 0.33767271945011856,
217
+ "uncertain_fraction": 0.055,
218
+ "latency_median_ms": null,
219
+ "peak_gpu_mb": null,
220
+ "false_positive_rate": 0.12
221
+ },
222
+ "resolution_short_edge": 448,
223
+ "patch_size": 14,
224
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
225
+ "baseline_threshold": 0.22504067420959473,
226
+ "decision_policy": "normal-q95-v1"
227
+ },
228
+ "chewinggum": {
229
+ "category": "chewinggum",
230
+ "available": true,
231
+ "model_id": "dinov2-highres-chewinggum-v1-q95-v1",
232
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
233
+ "profile": "dinov2_highres_v1",
234
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
235
+ "bank_sha256": "37165883dcc6304f60346c2052c7fa4151d4048b44fdb80a4f60ffc48477ed7d",
236
+ "threshold": 0.15658171474933624,
237
+ "uncertainty_margin": 0.007829085737466813,
238
+ "heatmap_scale": 0.11275605440139722,
239
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
240
+ "bank_size_bytes": 18458527,
241
+ "encoder_size_bytes": 88249960,
242
+ "calibration": {
243
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
244
+ "normal": 46,
245
+ "anomalous": 0,
246
+ "seed": 42,
247
+ "policy": "normal-q95-v1",
248
+ "quantile": 0.95,
249
+ "calibration_above_threshold": 3,
250
+ "future_false_positive_rate_guaranteed": false
251
+ },
252
+ "train_normal_count": 407,
253
+ "metrics": {
254
+ "image_auroc": 0.9885999999999999,
255
+ "average_precision": 0.9944721280040146,
256
+ "precision": 0.9795918367346939,
257
+ "recall": 0.96,
258
+ "f1": 0.9696969696969697,
259
+ "confusion_matrix": [
260
+ [
261
+ 48,
262
+ 2
263
+ ],
264
+ [
265
+ 4,
266
+ 96
267
+ ]
268
+ ],
269
+ "n_images": 150,
270
+ "pixel_auroc_224": 0.9952665200400623,
271
+ "pixel_average_precision_224": 0.6597053568827396,
272
+ "uncertain_fraction": 0.0,
273
+ "latency_median_ms": null,
274
+ "peak_gpu_mb": null,
275
+ "false_positive_rate": 0.04
276
+ },
277
+ "resolution_short_edge": 448,
278
+ "patch_size": 14,
279
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
280
+ "baseline_threshold": 0.1614319533109665,
281
+ "decision_policy": "normal-q95-v1"
282
+ },
283
+ "fryum": {
284
+ "category": "fryum",
285
+ "available": true,
286
+ "model_id": "dinov2-highres-fryum-v1-q95-v1",
287
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
288
+ "profile": "dinov2_highres_v1",
289
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
290
+ "bank_sha256": "ca73c0cd2b2e75d80874f7dd6270f09b13c55292fa93b1da1eeef361d8375ba4",
291
+ "threshold": 0.13227128088474271,
292
+ "uncertainty_margin": 0.006613564044237136,
293
+ "heatmap_scale": 0.10057919770479187,
294
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
295
+ "bank_size_bytes": 18381371,
296
+ "encoder_size_bytes": 88249960,
297
+ "calibration": {
298
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
299
+ "normal": 45,
300
+ "anomalous": 0,
301
+ "seed": 42,
302
+ "policy": "normal-q95-v1",
303
+ "quantile": 0.95,
304
+ "calibration_above_threshold": 3,
305
+ "future_false_positive_rate_guaranteed": false
306
+ },
307
+ "train_normal_count": 405,
308
+ "metrics": {
309
+ "image_auroc": 0.9924,
310
+ "average_precision": 0.9963591766254614,
311
+ "precision": 0.9894736842105263,
312
+ "recall": 0.94,
313
+ "f1": 0.964102564102564,
314
+ "confusion_matrix": [
315
+ [
316
+ 49,
317
+ 1
318
+ ],
319
+ [
320
+ 6,
321
+ 94
322
+ ]
323
+ ],
324
+ "n_images": 150,
325
+ "pixel_auroc_224": 0.9735429349543053,
326
+ "pixel_average_precision_224": 0.4360142987071739,
327
+ "uncertain_fraction": 0.03333333333333333,
328
+ "latency_median_ms": null,
329
+ "peak_gpu_mb": null,
330
+ "false_positive_rate": 0.02
331
+ },
332
+ "resolution_short_edge": 448,
333
+ "patch_size": 14,
334
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
335
+ "baseline_threshold": 0.15253151953220367,
336
+ "decision_policy": "normal-q95-v1"
337
+ },
338
+ "macaroni1": {
339
+ "category": "macaroni1",
340
+ "available": true,
341
+ "model_id": "dinov2-highres-macaroni1-v1-q95-v1",
342
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
343
+ "profile": "dinov2_highres_v1",
344
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
345
+ "bank_sha256": "28ebf491e124df62266c5c3b72ea5bfebb50ef93c189d5615d3ae866e8675432",
346
+ "threshold": 0.14440965577960013,
347
+ "uncertainty_margin": 0.007220482788980007,
348
+ "heatmap_scale": 0.09929629743099211,
349
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
350
+ "bank_size_bytes": 23251984,
351
+ "encoder_size_bytes": 88249960,
352
+ "calibration": {
353
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
354
+ "normal": 90,
355
+ "anomalous": 0,
356
+ "seed": 42,
357
+ "policy": "normal-q95-v1",
358
+ "quantile": 0.95,
359
+ "calibration_above_threshold": 5,
360
+ "future_false_positive_rate_guaranteed": false
361
+ },
362
+ "train_normal_count": 810,
363
+ "metrics": {
364
+ "image_auroc": 0.909,
365
+ "average_precision": 0.9254645749095916,
366
+ "precision": 0.9827586206896551,
367
+ "recall": 0.57,
368
+ "f1": 0.721518987341772,
369
+ "confusion_matrix": [
370
+ [
371
+ 99,
372
+ 1
373
+ ],
374
+ [
375
+ 43,
376
+ 57
377
+ ]
378
+ ],
379
+ "n_images": 200,
380
+ "pixel_auroc_224": 0.9932559185087934,
381
+ "pixel_average_precision_224": 0.22125282869927704,
382
+ "uncertain_fraction": 0.1,
383
+ "latency_median_ms": null,
384
+ "peak_gpu_mb": null,
385
+ "false_positive_rate": 0.01
386
+ },
387
+ "resolution_short_edge": 448,
388
+ "patch_size": 14,
389
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
390
+ "baseline_threshold": 0.21722275018692017,
391
+ "decision_policy": "normal-q95-v1"
392
+ },
393
+ "macaroni2": {
394
+ "category": "macaroni2",
395
+ "available": true,
396
+ "model_id": "dinov2-highres-macaroni2-v1-q95-v1",
397
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
398
+ "profile": "dinov2_highres_v1",
399
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
400
+ "bank_sha256": "8dfe4f82ece83668ea4a97c18ef134ab16bea8ab6d2913959eb810942537737a",
401
+ "threshold": 0.144144269824028,
402
+ "uncertainty_margin": 0.0072072134912014,
403
+ "heatmap_scale": 0.11770220518112187,
404
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
405
+ "bank_size_bytes": 23243554,
406
+ "encoder_size_bytes": 88249960,
407
+ "calibration": {
408
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
409
+ "normal": 90,
410
+ "anomalous": 0,
411
+ "seed": 42,
412
+ "policy": "normal-q95-v1",
413
+ "quantile": 0.95,
414
+ "calibration_above_threshold": 5,
415
+ "future_false_positive_rate_guaranteed": false
416
+ },
417
+ "train_normal_count": 810,
418
+ "metrics": {
419
+ "image_auroc": 0.845,
420
+ "average_precision": 0.8446602099722114,
421
+ "precision": 0.8727272727272727,
422
+ "recall": 0.48,
423
+ "f1": 0.6193548387096774,
424
+ "confusion_matrix": [
425
+ [
426
+ 93,
427
+ 7
428
+ ],
429
+ [
430
+ 52,
431
+ 48
432
+ ]
433
+ ],
434
+ "n_images": 200,
435
+ "pixel_auroc_224": 0.9942242061289198,
436
+ "pixel_average_precision_224": 0.15769057747120718,
437
+ "uncertain_fraction": 0.19,
438
+ "latency_median_ms": null,
439
+ "peak_gpu_mb": null,
440
+ "false_positive_rate": 0.07
441
+ },
442
+ "resolution_short_edge": 448,
443
+ "patch_size": 14,
444
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
445
+ "baseline_threshold": 0.15817631781101227,
446
+ "decision_policy": "normal-q95-v1"
447
+ },
448
+ "pcb2": {
449
+ "category": "pcb2",
450
+ "available": true,
451
+ "model_id": "dinov2-highres-pcb2-v1-q95-v1",
452
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
453
+ "profile": "dinov2_highres_v1",
454
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
455
+ "bank_sha256": "694689b6703e399ea4076343a40ddff6a920f67c0c344048f0b2ef6402f3abb8",
456
+ "threshold": 0.1252228133380413,
457
+ "uncertainty_margin": 0.006261140666902066,
458
+ "heatmap_scale": 0.09851976394653338,
459
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
460
+ "bank_size_bytes": 23253151,
461
+ "encoder_size_bytes": 88249960,
462
+ "calibration": {
463
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
464
+ "normal": 91,
465
+ "anomalous": 0,
466
+ "seed": 42,
467
+ "policy": "normal-q95-v1",
468
+ "quantile": 0.95,
469
+ "calibration_above_threshold": 5,
470
+ "future_false_positive_rate_guaranteed": false
471
+ },
472
+ "train_normal_count": 810,
473
+ "metrics": {
474
+ "image_auroc": 0.9062,
475
+ "average_precision": 0.9098166487308751,
476
+ "precision": 0.875,
477
+ "recall": 0.84,
478
+ "f1": 0.8571428571428572,
479
+ "confusion_matrix": [
480
+ [
481
+ 88,
482
+ 12
483
+ ],
484
+ [
485
+ 16,
486
+ 84
487
+ ]
488
+ ],
489
+ "n_images": 200,
490
+ "pixel_auroc_224": 0.9714470671684394,
491
+ "pixel_average_precision_224": 0.26303790724220233,
492
+ "uncertain_fraction": 0.09,
493
+ "latency_median_ms": null,
494
+ "peak_gpu_mb": null,
495
+ "false_positive_rate": 0.12
496
+ },
497
+ "resolution_short_edge": 448,
498
+ "patch_size": 14,
499
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
500
+ "baseline_threshold": 0.341839998960495,
501
+ "decision_policy": "normal-q95-v1"
502
+ },
503
+ "pcb3": {
504
+ "category": "pcb3",
505
+ "available": true,
506
+ "model_id": "dinov2-highres-pcb3-v1-q95-v1",
507
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
508
+ "profile": "dinov2_highres_v1",
509
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
510
+ "bank_sha256": "704ee7d0827ede2b0a795a0efae8eb524751d2d1cb054cf7278c22888550a44c",
511
+ "threshold": 0.1346755474805832,
512
+ "uncertainty_margin": 0.00673377737402916,
513
+ "heatmap_scale": 0.1029075479507446,
514
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
515
+ "bank_size_bytes": 23242572,
516
+ "encoder_size_bytes": 88249960,
517
+ "calibration": {
518
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
519
+ "normal": 91,
520
+ "anomalous": 0,
521
+ "seed": 42,
522
+ "policy": "normal-q95-v1",
523
+ "quantile": 0.95,
524
+ "calibration_above_threshold": 5,
525
+ "future_false_positive_rate_guaranteed": false
526
+ },
527
+ "train_normal_count": 814,
528
+ "metrics": {
529
+ "image_auroc": 0.9547524752475247,
530
+ "average_precision": 0.9645187368797429,
531
+ "precision": 0.90625,
532
+ "recall": 0.87,
533
+ "f1": 0.8877551020408163,
534
+ "confusion_matrix": [
535
+ [
536
+ 92,
537
+ 9
538
+ ],
539
+ [
540
+ 13,
541
+ 87
542
+ ]
543
+ ],
544
+ "n_images": 201,
545
+ "pixel_auroc_224": 0.9805302802001331,
546
+ "pixel_average_precision_224": 0.33774957148695994,
547
+ "uncertain_fraction": 0.08955223880597014,
548
+ "latency_median_ms": null,
549
+ "peak_gpu_mb": null,
550
+ "false_positive_rate": 0.0891089108910891
551
+ },
552
+ "resolution_short_edge": 448,
553
+ "patch_size": 14,
554
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
555
+ "baseline_threshold": 0.16282716393470764,
556
+ "decision_policy": "normal-q95-v1"
557
+ },
558
+ "pcb4": {
559
+ "category": "pcb4",
560
+ "available": true,
561
+ "model_id": "dinov2-highres-pcb4-v1-q95-v1",
562
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
563
+ "profile": "dinov2_highres_v1",
564
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
565
+ "bank_sha256": "8847deec2a0b8daab9139e7680d502352161e994bb5d2fe4bc313df6af4cc5d5",
566
+ "threshold": 0.1401524841785431,
567
+ "uncertainty_margin": 0.007007624208927155,
568
+ "heatmap_scale": 0.10422959923744202,
569
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
570
+ "bank_size_bytes": 23248613,
571
+ "encoder_size_bytes": 88249960,
572
+ "calibration": {
573
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
574
+ "normal": 91,
575
+ "anomalous": 0,
576
+ "seed": 42,
577
+ "policy": "normal-q95-v1",
578
+ "quantile": 0.95,
579
+ "calibration_above_threshold": 5,
580
+ "future_false_positive_rate_guaranteed": false
581
+ },
582
+ "train_normal_count": 813,
583
+ "metrics": {
584
+ "image_auroc": 0.9865346534653465,
585
+ "average_precision": 0.9822685098306626,
586
+ "precision": 0.96,
587
+ "recall": 0.96,
588
+ "f1": 0.96,
589
+ "confusion_matrix": [
590
+ [
591
+ 97,
592
+ 4
593
+ ],
594
+ [
595
+ 4,
596
+ 96
597
+ ]
598
+ ],
599
+ "n_images": 201,
600
+ "pixel_auroc_224": 0.9806894408642699,
601
+ "pixel_average_precision_224": 0.3216614985757051,
602
+ "uncertain_fraction": 0.04477611940298507,
603
+ "latency_median_ms": null,
604
+ "peak_gpu_mb": null,
605
+ "false_positive_rate": 0.039603960396039604
606
+ },
607
+ "resolution_short_edge": 448,
608
+ "patch_size": 14,
609
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
610
+ "baseline_threshold": 0.17516569793224335,
611
+ "decision_policy": "normal-q95-v1"
612
+ },
613
+ "pipe_fryum": {
614
+ "category": "pipe_fryum",
615
+ "available": true,
616
+ "model_id": "dinov2-highres-pipe_fryum-v1-q95-v1",
617
+ "method": "AnomalyDINO-inspired DINOv2 ViT-S/14; custom full-data adaptation",
618
+ "profile": "dinov2_highres_v1",
619
+ "encoder_sha256": "ae1e99fcefd534ed978cdeb8326f08030c96e28b7a81ffcbc98a857c84d14be1",
620
+ "bank_sha256": "e28bf35ca2e553dd21821230f34682b94b11a5d203459f61049a774663c0d819",
621
+ "threshold": 0.17671903371810913,
622
+ "uncertainty_margin": 0.008835951685905457,
623
+ "heatmap_scale": 0.1333938717842105,
624
+ "uncertainty_policy": "Fixed \u00b15% of threshold; operational heuristic, not calibrated confidence",
625
+ "bank_size_bytes": 18401041,
626
+ "encoder_size_bytes": 88249960,
627
+ "calibration": {
628
+ "method": "Fixed linear 95th percentile of heldout normal scores; no anomalous calibration",
629
+ "normal": 45,
630
+ "anomalous": 0,
631
+ "seed": 42,
632
+ "policy": "normal-q95-v1",
633
+ "quantile": 0.95,
634
+ "calibration_above_threshold": 3,
635
+ "future_false_positive_rate_guaranteed": false
636
+ },
637
+ "train_normal_count": 405,
638
+ "metrics": {
639
+ "image_auroc": 0.9891999999999999,
640
+ "average_precision": 0.9951337553868363,
641
+ "precision": 0.9801980198019802,
642
+ "recall": 0.99,
643
+ "f1": 0.9850746268656716,
644
+ "confusion_matrix": [
645
+ [
646
+ 48,
647
+ 2
648
+ ],
649
+ [
650
+ 1,
651
+ 99
652
+ ]
653
+ ],
654
+ "n_images": 150,
655
+ "pixel_auroc_224": 0.9927315263511816,
656
+ "pixel_average_precision_224": 0.47984772131593945,
657
+ "uncertain_fraction": 0.013333333333333334,
658
+ "latency_median_ms": null,
659
+ "peak_gpu_mb": null,
660
+ "false_positive_rate": 0.04
661
+ },
662
+ "resolution_short_edge": 448,
663
+ "patch_size": 14,
664
+ "foreground": "Unsupervised PCA threshold/sign heuristic; can fail",
665
+ "baseline_threshold": 0.18373285233974457,
666
+ "decision_policy": "normal-q95-v1"
667
+ }
668
+ },
669
+ "macro_mean": {
670
+ "image_auroc": 0.950126705170517,
671
+ "average_precision": 0.956391637511357,
672
+ "precision": 0.932608381224698,
673
+ "recall": 0.8383333333333334,
674
+ "f1": 0.8755953086666709,
675
+ "pixel_auroc_224": 0.9861669559777334,
676
+ "pixel_average_precision_224": 0.4315059138196397,
677
+ "false_positive_rate": 0.07433718371837184
678
+ },
679
+ "decision_policy": "normal-q95-v1",
680
+ "baseline_evaluation_sha256": "4265f2fe45c49d3e71dacba9883e935b190829511fa42ecbc45c26e495984ec0",
681
+ "validation_scope": "Normal-only calibration and cached-score/software consistency checks; future recall and false-alarm rates are not guaranteed.",
682
+ "decision_policy_reason": "User requested prioritizing anomaly recall and accepts more false alarms.",
683
+ "policy_selection_context": "Policy adopted after viewing retrospective threshold-sensitivity results; not an independent or test-blind validation.",
684
+ "source_sha256": {
685
+ "experiments\\dinov2_highres_v1\\pcb1\\normal_calibration.json": "8224a5c38e21ae2b65497c109f45ce586e231525d43946779e0309225faf7010",
686
+ "experiments\\dinov2_highres_v1\\pcb1\\test_progress.json": "ba9bdde38b06df345c8f5a72ff1358a40d04287f609cac4bbac629cd652e6640",
687
+ "experiments\\dinov2_highres_v1\\capsules\\normal_calibration.json": "05a2f76ac29bb3d3e794155587f90e2ad3099b2df1bc14b0c8b6447aaab6f4f6",
688
+ "experiments\\dinov2_highres_v1\\capsules\\test_progress.json": "a214382658449bec9468a1d1103775cfa989b9f4d96c728ea756b1539c7a05e6",
689
+ "experiments\\dinov2_highres_v1\\cashew\\normal_calibration.json": "20b872ab18468386c96cee420ecddff58d4d9bb40ceb16edbb77a7965413dac2",
690
+ "experiments\\dinov2_highres_v1\\cashew\\test_progress.json": "dc236ba067baeac20b431da6b323abdcccadbfdd4fb58148f26dd0b2fd4733c1",
691
+ "experiments\\dinov2_highres_v1\\candle\\normal_calibration.json": "097689c7796940d5bcb30117fa22f454b906c519d7a4472bdd87e6434a3eb500",
692
+ "experiments\\dinov2_highres_v1\\candle\\test_progress.json": "2ec3a02bcc0c95d94242d4d31469b53126ddeae9ce5d777f0706dec984c362b2",
693
+ "experiments\\dinov2_highres_v1\\chewinggum\\normal_calibration.json": "012178e1e91e311e511a7f8b2ebabbff4fe1146a7d6a2227e374c264c763b995",
694
+ "experiments\\dinov2_highres_v1\\chewinggum\\test_progress.json": "ea5102f1d9e668ad8ff256dfcba10813b3f3723e0b09606b87745f71347b1a0f",
695
+ "experiments\\dinov2_highres_v1\\fryum\\normal_calibration.json": "6cd8984692afd2c20825b45a2c18d07550367510e376ce6c70d5bb2e44990432",
696
+ "experiments\\dinov2_highres_v1\\fryum\\test_progress.json": "52e7dababf991acdeda16907ae38cc9a2599a0164becbfcd7b3bbd96658c944f",
697
+ "experiments\\dinov2_highres_v1\\macaroni1\\normal_calibration.json": "6c83ed692639479b8b18914c7bea0c884eea7a1c0583106ea9bcd3d30c01dfde",
698
+ "experiments\\dinov2_highres_v1\\macaroni1\\test_progress.json": "d67ebfb2159c5dbe1cee314c300deb04123751caefd99288e99961dcfac1bb04",
699
+ "experiments\\dinov2_highres_v1\\macaroni2\\normal_calibration.json": "8c80102797cc6d81fd1862ec5c27465b3293e206ccb0b2af40973526a93a96a6",
700
+ "experiments\\dinov2_highres_v1\\macaroni2\\test_progress.json": "12bfe59625f5039623c341451c9e4ce0721271d98d7a8a815b2dc443465a88d8",
701
+ "experiments\\dinov2_highres_v1\\pcb2\\normal_calibration.json": "3b0713db8eebc9f7ea81ac1e758c42799e325aa0050a3c0c2ac1454e7457cd3a",
702
+ "experiments\\dinov2_highres_v1\\pcb2\\test_progress.json": "3b08920766efc75a65d1f7f4518adb086241b0ba71e2659a20edcf57a783afdb",
703
+ "experiments\\dinov2_highres_v1\\pcb3\\normal_calibration.json": "2c0e355bca9f8554fad3715d3a31acf733b30788c7b7e97a931cd24fb4d6ebd3",
704
+ "experiments\\dinov2_highres_v1\\pcb3\\test_progress.json": "e5e8d8d7adb1198b6cdca2f5bda7da0e71b4e30a672ae93714eb6efe7aefff0b",
705
+ "experiments\\dinov2_highres_v1\\pcb4\\normal_calibration.json": "2b364ef709f3ead9196871d7f0351ae38f477fbf98d6f94ca8607128a853b28a",
706
+ "experiments\\dinov2_highres_v1\\pcb4\\test_progress.json": "dd844a6415590d7effee5ba32eb7f2fbd737d7967efafebd845af506fd7685ae",
707
+ "experiments\\dinov2_highres_v1\\pipe_fryum\\normal_calibration.json": "21b3357ff4836e8b4d7fb7fee7e48fc7579a62d9c11dbf888f2de05397a88d56",
708
+ "experiments\\dinov2_highres_v1\\pipe_fryum\\test_progress.json": "0af8805dd69be9f39b2bedb13f8bd15e853f326e1ed9940b540d26ff01cf6dbf"
709
+ }
710
+ }