skin-lesion-api / src /llm_explain.py
Ranjith445's picture
Initial HF Space deploy
e4ee3e8
Raw
History Blame Contribute Delete
4.67 kB
import os
import sys
from dotenv import load_dotenv
load_dotenv(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".env"))
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config import (
LLM_PROVIDER, LLM_MODEL, GROQ_API_KEY,
MAX_TOKENS, TEMPERATURE, CLASS_NAMES, IDX_TO_CLASS,
)
from src.rag import retrieve
SYSTEM_PROMPT = """You are a clinical dermatology AI assistant.
You explain skin lesion classifications to patients and doctors clearly and compassionately.
Always remind users that your analysis is for educational purposes only and not a substitute for professional medical diagnosis.
Use the retrieved medical context to provide accurate, evidence-based explanations."""
def _call_groq(messages: list, system: str) -> str:
from groq import Groq
client = Groq(api_key=GROQ_API_KEY)
full_messages = [{"role": "system", "content": system}] + messages
response = client.chat.completions.create(
model=LLM_MODEL,
messages=full_messages,
max_tokens=MAX_TOKENS,
temperature=TEMPERATURE,
)
return response.choices[0].message.content
def _call_llm(messages: list, system: str) -> str:
provider = LLM_PROVIDER.lower()
if provider == "groq":
return _call_groq(messages, system)
raise ValueError(f"Unsupported LLM provider: {provider}")
# ── Public API ─────────────────────────────────────────────────────────────────
def explain_prediction(
predicted_class: str,
confidence: float,
probabilities: dict,
) -> str:
"""Generate a clinical explanation for a prediction using RAG + LLM."""
class_name = CLASS_NAMES[list(IDX_TO_CLASS.values()).index(predicted_class)]
# Retrieve relevant context from DermNet knowledge base
query = f"{class_name} symptoms diagnosis treatment dermoscopy"
context = retrieve(query, top_k=5)
context_text = "\n\n".join(
f"[Source: {c['source']}]\n{c['text']}" for c in context
)
# Build top-3 probabilities string
top3 = list(probabilities.items())[:3]
top3_str = ", ".join(f"{k}: {v*100:.1f}%" for k, v in top3)
user_message = f"""
A skin lesion image was analyzed by an AI classification model.
**Prediction:** {class_name} ({predicted_class})
**Confidence:** {confidence*100:.1f}%
**Top-3 probabilities:** {top3_str}
**Retrieved Medical Context from DermNet NZ:**
{context_text}
Please provide:
1. A brief description of this condition
2. Key visual/clinical features that characterize it
3. Risk level and urgency (benign vs potentially malignant)
4. Recommended next steps for the patient
5. A compassionate closing note
Keep the response clear, structured, and under 400 words.
"""
messages = [{"role": "user", "content": user_message}]
return _call_llm(messages, SYSTEM_PROMPT)
def answer_question(question: str, predicted_class: str = None) -> str:
"""Answer a follow-up question about the diagnosis using RAG + LLM."""
query = question + (f" {predicted_class}" if predicted_class else "")
context = retrieve(query, top_k=4)
context_text = "\n\n".join(
f"[Source: {c['source']}]\n{c['text']}" for c in context
)
user_message = f"""
The patient asks: "{question}"
{f'Context: The patient was previously diagnosed with {predicted_class}.' if predicted_class else ''}
**Retrieved Medical Context from DermNet NZ:**
{context_text}
Please answer clearly and compassionately. Remind the user to consult a dermatologist for personal medical advice.
"""
messages = [{"role": "user", "content": user_message}]
return _call_llm(messages, SYSTEM_PROMPT)
# ── CLI test ───────────────────────────────────────────────────────────────────
if __name__ == "__main__":
from dotenv import load_dotenv
load_dotenv()
print("=" * 60)
print("Testing RAG + Groq LLM explanation")
print("=" * 60)
explanation = explain_prediction(
predicted_class="mel",
confidence=0.8654,
probabilities={"mel": 0.8654, "nv": 0.0821, "bkl": 0.0312, "bcc": 0.0124, "akiec": 0.0089},
)
print("\n[Explanation]\n")
print(explanation)
print("\n" + "=" * 60)
print("Testing follow-up Q&A")
print("=" * 60)
answer = answer_question("Should I be worried? What should I do next?", predicted_class="mel")
print("\n[Answer]\n")
print(answer)