Modules — overview¶
STELLAR ships an always-on core (UMAP + gene expression + per-cell-type violins) plus a set of opt-in analysis modules. Each module is a self-contained sub-package that:
- declares its own ingest step (raw inputs → parquet),
- mounts its own FastAPI router under
/api/, - registers its own SPA tab in the frontend nav,
- and — when the
copilotmodule is enabled — contributes Claude tools that operate on its tables.
Modules are dependency-isolated extras: nothing in stellar/core/
imports from stellar/modules/<name>/, so a base install stays slim.
Built-in modules¶
| Name | extras_key | Provides | Required inputs |
|---|---|---|---|
de |
de |
Volcano + sortable table of precomputed DE results | Two parquet files (comparisons + per-gene results) |
hdwgcna |
hdwgcna |
Co-expression modules, hub genes, radial network, optional DME | Three parquet files (+ one optional) |
cellchat |
cellchat |
Source × target pathway heatmap + ligand-receptor table + group delta | Four parquet files extracted from CellChat .rds |
milo |
milo |
Beeswarm-on-UMAP of neighborhood DA + sortable table | Three parquet files from milopy / miloR |
enrichment |
enrichment |
Live EnrichR pass-through (pathway barplots) | None — outbound HTTP at request time |
copilot |
copilot |
Claude-backed chat with auto-collected tools + optional PubMed | ANTHROPIC_API_KEY (env var name configurable); optional NCBI_API_KEY |
Install one or more extras:
pip install 'stellar-atlas[de]' # one module
pip install 'stellar-atlas[de,hdwgcna,copilot]' # several
pip install 'stellar-atlas[full]' # everything
Then turn each one on in stellar.yaml:
modules:
de:
enabled: true
source_dir: data/external/de
hdwgcna:
enabled: true
source_dir: data/external/hdwgcna
copilot:
enabled: true
The module contract¶
Every module is a Python sub-package under stellar/modules/<name>/
that exposes exactly one Module subclass. The core
engine discovers enabled modules from stellar.yaml and calls each
lifecycle hook in turn:
stellar ingest stellar serve
│ │
▼ ▼
┌────────────────────────────────────┐ ┌──────────────────────────────┐
│ 1. Module.ingest(ctx) │ │ 3. Module.routes() → mount │
│ raw inputs → parquet │ │ 4. Module.claude_tools() │
│ 2. Module.duckdb_schema() │ │ 5. Module.claude_dispatch() │
│ extra DDL after parquet load │ │ 6. Module.claude_system_ │
└────────────────────────────────────┘ │ prompt() │
│ 7. Module.frontend_tabs() │
└──────────────────────────────┘
Hooks default to no-op, so a module participates only in the steps it
needs. A module that ingests data but has no AI surface just overrides
ingest, duckdb_schema, routes, and frontend_tabs; everything
else stays empty.
See Extending for the full contract and a worked example of adding your own module.
Adding a third-party module¶
You're not limited to the built-in registry. Third-party modules can
pass their own Module instances directly to
stellar.core.orchestrate.run_ingest() and
stellar.backend.app.create_app(). See Extending.