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