Skip to content

CodeSeoul/MedDaaS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

65 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MedDaaS - Medical Data-as-a-Service

An AI-powered radiology training platform that helps medical students and radiologists improve their diagnostic skills through intelligent medical document comparison and personalized feedback.

Table of Contents

Overview

MedDaaS enables radiology students to practice reading chest X-rays by writing their own diagnostic reports, which are then compared against expert ground truth reports using advanced AI. The system provides detailed, finding-by-finding analysis with scores, educational feedback, and actionable suggestions for improvement.

Key Features

  • AI-Powered Report Comparison: Uses SpoonAI SDK with graph-based workflows to orchestrate multi-step LLM analysis
  • Real-Time Streaming: Server-Sent Events (SSE) deliver findings progressively as they're analyzed
  • Multi-Dimensional Scoring: Evaluates reports on similarity, completeness, accuracy, and medical terminology
  • Educational Feedback: AI-generated strengths, improvement areas, and specific recommendations
  • Learning History: Track progress over time with detailed comparison history and statistics
  • Interactive Image Viewer: Fullscreen mode with zoom, brightness, and contrast controls
  • User Authentication: Secure session-based auth with bcrypt password hashing and automatic salting
  • Semantic Image Search: RAG-based retrieval using FAISS vector store with cosine similarity

Technology Stack

Backend

  • FastAPI - Modern async Python web framework
  • SpoonAI SDK - LLM orchestration with graph-based workflows
  • PostgreSQL - Persistent data storage
  • SQLAlchemy - Async ORM with Alembic migrations
  • FAISS - Vector similarity search for RAG capabilities

Frontend

  • Jinja2 - Server-side HTML templating
  • Bootstrap CSS - Responsive UI framework
  • Vanilla JavaScript - EventSource API for SSE streaming

LLM Providers (Configurable)

  • Google Gemini (default)
  • Anthropic Claude
  • OpenAI GPT
  • DeepSeek

Infrastructure

  • Docker Compose - Container orchestration
  • Ollama - Local embeddings (optional GPU support)
  • uv - Fast Python package management

Architecture

Graph-Based Workflow

The comparison workflow uses SpoonAI's StateGraph for structured AI orchestration:

START
  β”‚
  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  retrieve_ground_truth  β”‚ ─── Fetch expert report from database
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚
  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    compare_reports      β”‚ ─── LLM analyzes student vs ground truth
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β–Ό                                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  score_report   β”‚    β”‚  generate_summary   β”‚  ← Parallel execution
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚                                β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚
  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       finalize          β”‚ ─── Assemble final results
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”‚
  β–Ό
 END

Custom SpoonAI Tools

MedDaaS implements four custom tools using SpoonAI's BaseTool class, each designed for a specific step in the radiology report analysis workflow:

Tool Type Description
RetrieveGroundTruthTool Data Access Fetches expert radiologist reports from the ground truth repository by image ID
CompareReportsTool LLM-Powered Performs semantic comparison between student and expert reports, extracting findings with streaming support
ScoreReportTool Rule-Based Calculates multi-dimensional scores using finding analysis and medical terminology matching
GenerateSummaryTool LLM-Powered Generates educational feedback with strengths, improvements, and suggestions via streaming

Tool Implementation Pattern

All tools extend BaseTool and implement the async execute() method:

from spoon_ai.tools import BaseTool
from spoon_ai.tools.base import ToolResult

class CompareReportsTool(BaseTool):
    name: str = "compare_reports"
    description: str = "Compares student report against ground truth..."
    parameters: dict = {
        "type": "object",
        "properties": {
            "student_report": {"type": "string", ...},
            "ground_truth_report": {"type": "string", ...},
        },
        "required": ["student_report", "ground_truth_report"],
    }

    async def execute(self, *, student_report: str, ground_truth_report: str, **kwargs) -> ToolResult:
        # LLM-based semantic comparison
        response = await llm.ask(messages=[...], system_msg=COMPARISON_SYSTEM_MSG)
        return ToolResult(output=comparison_data)

Streaming Tool Execution

The CompareReportsTool and GenerateSummaryTool support streaming via execute_stream() with callbacks:

async def execute_stream(
    self,
    *,
    student_report: str,
    ground_truth_report: str,
    on_finding: Callable[[Dict[str, Any]], None],  # Called for each finding
    **kwargs,
) -> ToolResult:
    async for chunk in llm.astream(messages=[...]):
        # Extract complete JSON objects from stream
        new_findings = extractor.feed(chunk.delta)
        for finding in new_findings:
            on_finding(finding)  # Emit to SSE immediately
    return ToolResult(output={"findings": all_findings})

Medical Terminology Scoring

The ScoreReportTool includes a curated list of 30+ radiology terms for terminology assessment:

MEDICAL_TERMS = [
    "consolidation", "opacity", "infiltrate", "effusion", "pneumothorax",
    "cardiomegaly", "mediastinum", "hilar", "pulmonary", "bilateral",
    "costophrenic", "diaphragm", "atelectasis", "pleural", "cardiac", ...
]

Scoring System

Reports are evaluated across four dimensions:

Score Description
Similarity Overall semantic match to ground truth
Completeness Coverage of key findings from expert report
Accuracy Correctness of stated observations
Terminology Proper use of medical/radiological terms

Finding Classification

Each finding is classified as:

  • Correct - Accurately identified and matches ground truth
  • Partial - Partially correct observation
  • Missed - Present in ground truth but not in student report
  • Extra - Student mentioned but not in ground truth

Semantic Similarity Search (RAG)

MedDaaS includes a RAG (Retrieval-Augmented Generation) system for intelligent image retrieval based on natural language queries.

How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        RAG Pipeline                                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                              β”‚
β”‚  INGESTION (Offline)                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚ Medical      β”‚    β”‚ Embedding    β”‚    β”‚ FAISS        β”‚                   β”‚
β”‚  β”‚ Reports      │───▢│ Model        │───▢│ Vector       β”‚                   β”‚
β”‚  β”‚ (JSON)       β”‚    β”‚ (Ollama/     β”‚    β”‚ Store        β”‚                   β”‚
β”‚  β”‚              β”‚    β”‚  Gemini)     β”‚    β”‚              β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚        β”‚                                        β”‚                            β”‚
β”‚        β–Ό                                        β–Ό                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚
β”‚  β”‚ Findings +   β”‚                      β”‚ Vector       β”‚                     β”‚
β”‚  β”‚ Impression + β”‚                      β”‚ Embeddings + β”‚                     β”‚
β”‚  β”‚ MeSH Terms   β”‚                      β”‚ Metadata     β”‚                     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β”‚                                                                              β”‚
β”‚  RETRIEVAL (Runtime)                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚ User Query   β”‚    β”‚ Query        β”‚    β”‚ Cosine       β”‚                   β”‚
β”‚  β”‚ "chest xray  │───▢│ Embedding    │───▢│ Similarity   β”‚                   β”‚
β”‚  β”‚  with pleuralβ”‚    β”‚              β”‚    β”‚ Search       β”‚                   β”‚
β”‚  β”‚  effusion"   β”‚    β”‚              β”‚    β”‚              β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                                                 β”‚                            β”‚
β”‚                                                 β–Ό                            β”‚
β”‚                                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚
β”‚                                        β”‚ Top-1 Match  β”‚                     β”‚
β”‚                                        β”‚ (Image +     β”‚                     β”‚
β”‚                                        β”‚  Metadata)   β”‚                     β”‚
β”‚                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β”‚                                                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Embedding Format

Medical reports are embedded using a structured format:

Findings: {radiological findings text}
Impression: {clinical impression}
Conditions: {parsed MeSH terms}

Stored Metadata

Each vector in the FAISS store includes:

Field Description
image_id Unique X-ray identifier
image_path Path to PNG image file
findings Expert radiological findings
impression Clinical impression summary
mesh_terms Parsed MeSH medical terms
difficulty Computed difficulty (easy/medium/hard)

Difficulty Classification

Cases are automatically classified by complexity:

  • Easy: Normal findings or single condition
  • Medium: 2-3 conditions present
  • Hard: 4+ conditions or complex pathology (mass, effusion, pneumothorax, etc.)

Embedding Providers

Provider Model Use Case
Ollama nomic-embed-text Local, no rate limits, GPU optional
Gemini gemini-embedding-001 Cloud, fast, good accuracy
OpenAI text-embedding-3-small Cloud, highest accuracy

Setup

Prerequisites

  1. uv - Python package manager
  2. Docker - For PostgreSQL and Ollama

Installation

  1. Clone the repository:

    git clone https://github.com/CodeSeoul/MedDaaS.git
    cd MedDaaS
  2. Create environment file:

    cp .env.example .env
  3. Configure .env with your settings:

    # Required: At least one LLM API key
    GEMINI_API_KEY=your_gemini_key
    # ANTHROPIC_API_KEY=your_anthropic_key
    # OPENAI_API_KEY=your_openai_key
    
    # Database
    DATABASE_PASSWORD=your_secure_password
    
    # Session security
    SESSION_SECRET_KEY=your_secret_key_here
  4. Install dependencies:

    uv sync
  5. Start infrastructure:

    docker compose up -d
  6. (Optional) Pull embedding model for RAG:

    docker exec ollama ollama pull nomic-embed-text

Running

uv run main.py

Access the application:

Data Preparation

MedDaaS uses data from the NIH Open-i:images and NIH Open-i:reports chest X-ray dataset.

1. Download Data

Download the PNG images and XML reports from the NIH website.

2. Parse Reports

Convert XML reports to JSON format:

uv run python report_xml_parsing.py \
  --xml_dir /path/to/NLMCXR_reports/ecgen-radiology \
  --out_json ./data/reports/all.json \
  --images_root ./data/images/

3. Generate Embeddings (Optional)

For semantic search capabilities:

uv run python ingest_reports.py --limit 100 --provider ollama --input ./data/reports/all.json

API Endpoints

Authentication

Method Endpoint Description
POST /api/v1/auth/signup Create new account
POST /api/v1/auth/login Authenticate user
GET /api/v1/auth/me Current user info

Reports

Method Endpoint Description
POST /api/v1/reports/compare Compare student report (sync)
GET /api/v1/reports/compare/stream Compare with SSE streaming
GET /api/v1/reports/images List available images
GET /api/v1/reports/images/random Random image for practice
GET /api/v1/reports/images/{id} Get image metadata

History & Stats

Method Endpoint Description
GET /api/v1/history/me User's comparison history
GET /api/v1/history/{id} Specific comparison details
GET /api/v1/stats/me User performance statistics

Project Structure

MedDaaS/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ agents/              # SpoonAI graph workflows
β”‚   β”œβ”€β”€ api/routes/          # FastAPI endpoints
β”‚   β”œβ”€β”€ core/                # Config, auth, exceptions
β”‚   β”œβ”€β”€ db/                  # Database session management
β”‚   β”œβ”€β”€ models/              # SQLAlchemy models
β”‚   β”œβ”€β”€ prompts/             # LLM prompt templates
β”‚   β”œβ”€β”€ repositories/        # Data access layer
β”‚   β”œβ”€β”€ schemas/             # Pydantic models
β”‚   β”œβ”€β”€ services/            # Business logic
β”‚   β”œβ”€β”€ templates/           # Jinja2 HTML templates
β”‚   β”œβ”€β”€ tools/               # SpoonAI tool implementations
β”‚   └── utils/               # Helpers (JSON streaming, etc.)
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ images/              # X-ray PNG files
β”‚   └── reports/             # Ground truth JSON
β”œβ”€β”€ docker-compose.yml       # PostgreSQL + Ollama
β”œβ”€β”€ main.py                  # Application entry point
└── pyproject.toml           # Dependencies

Hackathon Requirements Satisfaction

This project was built for the Scoop AI Hackathon: Seoul Bowl (December 20-21, 2025), organized by Neo and SpoonOS.

Track: AI4Science & Engineering

MedDaaS directly addresses the AI4Science track by accelerating medical education through AI-powered diagnostic training.

Judging Criteria Alignment

Criteria How MedDaaS Satisfies It
Innovation & Originality Novel application of graph-based LLM workflows to medical education; finding-by-finding streaming analysis provides real-time learning feedback unlike traditional batch processing
Technical Execution Production-ready architecture with async FastAPI, SpoonAI graph orchestration, PostgreSQL persistence, SSE streaming, and multi-provider LLM support
Real-World Impact Addresses critical shortage of radiology training resources; enables scalable, personalized education without requiring expert radiologist time
User Experience & Design Intuitive UI with progressive result streaming, collapsible report cards, fullscreen image viewer with zoom/contrast controls, and responsive design
Presentation & Teamwork Built collaboratively by CodeSeoul members; clean codebase with separation of concerns and comprehensive documentation

Key Technical Highlights

  1. SpoonAI SDK Integration

    • Graph-based workflow orchestration for complex multi-step AI tasks
    • Parallel node execution for scoring and summary generation
    • Streaming tool execution with callbacks
  2. Agentic AI Capabilities

    • Autonomous report analysis without human intervention
    • Multi-dimensional evaluation with educational feedback
    • Context-aware suggestions based on specific findings
  3. Production Architecture

    • Async/await throughout for high concurrency
    • Database fallback for offline operation
    • Configurable LLM providers (Gemini, Claude, GPT, DeepSeek)
  4. RAG-Based Semantic Search

    • FAISS vector store for efficient similarity search
    • Medical text embeddings with MeSH term enrichment
    • Cosine similarity retrieval for top-k matching
    • Multi-provider embedding support (Ollama, Gemini, OpenAI)

Sponsors Used

  • SpoonAI SDK - Core orchestration framework for LLM workflows
  • Google Cloud - Gemini API for language model inference

Contributing

This project was developed by MedDaaS team for the Scoop AI Hackathon.

License

MIT License - See LICENSE file for details.


Built with SpoonAI for the Scoop AI Hackathon: Seoul Bowl 2025

About

Radiology training project for Spoon OS hackathon

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •