Spaces:
Sleeping
Sleeping
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,638 +0,0 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
import os
|
| 3 |
-
import torch
|
| 4 |
-
import re
|
| 5 |
-
import json
|
| 6 |
-
from transformers import AutoTokenizer, AutoModelForCausalLM
|
| 7 |
-
import logging
|
| 8 |
-
|
| 9 |
-
# Configure logging
|
| 10 |
-
logging.basicConfig(level=logging.INFO)
|
| 11 |
-
logger = logging.getLogger(__name__)
|
| 12 |
-
|
| 13 |
-
class StandaloneMedicalChatbot:
|
| 14 |
-
def __init__(self, use_gpu=False):
|
| 15 |
-
"""Standalone medical chatbot with built-in ML improvements"""
|
| 16 |
-
self.use_gpu = use_gpu and torch.cuda.is_available()
|
| 17 |
-
self.device = "cuda" if self.use_gpu else "cpu"
|
| 18 |
-
|
| 19 |
-
# Initialize model components
|
| 20 |
-
self.model = None
|
| 21 |
-
self.tokenizer = None
|
| 22 |
-
self.knowledge_chunks = []
|
| 23 |
-
|
| 24 |
-
# Setup medical intelligence
|
| 25 |
-
self.setup_medical_keywords()
|
| 26 |
-
self.load_biomedical_model()
|
| 27 |
-
|
| 28 |
-
def setup_medical_keywords(self):
|
| 29 |
-
"""Setup comprehensive medical keyword dictionaries"""
|
| 30 |
-
self.medical_keywords = {
|
| 31 |
-
'symptoms': [
|
| 32 |
-
'fever', 'temperature', 'hot', 'cough', 'cold', 'headache', 'pain', 'ache',
|
| 33 |
-
'sore', 'swelling', 'rash', 'vomiting', 'nausea', 'diarrhea', 'fatigue',
|
| 34 |
-
'tired', 'breathing', 'breath', 'wheeze', 'sneeze', 'runny nose', 'congestion',
|
| 35 |
-
'itchy', 'burning', 'stinging', 'cramping', 'bloating', 'dizzy', 'weakness'
|
| 36 |
-
],
|
| 37 |
-
'conditions': [
|
| 38 |
-
'asthma', 'pneumonia', 'bronchitis', 'allergy', 'allergic', 'infection',
|
| 39 |
-
'flu', 'influenza', 'covid', 'coronavirus', 'strep', 'throat', 'ear infection',
|
| 40 |
-
'gastroenteritis', 'dehydration', 'constipation', 'reflux', 'eczema',
|
| 41 |
-
'dermatitis', 'chickenpox', 'measles', 'mumps', 'rubella', 'rsv'
|
| 42 |
-
],
|
| 43 |
-
'treatments': [
|
| 44 |
-
'medicine', 'medication', 'drug', 'antibiotic', 'paracetamol', 'acetaminophen',
|
| 45 |
-
'ibuprofen', 'aspirin', 'treatment', 'therapy', 'dose', 'dosage', 'prescription',
|
| 46 |
-
'vaccine', 'vaccination', 'immunization', 'shot', 'injection', 'remedy',
|
| 47 |
-
'cure', 'heal', 'recover', 'rest', 'fluids', 'hydration'
|
| 48 |
-
],
|
| 49 |
-
'anatomy': [
|
| 50 |
-
'head', 'chest', 'stomach', 'belly', 'throat', 'ear', 'eye', 'nose', 'mouth',
|
| 51 |
-
'skin', 'lung', 'lungs', 'heart', 'brain', 'kidney', 'liver', 'blood',
|
| 52 |
-
'bones', 'muscle', 'joint', 'neck', 'back', 'arm', 'leg', 'hand', 'foot'
|
| 53 |
-
],
|
| 54 |
-
'age_groups': [
|
| 55 |
-
'baby', 'babies', 'infant', 'toddler', 'child', 'children', 'pediatric',
|
| 56 |
-
'newborn', 'kid', 'kids', 'teenager', 'adolescent', 'preschooler',
|
| 57 |
-
'months', 'years', 'old', 'age', 'young', 'little'
|
| 58 |
-
],
|
| 59 |
-
'medical_context': [
|
| 60 |
-
'doctor', 'physician', 'pediatrician', 'hospital', 'clinic', 'nurse',
|
| 61 |
-
'diagnosis', 'symptom', 'symptoms', 'health', 'medical', 'care', 'urgent',
|
| 62 |
-
'emergency', 'when to call', 'normal', 'abnormal', 'healthy', 'sick',
|
| 63 |
-
'illness', 'disease', 'condition', 'problem', 'concern', 'help'
|
| 64 |
-
],
|
| 65 |
-
'emergency': [
|
| 66 |
-
'emergency', 'urgent', 'critical', 'severe', 'serious', 'dangerous',
|
| 67 |
-
'life threatening', 'call doctor', 'hospital', '911', 'ambulance',
|
| 68 |
-
'immediate', 'right away', 'cannot breathe', 'unconscious', 'seizure'
|
| 69 |
-
]
|
| 70 |
-
}
|
| 71 |
-
|
| 72 |
-
# Non-medical keywords
|
| 73 |
-
self.non_medical_keywords = [
|
| 74 |
-
'weather', 'cooking', 'recipe', 'food', 'restaurant', 'movie', 'film',
|
| 75 |
-
'music', 'song', 'sports', 'football', 'basketball', 'game', 'play',
|
| 76 |
-
'travel', 'vacation', 'holiday', 'work', 'job', 'school', 'homework',
|
| 77 |
-
'money', 'shopping', 'buy', 'sell', 'car', 'vehicle', 'computer',
|
| 78 |
-
'phone', 'technology', 'politics', 'news', 'celebrity', 'entertainment'
|
| 79 |
-
]
|
| 80 |
-
|
| 81 |
-
# Create flat list of medical keywords
|
| 82 |
-
self.all_medical_keywords = []
|
| 83 |
-
for category_keywords in self.medical_keywords.values():
|
| 84 |
-
self.all_medical_keywords.extend(category_keywords)
|
| 85 |
-
|
| 86 |
-
def load_biomedical_model(self):
|
| 87 |
-
"""Load a lightweight biomedical model"""
|
| 88 |
-
try:
|
| 89 |
-
logger.info("Loading biomedical language model...")
|
| 90 |
-
|
| 91 |
-
# Use a lightweight medical model or fallback to general model
|
| 92 |
-
model_name = "microsoft/DialoGPT-medium" # Lightweight conversational model
|
| 93 |
-
|
| 94 |
-
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 95 |
-
self.model = AutoModelForCausalLM.from_pretrained(
|
| 96 |
-
model_name,
|
| 97 |
-
torch_dtype=torch.float16 if self.use_gpu else torch.float32,
|
| 98 |
-
device_map="auto" if self.use_gpu else None
|
| 99 |
-
)
|
| 100 |
-
|
| 101 |
-
# Add padding token if not present
|
| 102 |
-
if self.tokenizer.pad_token is None:
|
| 103 |
-
self.tokenizer.pad_token = self.tokenizer.eos_token
|
| 104 |
-
|
| 105 |
-
logger.info(f"Model loaded successfully on {self.device}")
|
| 106 |
-
|
| 107 |
-
except Exception as e:
|
| 108 |
-
logger.error(f"Error loading model: {e}")
|
| 109 |
-
logger.info("Model loading failed, will use template-based responses")
|
| 110 |
-
self.model = None
|
| 111 |
-
self.tokenizer = None
|
| 112 |
-
|
| 113 |
-
def load_medical_data(self, file_path):
|
| 114 |
-
"""Load and chunk medical data"""
|
| 115 |
-
try:
|
| 116 |
-
if not os.path.exists(file_path):
|
| 117 |
-
logger.error(f"Medical data file not found: {file_path}")
|
| 118 |
-
return False
|
| 119 |
-
|
| 120 |
-
with open(file_path, 'r', encoding='utf-8') as f:
|
| 121 |
-
content = f.read()
|
| 122 |
-
|
| 123 |
-
# Simple chunking by paragraphs or sentences
|
| 124 |
-
chunks = []
|
| 125 |
-
|
| 126 |
-
# Split by double newlines (paragraphs)
|
| 127 |
-
paragraphs = content.split('\n\n')
|
| 128 |
-
|
| 129 |
-
for i, paragraph in enumerate(paragraphs):
|
| 130 |
-
if len(paragraph.strip()) > 50: # Minimum chunk size
|
| 131 |
-
chunks.append({
|
| 132 |
-
'id': i,
|
| 133 |
-
'text': paragraph.strip(),
|
| 134 |
-
'source': file_path
|
| 135 |
-
})
|
| 136 |
-
|
| 137 |
-
# If no paragraphs, split by sentences
|
| 138 |
-
if len(chunks) < 10:
|
| 139 |
-
sentences = re.split(r'[.!?]+', content)
|
| 140 |
-
chunks = []
|
| 141 |
-
for i, sentence in enumerate(sentences):
|
| 142 |
-
if len(sentence.strip()) > 30:
|
| 143 |
-
chunks.append({
|
| 144 |
-
'id': i,
|
| 145 |
-
'text': sentence.strip(),
|
| 146 |
-
'source': file_path
|
| 147 |
-
})
|
| 148 |
-
|
| 149 |
-
self.knowledge_chunks = chunks
|
| 150 |
-
logger.info(f"Loaded {len(self.knowledge_chunks)} knowledge chunks")
|
| 151 |
-
return True
|
| 152 |
-
|
| 153 |
-
except Exception as e:
|
| 154 |
-
logger.error(f"Error loading medical data: {e}")
|
| 155 |
-
return False
|
| 156 |
-
|
| 157 |
-
def calculate_medical_relevance(self, query):
|
| 158 |
-
"""Calculate medical relevance score"""
|
| 159 |
-
if not query:
|
| 160 |
-
return 0.0
|
| 161 |
-
|
| 162 |
-
query_lower = query.lower()
|
| 163 |
-
words = re.findall(r'\b\w+\b', query_lower)
|
| 164 |
-
|
| 165 |
-
if not words:
|
| 166 |
-
return 0.0
|
| 167 |
-
|
| 168 |
-
# Count medical keyword matches
|
| 169 |
-
medical_matches = 0
|
| 170 |
-
for word in words:
|
| 171 |
-
for keyword in self.all_medical_keywords:
|
| 172 |
-
if word == keyword or keyword in word or word in keyword:
|
| 173 |
-
medical_matches += 1
|
| 174 |
-
break
|
| 175 |
-
|
| 176 |
-
# Count non-medical keywords
|
| 177 |
-
non_medical_matches = sum(1 for word in words if word in self.non_medical_keywords)
|
| 178 |
-
|
| 179 |
-
# Calculate score
|
| 180 |
-
medical_score = medical_matches / len(words)
|
| 181 |
-
non_medical_penalty = non_medical_matches / len(words)
|
| 182 |
-
|
| 183 |
-
relevance_score = medical_score - (non_medical_penalty * 0.7)
|
| 184 |
-
return max(0.0, min(1.0, relevance_score))
|
| 185 |
-
|
| 186 |
-
def classify_medical_intent(self, query):
|
| 187 |
-
"""Classify medical intent with improved fever detection"""
|
| 188 |
-
query_lower = query.lower()
|
| 189 |
-
intent_scores = {}
|
| 190 |
-
|
| 191 |
-
# Special handling for fever-related queries
|
| 192 |
-
fever_keywords = ['fever', 'temperature', 'hot', 'warm', 'degrees', 'thermometer']
|
| 193 |
-
if any(keyword in query_lower for keyword in fever_keywords):
|
| 194 |
-
# This is definitely a fever/symptom query
|
| 195 |
-
return "fever_management", 0.9
|
| 196 |
-
|
| 197 |
-
for category, keywords in self.medical_keywords.items():
|
| 198 |
-
score = 0
|
| 199 |
-
for keyword in keywords:
|
| 200 |
-
if keyword in query_lower:
|
| 201 |
-
# Give higher weight to exact matches
|
| 202 |
-
if f" {keyword} " in f" {query_lower} ":
|
| 203 |
-
score += 2
|
| 204 |
-
else:
|
| 205 |
-
score += 1
|
| 206 |
-
intent_scores[category] = score
|
| 207 |
-
|
| 208 |
-
if max(intent_scores.values()) > 0:
|
| 209 |
-
best_intent = max(intent_scores, key=intent_scores.get)
|
| 210 |
-
max_score = intent_scores[best_intent]
|
| 211 |
-
confidence = min(1.0, max_score / 3) # Normalize confidence
|
| 212 |
-
return best_intent, confidence
|
| 213 |
-
|
| 214 |
-
return "general", 0.1
|
| 215 |
-
|
| 216 |
-
def search_relevant_chunks(self, query, top_k=5):
|
| 217 |
-
"""Enhanced search for relevant knowledge chunks"""
|
| 218 |
-
if not self.knowledge_chunks:
|
| 219 |
-
return []
|
| 220 |
-
|
| 221 |
-
query_lower = query.lower()
|
| 222 |
-
query_words = set(re.findall(r'\b\w+\b', query_lower))
|
| 223 |
-
|
| 224 |
-
# Get intent for boosting
|
| 225 |
-
intent, _ = self.classify_medical_intent(query)
|
| 226 |
-
intent_keywords = set(self.medical_keywords.get(intent, []))
|
| 227 |
-
|
| 228 |
-
# Special fever keywords for boosting fever-related content
|
| 229 |
-
fever_keywords = {'fever', 'temperature', 'hot', 'warm', 'degrees', 'thermometer'}
|
| 230 |
-
is_fever_query = bool(fever_keywords.intersection(query_words))
|
| 231 |
-
|
| 232 |
-
scored_chunks = []
|
| 233 |
-
for chunk in self.knowledge_chunks:
|
| 234 |
-
chunk_text = chunk['text'].lower()
|
| 235 |
-
chunk_words = set(re.findall(r'\b\w+\b', chunk_text))
|
| 236 |
-
|
| 237 |
-
# Calculate different similarity scores
|
| 238 |
-
|
| 239 |
-
# 1. Exact word matches
|
| 240 |
-
exact_matches = len(query_words.intersection(chunk_words))
|
| 241 |
-
exact_score = exact_matches / len(query_words) if query_words else 0
|
| 242 |
-
|
| 243 |
-
# 2. Partial matches
|
| 244 |
-
partial_matches = 0
|
| 245 |
-
for q_word in query_words:
|
| 246 |
-
for c_word in chunk_words:
|
| 247 |
-
if q_word in c_word or c_word in q_word:
|
| 248 |
-
partial_matches += 0.5
|
| 249 |
-
break
|
| 250 |
-
partial_score = partial_matches / len(query_words) if query_words else 0
|
| 251 |
-
|
| 252 |
-
# 3. Intent keyword matches
|
| 253 |
-
intent_matches = len(intent_keywords.intersection(chunk_words))
|
| 254 |
-
intent_score = intent_matches * 0.2
|
| 255 |
-
|
| 256 |
-
# 4. Special fever boost
|
| 257 |
-
fever_boost = 0
|
| 258 |
-
if is_fever_query:
|
| 259 |
-
fever_matches = len(fever_keywords.intersection(chunk_words))
|
| 260 |
-
fever_boost = fever_matches * 0.5
|
| 261 |
-
|
| 262 |
-
# 5. Medical keyword density
|
| 263 |
-
medical_words_in_chunk = sum(1 for word in chunk_words if word in self.all_medical_keywords)
|
| 264 |
-
medical_density = medical_words_in_chunk / len(chunk_words) if chunk_words else 0
|
| 265 |
-
|
| 266 |
-
# Combine scores
|
| 267 |
-
final_score = (
|
| 268 |
-
exact_score * 0.4 +
|
| 269 |
-
partial_score * 0.3 +
|
| 270 |
-
intent_score +
|
| 271 |
-
fever_boost +
|
| 272 |
-
medical_density * 0.1
|
| 273 |
-
)
|
| 274 |
-
|
| 275 |
-
# Length bonus for substantial chunks
|
| 276 |
-
if len(chunk['text']) > 100:
|
| 277 |
-
final_score *= 1.1
|
| 278 |
-
|
| 279 |
-
scored_chunks.append((final_score, chunk))
|
| 280 |
-
|
| 281 |
-
# Sort and return top chunks
|
| 282 |
-
scored_chunks.sort(key=lambda x: x[0], reverse=True)
|
| 283 |
-
return [chunk for score, chunk in scored_chunks[:top_k] if score > 0.1]
|
| 284 |
-
|
| 285 |
-
def generate_response_with_model(self, query, context):
|
| 286 |
-
"""Generate response using the language model"""
|
| 287 |
-
if not self.model or not self.tokenizer:
|
| 288 |
-
return None
|
| 289 |
-
|
| 290 |
-
try:
|
| 291 |
-
# Prepare prompt with better structure
|
| 292 |
-
prompt = f"Medical Question: {query}\n\nRelevant Medical Information:\n{context[:800]}\n\nProvide a helpful medical response:"
|
| 293 |
-
|
| 294 |
-
# Tokenize
|
| 295 |
-
inputs = self.tokenizer.encode(prompt, return_tensors="pt", max_length=512, truncation=True)
|
| 296 |
-
if self.use_gpu:
|
| 297 |
-
inputs = inputs.to(self.device)
|
| 298 |
-
|
| 299 |
-
# Generate
|
| 300 |
-
with torch.no_grad():
|
| 301 |
-
outputs = self.model.generate(
|
| 302 |
-
inputs,
|
| 303 |
-
max_length=inputs.shape[1] + 150,
|
| 304 |
-
num_return_sequences=1,
|
| 305 |
-
temperature=0.7,
|
| 306 |
-
do_sample=True,
|
| 307 |
-
pad_token_id=self.tokenizer.eos_token_id
|
| 308 |
-
)
|
| 309 |
-
|
| 310 |
-
# Decode response
|
| 311 |
-
response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 312 |
-
|
| 313 |
-
# Extract just the answer part
|
| 314 |
-
if "Provide a helpful medical response:" in response:
|
| 315 |
-
answer = response.split("Provide a helpful medical response:")[-1].strip()
|
| 316 |
-
elif "Medical response:" in response.lower():
|
| 317 |
-
answer = response.split("Medical response:")[-1].strip()
|
| 318 |
-
else:
|
| 319 |
-
# Take the part after the original prompt
|
| 320 |
-
answer = response[len(prompt):].strip()
|
| 321 |
-
|
| 322 |
-
return answer if len(answer) > 10 else None
|
| 323 |
-
|
| 324 |
-
except Exception as e:
|
| 325 |
-
logger.error(f"Error generating response with model: {e}")
|
| 326 |
-
return None
|
| 327 |
-
|
| 328 |
-
def generate_template_response(self, query, context, intent):
|
| 329 |
-
"""Generate template-based response with improved fever handling"""
|
| 330 |
-
|
| 331 |
-
# Check if this is a fever query
|
| 332 |
-
query_lower = query.lower()
|
| 333 |
-
if any(keyword in query_lower for keyword in ['fever', 'temperature', 'hot', 'warm']):
|
| 334 |
-
# Generate fever-specific response
|
| 335 |
-
if context:
|
| 336 |
-
fever_response = self.create_fever_specific_response(context)
|
| 337 |
-
if fever_response:
|
| 338 |
-
return fever_response
|
| 339 |
-
|
| 340 |
-
# Template responses based on intent
|
| 341 |
-
templates = {
|
| 342 |
-
'fever_management': [
|
| 343 |
-
f"For fever management in children:\n\n{context[:400]}...\n\n⚠️ Always monitor your child closely and consult a doctor if fever is high or persistent."
|
| 344 |
-
],
|
| 345 |
-
'symptoms': [
|
| 346 |
-
f"Regarding symptoms in children:\n\n{context[:400]}...\n\nIf symptoms persist or worsen, please consult a healthcare professional."
|
| 347 |
-
],
|
| 348 |
-
'treatments': [
|
| 349 |
-
f"Treatment information from medical sources:\n\n{context[:400]}...\n\n⚠️ Always consult a doctor before starting any treatment."
|
| 350 |
-
],
|
| 351 |
-
'emergency': [
|
| 352 |
-
f"⚠️ For emergency situations:\n\n{context[:300]}...\n\n🚨 IMPORTANT: If this is a medical emergency, call emergency services immediately!"
|
| 353 |
-
],
|
| 354 |
-
'conditions': [
|
| 355 |
-
f"Medical information about this condition:\n\n{context[:400]}...\n\nConsult your pediatrician for proper diagnosis and treatment."
|
| 356 |
-
]
|
| 357 |
-
}
|
| 358 |
-
|
| 359 |
-
# Select appropriate template
|
| 360 |
-
if intent in templates:
|
| 361 |
-
template_list = templates[intent]
|
| 362 |
-
template = template_list[0] # Use first template for now
|
| 363 |
-
else:
|
| 364 |
-
template = f"Based on medical information:\n\n{context[:400]}...\n\nPlease consult with a healthcare professional for personalized advice."
|
| 365 |
-
|
| 366 |
-
return template
|
| 367 |
-
|
| 368 |
-
def create_fever_specific_response(self, context):
|
| 369 |
-
"""Create fever-specific response from context"""
|
| 370 |
-
if not context:
|
| 371 |
-
return None
|
| 372 |
-
|
| 373 |
-
# Look for fever-related sentences in the context
|
| 374 |
-
sentences = re.split(r'[.!?]+', context)
|
| 375 |
-
fever_sentences = []
|
| 376 |
-
|
| 377 |
-
fever_keywords = ['fever', 'temperature', 'hot', 'warm', 'degrees', 'thermometer', 'acetaminophen', 'ibuprofen', 'fluids']
|
| 378 |
-
|
| 379 |
-
for sentence in sentences:
|
| 380 |
-
sentence = sentence.strip()
|
| 381 |
-
if len(sentence) > 20:
|
| 382 |
-
sentence_lower = sentence.lower()
|
| 383 |
-
if any(keyword in sentence_lower for keyword in fever_keywords):
|
| 384 |
-
fever_sentences.append(sentence)
|
| 385 |
-
|
| 386 |
-
if fever_sentences:
|
| 387 |
-
# Take the most relevant fever sentences
|
| 388 |
-
response = "For managing fever in children:\n\n" + '. '.join(fever_sentences[:3]) + '.'
|
| 389 |
-
response += "\n\n⚠️ Monitor your child's temperature regularly and seek medical attention if fever is high (>104°F/40°C) or if your child appears very ill."
|
| 390 |
-
return response
|
| 391 |
-
|
| 392 |
-
return None
|
| 393 |
-
|
| 394 |
-
def chat(self, user_input):
|
| 395 |
-
"""Main chat method with enhanced fever handling"""
|
| 396 |
-
try:
|
| 397 |
-
# Check medical relevance
|
| 398 |
-
relevance_score = self.calculate_medical_relevance(user_input)
|
| 399 |
-
|
| 400 |
-
if relevance_score < 0.2:
|
| 401 |
-
return ("I'm designed to help with pediatric medical questions. Your query doesn't seem to be medical-related. "
|
| 402 |
-
"Please ask about children's health, symptoms, treatments, or medical conditions.\n\n"
|
| 403 |
-
"Examples:\n"
|
| 404 |
-
"• 'What should I do if my child has a fever?'\n"
|
| 405 |
-
"• 'How to treat a child's cough?'\n"
|
| 406 |
-
"• 'When should I call the doctor for my baby?'")
|
| 407 |
-
|
| 408 |
-
# Classify intent with special fever handling
|
| 409 |
-
intent, intent_confidence = self.classify_medical_intent(user_input)
|
| 410 |
-
|
| 411 |
-
# Search for relevant information
|
| 412 |
-
relevant_chunks = self.search_relevant_chunks(user_input, top_k=4)
|
| 413 |
-
|
| 414 |
-
if not relevant_chunks:
|
| 415 |
-
# Provide helpful response even without chunks
|
| 416 |
-
if 'fever' in user_input.lower():
|
| 417 |
-
return ("For fever in children, general guidelines include:\n\n"
|
| 418 |
-
"• Monitor temperature regularly\n"
|
| 419 |
-
"• Ensure adequate fluid intake\n"
|
| 420 |
-
"• Consider age-appropriate fever reducers (consult your pediatrician)\n"
|
| 421 |
-
"• Watch for signs of dehydration or severe illness\n"
|
| 422 |
-
"• Seek medical attention if fever is very high or persistent\n\n"
|
| 423 |
-
"⚠️ Always consult with your pediatrician for specific advice about your child's fever.")
|
| 424 |
-
else:
|
| 425 |
-
return (f"I understand you're asking about {intent}-related medical information, "
|
| 426 |
-
f"but I couldn't find specific details in my knowledge base. "
|
| 427 |
-
f"Please consult with a pediatrician for personalized medical advice about your child's condition.")
|
| 428 |
-
|
| 429 |
-
# Prepare context
|
| 430 |
-
context = "\n\n".join([chunk['text'] for chunk in relevant_chunks])
|
| 431 |
-
|
| 432 |
-
# Try to generate response with model
|
| 433 |
-
response = self.generate_response_with_model(user_input, context)
|
| 434 |
-
|
| 435 |
-
# Fallback to template response
|
| 436 |
-
if not response or len(response) < 20:
|
| 437 |
-
response = self.generate_template_response(user_input, context, intent)
|
| 438 |
-
|
| 439 |
-
# Add helpful footer
|
| 440 |
-
footer = "\n\n💡 This information is for educational purposes only. Always consult with a qualified pediatrician for medical advice, diagnosis, and treatment."
|
| 441 |
-
|
| 442 |
-
if intent == "emergency" or any(word in user_input.lower() for word in ['urgent', 'emergency', 'serious']):
|
| 443 |
-
footer = "\n\n🚨 IMPORTANT: For medical emergencies, contact emergency services immediately (911 in the US, 999 in the UK, etc.)"
|
| 444 |
-
|
| 445 |
-
return response + footer
|
| 446 |
-
|
| 447 |
-
except Exception as e:
|
| 448 |
-
logger.error(f"Error in chat: {e}")
|
| 449 |
-
return f"I encountered an error processing your question: {str(e)}. Please try rephrasing your medical question."
|
| 450 |
-
|
| 451 |
-
def initialize_standalone_chatbot():
|
| 452 |
-
"""Initialize the standalone chatbot"""
|
| 453 |
-
try:
|
| 454 |
-
print("🚀 Initializing Standalone Medical Chatbot...")
|
| 455 |
-
|
| 456 |
-
use_gpu = torch.cuda.is_available()
|
| 457 |
-
chatbot = StandaloneMedicalChatbot(use_gpu=use_gpu)
|
| 458 |
-
|
| 459 |
-
# Load medical data
|
| 460 |
-
medical_file = "Pediatric_cleaned.txt"
|
| 461 |
-
if os.path.exists(medical_file):
|
| 462 |
-
success = chatbot.load_medical_data(medical_file)
|
| 463 |
-
if success:
|
| 464 |
-
status = f"✅ Standalone Medical Chatbot loaded! Medical file '{medical_file}' processed with {len(chatbot.knowledge_chunks)} knowledge chunks."
|
| 465 |
-
return chatbot, status, True
|
| 466 |
-
else:
|
| 467 |
-
status = f"⚠️ Chatbot initialized but failed to load medical data from '{medical_file}'."
|
| 468 |
-
return chatbot, status, False
|
| 469 |
-
else:
|
| 470 |
-
status = f"⚠️ Medical file '{medical_file}' not found. Chatbot will work with basic medical knowledge only."
|
| 471 |
-
return chatbot, status, False
|
| 472 |
-
|
| 473 |
-
except Exception as e:
|
| 474 |
-
error_msg = f"❌ Failed to initialize chatbot: {str(e)}"
|
| 475 |
-
print(error_msg)
|
| 476 |
-
return None, error_msg, False
|
| 477 |
-
|
| 478 |
-
# Initialize chatbot
|
| 479 |
-
print("🏥 Starting Standalone Pediatric Medical Assistant...")
|
| 480 |
-
chatbot, startup_status, medical_file_loaded = initialize_standalone_chatbot()
|
| 481 |
-
|
| 482 |
-
def generate_response(user_input, history):
|
| 483 |
-
"""Generate response"""
|
| 484 |
-
if not chatbot:
|
| 485 |
-
return history + [("System Error", "❌ Chatbot failed to initialize. Please refresh the page and try again.")], ""
|
| 486 |
-
|
| 487 |
-
if not user_input.strip():
|
| 488 |
-
return history, ""
|
| 489 |
-
|
| 490 |
-
try:
|
| 491 |
-
bot_response = chatbot.chat(user_input)
|
| 492 |
-
history = history + [(user_input, bot_response)]
|
| 493 |
-
return history, ""
|
| 494 |
-
|
| 495 |
-
except Exception as e:
|
| 496 |
-
error_response = f"⚠️ Sorry, I encountered an error: {str(e)}. Please try rephrasing your question."
|
| 497 |
-
history = history + [(user_input, error_response)]
|
| 498 |
-
return history, ""
|
| 499 |
-
|
| 500 |
-
# Custom CSS
|
| 501 |
-
custom_css = """
|
| 502 |
-
.gradio-container {
|
| 503 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 504 |
-
}
|
| 505 |
-
|
| 506 |
-
.chatbot {
|
| 507 |
-
height: 500px !important;
|
| 508 |
-
}
|
| 509 |
-
|
| 510 |
-
.standalone-badge {
|
| 511 |
-
background: linear-gradient(90deg, #2196f3, #1976d2);
|
| 512 |
-
color: white;
|
| 513 |
-
padding: 6px 15px;
|
| 514 |
-
border-radius: 25px;
|
| 515 |
-
font-size: 0.9em;
|
| 516 |
-
font-weight: bold;
|
| 517 |
-
display: inline-block;
|
| 518 |
-
margin-bottom: 15px;
|
| 519 |
-
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
| 520 |
-
}
|
| 521 |
-
"""
|
| 522 |
-
|
| 523 |
-
# Create Gradio interface
|
| 524 |
-
with gr.Blocks(css=custom_css, title="Standalone Medical Assistant") as demo:
|
| 525 |
-
gr.Markdown(
|
| 526 |
-
"""
|
| 527 |
-
# 🩺 Standalone Pediatric Medical Assistant
|
| 528 |
-
|
| 529 |
-
<div class="standalone-badge">🚀 FULLY STANDALONE - NO EXTERNAL DEPENDENCIES</div>
|
| 530 |
-
|
| 531 |
-
This is a complete standalone medical chatbot that includes:
|
| 532 |
-
- **🧠 Built-in Medical Intelligence** - No external ML libraries needed
|
| 533 |
-
- **🎯 Smart Relevance Detection** - Filters medical vs non-medical queries
|
| 534 |
-
- **🔍 Advanced Search** - Multi-factor similarity scoring
|
| 535 |
-
- **💬 Conversational AI** - Powered by transformer models
|
| 536 |
-
- **📚 Medical Knowledge Base** - Pediatric-focused information
|
| 537 |
-
|
| 538 |
-
**⚠️ Medical Disclaimer:** This tool provides educational information only.
|
| 539 |
-
Always consult qualified healthcare professionals for medical diagnosis, treatment, and personalized advice.
|
| 540 |
-
"""
|
| 541 |
-
)
|
| 542 |
-
|
| 543 |
-
# Status display
|
| 544 |
-
gr.Markdown(f"**System Status:** {startup_status}")
|
| 545 |
-
|
| 546 |
-
# Main chat interface
|
| 547 |
-
with gr.Row():
|
| 548 |
-
with gr.Column(scale=4):
|
| 549 |
-
chatbot_ui = gr.Chatbot(
|
| 550 |
-
label="💬 Standalone Medical AI Chat",
|
| 551 |
-
height=500,
|
| 552 |
-
show_label=True,
|
| 553 |
-
avatar_images=("👤", "🤖")
|
| 554 |
-
)
|
| 555 |
-
|
| 556 |
-
with gr.Row():
|
| 557 |
-
user_input = gr.Textbox(
|
| 558 |
-
placeholder="Ask any pediatric medical question... (e.g., 'What should I do if my child has a fever?')",
|
| 559 |
-
lines=2,
|
| 560 |
-
max_lines=5,
|
| 561 |
-
show_label=False,
|
| 562 |
-
scale=4
|
| 563 |
-
)
|
| 564 |
-
submit_btn = gr.Button("Send 📤", variant="primary", scale=1)
|
| 565 |
-
|
| 566 |
-
with gr.Column(scale=1):
|
| 567 |
-
gr.Markdown(
|
| 568 |
-
"""
|
| 569 |
-
### ✨ Key Features:
|
| 570 |
-
|
| 571 |
-
**��� Intelligent Filtering**
|
| 572 |
-
- Detects medical relevance
|
| 573 |
-
- Redirects non-medical queries
|
| 574 |
-
- Provides helpful suggestions
|
| 575 |
-
|
| 576 |
-
**🧠 Medical Understanding**
|
| 577 |
-
- Symptom recognition
|
| 578 |
-
- Treatment information
|
| 579 |
-
- Emergency detection
|
| 580 |
-
- Age-appropriate advice
|
| 581 |
-
|
| 582 |
-
**🔍 Smart Search**
|
| 583 |
-
- Multi-factor scoring
|
| 584 |
-
- Intent-based boosting
|
| 585 |
-
- Context-aware matching
|
| 586 |
-
|
| 587 |
-
### 💡 Try These Questions:
|
| 588 |
-
|
| 589 |
-
- "My 2-year-old has a fever of 101°F, what should I do?"
|
| 590 |
-
- "How do I know if my baby's cough is serious?"
|
| 591 |
-
- "What are normal sleep patterns for toddlers?"
|
| 592 |
-
- "When should I call the doctor for vomiting?"
|
| 593 |
-
- "How to treat diaper rash naturally?"
|
| 594 |
-
- "Signs of allergic reactions in children"
|
| 595 |
-
|
| 596 |
-
### 🔧 Technical Info:
|
| 597 |
-
- **Base:** Transformer language model
|
| 598 |
-
- **Search:** Multi-factor similarity
|
| 599 |
-
- **Knowledge:** Pediatric medical database
|
| 600 |
-
- **Relevance:** Keyword-based ML
|
| 601 |
-
"""
|
| 602 |
-
)
|
| 603 |
-
|
| 604 |
-
# Event handlers
|
| 605 |
-
user_input.submit(
|
| 606 |
-
fn=generate_response,
|
| 607 |
-
inputs=[user_input, chatbot_ui],
|
| 608 |
-
outputs=[chatbot_ui, user_input],
|
| 609 |
-
show_progress=True
|
| 610 |
-
)
|
| 611 |
-
|
| 612 |
-
submit_btn.click(
|
| 613 |
-
fn=generate_response,
|
| 614 |
-
inputs=[user_input, chatbot_ui],
|
| 615 |
-
outputs=[chatbot_ui, user_input],
|
| 616 |
-
show_progress=True
|
| 617 |
-
)
|
| 618 |
-
|
| 619 |
-
# Footer
|
| 620 |
-
gr.Markdown(
|
| 621 |
-
"""
|
| 622 |
-
---
|
| 623 |
-
**🏥 Standalone Medical AI** | Self-Contained System | For Educational Purposes Only
|
| 624 |
-
|
| 625 |
-
**No External Dependencies:** This system runs entirely standalone without requiring
|
| 626 |
-
external ML libraries like NLTK, scikit-learn, or sentence-transformers.
|
| 627 |
-
|
| 628 |
-
**Always Remember:** Consult healthcare professionals for medical emergencies and personalized advice.
|
| 629 |
-
"""
|
| 630 |
-
)
|
| 631 |
-
|
| 632 |
-
if __name__ == "__main__":
|
| 633 |
-
demo.launch(
|
| 634 |
-
server_name="0.0.0.0",
|
| 635 |
-
server_port=7860,
|
| 636 |
-
show_error=True,
|
| 637 |
-
share=False
|
| 638 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|