diff --git a/backend/src/routes/chat.ts b/backend/src/routes/chat.ts
index cb47fe7..c172db5 100644
--- a/backend/src/routes/chat.ts
+++ b/backend/src/routes/chat.ts
@@ -78,7 +78,23 @@ function inlineTextFiles(messages: UIMessage[]): UIMessage[] {
});
}
-function systemPrompt(veilActive: boolean, providerLabel: string): string {
+// A short directive for the clinician's chosen "situation" mode, appended to the
+// base prompt. Chat mode adds nothing.
+function modeDirective(mode: string | undefined): string {
+ if (mode === "analysis") {
+ 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 "";
+}
+
+function systemPrompt(
+ veilActive: boolean,
+ providerLabel: string,
+ mode?: string,
+): string {
return [
"You are temetro, a clinical assistant that helps clinicians retrieve,",
"organize, and add patient information. You operate over a real patient",
@@ -130,6 +146,7 @@ function systemPrompt(veilActive: boolean, providerLabel: string): string {
veilActive
? `Privacy: this conversation runs on an external provider (${providerLabel}). Patient identifiers are de-identified as tokens like [PATIENT_1] / [MRN_1]; refer to patients generically ("this patient") rather than repeating tokens.`
: "",
+ modeDirective(mode),
]
.filter(Boolean)
.join("\n");
@@ -137,10 +154,11 @@ function systemPrompt(veilActive: boolean, providerLabel: string): string {
chatRouter.post("/", async (req, res, next) => {
try {
- const { messages, model: requestedModel } = req.body as {
+ const { messages, model: requestedModel, mode } = req.body as {
messages: UIMessage[];
model?: string;
effort?: string;
+ mode?: string;
};
if (!Array.isArray(messages)) {
res.status(400).json({ error: "messages must be an array." });
@@ -171,7 +189,7 @@ chatRouter.post("/", async (req, res, next) => {
};
const modelMessages = await convertToModelMessages(inlineTextFiles(messages));
- const system = systemPrompt(veil.active, resolved.providerLabel);
+ const system = systemPrompt(veil.active, resolved.providerLabel, mode);
const stream = createUIMessageStream({
execute: async ({ writer }) => {
diff --git a/frontend/components/chat/chat-input.tsx b/frontend/components/chat/chat-input.tsx
index 1fcc16f..fa5071a 100644
--- a/frontend/components/chat/chat-input.tsx
+++ b/frontend/components/chat/chat-input.tsx
@@ -11,19 +11,17 @@ import {
} from "react";
import { useTranslation } from "react-i18next";
-import { ModelPicker } from "@/components/chat/model-picker";
+import { ModePicker } from "@/components/chat/mode-picker";
import { PatientFormDialog } from "@/components/chat/patient-form-dialog";
-import type { Effort } from "@/lib/ai-models";
+import type { ChatMode } from "@/lib/chat-modes";
import { cn } from "@/lib/utils";
type ChatInputProps = {
onSubmit: (text: string, files: File[]) => void;
status: ChatStatus;
onStop?: () => void;
- model: string;
- effort: Effort;
- onModelChange: (model: string) => void;
- onEffortChange: (effort: Effort) => void;
+ mode: ChatMode;
+ onModeChange: (mode: ChatMode) => void;
};
const iconButton =
@@ -37,10 +35,8 @@ export function ChatInput({
onSubmit,
status,
onStop,
- model,
- effort,
- onModelChange,
- onEffortChange,
+ mode,
+ onModeChange,
}: ChatInputProps) {
const { t } = useTranslation();
@@ -181,11 +177,9 @@ export function ChatInput({
-