From 1c52dcaf84c0159afc5ac84c93543688f5214458 Mon Sep 17 00:00:00 2001 From: Khalid Abdi Date: Thu, 18 Jun 2026 21:43:37 +0300 Subject: [PATCH] fix(chat): graph mode renders the record graph, not cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Graph mode, a natural-language request ("graph for John Smith") went through the getPatient tool, which always emitted record cards — so the clinician got Summary/Vitals cards instead of a graph (the mode prompt even wrongly claimed the graph rendered automatically). Thread the composer mode into the chat tools and have getPatient emit data-recordGraph in graph mode (matching the client-side /patient fast-path), with the cards kept for chat/analysis modes. Co-Authored-By: Claude Opus 4.8 --- backend/src/routes/chat.ts | 4 ++-- backend/src/services/ai/tools.ts | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/backend/src/routes/chat.ts b/backend/src/routes/chat.ts index 1ee7345..c68e1c6 100644 --- a/backend/src/routes/chat.ts +++ b/backend/src/routes/chat.ts @@ -90,7 +90,7 @@ function modeDirective(mode: string | undefined): string { return "Mode — Analysis: the clinician wants interpretation, not just retrieval. After fetching a patient's data, surface patterns and correlations across their problems, labs and visits (e.g. recurring complaints, trends, likely links) and call out anything notable. Stay grounded in the tool results."; } if (mode === "graph") { - return "Mode — Graph: the clinician wants to see how a patient's problems and visits connect. When they reference a patient, call getPatient (its record graph renders automatically) and briefly describe the key relationships between illnesses and encounters."; + return "Mode — Graph: the clinician wants to see how a patient's problems and visits connect. When they reference a patient, call getPatient — in this mode it renders the patient's record GRAPH (not cards) automatically — then briefly describe the key relationships between illnesses and encounters. Do not say you cannot draw a graph; getPatient produces it."; } return ""; } @@ -222,7 +222,7 @@ chatRouter.post("/", async (req, res, next) => { }); } - const tools = createChatTools({ ...ctx, veil, writer }); + const tools = createChatTools({ ...ctx, mode, veil, writer }); if (resolved.isExternal && veil.active) { // Non-streamed pass so we can rehydrate identifier tokens before the diff --git a/backend/src/services/ai/tools.ts b/backend/src/services/ai/tools.ts index 8f8d2db..f9b468a 100644 --- a/backend/src/services/ai/tools.ts +++ b/backend/src/services/ai/tools.ts @@ -33,6 +33,9 @@ export type ToolContext = { // The signed-in clinician — needed to scope task visibility (and to stamp the // creator when an add is committed via the REST endpoints on approval). viewer: { userId: string; userName: string; memberRole: string }; + // The composer's "situation" mode (chat | analysis | graph). In graph mode + // getPatient renders the record graph instead of the record cards. + mode?: string; veil: Veil; writer: UIMessageStreamWriter; }; @@ -57,7 +60,7 @@ function forModel(p: Patient) { } export function createChatTools(ctx: ToolContext) { - const { orgId, demographicsOnly, scopeProviderId, viewer, veil, writer } = + const { orgId, demographicsOnly, scopeProviderId, viewer, mode, veil, writer } = ctx; // Emit a Chain-of-Thought step to the UI as the agent works. Steps stream live @@ -86,7 +89,7 @@ export function createChatTools(ctx: ToolContext) { // Look up one patient by file number (MRN) and show their record cards. getPatient: tool({ description: - "Retrieve a patient's full record by file number (MRN) and display it as record cards. Use when the clinician asks about a specific patient.", + "Retrieve a patient's full record by file number (MRN). Displays it as record cards (or, in Graph mode, as the patient's record graph). Use when the clinician asks about a specific patient.", inputSchema: z.object({ fileNumber: z .string() @@ -102,8 +105,13 @@ export function createChatTools(ctx: ToolContext) { scopeProviderId, ); if (!patient) return { found: false as const, fileNumber }; - // Real data → clinician UI (cards). Redacted data → model. - writer.write({ type: "data-patientCard", data: patient }); + // Real data → clinician UI. In graph mode render the record graph; in + // chat/analysis modes render the record cards. Redacted data → model. + writer.write( + mode === "graph" + ? { type: "data-recordGraph", data: patient } + : { type: "data-patientCard", data: patient }, + ); return { found: true as const, patient: forModel(veil.redactPatient(patient)) }; }, }),