From d6d41d79d3bdb126974bb0b2605f7a82de9babb3 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sun, 12 Jul 2026 18:10:03 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20CodeRabbit=20Chat:=20Simplify=20?= =?UTF-8?q?PR=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ClinicalDashboard.tsx | 5 +- .../clinical-dashboard/answer-content.tsx | 46 ++++++++++++------- .../answer-result-surface.tsx | 8 +++- .../clinical-dashboard/evidence-panels.tsx | 5 +- .../clinical-dashboard/output-panel.tsx | 6 +-- src/lib/rag-answer-text.ts | 16 +++---- 6 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx index 6d884ce42..4fbf19e5e 100644 --- a/src/components/ClinicalDashboard.tsx +++ b/src/components/ClinicalDashboard.tsx @@ -68,6 +68,7 @@ import { import { GuideDialog, GuideTrigger, UtilityDrawer } from "@/components/clinical-dashboard/dashboard-shell"; import { sanitizeAnswerDisplayText, sanitizeDisplayText } from "@/components/clinical-dashboard/display-text"; import { + isPreformattedGroundedAnswer, NaturalLanguageAnswer, ScopeAndGovernanceNotice, UserQuestionBubble, @@ -523,7 +524,7 @@ function PriorAnswerTurnSurface({ () => buildAnswerRenderModel(turn.answer, { sources: turn.sources }), [turn.answer, turn.sources], ); - const turnPreformatted = Boolean(turn.answer.preformatted && turn.answer.grounded); + const turnPreformatted = isPreformattedGroundedAnswer(turn.answer); const safeText = useMemo( () => sanitizeAnswerDisplayText(turn.answer.answer, { preformatted: turnPreformatted }), [turn.answer.answer, turnPreformatted], @@ -2861,7 +2862,7 @@ export function ClinicalDashboard({ currentRelevance?.isSourceBacked !== false && answerRenderModel?.trust !== "unsupported"; const sourceLookup = useMemo(() => new Map(sources.map((source) => [source.id, source])), [sources]); - const answerPreformatted = Boolean(answer?.preformatted && answer?.grounded); + const answerPreformatted = isPreformattedGroundedAnswer(answer); const safeAnswerText = useMemo( () => sanitizeAnswerDisplayText(answer?.answer ?? "", { preformatted: answerPreformatted }), [answer?.answer, answerPreformatted], diff --git a/src/components/clinical-dashboard/answer-content.tsx b/src/components/clinical-dashboard/answer-content.tsx index 4325f415a..9f194a162 100644 --- a/src/components/clinical-dashboard/answer-content.tsx +++ b/src/components/clinical-dashboard/answer-content.tsx @@ -53,6 +53,7 @@ import type { AnswerSection, AnswerSectionKind, BestSourceRecommendation, + RagAnswer, SearchResult, SearchScopeSummary, VisualEvidenceCard, @@ -234,12 +235,39 @@ export type AnswerDisplayTextOptions = { preserveBold?: boolean; }; +/** + * Reports whether an answer is a server-`preformatted`, grounded answer whose + * display text should bypass prose sanitization and be shown as-is. + * + * @param answer - The answer to check, or `null`/`undefined` when unavailable + * @returns `true` when the answer is preformatted and grounded + */ +export function isPreformattedGroundedAnswer( + answer: Pick | null | undefined, +) { + return Boolean(answer?.preformatted && answer?.grounded); +} + // Fragments carrying a safety-critical signal must never be dropped by the // compact 3-fragment / 85-word cap — a withhold/threshold/escalation caveat // hidden from the primary prose is a clinical-safety regression. const primaryAnswerSafetySignalPattern = /\b(?:withhold|withheld|stop|cease|hold|held|threshold|escalat\w*|urgent|red\s*zone|amber|immediately|do not|contraindicat\w*|toxic)\b/i; +// Shared tail of the sanitize path: run the display sanitizer, then strip the +// synthetic-demo notice both plainAnswerText and primaryAnswerDisplayText need +// removed before the text reaches the screen. +function sanitizeAndStripSyntheticNotice(value: string, options: AnswerDisplayTextOptions) { + return sanitizeAnswerDisplayText(value, { + minLength: 8, + minTokens: 2, + preformatted: options.preformatted, + preserveBold: options.preserveBold, + }) + .replace(/(?:\s*\n\s*)?Synthetic demo only:.*$/i, "") + .trim(); +} + /** * Produces sanitized, display-ready text for an answer. * @@ -251,14 +279,7 @@ export function plainAnswerText(value: string, options: AnswerDisplayTextOptions // clinicalProseUsefulness runs the source-noise stripper, so preformatted // answers bypass it and go straight to the lossless display path. const base = options.preformatted ? value : clinicalProseUsefulness(value).text || value; - return sanitizeAnswerDisplayText(base, { - minLength: 8, - minTokens: 2, - preformatted: options.preformatted, - preserveBold: options.preserveBold, - }) - .replace(/(?:\s*\n\s*)?Synthetic demo only:.*$/i, "") - .trim(); + return sanitizeAndStripSyntheticNotice(base, options); } /** @@ -276,14 +297,7 @@ export function primaryAnswerDisplayText(value: string, options: AnswerDisplayTe // Skip whole-text clinicalProseUsefulness: its 3-token floor drops short // safety sentences ("Stop lithium.") before the fragment-level safety // bypass below can rescue them. - const cleaned = sanitizeAnswerDisplayText(value, { - minLength: 8, - minTokens: 2, - preformatted: false, - preserveBold: options.preserveBold, - }) - .replace(/(?:\s*\n\s*)?Synthetic demo only:.*$/i, "") - .trim(); + const cleaned = sanitizeAndStripSyntheticNotice(value, { preformatted: false, preserveBold: options.preserveBold }); const fragments = cleaned .split(/\r?\n+/) .flatMap((line: string) => diff --git a/src/components/clinical-dashboard/answer-result-surface.tsx b/src/components/clinical-dashboard/answer-result-surface.tsx index 1619c3c19..b0f4c5485 100644 --- a/src/components/clinical-dashboard/answer-result-surface.tsx +++ b/src/components/clinical-dashboard/answer-result-surface.tsx @@ -7,7 +7,11 @@ import { ClipboardCheck, ExternalLink, Layers, ShieldAlert } from "lucide-react" import { type AnswerFeedbackType } from "@/lib/answer-feedback"; import { AnswerFollowUpSuggestions } from "@/components/clinical-dashboard/answer-follow-up-suggestions"; import { CrossModeLinksSection } from "@/components/clinical-dashboard/cross-mode-links"; -import { NaturalLanguageAnswer, UserQuestionBubble } from "@/components/clinical-dashboard/answer-content"; +import { + isPreformattedGroundedAnswer, + NaturalLanguageAnswer, + UserQuestionBubble, +} from "@/components/clinical-dashboard/answer-content"; import { AnswerSupportSummaryCard, answerHasCentralTable, @@ -215,7 +219,7 @@ function StagedAnswerResultSurfaceImpl({
section.id === "bottom-line") ?? sections[0]; - const primaryAnswer = plainAnswerText(answer.answer, { - preformatted: Boolean(answer.preformatted && answer.grounded), - }); + const primaryAnswer = plainAnswerText(answer.answer, { preformatted: isPreformattedGroundedAnswer(answer) }); const detailSections = sections .filter((section) => section.id !== "verify-source") .filter((section) => (showLead ? section.id !== leadSection?.id : section.id !== "bottom-line")) diff --git a/src/lib/rag-answer-text.ts b/src/lib/rag-answer-text.ts index de42c1c13..7996b4370 100644 --- a/src/lib/rag-answer-text.ts +++ b/src/lib/rag-answer-text.ts @@ -209,14 +209,6 @@ function separateSettingRunOns(value: string): string { .replace(/\bfor community patients,?\s+for inpatients,?/gi, "for community patients. For inpatients,"); } -/** - * Cleans clinical answer text by removing artifacts, normalizing formatting, and improving readability. - * - * @param value - The clinical answer text to polish - * @param options - Formatting options - * @param options.preserveBold - Whether to preserve inline bold markers - * @returns The polished clinical answer text - */ function stripAnswerArtifactNoise(text: string) { return text .replace(productCatalogueFragmentPattern, " ") @@ -247,6 +239,14 @@ function restoreBoldSpans(text: string, boldSpans: string[]) { return restored; } +/** + * Cleans clinical answer text by removing artifacts, normalizing formatting, and improving readability. + * + * @param value - The clinical answer text to polish + * @param options - Formatting options + * @param options.preserveBold - Whether to preserve inline bold markers + * @returns The polished clinical answer text + */ export function polishClinicalAnswerProse(value: string, options: { preserveBold?: boolean } = {}) { // Bold markers normally come off before bullet normalization so an emphasized // item ("o **Reduce dose**") still reads as a sub-bullet to the "o" matcher.