fix: make OCR portable and reliable across AMD64 and ARM64 (#519)

* fix: make OCR portable and reliable

* fix: harden OCR installation portability

* fix: pin OCR partials across downloads

* fix: make OCR execution reliably asynchronous

* fix: harden OCR portability and docs routes

* fix: preserve decoder and docs safeguards
This commit is contained in:
SnapOtter
2026-07-15 03:34:24 +08:00
committed by GitHub
parent 58121f205f
commit 991c981529
409 changed files with 67151 additions and 8076 deletions
+63 -14
View File
@@ -1,7 +1,8 @@
/**
* FULL TOOL AUDIT Tests every tool in SnapOtter on Windows/amd64.
* Verifies: (1) tool works, (2) GPU tools use GPU not CPU fallback,
* (3) no unexpected model/method downgrades.
* Verifies: (1) tool works, (2) GPU-capable tools do not unexpectedly fall
* back, and (3) execution provenance matches the declared contract. OCR is
* deliberately CPU-only on every host, including NVIDIA systems.
*/
const BASE = "http://localhost:1349";
const USERNAME = "admin";
@@ -44,7 +45,7 @@ async function test(name, path, settings, checks = {}) {
log(name, "FAIL", body.details || body.error || `HTTP ${status}`);
return;
}
// Check expected model/method
// Check expected model/method/provenance
if (checks.expectKey && checks.expectValue) {
const actual = body[checks.expectKey];
if (actual !== checks.expectValue) {
@@ -56,12 +57,24 @@ async function test(name, path, settings, checks = {}) {
return;
}
}
for (const [key, expected] of Object.entries(checks.expectFields ?? {})) {
if (body[key] !== expected) {
log(name, "FAIL", `Expected ${key}=${expected} but got ${body[key]}`);
return;
}
}
// Build detail string
const parts = [];
for (const k of [
"method",
"model",
"engine",
"provider",
"device",
"requestedQuality",
"actualQuality",
"runtimeVersion",
"modelVersion",
"format",
"width",
"height",
@@ -102,10 +115,22 @@ async function main() {
console.log(`GPU detected: ${health.ai?.gpu}`);
console.log(`Version: ${health.version}\n`);
const featuresRes = await fetch(`${BASE}/api/v1/features`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!featuresRes.ok) {
throw new Error(`Could not read feature capabilities: HTTP ${featuresRes.status}`);
}
const features = await featuresRes.json();
const ocrFeature = features.bundles?.find((bundle) => bundle.id === "ocr");
if (!ocrFeature?.availableQualities?.includes("fast")) {
throw new Error("OCR feature state does not advertise the built-in Fast tier");
}
// ════════════════════════════════════════════════════════════════
// SECTION 1: GPU/AI TOOLS — verify correct model, no fallbacks
// SECTION 1: GPU/AI TOOLS — verify correct model and declared provider
// ════════════════════════════════════════════════════════════════
console.log("--- GPU/AI TOOLS (must use GPU, no CPU fallback) ---\n");
console.log("--- GPU/AI TOOLS (GPU where supported; OCR is CPU-only) ---\n");
// Background removal — all models
await test(
@@ -197,17 +222,41 @@ async function main() {
// OCR
await test(
"OCR (tesseract)",
"OCR (Fast built-in)",
"ocr",
{ engine: "tesseract" },
{ expectKey: "engine", expectValue: "tesseract" },
);
await test(
"OCR (paddleocr)",
"ocr",
{ engine: "paddleocr" },
{ expectKey: "engine", expectValue: "paddleocr-v5" },
{ quality: "fast" },
{
expectFields: {
engine: "tesseract",
provider: "native",
device: "cpu",
requestedQuality: "fast",
actualQuality: "fast",
degraded: false,
},
},
);
for (const quality of ["balanced", "best"]) {
if (!ocrFeature.availableQualities.includes(quality)) {
log(`OCR (${quality})`, "SKIP", `signed accurate runtime unavailable (${ocrFeature.status})`);
continue;
}
await test(
`OCR (${quality})`,
"ocr",
{ quality },
{
expectFields: {
engine: "rapidocr-onnx",
provider: "CPUExecutionProvider",
device: "cpu",
requestedQuality: quality,
actualQuality: quality,
degraded: false,
},
},
);
}
// Face operations (MediaPipe)
await test("Face Blur", "blur-faces", { intensity: 50 });