Spaces:
Sleeping
Sleeping
File size: 18,647 Bytes
f368eec |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
"""Drug Interaction Chatbot for natural language interaction with the drug interaction system."""
import re
import uuid
import io
import matplotlib.pyplot as plt
import networkx as nx
from typing import Dict, List, Tuple, Optional, Any
from .biomedical_llm import BiomedicalLLM
from .drug_interaction_db import DrugInteractionDatabase
from .ddi_processor import DDIProcessor
class DrugInteractionChatbot:
"""Chatbot interface for drug interaction analysis system."""
def __init__(self, model_name="stanford-crfm/BioMedLM"):
"""Initialize the Drug Interaction Chatbot with Biomedical LLM"""
self.db = DrugInteractionDatabase()
self.bio_llm = BiomedicalLLM(model_name)
self.processor = DDIProcessor(self.db, self.bio_llm)
def process_message(self, message):
"""Process a user message and provide an appropriate response"""
# Check if this is a clinical notes analysis request
if any(term in message.lower() for term in ["clinical note", "patient note", "extract from", "analyze note", "medical record"]):
# Extract the clinical note part
note_pattern = r"(?:clinical note|patient note|medical record)[s]?:?\s*([\s\S]+)$"
note_match = re.search(note_pattern, message, re.IGNORECASE)
if note_match:
clinical_text = note_match.group(1).strip()
extracted_info = self.processor.extract_drugs_from_clinical_notes(clinical_text)
# Format the response
response = "π **Analysis of Clinical Notes**\n\n"
# Add medications
if extracted_info["medications"]:
response += "**Medications Identified:**\n"
for med in extracted_info["medications"]:
name = med.get("name", "Unknown")
dosage = med.get("dosage", "Not specified")
frequency = med.get("frequency", "Not specified")
if dosage != "Not specified" or frequency != "Not specified":
response += f"- {name}: {dosage} {frequency}\n"
else:
response += f"- {name}\n"
response += "\n"
else:
response += "No medications were identified in the clinical notes.\n\n"
# Add potential interactions
if extracted_info["potential_interactions"]:
response += "**Potential Interactions:**\n"
for interaction in extracted_info["potential_interactions"]:
drug1 = interaction.get("drug1", "Unknown")
drug2 = interaction.get("drug2", "Unknown")
concern = interaction.get("concern", "Potential interaction")
response += f"- {drug1} + {drug2}: {concern}\n"
response += "\n"
else:
# Try to identify interactions from the medications list
meds = [med.get("name") for med in extracted_info["medications"] if med.get("name")]
potential_interactions = []
# Check all pairs of medications
for i in range(len(meds)):
for j in range(i+1, len(meds)):
interactions, _ = self.db.get_interactions(meds[i], meds[j])
if interactions:
for d1, d2, desc, severity, _ in interactions:
potential_interactions.append(f"- {meds[i]} + {meds[j]}: {desc} ({severity})")
if potential_interactions:
response += "**Potential Interactions:**\n"
response += "\n".join(potential_interactions) + "\n\n"
else:
response += "No potential interactions were identified among the medications.\n\n"
response += "Please consult with a healthcare professional for a comprehensive review of drug interactions and medical advice."
return response
# Check if user is asking for information about a specific drug
drug_info_pattern = r"(tell me about|information on|what is|info about|details on)\s+(.+?)(?:\?|$)"
drug_info_match = re.search(drug_info_pattern, message.lower())
if drug_info_match:
drug_name = drug_info_match.group(2).strip()
canonical = self.db.search_drug(drug_name)
# If not in database, use original name but still try to get info
if not canonical:
canonical = drug_name
# Get drug information from biomedical LLM
drug_info = self.processor.get_drug_information(canonical)
if drug_info:
# Format the response
response = f"π **Information about {drug_info['drug_name']}**\n\n"
if drug_info.get("drug_class") and drug_info["drug_class"] != "Information not available":
response += f"**Drug Class:** {drug_info['drug_class']}\n\n"
if drug_info.get("mechanism") and drug_info["mechanism"] != "Information not available":
response += f"**Mechanism of Action:** {drug_info['mechanism']}\n\n"
if drug_info.get("indications") and drug_info["indications"][0] != "Information not available":
response += "**Common Indications:**\n"
for indication in drug_info["indications"]:
response += f"- {indication}\n"
response += "\n"
if drug_info.get("side_effects") and drug_info["side_effects"][0] != "Information not available":
response += "**Common Side Effects:**\n"
for effect in drug_info["side_effects"]:
response += f"- {effect}\n"
response += "\n"
if drug_info.get("common_interactions") and drug_info["common_interactions"][0] != "Information not available":
response += "**Common Interactions:**\n"
for interaction in drug_info["common_interactions"]:
response += f"- {interaction}\n"
response += "\n"
if drug_info.get("contraindications") and drug_info["contraindications"][0] != "Information not available":
response += "**Contraindications:**\n"
for contraindication in drug_info["contraindications"]:
response += f"- {contraindication}\n"
response += "\n"
response += "This information is for educational purposes only. Always consult a healthcare professional for medical advice."
return response
else:
return f"I couldn't find detailed information about {drug_name}. Please check the spelling or try another medication."
# Check if this is a drug interaction query
if re.search(r'take|interact|safe|drug|interaction|medicine|pill|medication', message.lower()):
result = self.processor.process_query(message)
if result["status"] == "error":
return result["message"]
elif result["status"] == "not_found":
return result["message"]
elif result["status"] == "no_interaction":
return (f"Based on our database and biomedical literature analysis, no known interactions were found between {result['drugs'][0]} "
f"and {result['drugs'][1]}. However, always consult with a healthcare "
f"professional before combining medications.")
elif result["status"] == "found":
drug1, drug2 = result['drugs']
interactions = result["interactions"]
# Generate response
response = f"β οΈ **Potential interaction found between {drug1} and {drug2}:**\n\n"
for i, interaction in enumerate(interactions, 1):
severity = interaction["severity"]
# Add appropriate emoji based on severity
if severity.lower() == "severe":
emoji = "π΄"
elif severity.lower() == "moderate":
emoji = "π "
else:
emoji = "π‘"
response += f"{emoji} **{severity} interaction:** {interaction['description']}\n"
response += f" Source: {interaction['source']}\n\n"
# Add any management recommendations if available
try:
literature_info = self.bio_llm.extract_ddi_from_literature(drug1, drug2)
if "interactions" in literature_info and literature_info["interactions"]:
management = literature_info["interactions"][0].get("management")
if management:
response += f"π **Management Recommendation:** {management}\n\n"
except:
pass
response += "βοΈ Please consult with a healthcare professional before taking these medications together."
# Generate visualization
try:
G, error = self.processor.generate_network(drug1, depth=1)
if G:
response += "\n\nA visualization of this interaction has been generated."
# In a real implementation, we would save the graph image and provide a link or display it
except Exception as e:
pass # Handle gracefully if visualization fails
return response
# Check if the user is asking for all interactions for a specific drug
pattern = r"(what|show|list|tell).+?(interaction|interacts).+?(with|for|of)\s+(.+?)(?:\?|$)"
match = re.search(pattern, message.lower())
if match:
drug_name = match.group(4).strip()
canonical = self.db.search_drug(drug_name)
if not canonical:
return f"I couldn't find information about '{drug_name}' in our database."
interactions, _ = self.db.get_all_interactions(canonical)
if not interactions:
return f"No known interactions were found for {canonical} in our database."
response = f"**Known interactions for {canonical}:**\n\n"
# Group by severity
severe = []
moderate = []
mild = []
for _, other_drug, desc, severity, source in interactions:
if severity.lower() == "severe":
severe.append((other_drug, desc, source))
elif severity.lower() == "moderate":
moderate.append((other_drug, desc, source))
else:
mild.append((other_drug, desc, source))
# Add severe interactions
if severe:
response += "π΄ **Severe interactions:**\n"
for drug, desc, source in severe:
response += f"- **{drug}**: {desc} ({source})\n"
response += "\n"
# Add moderate interactions
if moderate:
response += "π **Moderate interactions:**\n"
for drug, desc, source in moderate:
response += f"- **{drug}**: {desc} ({source})\n"
response += "\n"
# Add mild interactions
if mild:
response += "π‘ **Mild interactions:**\n"
for drug, desc, source in mild:
response += f"- **{drug}**: {desc} ({source})\n"
response += "\n"
response += "Please consult with a healthcare professional for personalized advice."
return response
# Check if the user is requesting a visualization
if re.search(r'(visualize|visualization|graph|chart|network|diagram).+?(drug|interaction|medicine)', message.lower()):
drug_match = re.search(r'(visualize|visualization|graph|chart|network|diagram).+?(for|of|between)\s+(.+?)(?:\?|$)', message.lower())
if drug_match:
drug_name = drug_match.group(3).strip()
canonical = self.db.search_drug(drug_name)
if not canonical:
return f"I couldn't find information about '{drug_name}' in our database."
try:
G, error = self.processor.generate_network(canonical, depth=1)
if error:
return error
return f"I've generated a network visualization for {canonical}'s interactions. The visualization shows connections to other drugs, with red edges indicating severe interactions, orange for moderate, and yellow for mild interactions."
except Exception as e:
return f"Sorry, I encountered an error while generating the visualization: {str(e)}"
else:
try:
G, error = self.processor.generate_network()
if error:
return error
return "I've generated a general drug interaction network visualization showing connections between several common drugs. Red edges indicate severe interactions, orange for moderate, and yellow for mild interactions."
except Exception as e:
return f"Sorry, I encountered an error while generating the visualization: {str(e)}"
# If not specifically about drugs
return ("I'm a drug interaction assistant powered by biomedical language models. You can ask me about:\n\n"
"1. Potential interactions between medications (e.g., 'Can I take aspirin and warfarin together?')\n"
"2. Information about specific drugs (e.g., 'Tell me about metformin')\n"
"3. Analysis of clinical notes (e.g., 'Analyze these clinical notes: [paste notes here]')\n"
"4. Visualizations of drug interaction networks (e.g., 'Show me a visualization for warfarin')")
def generate_visualization(self, drug_name=None, depth=1):
"""Generate a visualization of drug interactions"""
G, error = self.processor.generate_network(drug_name, depth)
if error:
return None, error
# Create a unique filename
viz_id = str(uuid.uuid4())
filename = f"static/visualizations/{viz_id}.png"
# Create the visualization
plt.figure(figsize=(12, 10))
# Get positions
pos = nx.spring_layout(G, seed=42)
# Draw nodes
node_sizes = [G.nodes[node].get('size', 10) for node in G.nodes()]
node_colors = [G.nodes[node].get('color', 'blue') for node in G.nodes()]
nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color=node_colors, alpha=0.8)
# Draw edges with colors based on severity
edge_colors = []
edge_widths = []
for u, v, data in G.edges(data=True):
edge_colors.append(data.get('color', 'gray'))
edge_widths.append(data.get('weight', 1))
nx.draw_networkx_edges(G, pos, edge_color=edge_colors, width=edge_widths, alpha=0.7)
# Add labels
nx.draw_networkx_labels(G, pos, font_size=10, font_family="sans-serif")
# Save to file
plt.axis('off')
plt.tight_layout()
plt.savefig(filename, format='png', dpi=150)
plt.close()
return filename, None
def get_visualization_bytes(self, drug_name=None, depth=1):
"""Get visualization as bytes for web display"""
G, error = self.processor.generate_network(drug_name, depth)
if error:
return None, error
# Create the visualization
plt.figure(figsize=(12, 10))
# Get positions
pos = nx.spring_layout(G, seed=42)
# Draw nodes
node_sizes = [G.nodes[node].get('size', 10) for node in G.nodes()]
node_colors = [G.nodes[node].get('color', 'blue') for node in G.nodes()]
nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color=node_colors, alpha=0.8)
# Draw edges with colors based on severity
edge_colors = []
edge_widths = []
for u, v, data in G.edges(data=True):
edge_colors.append(data.get('color', 'gray'))
edge_widths.append(data.get('weight', 1))
nx.draw_networkx_edges(G, pos, edge_color=edge_colors, width=edge_widths, alpha=0.7)
# Add labels
nx.draw_networkx_labels(G, pos, font_size=10, font_family="sans-serif")
# Save to BytesIO
buf = io.BytesIO()
plt.axis('off')
plt.tight_layout()
plt.savefig(buf, format='png', dpi=150)
buf.seek(0)
plt.close()
return buf, None |