Leukemia-AI / graph /workflow.py
Chaudhry Muhammad Suleman
feat: Add LeukemiaScope Agentic AI multi-agent workflow
e888b38
"""
LeukemiaScope Agentic Workflow
LangGraph-based multi-agent system for blood cell analysis
"""
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from PIL import Image
# Import agent nodes
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from agents.image_analyzer import image_analyzer_node
from agents.clinical_advisor import clinical_advisor_node
from agents.report_generator import report_generator_node
class WorkflowState(TypedDict):
"""State shared across all agents in the workflow"""
# Input
image: Image.Image
patient_id: str
patient_context: str
# Image Analysis Results
classification: str
confidence: float
is_leukemia: bool
raw_response: str
analysis_complete: bool
# Clinical Advice
clinical_advice: str
next_steps: list
severity: str
requires_urgent_action: bool
clinical_complete: bool
# Report
report: str
report_complete: bool
# Errors
error: str
def should_consult_clinical_advisor(state: WorkflowState) -> str:
"""
Conditional edge: Route to Clinical Advisor only if leukemia is detected
Args:
state: Current workflow state
Returns:
"clinical_advisor" if leukemia detected, else "report_generator"
"""
if state.get("is_leukemia", False):
return "clinical_advisor"
return "report_generator"
def create_workflow() -> StateGraph:
"""
Create the LangGraph workflow for blood cell analysis
Workflow:
1. Image Analyzer (MedGemma) - Always runs first
2. Clinical Advisor - Only if leukemia detected
3. Report Generator - Always runs, generates final report
"""
# Create the graph
workflow = StateGraph(WorkflowState)
# Add nodes
workflow.add_node("image_analyzer", image_analyzer_node)
workflow.add_node("clinical_advisor", clinical_advisor_node)
workflow.add_node("report_generator", report_generator_node)
# Set entry point
workflow.set_entry_point("image_analyzer")
# Add conditional edge from image_analyzer
workflow.add_conditional_edges(
"image_analyzer",
should_consult_clinical_advisor,
{
"clinical_advisor": "clinical_advisor",
"report_generator": "report_generator"
}
)
# Clinical advisor always leads to report generator
workflow.add_edge("clinical_advisor", "report_generator")
# Report generator ends the workflow
workflow.add_edge("report_generator", END)
return workflow
def compile_workflow():
"""Compile the workflow into an executable graph"""
workflow = create_workflow()
return workflow.compile()
# Global compiled workflow
_app = None
def get_app():
"""Get or create the compiled workflow app"""
global _app
if _app is None:
_app = compile_workflow()
return _app
def run_analysis(image: Image.Image, patient_id: str = "Anonymous", patient_context: str = "") -> dict:
"""
Run the complete analysis workflow
Args:
image: PIL Image of blood cell
patient_id: Optional patient identifier
patient_context: Optional context about patient
Returns:
Final state with all results
"""
app = get_app()
# Initial state
initial_state = {
"image": image,
"patient_id": patient_id,
"patient_context": patient_context,
"classification": "",
"confidence": 0.0,
"is_leukemia": False,
"raw_response": "",
"analysis_complete": False,
"clinical_advice": "",
"next_steps": [],
"severity": "Low",
"requires_urgent_action": False,
"clinical_complete": False,
"report": "",
"report_complete": False,
"error": ""
}
# Run workflow
final_state = app.invoke(initial_state)
return final_state