Skip to content

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 copilot module 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.