Spaces:
Sleeping
Sleeping
File size: 10,728 Bytes
0d340ad 98b5940 0d340ad 321a905 98b5940 321a905 0d340ad 98b5940 321a905 0d340ad 321a905 0d340ad 5e1e02b 0d340ad 321a905 0d340ad 321a905 0d340ad 321a905 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 321a905 0d340ad 321a905 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 321a905 0d340ad 98b5940 0d340ad 98b5940 0d340ad 321a905 0d340ad 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad 98b5940 0d340ad ba882c4 b7f8ba3 0d340ad 98b5940 0d340ad | 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 | import os
import json
import operator
from typing import TypedDict, Annotated
import gradio as gr
from gradio_client import Client, handle_file
from neo4j import GraphDatabase
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_core.documents import Document
from langchain_experimental.graph_transformers import LLMGraphTransformer
from langgraph.graph import StateGraph, END
# ==========================================
# 1. CONFIGURATION
# ==========================================
AZURE_OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_API_KEY")
AZURE_OPENAI_API_BASE = os.environ.get("AZURE_OPENAI_API_BASE_URL")
AZURE_DEPLOYMENT_NAME = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME")
NEO4J_URI = os.environ.get("NEO4J_URI")
NEO4J_USERNAME = os.environ.get("NEO4J_USERNAME", "neo4j")
NEO4J_PASSWORD = os.environ.get("NEO4J_PASSWORD")
# ==========================================
# 2. SETUP
# ==========================================
llm = ChatOpenAI(
api_key=AZURE_OPENAI_API_KEY,
base_url=AZURE_OPENAI_API_BASE,
model=AZURE_DEPLOYMENT_NAME,
temperature=0
)
try: ChatOpenAI.model_rebuild()
except: pass
def ingest_data():
if not NEO4J_URI or not NEO4J_PASSWORD:
print("⚠️ Neo4j Credentials missing. Skipping ingestion.")
return
print("--- 🔄 Initializing Knowledge Graph ---")
try:
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD))
prime_kg_data = [
("Ischemic Stroke", "associated_with", "Hypertension"),
("Ischemic Stroke", "associated_with", "Atrial Fibrillation"),
("Ischemic Stroke", "treated_by", "Alteplase"),
("Ischemic Stroke", "treated_by", "Aspirin"),
("Ischemic Stroke", "treated_by", "Clopidogrel"),
("Alteplase", "mechanism", "Fibrinolytic Agent"),
("Alteplase", "contraindicated_in", "Active Bleeding"),
("Intracranial Hemorrhage", "associated_with", "Hypertension"),
("Intracranial Hemorrhage", "associated_with", "Cerebral Amyloid Angiopathy"),
("Intracranial Hemorrhage", "treated_by", "Mannitol"),
("Intracranial Hemorrhage", "treated_by", "Nicardipine"),
("Mannitol", "mechanism", "Osmotic Diuretic"),
("Ischemic Stroke", "presents_as", "Hemiparesis"),
("Ischemic Stroke", "presents_as", "Aphasia"),
("Intracranial Hemorrhage", "presents_as", "Severe Headache"),
("Intracranial Hemorrhage", "presents_as", "Vomiting")
]
query = """
UNWIND $data as row
MERGE (h:MedicalEntity {name: row[0]})
MERGE (t:MedicalEntity {name: row[2]})
MERGE (h)-[:PRIMEKG_REL {type: row[1]}]->(t)
"""
with driver.session() as session:
session.run("MATCH (n) DETACH DELETE n")
session.run(query, data=prime_kg_data)
aha_guideline_text = """
For patients eligible for intravenous fibrinolytic therapy, Alteplase (0.9 mg/kg) is recommended within 3 hours of onset of ischemic stroke.
Mechanical Thrombectomy is indicated for patients with large vessel occlusion (LVO) in the anterior circulation within 6 hours.
For patients with acute Intracranial Hemorrhage, patients with systolic blood pressure >220 mmHg should be treated with aggressive blood pressure reduction.
Aspirin is recommended for secondary prevention 24 to 48 hours after thrombolysis.
"""
docs = [Document(page_content=aha_guideline_text)]
llm_transformer = LLMGraphTransformer(llm=llm)
graph_documents = llm_transformer.convert_to_graph_documents(docs)
with driver.session() as session:
for doc in graph_documents:
for node in doc.nodes:
session.run("MERGE (n:GuidelineEntity {id: $id, type: $type})", id=node.id, type=node.type)
for edge in doc.relationships:
session.run("""
MATCH (s:GuidelineEntity {id: $source})
MATCH (t:GuidelineEntity {id: $target})
MERGE (s)-[:GUIDELINE_REL {type: $type}]->(t)
""", source=edge.source.id, target=edge.target.id, type=edge.type)
driver.close()
print("✅ Knowledge Graph Built Successfully.")
except Exception as e:
print(f"❌ KG Initialization Failed: {e}")
ingest_data()
# ==========================================
# 3. TOOLS
# ==========================================
@tool
def detect_stroke_type(image_path: str) -> str:
"""Analyzes a patient's CT scan using the remote Vision AI model."""
HF_SPACE_URL = "Siyad798/stroke-diagnosis-ai"
print(f"--- 🧠 Calling Vision AI for {image_path} ---")
if not os.path.exists(image_path): return json.dumps({"error": f"File not found locally: {image_path}"})
try:
client = Client(HF_SPACE_URL)
result = client.predict(files=[handle_file(image_path)], api_name="/predict")
ai_output = {}
if isinstance(result, (list, tuple)) and len(result) > 1:
json_data = result[1]
if isinstance(json_data, dict): ai_output = json_data
elif isinstance(json_data, str) and os.path.exists(json_data):
with open(json_data, 'r') as f: ai_output = json.load(f)
elif isinstance(json_data, str):
try: ai_output = json.loads(json_data)
except: ai_output = {"raw_output": json_data}
else: ai_output = result
if not ai_output: ai_output = {"error": "Empty response", "diagnosis": "Unknown"}
return json.dumps(ai_output)
except Exception as e: return json.dumps({"error": f"Inference Failed: {str(e)}", "diagnosis": "Unknown"})
@tool
def get_treatment_guideline(stroke_type: str) -> str:
"""Queries AHA/ASA Guidelines from Neo4j Knowledge Graph."""
search_term = "Ischemic Stroke" if "Ischemic" in stroke_type else "Intracranial Hemorrhage"
print(f"--- 🕸️ Querying KG for: {search_term} ---")
if not NEO4J_URI or not NEO4J_PASSWORD: return json.dumps({"error": "Neo4j credentials not configured."})
try:
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD))
with driver.session() as session:
q1 = "MATCH (s:StrokeType {name: $st})-[r:INDICATED_FOR]->(t:Treatment) RETURN t.name as Treatment, t.class as Class, t.dosage as Dosage, r.criteria as Criteria"
treatments = session.run(q1, st=search_term).data()
q2 = "MATCH (c:ClinicalCondition)-[:CONTRAINDICATES]->(t:Treatment)<-[:INDICATED_FOR]-(s:StrokeType {name: $st}) RETURN t.name as Drug, c.name as Contraindication"
safety = session.run(q2, st=search_term).data()
driver.close()
return json.dumps({"Guidelines_Source": "AHA/ASA 2019", "Recommended_Treatments": treatments, "Safety_Warnings": safety})
except Exception as e: return json.dumps({"error": str(e)})
# ==========================================
# 4. AGENT WORKFLOW
# ==========================================
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
image_path: str
stroke_diagnosis: dict
guidelines: dict
final_report: str
def detector_node(state: AgentState):
print("--- Node: Stroke Detector ---")
result_json = detect_stroke_type.invoke(state["image_path"])
diagnosis = json.loads(result_json)
msg = f"Imaging Analysis Complete. Diagnosis: {diagnosis.get('diagnosis')}."
return {"messages": [HumanMessage(content=msg)], "stroke_diagnosis": diagnosis}
def advisor_node(state: AgentState):
print("--- Node: Guideline Advisor ---")
diagnosis = state["stroke_diagnosis"].get("diagnosis", "Unknown")
guidelines_json = get_treatment_guideline.invoke(diagnosis)
guidelines = json.loads(guidelines_json)
return {"messages": [HumanMessage(content=f"Guidelines Retrieved for {diagnosis}.")], "guidelines": guidelines}
def synthesizer_node(state: AgentState):
print("--- Node: Report Synthesizer ---")
prompt = f"""
You are a Stroke Neurologist Assistant. Write a clinical report based on:
1. **Imaging AI Result**: {json.dumps(state['stroke_diagnosis'], indent=2)}
2. **Clinical Guidelines**: {json.dumps(state['guidelines'], indent=2)}
3. **Patient Context**: {state['messages'][0].content}
**Structure:**
- Diagnosis & Severity
- Imaging Findings
- Recommended Interventions (Cite Evidence)
- Safety Checks & Contraindications
"""
response = llm.invoke(prompt)
return {"final_report": response.content}
workflow = StateGraph(AgentState)
workflow.add_node("detector", detector_node)
workflow.add_node("advisor", advisor_node)
workflow.add_node("synthesizer", synthesizer_node)
workflow.set_entry_point("detector")
workflow.add_edge("detector", "advisor")
workflow.add_edge("advisor", "synthesizer")
workflow.add_edge("synthesizer", END)
app = workflow.compile()
# ==========================================
# 5. GRADIO UI
# ==========================================
def predict_stroke_report(uploaded_file, patient_context_input):
if not uploaded_file: return "Please upload a CT scan file."
image_path = uploaded_file.name
inputs = {"messages": [HumanMessage(content=patient_context_input)], "image_path": image_path}
final_report_content = "Processing..."
try:
for event in app.stream(inputs, stream_mode="values"): pass
final_state = event
final_report_content = final_state.get("final_report", "Error generating report.")
except Exception as e: final_report_content = f"Critical Error: {str(e)}"
return final_report_content
# Safe initialization without arguments
with gr.Blocks() as demo:
gr.Markdown("# 🧠 AI Stroke Diagnosis & Guideline Agent")
gr.Markdown("Integrates Vision AI (DenseNet) + Knowledge Graph (Neo4j) + LLM (Azure OpenAI)")
with gr.Row():
with gr.Column():
file_input = gr.File(label="Upload CT (ZIP/DCM)", file_types=[".zip", ".dcm"])
patient_context = gr.Textbox(label="Patient Context", lines=4, value="Patient 55M, onset 2 hours ago. BP 150/90. No history of bleeding.")
submit_btn = gr.Button("Generate Report", variant="primary")
with gr.Column():
report_output = gr.Markdown(label="Clinical Report")
submit_btn.click(predict_stroke_report, inputs=[file_input, patient_context], outputs=report_output)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860) |