Spaces:
Sleeping
Sleeping
Hackathon Builder commited on
Commit Β·
2bb04fc
1
Parent(s): 6ed6bd7
Fix: React Error #31 by enforcing strict AI schemas and defensive rendering
Browse files
backend/main.py
CHANGED
|
@@ -127,57 +127,58 @@ def _patient_card(p: Dict) -> Dict:
|
|
| 127 |
# ββ Agents ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 128 |
|
| 129 |
def agent_triage(record: Dict, override_key: str = None) -> Dict:
|
| 130 |
-
prompt = f"""You are an ER Danger Pattern Specialist.
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
r = _gemini(prompt, override_key=override_key)
|
| 136 |
if r: return r
|
| 137 |
|
| 138 |
-
# Fallback
|
| 139 |
v = record.get("vitals", {})
|
| 140 |
spo2 = float(v.get("SpO2", 100)) if v.get("SpO2") else 100
|
| 141 |
hr = float(v.get("Heart Rate", 80)) if v.get("Heart Rate") else 80
|
| 142 |
sev = 1 if (spo2 < 90 or hr > 130 or hr < 45) else 2 if (spo2 < 93 or hr > 110) else 3
|
| 143 |
labels = {1: "IMMEDIATE", 2: "EMERGENT", 3: "URGENT"}
|
| 144 |
-
|
| 145 |
return {
|
| 146 |
"critical_risks": [], "severity": sev, "severity_label": labels[sev],
|
| 147 |
-
"chief_complaint": "Stabilization (LLM
|
| 148 |
-
"agent_reasoning": ["Connectivity Error: Gemini brain did not respond.
|
| 149 |
}
|
| 150 |
|
| 151 |
def agent_contraindication(record: Dict, proposed_treatment: str = "", triage: Dict = None, override_key: str = None) -> Dict:
|
| 152 |
context = f"TREATMENT: {proposed_treatment}" if proposed_treatment else "SAFETY SCAN"
|
| 153 |
-
prompt = f"ED Safety Guardian. PATIENT: {json.dumps(record)}\nTRIAGE: {json.dumps(triage)}\n{context}\nReturn JSON: {{ 'treatment_verdict': '
|
| 154 |
-
|
| 155 |
r = _gemini(prompt, override_key=override_key)
|
| 156 |
-
return r or {
|
| 157 |
-
"treatment_verdict": "NOT_EVALUATED",
|
| 158 |
-
"verdict_reason": "AI brain offline. Manual review required.",
|
| 159 |
-
"agent_reasoning": ["Safety Agent failed to connect to Gemini."]
|
| 160 |
-
}
|
| 161 |
|
| 162 |
def agent_labs(record: Dict, triage: Dict = None, override_key: str = None) -> Dict:
|
| 163 |
-
prompt = f"Pathologist. PATIENT: {json.dumps(record)}\
|
| 164 |
r = _gemini(prompt, override_key=override_key)
|
| 165 |
-
return r or {"critical_labs": [], "agent_reasoning": ["
|
| 166 |
|
| 167 |
def agent_differentials(record: Dict, triage: Dict = None, labs: Dict = None, override_key: str = None) -> Dict:
|
| 168 |
-
prompt = f"Chief Resident. PATIENT: {json.dumps(record)}\
|
| 169 |
r = _gemini(prompt, override_key=override_key)
|
| 170 |
-
return r or {"differentials": [], "agent_reasoning": ["
|
| 171 |
|
| 172 |
def agent_action_protocol(record: Dict, triage: Dict, contra: Dict, labs: Dict, diffs: Dict, override_key: str = None) -> Dict:
|
| 173 |
-
prompt = f"ED Attending.
|
| 174 |
r = _gemini(prompt, override_key=override_key)
|
| 175 |
-
return r or {
|
| 176 |
-
"primary_directive": "Stabilize patient immediately (LLM Offline)",
|
| 177 |
-
"action_mapping": [],
|
| 178 |
-
"clinical_summary": "System operating in safety-fallback mode due to connectivity issues.",
|
| 179 |
-
"agent_reasoning": ["Attending Agent failed to reach Gemini brain."]
|
| 180 |
-
}
|
| 181 |
|
| 182 |
# ββ API Endpoints βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 183 |
|
|
|
|
| 127 |
# ββ Agents ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 128 |
|
| 129 |
def agent_triage(record: Dict, override_key: str = None) -> Dict:
|
| 130 |
+
prompt = f"""You are an ER Danger Pattern Specialist.
|
| 131 |
+
Analyze PATIENT: {json.dumps(record)}
|
| 132 |
+
Return ONLY JSON:
|
| 133 |
+
{{
|
| 134 |
+
"critical_risks": [
|
| 135 |
+
{{
|
| 136 |
+
"finding": "e.g. Glucose 48",
|
| 137 |
+
"danger": "e.g. Brain starvation",
|
| 138 |
+
"risk": "e.g. Seizure/Coma",
|
| 139 |
+
"urgency": "IMMEDIATE"
|
| 140 |
+
}}
|
| 141 |
+
],
|
| 142 |
+
"severity": 1,
|
| 143 |
+
"severity_label": "IMMEDIATE",
|
| 144 |
+
"chief_complaint": "Summary",
|
| 145 |
+
"agent_reasoning": ["Step 1", "Step 2"]
|
| 146 |
+
}}"""
|
| 147 |
r = _gemini(prompt, override_key=override_key)
|
| 148 |
if r: return r
|
| 149 |
|
| 150 |
+
# Fallback
|
| 151 |
v = record.get("vitals", {})
|
| 152 |
spo2 = float(v.get("SpO2", 100)) if v.get("SpO2") else 100
|
| 153 |
hr = float(v.get("Heart Rate", 80)) if v.get("Heart Rate") else 80
|
| 154 |
sev = 1 if (spo2 < 90 or hr > 130 or hr < 45) else 2 if (spo2 < 93 or hr > 110) else 3
|
| 155 |
labels = {1: "IMMEDIATE", 2: "EMERGENT", 3: "URGENT"}
|
|
|
|
| 156 |
return {
|
| 157 |
"critical_risks": [], "severity": sev, "severity_label": labels[sev],
|
| 158 |
+
"chief_complaint": "Stabilization (LLM Offline)",
|
| 159 |
+
"agent_reasoning": ["Connectivity Error: Gemini brain did not respond."]
|
| 160 |
}
|
| 161 |
|
| 162 |
def agent_contraindication(record: Dict, proposed_treatment: str = "", triage: Dict = None, override_key: str = None) -> Dict:
|
| 163 |
context = f"TREATMENT: {proposed_treatment}" if proposed_treatment else "SAFETY SCAN"
|
| 164 |
+
prompt = f"ED Safety Guardian. PATIENT: {json.dumps(record)}\nTRIAGE: {json.dumps(triage)}\n{context}\nReturn ONLY JSON: {{ 'treatment_verdict': 'SAFE|CAUTION|CONTRAINDICATED', 'verdict_reason': 'string', 'agent_reasoning': ['string'] }}"
|
|
|
|
| 165 |
r = _gemini(prompt, override_key=override_key)
|
| 166 |
+
return r or {"treatment_verdict": "NOT_EVALUATED", "verdict_reason": "AI offline.", "agent_reasoning": ["Failed to connect."]}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
def agent_labs(record: Dict, triage: Dict = None, override_key: str = None) -> Dict:
|
| 169 |
+
prompt = f"Pathologist. PATIENT: {json.dumps(record)}\nReturn ONLY JSON: {{ 'critical_labs': [{{'name':'string','value':'string','significance':'string'}}], 'agent_reasoning': ['string'] }}"
|
| 170 |
r = _gemini(prompt, override_key=override_key)
|
| 171 |
+
return r or {"critical_labs": [], "agent_reasoning": ["Failed to connect."]}
|
| 172 |
|
| 173 |
def agent_differentials(record: Dict, triage: Dict = None, labs: Dict = None, override_key: str = None) -> Dict:
|
| 174 |
+
prompt = f"Chief Resident. PATIENT: {json.dumps(record)}\nReturn ONLY JSON: {{ 'differentials': [{{'diagnosis':'string','likelihood':'HIGH','supporting_evidence':['string']}}], 'agent_reasoning': ['string'] }}"
|
| 175 |
r = _gemini(prompt, override_key=override_key)
|
| 176 |
+
return r or {"differentials": [], "agent_reasoning": ["Failed to connect."]}
|
| 177 |
|
| 178 |
def agent_action_protocol(record: Dict, triage: Dict, contra: Dict, labs: Dict, diffs: Dict, override_key: str = None) -> Dict:
|
| 179 |
+
prompt = f"ED Attending. Return ONLY JSON: {{ 'primary_directive': 'string', 'action_mapping': [{{'condition':'string','action':'string'}}], 'clinical_summary': 'string', 'agent_reasoning': ['string'] }}"
|
| 180 |
r = _gemini(prompt, override_key=override_key)
|
| 181 |
+
return r or {"primary_directive": "Stabilize patient.", "action_mapping": [], "clinical_summary": "Offline.", "agent_reasoning": ["Failed to connect."]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
# ββ API Endpoints βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 184 |
|
frontend/dist/assets/{index-C0LhoLqc.js β index-DiPQ1rSL.js}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/dist/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
| 7 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 8 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 9 |
<title>CodeBlue Context</title>
|
| 10 |
-
<script type="module" crossorigin src="/assets/index-
|
| 11 |
<link rel="stylesheet" crossorigin href="/assets/index-DKXwe-v6.css">
|
| 12 |
</head>
|
| 13 |
<body>
|
|
|
|
| 7 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 8 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 9 |
<title>CodeBlue Context</title>
|
| 10 |
+
<script type="module" crossorigin src="/assets/index-DiPQ1rSL.js"></script>
|
| 11 |
<link rel="stylesheet" crossorigin href="/assets/index-DKXwe-v6.css">
|
| 12 |
</head>
|
| 13 |
<body>
|
frontend/src/BriefPanel.jsx
CHANGED
|
@@ -4,6 +4,13 @@ import { ShieldAlert, FlaskConical, Stethoscope, ListChecks, AlertTriangle, Cloc
|
|
| 4 |
|
| 5 |
const API_BASE = import.meta.env.VITE_API_BASE || '';
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
function Section({ title, icon, children, accent }) {
|
| 8 |
return (
|
| 9 |
<div className="fade-up" style={{
|
|
@@ -191,23 +198,23 @@ export function BriefPanel({ data, patient }) {
|
|
| 191 |
</div>
|
| 192 |
)}
|
| 193 |
|
| 194 |
-
{/* Immediate Critical Risks */}
|
| 195 |
{triage.critical_risks?.length > 0 && (
|
| 196 |
<SectionBlock title="Immediate Critical Risks" icon={<ShieldAlert size={14}/>} accent="var(--red-critical)">
|
| 197 |
<div className="pulse-critical" style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
| 198 |
{triage.critical_risks.map((r, i) => (
|
| 199 |
-
<div key={i} style={{
|
| 200 |
background: 'rgba(239, 68, 68, 0.08)', border: '1px solid rgba(239, 68, 68, 0.2)',
|
| 201 |
borderRadius: 10, padding: '10px 14px'
|
| 202 |
}}>
|
| 203 |
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
|
| 204 |
<AlertTriangle size={14} color="var(--red-critical)" />
|
| 205 |
<span style={{ fontWeight: 800, fontSize: 12, color: 'var(--red-critical)' }}>
|
| 206 |
-
{r.condition} ({r.evidence})
|
| 207 |
</span>
|
| 208 |
</div>
|
| 209 |
<div style={{ fontSize: 11, color: 'var(--text-secondary)', marginLeft: 22 }}>
|
| 210 |
-
β risk
|
| 211 |
</div>
|
| 212 |
</div>
|
| 213 |
))}
|
|
@@ -265,7 +272,7 @@ export function BriefPanel({ data, patient }) {
|
|
| 265 |
{collectiveReasoning.map((r, i) => (
|
| 266 |
<div key={i} style={{ fontSize: 10.5, color: 'var(--text-secondary)', display: 'flex', gap: 10 }}>
|
| 267 |
<span style={{ fontWeight: 800, color: 'var(--purple-ai)', minWidth: 60, textTransform: 'uppercase', fontSize: 9 }}>{r.agent}</span>
|
| 268 |
-
<span style={{ lineHeight: 1.4 }}>{r.text}</span>
|
| 269 |
</div>
|
| 270 |
))}
|
| 271 |
</div>
|
|
|
|
| 4 |
|
| 5 |
const API_BASE = import.meta.env.VITE_API_BASE || '';
|
| 6 |
|
| 7 |
+
const renderText = (val) => {
|
| 8 |
+
if (val === null || val === undefined) return '';
|
| 9 |
+
if (typeof val === 'string') return val;
|
| 10 |
+
if (typeof val === 'object') return JSON.stringify(val);
|
| 11 |
+
return String(val);
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
function Section({ title, icon, children, accent }) {
|
| 15 |
return (
|
| 16 |
<div className="fade-up" style={{
|
|
|
|
| 198 |
</div>
|
| 199 |
)}
|
| 200 |
|
| 201 |
+
{/* Immediate Critical Risks (Condition-First) */}
|
| 202 |
{triage.critical_risks?.length > 0 && (
|
| 203 |
<SectionBlock title="Immediate Critical Risks" icon={<ShieldAlert size={14}/>} accent="var(--red-critical)">
|
| 204 |
<div className="pulse-critical" style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
| 205 |
{triage.critical_risks.map((r, i) => (
|
| 206 |
+
<div key={`${r.condition}-${i}`} style={{
|
| 207 |
background: 'rgba(239, 68, 68, 0.08)', border: '1px solid rgba(239, 68, 68, 0.2)',
|
| 208 |
borderRadius: 10, padding: '10px 14px'
|
| 209 |
}}>
|
| 210 |
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
|
| 211 |
<AlertTriangle size={14} color="var(--red-critical)" />
|
| 212 |
<span style={{ fontWeight: 800, fontSize: 12, color: 'var(--red-critical)' }}>
|
| 213 |
+
{renderText(r.finding || r.condition)} {r.evidence && `(${renderText(r.evidence)})`}
|
| 214 |
</span>
|
| 215 |
</div>
|
| 216 |
<div style={{ fontSize: 11, color: 'var(--text-secondary)', marginLeft: 22 }}>
|
| 217 |
+
β {renderText(r.risk)} <span style={{ opacity: 0.6 }}>[{renderText(r.danger)}]</span>
|
| 218 |
</div>
|
| 219 |
</div>
|
| 220 |
))}
|
|
|
|
| 272 |
{collectiveReasoning.map((r, i) => (
|
| 273 |
<div key={i} style={{ fontSize: 10.5, color: 'var(--text-secondary)', display: 'flex', gap: 10 }}>
|
| 274 |
<span style={{ fontWeight: 800, color: 'var(--purple-ai)', minWidth: 60, textTransform: 'uppercase', fontSize: 9 }}>{r.agent}</span>
|
| 275 |
+
<span style={{ lineHeight: 1.4 }}>{renderText(r.text)}</span>
|
| 276 |
</div>
|
| 277 |
))}
|
| 278 |
</div>
|