☀️

RAGFlow API Module

HTTP/WebSocket Adapter Layer for Multi-Tenant RAG Platform

Overview

The RAGFlow API module is the HTTP/WebSocket adapter layer that connects external clients to the RAG (Retrieval-Augmented Generation) platform core. It implements:

Architecture Diagram

graph TD A["Client
(Web/Mobile/API)"] -->|HTTP/WebSocket| B["Quart Server
(Async Handler)"] B -->|Request Validation| C["Auth Middleware
(JWT Decode)"] C -->|Load Tenant| D["Multi-Tenant Context"] D -->|Route to Handler| E["REST Endpoints
(apps/)"] E -->|Business Logic| F["Service Layer
(15+ Services)"] F -->|Tenant Filtering| G["Peewee ORM
(Synchronous)"] G -->|SQL Queries| H["MySQL/PostgreSQL
(Transactional)"] F -->|Queue Task| I["Redis Queue"] I -->|Poll & Process| J["Background Executor
(Task Worker)"] J -->|Update Progress| K["Redis Cache"] B -->|Progress Stream| L["WebSocket Handler"] K -->|Fetch Status| L L -->|Push Update| A J -->|State Mutation| H M["Error Handler
(Custom Exceptions)"] -->|Convert to HTTP| E N["Connection Pool
(10-20 conns)"] -->|Absorb Contention| G

Design Philosophy

Key Principle: Optimize for concurrent I/O with a clear separation between request handling (async) and business logic (service layer). Delegate heavy processing (embeddings, parsing) to background workers instead of blocking HTTP handlers.

Why Async Web Server + Sync ORM?

The hybrid approach achieves high concurrency for I/O-bound workloads (uploads, API calls to external LLMs) while keeping business logic simple and debuggable:

Multi-Tenancy Enforcement

Every domain object carries a tenant_id. Auth layer loads the user's default tenant and passes it through the entire request lifecycle:

Service Layer: More Than CRUD

Domain services do not merely wrap database operations. They coordinate state transitions, manage soft deletes, queue tasks, and handle cross-table mutations atomically. Bypassing a service = bypassing multi-tenancy isolation.

Sub-Modules

Document Upload Lifecycle (Narrated Walkthrough)

Trace a PDF upload from HTTP request through task execution and progress reporting:

  1. HTTP POST /api/documents/upload — Client sends multipart form with PDF file, document name, and knowledge base ID. Request lands in apps/document_api.py handler.
  2. Auth Middleware Validates JWT — Token decoded in common/auth.py. User's default tenant extracted; tenant ID passed through request context.
  3. Handler Calls DocumentService.create() — Business logic layer receives request. Validates: tenant isolation, knowledge base ownership, file format, size limits.
  4. File Saved to Storage — PDF persisted to disk or cloud storage. File path stored in Document row.
  5. Document Row Inserted — Peewee ORM inserts row into documents table. Tenant ID enforced at ORM level.
  6. TaskService.queue() — DocumentService delegates heavy lifting to TaskService. Creates Task row with status=pending.
  7. Redis Queue Entry Created — Task ID pushed to Redis list. Background executor polls this queue.
  8. Handler Returns 202 Accepted — HTTP response sent immediately. Document created, processing queued.
  9. Background Executor Polls Redis — Separate process (not blocking HTTP handler) dequeues task ID.
  10. Parsing & Chunking — ParsingService extracts text. ChunkService breaks PDF into semantic chunks. Embeddings generated (external LLM call).
  11. Progress Updated in Redis — Executor pushes progress events: { "task_id": "...", "progress": 45 } to Redis.
  12. WebSocket Handler Fetches Status — Browser WebSocket listener polls /api/tasks/{task_id}/status or listens to server push.
  13. Client Receives Progress — UI updates progress bar in real-time without blocking HTTP.
  14. Task Completion — Executor marks task status=completed. Document now searchable.

Authentication & Multi-Tenancy

JWT Token Flow

All requests require a valid JWT token issued by the identity provider:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Auth middleware in common/auth.py decodes the token and extracts:

Tenant Isolation

Multi-tenancy is enforced at three levels:

  1. Auth Layer: User's tenant ID loaded from token
  2. ORM Layer: All queries filtered by WHERE tenant_id = ?
  3. Service Layer: Services validate tenant ownership before state mutations

Password Hashing

Passwords (if used for local auth) are hashed with bcrypt in apps/auth/password.py:

import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
bcrypt.checkpw(password.encode(), hashed)  # verify

Architectural Decisions

Decision Alternative Rationale
Async Web Server (Quart) Synchronous Flask Handle 100+ concurrent uploads without thread-per-request overhead. Optimal for I/O-bound workloads.
Synchronous Peewee ORM Async ORM (asyncpg, motor) Simpler to debug, fewer abstractions. Connection pool absorbs DB contention. Avoids async driver complexity.
Service Layer Pattern Direct ORM queries in handlers Isolate business logic, coordinate state transitions atomically, enforce multi-tenancy consistently.
Implicit Task Queue (Redis) Celery / RabbitMQ Simpler operations (no external broker), Redis widely deployed. Tradeoff: manual retry/dead-letter handling.
Tenant ID at Query Level Application-tier filtering Prevent data leaks from missed filters. ORM-level enforcement is harder to bypass accidentally.
Custom Peewee Fields (JSONField, ListField) JSON columns (native DB types) Portability across MySQL/PostgreSQL. Custom serialization logic stays in Python, not SQL.
Deadlock Retry Pattern Fail on first deadlock MySQL InnoDB deadlocks under high concurrency. Exponential backoff retry improves reliability without re-architecting transactions.
WebSocket for Real-Time Progress Polling / Server-Sent Events Bidirectional communication. Lower latency than polling. More efficient than HTTP long-polling.

Directory Structure

api/
├── apps/                       # Blueprint routing and REST handlers
│   ├── __init__.py            # Blueprint registration
│   ├── auth/                  # JWT decode, password hashing
│   ├── document_api.py        # /api/documents/* endpoints
│   ├── dialog_api.py          # /api/dialogs/* endpoints
│   ├── task_api.py            # /api/tasks/* endpoints (status polling)
│   └── websocket/             # WebSocket handlers for real-time updates
├── db/
│   ├── db_models.py           # Peewee ORM models (15+ tables)
│   ├── services/              # 15+ domain services
│   │   ├── document_service.py
│   │   ├── dialog_service.py
│   │   ├── task_service.py
│   │   ├── llm_service.py
│   │   └── ...
│   └── joint_services/        # Cross-domain services
│       ├── user_account_service.py
│       ├── memory_message_service.py
│       └── tenant_model_service.py
├── common/
│   ├── auth.py               # Auth decorators, JWT decode
│   ├── exceptions.py         # Custom exception classes
│   └── utils.py              # Shared utility functions
└── utils/
    ├── file_ops.py           # File I/O helpers
    ├── serialization.py      # JSON/pickle serialization
    └── logging.py            # Custom logging setup