Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion scripts/enrich-documents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -293,6 +293,7 @@ async function classifyExistingImages(supabase: SupabaseAdmin, documentId: strin
classifiedImageSkipReason,
clinicalImagePolicyVersion,
lightweightPerceptualHash,
normalizeImageBbox,
},
{ classifyAndCaptionImageFromBase64 },
] = await Promise.all([import("@/lib/env"), import("@/lib/image-filtering"), import("@/lib/openai")]);
Expand DownExpand Up@@ -336,7 +337,7 @@ async function classifyExistingImages(supabase: SupabaseAdmin, documentId: strin
imageHash,
seenHashes,
image: {
bbox: image.bbox as [number, number, number, number] | null,
bbox: normalizeImageBbox(image.bbox),
width: image.width,
height: image.height,
sourceKind: image.source_kind as
Expand Down
13 changes: 10 additions & 3 deletions src/lib/image-filtering.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -274,9 +274,16 @@ export function isClinicalImageEvidence(image: {
return assessment.clinical_use_class === "clinical_evidence";
}

function bboxLooksLikeHeaderOrFooter(bbox: ExtractedImage["bbox"]) {
if (!bbox) return false;
const [, y0, , y1] = bbox;
export function normalizeImageBbox(value: unknown): [number, number, number, number] | null {
if (!Array.isArray(value) || value.length !== 4) return null;
const coords = value.map((entry) => Number(entry));
return coords.every(Number.isFinite) ? (coords as [number, number, number, number]) : null;
Comment thread
BigSimmo marked this conversation as resolved.
}

function bboxLooksLikeHeaderOrFooter(bbox: unknown) {
const coords = normalizeImageBbox(bbox);
if (!coords) return false;
const [, y0, , y1] = coords;
const height = Math.abs(y1 - y0);
if (height > 110) return false;
return y1 < 105 || y0 > 705;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/rag.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,7 +35,7 @@ import { logger } from "@/lib/logger";
import { queryCacheKeyForStorage, queryPrivacyMetadata, queryTextForStorage } from "@/lib/query-privacy";
import { normalizeSourceMetadata } from "@/lib/source-metadata";
import { isReviewedTablePromotable } from "@/lib/table-review";
import { isClinicalImageEvidence } from "@/lib/image-filtering";
import { isClinicalImageEvidence, normalizeImageBbox } from "@/lib/image-filtering";
import { chooseAnswerRoute, hasDirectTitleSupport, shouldRetryWithStrongAfterFast } from "@/lib/rag-routing";
import { fetchRelatedDocumentMetadata, fetchRelatedDocuments } from "@/lib/document-enrichment";
import { boldHighYieldClinicalText, boldRagAnswerHighYieldText, rankAnswerEvidence } from "@/lib/answer-ranking";
Expand DownExpand Up@@ -3165,7 +3165,7 @@ async function attachPageVisualEvidence(
page_number: image.page_number,
storage_path: image.storage_path,
caption: image.caption,
bbox: image.bbox as ChunkImage["bbox"],
bbox: normalizeImageBbox(image.bbox),
image_type: image.image_type as ChunkImage["image_type"],
searchable: image.searchable,
clinical_relevance_score: image.clinical_relevance_score,
Expand Down
28 changes: 28 additions & 0 deletions tests/image-filtering.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import {
classifiedImageSkipReason,
isClinicalImageEvidence,
lightweightPerceptualHash,
normalizeImageBbox,
} from "../src/lib/image-filtering";

describe("smart image filtering", () => {
Expand DownExpand Up@@ -42,6 +43,33 @@ describe("smart image filtering", () => {
).toBeNull();
});

it("ignores object-shaped bbox jsonb instead of crashing", () => {
expect(
cheapImageSkipReason({
bytesLength: 20_000,
imageHash: "obj",
seenHashes: new Set(),
image: {
sourceKind: "embedded",
width: 600,
height: 400,
bbox: { x0: 20, y0: 20, x1: 180, y1: 80 } as unknown as [number, number, number, number],
},
}),
).toBeNull();
});

it("normalizes bbox jsonb to a four-number tuple or null", () => {
expect(normalizeImageBbox([20, 20, 180, 80])).toEqual([20, 20, 180, 80]);
expect(normalizeImageBbox(["20", "20", "180", "80"])).toEqual([20, 20, 180, 80]);
expect(normalizeImageBbox({ x0: 20, y0: 20, x1: 180, y1: 80 })).toBeNull();
expect(normalizeImageBbox([20, 20, 180])).toBeNull();
expect(normalizeImageBbox([20, 20, 180, "wide"])).toBeNull();
expect(normalizeImageBbox([20, 20, 180, Number.NaN])).toBeNull();
expect(normalizeImageBbox("20,20,180,80")).toBeNull();
expect(normalizeImageBbox(null)).toBeNull();
});

it("keeps relevant clinical classifications searchable", () => {
expect(
classifiedImageSkipReason({
Expand Down
Loading