clinicpal / src /lib /utils.ts
Vrda's picture
Deploy ClinIcPal frontend
eed2ff1 verified
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import type { Severity, ErrorCategory, Settings, PromptPreset, PromptSettings } from '@/types';
// ---- Class Name Utility ----
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// ---- Category Information ----
export const CATEGORY_INFO = {
medication: {
id: 'medication' as const,
label: 'Medication',
icon: 'πŸ’Š',
description: 'Dosage errors, drug interactions, contraindications',
},
measurement: {
id: 'measurement' as const,
label: 'Measurement',
icon: 'πŸ“',
description: 'Vital signs, lab values outside possible ranges',
},
logical: {
id: 'logical' as const,
label: 'Logical',
icon: 'πŸ”—',
description: 'Temporal contradictions, diagnostic inconsistencies',
},
missing: {
id: 'missing' as const,
label: 'Missing Info',
icon: '❓',
description: 'Incomplete documentation, missing required elements',
},
terminology: {
id: 'terminology' as const,
label: 'Terminology',
icon: 'πŸ“„',
description: 'Misspellings, ambiguous abbreviations',
},
} as const;
// ---- Severity Information ----
export const SEVERITY_INFO = {
critical: {
id: 'critical' as const,
label: 'Critical',
className: 'glass-critical',
textColor: 'text-[var(--critical-text)]',
bgColor: 'bg-[var(--critical-bg)]',
borderColor: 'border-[var(--critical-border)]',
glowClass: 'pulse-glow-critical',
},
warning: {
id: 'warning' as const,
label: 'Warning',
className: 'glass-warning',
textColor: 'text-[var(--warning-text)]',
bgColor: 'bg-[var(--warning-bg)]',
borderColor: 'border-[var(--warning-border)]',
glowClass: 'pulse-glow-warning',
},
suggestion: {
id: 'suggestion' as const,
label: 'Suggestion',
className: 'glass-suggestion',
textColor: 'text-[var(--suggestion-text)]',
bgColor: 'bg-[var(--suggestion-bg)]',
borderColor: 'border-[var(--suggestion-border)]',
glowClass: '',
},
} as const;
// ---- Default Prompt (user-editable portion) ----
export const DEFAULT_PROMPT = `You are an emergency medicine clinical safety reviewer analyzing a real patient's emergency department documentation. Your ONLY task is to identify CRITICAL patient safety errors β€” the kind that could cause direct harm if missed.
FOCUS EXCLUSIVELY on these error types:
1. CONTRAINDICATED_MEDICATION β€” A prescribed drug that is dangerous given the patient's diagnosis, allergies, or concurrent medications (e.g., Amiodarone in WPW, penicillin with documented allergy, NSAID with active GI bleeding).
2. DANGEROUS_DOSAGE β€” Medication dose significantly outside the therapeutic range β€” 2x or more above maximum, clearly subtherapeutic, or wrong frequency that changes total daily dose dangerously (e.g., Metoprolol 500mg instead of 50mg).
3. CLINICAL_SCORE_ERROR β€” Risk stratification scores (CHAβ‚‚DSβ‚‚-VASc, NIHSS, Wells, HEART, GCS, etc.) are miscalculated, leading to a wrong treatment decision.
4. MISSING_CRITICAL_TREATMENT β€” A life-saving medication or intervention is clearly indicated by the diagnosis but completely absent from the treatment plan.
5. TREATMENT_LOGIC_FAILURE β€” The treatment plan directly contradicts the diagnosis or clinical findings (e.g., patient discharged when admission is clearly required).
6. MISSING_CRITICAL_WORKUP β€” Diagnostic tests urgently indicated by the clinical presentation are not ordered (e.g., no troponin in chest pain, no CT in suspected PE/stroke).
STRICT RULES:
- Report AT MOST 3 errors, strictly prioritized by patient safety impact.
- Only report errors you are β‰₯80% confident about.
- Do NOT report: style preferences, minor documentation gaps, formatting issues, or speculative concerns.
- If the clinical note has no critical safety errors, return an empty errors array.
- Less is more β€” a false alarm wastes physician time and erodes trust.`;
export const STRICT_PROMPT = `You are a rigorous clinical safety auditor. Analyze emergency department documentation with the highest standards. ONLY flag issues that represent genuine, immediate patient safety threats.
FOCUS EXCLUSIVELY on these error types:
1. CONTRAINDICATED_MEDICATION β€” Only flag drugs with clear, documented contraindications (allergy, organ failure, dangerous interaction).
2. DANGEROUS_DOSAGE β€” Only flag doses that are β‰₯2x above maximum or clearly subtherapeutic for a critical condition.
3. CLINICAL_SCORE_ERROR β€” Only flag score miscalculations that directly change the treatment decision.
4. MISSING_CRITICAL_TREATMENT β€” Only flag omissions of life-saving interventions that are standard of care.
5. TREATMENT_LOGIC_FAILURE β€” Only flag treatment plans that directly contradict the diagnosis.
6. MISSING_CRITICAL_WORKUP β€” Only flag urgently indicated tests that are completely absent.
ULTRA-STRICT RULES:
- Report AT MOST 2 errors, only the most critical.
- Only report errors you are β‰₯90% confident about.
- When in doubt, do NOT flag it. Zero false positives is the goal.
- If no clear safety errors exist, return an empty errors array.`;
export const VERBOSE_PROMPT = `You are a comprehensive clinical documentation reviewer for emergency department notes. Perform a thorough analysis identifying all potential patient safety issues.
Review these error categories in detail:
1. CONTRAINDICATED_MEDICATION β€” Check all prescribed drugs against documented allergies, diagnoses, organ function, and concurrent medications for dangerous interactions or contraindications.
2. DANGEROUS_DOSAGE β€” Verify all medication dosages against standard therapeutic ranges, weight-based dosing, renal/hepatic adjustments, and frequency schedules.
3. CLINICAL_SCORE_ERROR β€” Recalculate all risk stratification scores (CHAβ‚‚DSβ‚‚-VASc, NIHSS, Wells, HEART, GCS, CURB-65, etc.) and verify they match the documented values and resulting treatment decisions.
4. MISSING_CRITICAL_TREATMENT β€” Cross-reference the diagnosis with standard treatment protocols to identify any omitted life-saving medications or interventions.
5. TREATMENT_LOGIC_FAILURE β€” Analyze the treatment plan for internal contradictions with the diagnosis, clinical findings, and documented assessments.
6. MISSING_CRITICAL_WORKUP β€” Review the clinical presentation against standard diagnostic algorithms to identify any urgently indicated tests not ordered.
RULES:
- Report up to 3 errors, prioritized by patient safety impact.
- Only report errors you are β‰₯80% confident about.
- Provide detailed explanations with specific clinical reasoning for each finding.
- Include specific recommendations for corrective action.`;
// ---- MedGemma Backend Prompt (read-only display in frontend) ----
export const MEDGEMMA_BACKEND_PROMPT = `You are an emergency medicine clinical safety reviewer analyzing a real patient's emergency department documentation. Your ONLY task is to identify CRITICAL patient safety errors β€” the kind that could cause direct harm if missed.
FOCUS EXCLUSIVELY on these error types:
1. CONTRAINDICATED_MEDICATION
A prescribed drug that is dangerous given the patient's diagnosis, allergies, or concurrent medications (e.g., Amiodarone in WPW, penicillin with documented allergy, NSAID with active GI bleeding).
2. DANGEROUS_DOSAGE
Medication dose significantly outside the therapeutic range β€” 2x or more above maximum, clearly subtherapeutic, or wrong frequency that changes total daily dose dangerously (e.g., Metoprolol 500mg instead of 50mg, Furosemide 1x1 when 3x1 needed).
3. CLINICAL_SCORE_ERROR
Risk stratification scores (CHAβ‚‚DSβ‚‚-VASc, NIHSS, Wells, HEART, GCS, etc.) are miscalculated, leading to a wrong treatment decision (e.g., CHAβ‚‚DSβ‚‚-VASc scored 1 when it should be 2 β†’ patient denied anticoagulation).
4. MISSING_CRITICAL_TREATMENT
A life-saving medication or intervention is clearly indicated by the diagnosis but completely absent from the treatment plan (e.g., no aspirin/antiplatelet in STEMI, no anticoagulation for high-risk AF, no IV fluids in severe dehydration).
5. TREATMENT_LOGIC_FAILURE
The treatment plan directly contradicts the diagnosis or clinical findings (e.g., patient discharged when admission is clearly required, antibiotics stopped too early in active sepsis, no reperfusion in acute MI).
6. MISSING_CRITICAL_WORKUP
Diagnostic tests urgently indicated by the clinical presentation are not ordered (e.g., no troponin in chest pain, no CT in suspected PE/stroke, no imaging for acute abdomen, no blood cultures in sepsis).
STRICT RULES:
- Report AT MOST 3 errors, strictly prioritized by patient safety impact.
- Only report errors you are β‰₯80% confident about.
- Do NOT report: style preferences, minor documentation gaps, formatting issues, findings that are "could be improved" but not directly dangerous, or speculative concerns.
- If the clinical note has no critical safety errors, return an empty errors array.
- Less is more β€” a false alarm wastes physician time and erodes trust.
Output: Valid JSON with "errors" array and "summary" string. Each error has: type, severity, quote, problem, recommendation, confidence.`;
// ---- Built-in Prompt Presets ----
export const BUILT_IN_PRESETS: PromptPreset[] = [
{
id: 'default',
name: 'Default',
prompt: DEFAULT_PROMPT,
isBuiltIn: true,
},
{
id: 'strict',
name: 'Strict',
prompt: STRICT_PROMPT,
isBuiltIn: true,
},
{
id: 'verbose',
name: 'Verbose',
prompt: VERBOSE_PROMPT,
isBuiltIn: true,
},
];
// ---- Default Prompt Settings ----
export const DEFAULT_PROMPT_SETTINGS: PromptSettings = {
activePresetId: 'default',
customPrompt: DEFAULT_PROMPT,
savedPresets: [],
};
// ---- Default Settings ----
export const DEFAULT_SETTINGS: Settings = {
provider: 'local',
selectedModel: '',
temperature: 0.1,
confidenceThreshold: 0.7,
enabledSeverities: ['critical', 'warning', 'suggestion'] as Severity[],
enabledCategories: ['medication', 'measurement', 'logical', 'missing', 'terminology'] as ErrorCategory[],
promptSettings: DEFAULT_PROMPT_SETTINGS,
};
// ---- Format Confidence ----
export function formatConfidence(confidence: number): string {
return `${Math.round(confidence * 100)}%`;
}
// ---- Format Duration ----
export function formatDuration(ms: number): string {
if (ms < 1000) {
return `${ms}ms`;
}
const seconds = ms / 1000;
if (seconds < 60) {
return `${seconds.toFixed(1)}s`;
}
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.round(seconds % 60);
return `${minutes}m ${remainingSeconds}s`;
}
// ---- Truncate Text ----
export function truncateText(text: string, maxLength: number): string {
if (text.length <= maxLength) return text;
return text.slice(0, maxLength - 3) + '...';
}
// ---- Generate Unique ID ----
export function generateId(): string {
return Math.random().toString(36).substring(2, 11);
}
// ---- Debounce ----
export function debounce<T extends (...args: Parameters<T>) => ReturnType<T>>(
fn: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
// ---- Local Storage Helpers ----
export function getFromStorage<T>(key: string, defaultValue: T): T {
if (typeof window === 'undefined') return defaultValue;
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch {
return defaultValue;
}
}
export function setToStorage<T>(key: string, value: T): void {
if (typeof window === 'undefined') return;
try {
localStorage.setItem(key, JSON.stringify(value));
} catch {
// Storage might be full or disabled
}
}