- Notifications
You must be signed in to change notification settings - Fork 0
Fix flowchart-next-step retrieval: surface red-zone action evidence#119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
3a6f02b65412263fc2a84c68ae48d774b8421f65b975a7260af706afc5b376eccf43a21f75b50e807998d048637ecb2a2a2a0389ad75d60c32ee87eedce20674368556d49aea5bcf57c59de1431b7ccab993b41144d2f04f97bf1c3b29be689ed528bb4b21edffb53dee0268b870ac6854b7964c2f058a55b424dFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -24,6 +24,10 @@ import { | ||
| hasStructuredThresholdEvidence, | ||
| normalizedClinicalSearchTokens, | ||
| rankClinicalResults, | ||
| queriedZoneColour, | ||
| riskZoneActionPattern, | ||
| riskZoneContextPattern, | ||
| zoneContextPatternsForQuery, | ||
| } from "@/lib/clinical-search"; | ||
| import { env, isDemoMode, isLocalNoAuthMode, requestedOpenAIAnswerModels } from "@/lib/env"; | ||
| import { logger } from "@/lib/logger"; | ||
| @@ -1955,12 +1959,21 @@ export function buildRetrievalQueryVariants( | ||
| addVariant("admission discharge"); | ||
| } | ||
| if ( | ||
| /\b(?:flow\s*chart|flowchart|algorithm|pathway)\b/i.test(query) && | ||
| /\b(?:flow\s*chart|flowchart|algorithm|pathway|risk[\s-]*matrix)\b/i.test(query) && | ||
| /\b(?:risk|red\s*zone|red|urgent|escalat|next step)\b/i.test(query) | ||
| ) { | ||
| addVariant("risk flow"); | ||
| addVariant("red zone risk flow"); | ||
| addVariant("risk flow review urgent escalation"); | ||
| // websearch_to_tsquery ANDs every term, so the previous "red zone risk flow" | ||
| // and "risk flow review urgent escalation" variants required all terms in one | ||
| // chunk and did not reliably contribute candidates to the pool. A "<colour> zone" variant retrieves the small, | ||
| // precise set of zone-action chunks (escalation protocols, observation and | ||
| // response charts, risk-matrix cells) that answer zone / next-step questions. | ||
| // Match the zone the query actually names so an amber-zone question does not | ||
| // pull red-zone chunks into its candidate pool. | ||
| const zoneColour = queriedZoneColour(query); | ||
| if (zoneColour) { | ||
| addVariant(`${zoneColour} zone`); | ||
| } | ||
| } | ||
| addVariant(analysis.queryRewrite.searchQuery); | ||
| @@ -3171,7 +3184,10 @@ export function decideTextFastPath( | ||
| } | ||
| if (queryClass === "document_lookup") { | ||
| if (isRiskFlowchartNextStepQuery(query) && !hasRiskFlowchartActionEvidence(results)) { | ||
| // Flowchart/zone "next step" questions need the zone-action evidence (red | ||
| // zone -> escalate / urgent review), not just a lexically matching flowchart | ||
| // page; otherwise fall through to structured/vector retrieval. | ||
| if (isRiskFlowchartNextStepQuery(query) && !hasRiskFlowchartActionEvidence(query, results)) { | ||
| return { returnFastPath: false, reason: "risk_flowchart_requires_action_evidence" }; | ||
| } | ||
| if (directTitleSupport && strongestScore >= 0.32) { | ||
| @@ -3269,23 +3285,30 @@ function hasAnyTerm(text: string, pattern: RegExp) { | ||
| function isRiskFlowchartNextStepQuery(query: string) { | ||
| return ( | ||
| /\b(?:flow\s*chart|flowchart|algorithm|pathway|riskmatrix)\b/i.test(query) && | ||
| /\b(?:risk|red[\s-]*zone|red)\b/i.test(query) && | ||
| /\b(?:flow\s*chart|flowchart|algorithm|pathway|risk[\s-]*matrix)\b/i.test(query) && | ||
| riskZoneContextPattern.test(query) && | ||
BigSimmo marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /\b(?:next step|step after|after|action)\b/i.test(query) | ||
| ); | ||
| } | ||
| function hasRiskFlowchartActionEvidence(results: SearchResult[], limit = 5) { | ||
| function hasRiskFlowchartActionEvidence(query: string, results: SearchResult[], limit = 5) { | ||
| // A single result must carry BOTH the zone context and the action language | ||
| // (escalate / urgent review): scattering the two term groups across different | ||
| // results (or their image captions) let unrelated risk-assessment flowcharts | ||
| // pass. Deliberately does NOT require a flowchart word in the evidence — the | ||
| // escalation protocols that answer a red-zone question express the flowchart's | ||
| // decision steps as prose ("has any Purple or Red Zone criteria ... escalate | ||
| // for Senior Clinician Review") without ever saying "flowchart". | ||
| // | ||
| // The shared patterns are scoped to the colour the query names (a red-zone | ||
| // question must not fast-path on an amber-zone chunk); for risk-matrix / | ||
| // flowchart visual units the bare cell colour token counts as zone context. | ||
| const { zonePhrasePattern, bareColourPattern } = zoneContextPatternsForQuery(query); | ||
| return results.slice(0, limit).some((result) => { | ||
| const evidenceText = evidenceTextForGate(result); | ||
| const hasFlowchartRiskContext = | ||
| hasAnyTerm(evidenceText, /\b(?:flow\s*chart|flowchart|algorithm|pathway|matrix)\b/i) && | ||
| hasAnyTerm(evidenceText, /\b(?:risk|red[\s-]*zone|red)\b/i); | ||
| const hasAction = | ||
| hasAnyTerm(evidenceText, /\b(?:escalat(?:e|ion|ed|ing)?|urgent|next step|senior)\b/i) || | ||
| (visualEvidenceUnitTypes.has(result.index_unit?.unit_type ?? "") && | ||
| hasAnyTerm(evidenceText, /\b(?:escalat(?:e|ion|ed|ing)?|urgent|next step|senior)\b/i)); | ||
| return hasFlowchartRiskContext && hasAction; | ||
| if (!riskZoneActionPattern.test(evidenceText)) return false; | ||
| if (zonePhrasePattern.test(evidenceText)) return true; | ||
| return ["risk_matrix_cell", "flowchart_step", "diagram_decision"].includes(result.index_unit?.unit_type ?? "") && bareColourPattern.test(evidenceText); | ||
| }); | ||
| } | ||
| @@ -3480,8 +3503,12 @@ export function evaluateEvidenceCoverageGate( | ||
| sourceImageSatisfied, | ||
| }; | ||
| } | ||
| if (/\b(?:flow\s*chart|flowchart|red\s*zone|risk matrix)\b/i.test(query)) { | ||
| const accepted = hasRiskFlowchartActionEvidence(results); | ||
| // Only zone/next-step flowchart questions need the zone-action evidence | ||
| // gate; a plain flowchart document lookup ("which procedure flowchart | ||
| // covers X?") falls through to the ordinary title gate below so a direct | ||
| // title hit is not rejected for lacking zone evidence. | ||
| if (isRiskFlowchartNextStepQuery(query)) { | ||
| const accepted = hasRiskFlowchartActionEvidence(query, results); | ||
| return { | ||
| accepted, | ||
| reason: accepted ? "visual_flowchart_risk_gate" : "missing_visual_flowchart_risk_evidence", | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.