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

Upload community_contributions/codypharm/pharma_agents.py with huggingface_hub

Browse files
community_contributions/codypharm/pharma_agents.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import asyncio
4
+ import logging
5
+ from typing import Optional, List
6
+ from dotenv import load_dotenv
7
+ from openai import OpenAI
8
+
9
+ from schemas import PrescriptionInput, AgentReport, Finding, FinalVerdict
10
+ from tools.pharmacy_tools import (
11
+ INTERACTION_TOOLS,
12
+ DOSAGE_TOOLS,
13
+ ALLERGY_TOOLS,
14
+ CONTRAINDICATION_TOOLS,
15
+ ALL_TOOLS,
16
+ handle_tool_calls,
17
+ )
18
+
19
+ load_dotenv(override=True)
20
+
21
+
22
+ # ============================================================================
23
+ # BASE AGENT β€” logging helper (mirrors week8/agents/agent.py)
24
+ # ============================================================================
25
+
26
+ class Agent:
27
+ """Lightweight base with coloured logging."""
28
+
29
+ # Terminal colours
30
+ GREEN = '\033[32m'
31
+ YELLOW = '\033[33m'
32
+ BLUE = '\033[34m'
33
+ MAGENTA = '\033[35m'
34
+ CYAN = '\033[36m'
35
+ RED = '\033[31m'
36
+ BG_BLACK = '\033[40m'
37
+ RESET = '\033[0m'
38
+
39
+ name: str = ""
40
+ color: str = '\033[37m'
41
+
42
+ def log(self, message: str):
43
+ color_code = self.BG_BLACK + self.color
44
+ logging.info(f"{color_code}[{self.name}] {message}{self.RESET}")
45
+
46
+
47
+ # ============================================================================
48
+ # TOOL-CALLING AGENT MIXIN
49
+ # ============================================================================
50
+
51
+ class ToolAgent(Agent):
52
+ """
53
+ Agent that follows the autonomous_planning_agent pattern:
54
+ while finish_reason == "tool_calls" β†’ dispatch β†’ loop.
55
+ Final response is parsed into a Pydantic model.
56
+ """
57
+
58
+ MODEL: str = "gpt-4o-mini"
59
+ SYSTEM_PROMPT: str = ""
60
+ tools: list = [] # JSON tool defs
61
+ output_type = None # Pydantic model for structured output
62
+ max_turns: int = 10 # safety cap
63
+
64
+ def __init__(self):
65
+ self.openai = OpenAI()
66
+
67
+ def run(self, user_input: str):
68
+ """
69
+ Execute the tool-calling loop and return a parsed Pydantic object.
70
+ Mirrors autonomous_planning_agent.plan().
71
+ """
72
+ self.log(f"Starting run")
73
+ messages = [
74
+ {"role": "system", "content": self.SYSTEM_PROMPT},
75
+ {"role": "user", "content": user_input},
76
+ ]
77
+
78
+ turns = 0
79
+ done = False
80
+ while not done and turns < self.max_turns:
81
+ turns += 1
82
+
83
+ # If we have tools, allow the model to call them; otherwise just parse
84
+ if self.tools:
85
+ response = self.openai.chat.completions.create(
86
+ model=self.MODEL,
87
+ messages=messages,
88
+ tools=self.tools,
89
+ temperature=0.5,
90
+ )
91
+ else:
92
+ # No tools β€” single-shot structured output
93
+ response = self.openai.chat.completions.parse(
94
+ model=self.MODEL,
95
+ messages=messages,
96
+ response_format=self.output_type,
97
+ temperature=0.5,
98
+ )
99
+ parsed = response.choices[0].message.parsed
100
+ self.log(f"Completed (structured output, 1 turn)")
101
+ return parsed
102
+
103
+ choice = response.choices[0]
104
+
105
+ if choice.finish_reason == "tool_calls":
106
+ # Dispatch tool calls and feed results back
107
+ tool_results = handle_tool_calls(choice.message)
108
+ messages.append(choice.message)
109
+ messages.extend(tool_results)
110
+ self.log(f"Turn {turns}: called {len(choice.message.tool_calls)} tool(s)")
111
+ else:
112
+ # Model is done calling tools β€” now get structured output
113
+ done = True
114
+
115
+ # Final structured parse with accumulated context
116
+ self.log(f"Generating final report after {turns} turn(s)")
117
+
118
+ # Append the last assistant message if it had content
119
+ if not done:
120
+ self.log("Hit max turns β€” forcing final output")
121
+
122
+ # Add the assistant's last text reply to context, then do a structured parse
123
+ last_content = response.choices[0].message.content
124
+ if last_content:
125
+ messages.append({"role": "assistant", "content": last_content})
126
+
127
+ messages.append({
128
+ "role": "user",
129
+ "content": "Based on all the tool results above, provide your final structured report now."
130
+ })
131
+
132
+ final_response = self.openai.chat.completions.parse(
133
+ model=self.MODEL,
134
+ messages=messages,
135
+ response_format=self.output_type,
136
+ temperature=0.3,
137
+ )
138
+ result = final_response.choices[0].message.parsed
139
+ self.log(f"Completed with status: {getattr(result, 'status', 'N/A')}")
140
+ return result
141
+
142
+
143
+ # ============================================================================
144
+ # SPECIALIST AGENTS
145
+ # ============================================================================
146
+
147
+ class InteractionAgent(ToolAgent):
148
+ name = "InteractionChecker"
149
+ color = Agent.CYAN
150
+ MODEL = "gpt-4o"
151
+ tools = INTERACTION_TOOLS
152
+ output_type = AgentReport
153
+
154
+ SYSTEM_PROMPT = """You are a specialist in detecting possible drug-drug interactions.
155
+ You will be given patient and prescription data. Use your tools to check for interactions.
156
+
157
+ Available tools:
158
+ - check_drug_interaction: Check pairwise interaction between two drugs
159
+ - check_multi_drug_interactions: Scan all drug pairs at once (pass JSON array of drug names)
160
+ - check_duplicate_therapy: Detect duplicate medications
161
+ - check_therapeutic_duplication: Detect class-level duplication via ATC codes
162
+ - normalize_drug_name: Resolve brand/generic names via RxNorm
163
+
164
+ Call the tools you need, then provide your final AgentReport."""
165
+
166
+
167
+ class DosageAgent(ToolAgent):
168
+ name = "DosageChecker"
169
+ color = Agent.YELLOW
170
+ MODEL = "gpt-4o-mini"
171
+ tools = DOSAGE_TOOLS
172
+ output_type = AgentReport
173
+
174
+ SYSTEM_PROMPT = """You are a specialist in validating drug dosages.
175
+ 1. Calculate daily dose with calculate_daily_dose.
176
+ 2. If patient is a child (<18), use check_pediatric_dosing.
177
+ 3. If patient is elderly (65+), use check_geriatric_considerations.
178
+ 4. If renal impairment, use check_renal_dosing.
179
+ 5. If pregnant, use check_pregnancy_safety.
180
+
181
+ Call the relevant tools, then provide your final AgentReport."""
182
+
183
+
184
+ class AllergyAgent(ToolAgent):
185
+ name = "AllergyChecker"
186
+ color = Agent.RED
187
+ MODEL = "gpt-4o"
188
+ tools = ALLERGY_TOOLS
189
+ output_type = AgentReport
190
+
191
+ SYSTEM_PROMPT = """You are a specialist in detecting drug allergies and cross-sensitivity.
192
+ 1. Use check_drug_allergy to compare each drug against patient allergies (comma-separated).
193
+ 2. Use normalize_drug_name to resolve brand names to generic/ingredient level.
194
+ 3. Use get_drug_label_info for additional ingredient details if needed.
195
+
196
+ Call the relevant tools, then provide your final AgentReport."""
197
+
198
+
199
+ class ContraindicationAgent(ToolAgent):
200
+ name = "ContraindicationChecker"
201
+ color = Agent.MAGENTA
202
+ MODEL = "gpt-4o-mini"
203
+ tools = CONTRAINDICATION_TOOLS
204
+ output_type = AgentReport
205
+
206
+ SYSTEM_PROMPT = """You are a specialist in detecting drug contraindications.
207
+ You will be given drugs and patient conditions.
208
+
209
+ Available tools:
210
+ - check_contraindication: Check if a drug is contraindicated for a condition
211
+ - check_drug_recall: Check FDA enforcement database for active recalls
212
+ - get_controlled_substance_info: Check DEA schedule
213
+ - normalize_drug_name: Resolve brand/generic names
214
+ - get_drug_label_info: Get full FDA label
215
+
216
+ Call the relevant tools, then provide your final AgentReport."""
217
+
218
+
219
+ class TriageAgent(ToolAgent):
220
+ name = "TriageAgent"
221
+ color = Agent.GREEN
222
+ MODEL = "gpt-4o"
223
+ tools = [] # No tools β€” pure extraction
224
+ output_type = PrescriptionInput
225
+
226
+ SYSTEM_PROMPT = """You are a medical triage expert.
227
+ Convert the natural language prescription data into a structured object.
228
+ Ensure you extract:
229
+ - Patient Age and Weight (essential for dosage)
230
+ - Patient Allergies and Conditions
231
+ - List of Drugs with Dosage and Frequency
232
+ If any information is missing or ambiguous, infer from context or leave minimal defaults."""
233
+
234
+
235
+ class VerdictAgent(ToolAgent):
236
+ name = "FinalVerdictAgent"
237
+ color = Agent.BLUE
238
+ MODEL = "gpt-4o"
239
+ tools = [] # No tools β€” synthesises reports
240
+ output_type = FinalVerdict
241
+
242
+ SYSTEM_PROMPT = """You are the Chief Pharmacist.
243
+ You will receive reports from the Interaction, Allergy, Dosage and Contraindication agents.
244
+ Synthesize them into a single final decision.
245
+
246
+ RULES:
247
+ 1. If ANY agent flagged RED or CRITICAL β†’ status MUST be RED (Do Not Dispense).
248
+ 2. If YELLOW/WARNING issues β†’ status YELLOW (Dispense with Counseling).
249
+ 3. If all GREEN β†’ status GREEN (Dispense).
250
+ 4. Provide a clear, concise summary and specific actions."""
251
+
252
+
253
+ # ============================================================================
254
+ # CONVENIENCE INSTANCES β€” so app.py can import directly
255
+ # ============================================================================
256
+
257
+ triage_agent = TriageAgent()
258
+ interaction_agent = InteractionAgent()
259
+ allergy_agent = AllergyAgent()
260
+ dosage_agent = DosageAgent()
261
+ contraindication_agent = ContraindicationAgent()
262
+ verdict_agent = VerdictAgent()
263
+