Architecture
QuOptuna ships as a single Python package, src/quoptuna/, that bundles four cooperating layers: a core optimization engine (backend/), a FastAPI server (server/), a command-line interface (cli.py), and a pre-built Next.js static UI (web/). A legacy Streamlit dashboard (frontend/app.py) is also included as a fallback. This page explains how those pieces relate and how a request flows through them.
The layers
Section titled “The layers”The engine is the heart of the system: an Optuna wrapper that builds a search space, runs trials, scores them, and records results. It knows nothing about HTTP.
The server is a thin FastAPI application (quoptuna.server.main:app, titled “QuOptuna Next API”). It exposes REST routers, orchestrates runs as background tasks, and manages persistence. It also mounts the compiled Next.js UI at / and serves interactive API docs at /api/docs.
The web UI is a Next.js 15 / React 19 App Router application. The top-level frontend/ directory holds its dev source; make build_package runs npm run build and copies frontend/out into src/quoptuna/web, so a plain uvx quoptuna ships the fully-built UI.
The CLI drives the same engine headlessly for scripted or reproducible runs.
flowchart TD Browser["Browser (Next.js UI)"] -->|REST| API["FastAPI server"] CLI["CLI"] --> Engine["Optimization engine (Optuna)"] API --> Engine Engine --> Study[("Optuna study (SQLite in db/, or PostgreSQL)")] API --> Stores[("App database: runs, datasets, analysis tables")] API -->|mounts| BrowserPackaged vs dev mode
Section titled “Packaged vs dev mode”In packaged mode, one uvicorn process serves both the UI and the API on a single port (default 8000) — same origin, no CORS gymnastics for the user. This is what end users get from uvx quoptuna.
In dev mode, two processes run side by side: FastAPI on :8000 and the Next.js dev server on :3000. The server is CORS-enabled so the separate frontend origin can call it during development.
Request flow
Section titled “Request flow”Starting a run is asynchronous. The browser issues POST /api/v1/optimize, and the server launches the optimization as a FastAPI background task so the request returns immediately. The engine then writes trials into an Optuna study. The UI polls GET /api/v1/optimize/{id}/trials for live progress rather than holding a long connection open. The CLI skips HTTP and runs the same workflow in-process (server/services/headless.py).
Persistence model
Section titled “Persistence model”Server-side state lives in one SQLModel application database (DATABASE_URL, default sqlite:///./db/quoptuna_app.db), accessed through three service modules:
run_store— durable optimization-run records (quoptuna_runs). On startup it marks any stalerunning/pendingruns asinterrupted, which enables crash rehydration: a restarted server can recover and reconcile in-flight work.analysis_store— analysis snapshots, revisions, jobs, reports and artifacts (SHAP, confusion matrices, LLM reports). Analysis jobs left pending/running by a restart are marked failed on startup. Figure artifacts are written toARTIFACT_ROOT(defaultdb/analysis) or to S3 whenARTIFACT_STORAGE=s3.dataset_registry— maps eachdataset_id(quoptuna_datasets) to its persisted CSV path.
These are distinct from the Optuna study storage. The stores track orchestration and results metadata; the Optuna studies own the trial-level truth.
Auth model
Section titled “Auth model”Authentication is optional and handled by Auth0 (the auth0-server-python SDK). Sessions live in encrypted, httponly cookies — there is no server-side session store to manage. Enforcement is a no-op when the AUTH0_* environment variables are unset, so local, dev, and test runs work unauthenticated with zero configuration.