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
179 changes: 91 additions & 88 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 6 additions & 7 deletions src/lib/answer-verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Citation, DocumentTableFact, SearchResult } from "@/lib/types";
//
// The fast/strong LLM routes synthesize prose, so dose figures, ANC/FBC
// thresholds, and timing intervals are regenerated by the model and can be
// transcribed or rounded incorrectly. Numeric fidelity is the highest-risk
// transcribed or incorrectly rounded. Numeric fidelity is the highest-risk
// property of a clinical answer, so after generation we extract every
// numeric/dose/threshold token from the ANSWER and confirm each one appears
// verbatim in the text of a CITED chunk. Tokens that are not entailed by any
Expand All @@ -15,12 +15,11 @@ import type { Citation, DocumentTableFact, SearchResult } from "@/lib/types";
// which asserted figures are unsupported by the cited evidence.

// Matches clinical numeric tokens: doses, thresholds, ranges, percentages,
// and unit-bearing figures. Examples it should catch:
// 12.5 mg, 25-50 mg, 2.0 ×10⁹/L, 1500 mg/day, 0.4 mmol/L, 100 mcg, 12 hours,
// ANC 2.0, 80%, 3/7. Bare integers like "1" or "3" are excluded by default
// (too noisy / rarely a clinical figure) unless attached to a unit or range.
// and unit-bearing figures.
// Hardened to include complex clinical units (mg/kg/day, mmol/L, etc.) and
// ensure boundary safety to prevent hallucinated decimal bypass.
const NUMERIC_TOKEN_PATTERN =
/\b\d+(?:[.,]\d+)?(?:\s*[-–—]\s*\d+(?:[.,]\d+)?)?\s*(?:%|mg\/(?:day|kg|m2|dose)?|mg|mcg|microgram(?:s)?|micrograms?|μg|g\b|kg|ml|mL|l\b|mmol\/l|mmol\/L|mmol|mol\/l|umol\/l|µmol\/l|ng\/ml|units?\/?\w*|iu\b|×10\^?\d*\/?l?|x10\^?\d*\/?l?|×10⁹\/l|hours?|hrs?|hours|h\b|days?|weeks?|wk\b|months?|minutes?|mins?|years?|°c|mmhg|bpm)\b/giu;
/\b\d+(?:[.,]\d+)?(?:\s*[-–—]\s*\d+(?:[.,]\d+)?)?\s*(?:%|mg\/(?:day|kg|m2|dose)|mg|mcg|micrograms?|μg|g\b|kg|ml|mL|l\b|mmol\/[lL]|mmol|mol\/[lL]|umol\/[lL]|µmol\/[lL]|ng\/ml|units?\/?\w*|iu\b|×10\^?\d*\/?l?|x10\^?\d*\/?l?|×10⁹\/l|hours?|hrs?|h\b|days?|weeks?|wk\b|months?|minutes?|mins?|years?|°c|mmhg|bpm)\b/giu;

// Decimal numbers and ranges that, while not unit-bearing, are very likely
// clinical thresholds in context (e.g. "ANC 2.0", "INR 2-3"). We only treat a
Expand Down Expand Up @@ -133,4 +132,4 @@ export function verifyAnswerNumbers(
}

export const VERIFY_AGAINST_SOURCE_NOTE =
"Some figures in this answer could not be matched verbatim to the cited sources — verify against the source documents before acting.";
"CRITICAL: Some figures in this answer could not be matched verbatim to the cited sources — verify against the source documents before acting.";
6 changes: 3 additions & 3 deletions src/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ const envSchema = z.object({
OPENAI_STRONG_ANSWER_MODEL: z.string().default("gpt-5.4"),
// Reasoning models (gpt-5*) draw reasoning tokens from this same budget, so a
// low cap can starve the JSON answer payload and silently truncate clinical
// content (doses/thresholds cut mid-sentence). Raised default for headroom; if
// output is still cut off, createTextResult now flags it as truncated (GEN-C1).
OPENAI_MAX_OUTPUT_TOKENS: z.coerce.number().int().positive().default(2400),
// content (doses/thresholds cut mid-sentence). Raised default to 4000 for headroom;
// if output is still cut off, createTextResult now flags it as truncated (GEN-C1).
OPENAI_MAX_OUTPUT_TOKENS: z.coerce.number().int().positive().default(4000),
OPENAI_QUERY_CACHE_SIZE: z.coerce.number().int().nonnegative().default(200),
OPENAI_VISION_MODEL: z.string().default("gpt-5.4-mini"),
OPENAI_REQUEST_TIMEOUT_MS: z.coerce.number().int().positive().default(45000),
Expand Down
Loading