Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |