prabalGaur commited on
Commit
53b08ad
Β·
verified Β·
1 Parent(s): d35b1e6

Upload community_contributions/codypharm/app.py with huggingface_hub

Browse files
community_contributions/codypharm/app.py ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import asyncio
3
+ import json
4
+ import logging
5
+ from typing import List, Tuple
6
+ from concurrent.futures import ThreadPoolExecutor
7
+
8
+ logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
9
+
10
+ from pharma_agents import (
11
+ triage_agent,
12
+ interaction_agent,
13
+ allergy_agent,
14
+ dosage_agent,
15
+ contraindication_agent,
16
+ verdict_agent,
17
+ )
18
+ from schemas import Drug, PatientProfile, PrescriptionInput, Finding, AgentReport, FinalVerdict
19
+
20
+ # Thread pool for running blocking agent.run() calls concurrently
21
+ _executor = ThreadPoolExecutor(max_workers=4)
22
+
23
+
24
+ # ── Formatting functions ───────────────────────────────────────────────────────────────
25
+
26
+ def format_finding(f: Finding) -> str:
27
+ icons = {"SAFE": "βœ…", "WARNING": "⚠️", "CRITICAL": "❌", "ERROR": "🚫"}
28
+ icon = icons.get(f.severity.upper(), "❓")
29
+ return f"{icon} **{f.severity}** \n{f.message.strip()}"
30
+
31
+
32
+ def format_agent_report(r: AgentReport) -> str:
33
+ icons = {"GREEN": "🟒", "YELLOW": "🟑", "RED": "πŸ”΄"}
34
+ colors = {"GREEN": "green", "YELLOW": "#d97706", "RED": "crimson"}
35
+ icon = icons.get(r.status, "βšͺ")
36
+ color = colors.get(r.status, "gray")
37
+
38
+ lines = [f"### {icon} {r.agent_name} β€” **{r.status}**"]
39
+
40
+ if not r.findings:
41
+ lines.append("β†’ No issues detected.")
42
+ else:
43
+ lines.extend(format_finding(f) for f in r.findings)
44
+
45
+ return "\n".join(lines)
46
+
47
+
48
+ def format_verdict(v: FinalVerdict) -> str:
49
+ icons = {
50
+ "GREEN": "βœ… SAFE TO DISPENSE",
51
+ "YELLOW": "⚠️ CAUTION",
52
+ "RED": "🚫 DO NOT DISPENSE"
53
+ }
54
+ colors = {"GREEN": "green", "YELLOW": "#d97706", "RED": "crimson"}
55
+
56
+ icon = icons.get(v.status, "❓")
57
+ color = colors.get(v.status, "gray")
58
+
59
+ lines = [
60
+ f"# {icon}",
61
+ f"**Status:** <span style='color:{color}; font-weight:bold;'>{v.status}</span>\n",
62
+ f"**Summary** \n{v.summary.strip()}"
63
+ ]
64
+
65
+ if v.required_actions:
66
+ lines.append("\n**Required Actions**")
67
+ lines.extend(f"- {action.strip()}" for action in v.required_actions)
68
+
69
+ return "\n".join(lines)
70
+
71
+
72
+ def format_complete_output(reports: List[AgentReport], verdict: FinalVerdict) -> Tuple[str, str, str]:
73
+ verdict_md = format_verdict(verdict)
74
+
75
+ reports_md = ["# Agent Reports\n"]
76
+ if reports:
77
+ reports_md.extend(format_agent_report(r) for r in reports)
78
+ else:
79
+ reports_md.append("No agent reports available.")
80
+
81
+ raw_json = json.dumps({
82
+ "verdict": verdict.model_dump() if hasattr(verdict, "model_dump") else vars(verdict),
83
+ "reports": [r.model_dump() if hasattr(r, "model_dump") else vars(r) for r in reports]
84
+ }, indent=2)
85
+
86
+ return verdict_md, "\n".join(reports_md), raw_json
87
+
88
+
89
+ # ── Core analysis logic ──────────────────────────────────────────────────────────────
90
+
91
+ def safe_json(obj) -> str:
92
+ if hasattr(obj, "model_dump_json"):
93
+ return obj.model_dump_json(indent=2)
94
+ if hasattr(obj, "dict"):
95
+ return json.dumps(obj.dict(), indent=2)
96
+ return json.dumps(obj, indent=2, default=str)
97
+
98
+
99
+ async def analyse_prescription(
100
+ patient: PatientProfile,
101
+ drugs: List[Drug]
102
+ ) -> Tuple[str, str, str]:
103
+ input_data = PrescriptionInput(patient=patient, drugs=drugs)
104
+
105
+ # Triage: extract structured data from raw input
106
+ triage_output = triage_agent.run(input_data.model_dump_json())
107
+ context_str = safe_json(triage_output)
108
+
109
+ # Run 4 specialist agents concurrently via thread pool (they use blocking OpenAI calls)
110
+ loop = asyncio.get_event_loop()
111
+ results = await asyncio.gather(
112
+ loop.run_in_executor(_executor, interaction_agent.run, context_str),
113
+ loop.run_in_executor(_executor, allergy_agent.run, context_str),
114
+ loop.run_in_executor(_executor, dosage_agent.run, context_str),
115
+ loop.run_in_executor(_executor, contraindication_agent.run, context_str),
116
+ )
117
+
118
+ reports: List[AgentReport] = list(results)
119
+
120
+ # Verdict: synthesise all reports
121
+ verdict: FinalVerdict = verdict_agent.run(safe_json(reports))
122
+
123
+ return format_complete_output(reports, verdict)
124
+
125
+
126
+ # ── Input parsing & test data ─────────────────────────────────────────────────────────
127
+
128
+ async def run_analysis(
129
+ age: float | int | None,
130
+ weight: float | None,
131
+ allergies_raw: str,
132
+ conditions_raw: str,
133
+ drugs_raw: str
134
+ ) -> Tuple[str, str, str]:
135
+ try:
136
+ age = int(age) if age is not None else 40
137
+ weight = float(weight) if weight is not None else 70.0
138
+
139
+ allergies = [a.strip() for a in allergies_raw.split(",") if a.strip()]
140
+ conditions = [c.strip() for c in conditions_raw.split(",") if c.strip()]
141
+
142
+ drug_lines = [line.strip() for line in drugs_raw.split("\n") if line.strip()]
143
+ drugs = []
144
+
145
+ for line in drug_lines:
146
+ parts = line.split(maxsplit=2)
147
+ if len(parts) < 2: continue
148
+ drugs.append(Drug(
149
+ name=parts[0].strip(),
150
+ dosage=parts[1].strip(),
151
+ frequency=parts[2].strip() if len(parts) > 2 else "β€”"
152
+ ))
153
+
154
+ if not drugs:
155
+ err = "**Error**: Please enter at least one medication."
156
+ return err, err, err
157
+
158
+ patient = PatientProfile(
159
+ age=age,
160
+ weight_kg=weight,
161
+ allergies=allergies,
162
+ conditions=conditions
163
+ )
164
+
165
+ return await analyse_prescription(patient, drugs)
166
+
167
+ except Exception as e:
168
+ import traceback
169
+ tb = traceback.format_exc()
170
+ err_msg = f"**Processing error**\n\n{str(e)}\n\n```python\n{tb[-800:]}```"
171
+ return err_msg, err_msg, err_msg
172
+
173
+
174
+ def load_test_case_1():
175
+ return (
176
+ 42,
177
+ 68.0,
178
+ "penicillin, shellfish",
179
+ "hypertension, type 2 diabetes",
180
+ "Amoxicillin 500mg\nMetformin 850mg\nRamipril 10mg"
181
+ )
182
+
183
+
184
+ def load_test_case_2():
185
+ return (
186
+ 78,
187
+ 59.5,
188
+ "aspirin, codeine",
189
+ "atrial fibrillation, CKD stage 3, history of GI bleed",
190
+ "Apixaban 5mg 12-hourly\nParacetamol 1g QID prn\nIbuprofen 400mg TDS"
191
+ )
192
+
193
+
194
+ def load_test_case_3():
195
+ return (
196
+ 31,
197
+ 88.0,
198
+ "",
199
+ "epilepsy, depression",
200
+ "Carbamazepine 400mg BD\nSertraline 100mg daily\nParacetamol 1g QID prn"
201
+ )
202
+
203
+
204
+ def clear_form():
205
+ return (
206
+ 48, "", "", "", "",
207
+ "Waiting for prescription data...",
208
+ "Waiting for prescription data...",
209
+ "Waiting for prescription data...",
210
+ )
211
+
212
+ # ── Gradio UI ──────────────────────────────────────────────────────────────────────────
213
+
214
+ with gr.Blocks() as demo:
215
+
216
+ gr.Markdown("""
217
+ # Prescription Safety Review
218
+
219
+ Enter patient details and medications β†’ multi-agent safety check.
220
+ """)
221
+
222
+ with gr.Row():
223
+ with gr.Column(scale=1):
224
+ gr.Markdown("### Patient")
225
+ age_in = gr.Number(label="Age (years)", value=48, minimum=0, maximum=120)
226
+ weight_in = gr.Number(label="Weight (kg)", value=72.5, minimum=10, maximum=300)
227
+ allergies_in = gr.Textbox(label="Allergies (comma separated)", lines=2,
228
+ placeholder="penicillin, sulfa, latex")
229
+ conditions_in = gr.Textbox(label="Medical conditions", lines=3,
230
+ placeholder="type 2 diabetes, hypertension, CKD stage 3")
231
+
232
+ with gr.Column(scale=1):
233
+ gr.Markdown("### Medications")
234
+ drugs_in = gr.Textbox(
235
+ label="One medication per line (name dosage frequency)",
236
+ lines=9,
237
+ max_lines=14,
238
+ placeholder="Amoxicillin 500mg 8-hourly\nWarfarin 5mg daily\n..."
239
+ )
240
+
241
+ with gr.Row():
242
+ gr.Markdown("**Quick test cases:**")
243
+ btn_test1 = gr.Button("Case 1 – Middle-aged, common drugs", size="sm")
244
+ btn_test2 = gr.Button("Case 2 – Elderly + high risk", size="sm")
245
+ btn_test3 = gr.Button("Case 3 – Young adult + psych drugs", size="sm")
246
+
247
+ with gr.Row():
248
+ btn_analyze = gr.Button("Run Safety Check", variant="primary", scale=2)
249
+ btn_clear = gr.Button("Clear All", variant="secondary")
250
+
251
+ with gr.Tabs() as tabs:
252
+ with gr.TabItem("Final Verdict", elem_id="verdict-tab"):
253
+ result_verdict = gr.Markdown(
254
+ value="Waiting for input...",
255
+ label="Final Recommendation",
256
+ line_breaks=True,
257
+ height=520
258
+ )
259
+
260
+ with gr.TabItem("Agent Reports"):
261
+ result_reports = gr.Markdown(
262
+ value="Waiting for analysis...",
263
+ line_breaks=True,
264
+ height=520
265
+ )
266
+
267
+ with gr.TabItem("Raw JSON"):
268
+ result_json = gr.Code(
269
+ value="{}",
270
+ language="json",
271
+ lines=24,
272
+ interactive=False
273
+ )
274
+
275
+ # ── Event handlers ────────────────────────────────────────────────────────────────
276
+
277
+ btn_analyze.click(
278
+ fn=run_analysis,
279
+ inputs=[age_in, weight_in, allergies_in, conditions_in, drugs_in],
280
+ outputs=[result_verdict, result_reports, result_json]
281
+ )
282
+
283
+ btn_test1.click(
284
+ fn=load_test_case_1,
285
+ outputs=[age_in, weight_in, allergies_in, conditions_in, drugs_in]
286
+ )
287
+
288
+ btn_test2.click(
289
+ fn=load_test_case_2,
290
+ outputs=[age_in, weight_in, allergies_in, conditions_in, drugs_in]
291
+ )
292
+
293
+ btn_test3.click(
294
+ fn=load_test_case_3,
295
+ outputs=[age_in, weight_in, allergies_in, conditions_in, drugs_in]
296
+ )
297
+
298
+ btn_clear.click(
299
+ fn=clear_form,
300
+ outputs=[age_in, weight_in, allergies_in, conditions_in, drugs_in,
301
+ result_verdict, result_reports, result_json]
302
+ )
303
+
304
+ if __name__ == "__main__":
305
+ demo.launch(
306
+ theme=gr.themes.Soft(
307
+ primary_hue="indigo",
308
+ secondary_hue="slate",
309
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"]
310
+ )
311
+ )