Skip to content

API reference

Auto-generated Python API reference for the surfaces a third-party caller is likely to hit:

The deeper config-model reference (per-field tables and minimal / full YAML examples) lives at Configuration.


Configuration models

stellar.config holds the Pydantic schema for stellar.yaml. The field-by-field tables are at Configuration; the loader is documented here for code use.

The root model is rendered at Configuration → Top-level; the loader and the most-called helper are documented here.

load_config

stellar.config.load_config

load_config(path: str | Path) -> StellarConfig

Parse path as YAML and validate against :class:StellarConfig.

Raises:

Type Description
FileNotFoundError

If path doesn't exist.

ValidationError

On any schema violation. The error message points at the exact bad key + value (e.g. cohort.umap.obsm_key: input should be a valid string).

StellarConfig.enabled_modules

def enabled_modules(self) -> list[str]: ...

Return the list of module keys (e.g. ['de', 'hdwgcna']) where enabled is truthy. Used internally by stellar ingest / stellar serve to decide what to run.


Module contract

Adding a new module is one subclass of Module plus a folder under stellar/modules/<name>/. The full worked example (SCENIC) is in Extending; the canonical reference follows.

Module

stellar.module_api.Module

Bases: ABC

Base class for an opt-in STELLAR analysis module.

Subclasses set class-level attributes:

Attributes:

Name Type Description
name str

Slug used in URLs and config keys (e.g. "de").

title str

Human-readable name shown in nav / docs.

extras_key str | None

If set, the optional-deps group that gates this module (matches the key under [project.optional-dependencies] in pyproject.toml).

config_key str

Key under modules in stellar.yaml.

ingest
ingest(ctx: ModuleContext) -> None

Read raw inputs (per :attr:config_key in stellar.yaml) and write parquet under ctx.parquet_dir. Must be idempotent.

duckdb_schema
duckdb_schema() -> str

Return extra SQL to run after parquet load — typically views that join module tables to cells_v. Empty string means no-op.

routes
routes() -> APIRouter | None

Return a FastAPI router or None. Mounted under /api so the router's own prefix (e.g. /de) becomes /api/de/....

claude_tools
claude_tools() -> list[dict[str, Any]]

Anthropic tool schemas exposed when the copilot module is on.

claude_dispatch
claude_dispatch(
    stores: Any = None,
) -> dict[str, Callable[..., Any]]

{tool_name: callable} matching the names in :meth:claude_tools.

Each callable is invoked with the JSON arguments Claude emitted. stores is the live :class:stellar.core.stores.StoreRegistry — close over it to read the DuckDB / Lance backing this atlas. Pass-through stores=None is allowed for modules that don't need atlas state (e.g. Enrichment, which only calls EnrichR).

Example::

def claude_dispatch(self, stores):
    duck = stores.duck
    return {
        "list_things": lambda: duck.query("SELECT …").to_pylist(),
    }
claude_system_prompt
claude_system_prompt() -> str | None

Optional prompt fragment appended to the copilot's system prompt. Keep it concise (3–10 lines) and module-scoped.

frontend_tabs
frontend_tabs() -> list[TabDef]

Tabs to surface in the SPA nav. The frontend reads /api/config and renders only the tabs declared here for enabled modules.

ModuleContext

stellar.module_api.ModuleContext dataclass

ModuleContext(
    config: StellarConfig,
    project_root: Path,
    parquet_dir: Path,
)

Read-only build-time context passed to :meth:Module.ingest.

Attributes:

Name Type Description
config StellarConfig

Parsed stellar.yaml.

project_root Path

Directory containing stellar.yaml — paths in the config are resolved against this.

parquet_dir Path

Destination for module parquet output (project_root/data/parquet).

TabDef

stellar.module_api.TabDef

Bases: TypedDict

One entry in the SPA nav. Returned by :meth:Module.frontend_tabs.

Fields

path : str URL path under the SPA base, e.g. /de/conditions. label : str Display text in the nav bar. icon : str Optional emoji / glyph rendered before the label. order : int Sort key — lower numbers appear first. Core tabs sit at 0..9.


Stores

The read-only data-access layer. Both stores are constructed once per process by create_app() and shared by every request.

LanceStore

The gene-major matrix accessor.

stellar.core.stores.LanceStore

LanceStore(expr_path: Path, cells_path: Path)

Gene-major matrix accessor.

Each Lance row holds (gene_id, cell_idx, values) — a sparse representation of one gene's expression across all cells. Coloring a UMAP by any gene is one row lookup, no scan.

A sibling Lance table maps row index ↔ cell_id so the API can translate between client-facing string ids and dense vector indices.

cell_ids property
cell_ids: ndarray

Array of cell_id strings indexed by Lance row position.

cell_pos property
cell_pos: dict[str, int]

cell_id → row index. Built once, reused by every request.

Reads cell_ids before taking the lock — cell_ids itself acquires the same non-reentrant lock and would deadlock here.

get_gene
get_gene(gene_id: str) -> np.ndarray

Return a dense expression vector of length n_cells for one gene.

Cached per-instance. Raises :class:KeyError if the gene isn't in this matrix.

Gene names with apostrophes (rare for HGNC symbols, possible for user-loaded var indices) are escaped before going into the Lance filter — O'Connor's-marker'; DROP TABLE becomes a literal non-match rather than a parser break.

get_genes
get_genes(
    gene_ids: Iterable[str],
) -> tuple[np.ndarray, list[str]]

Return a (n_cells, k) dense matrix plus the gene order.

DuckStore

stellar.core.stores.DuckStore

DuckStore(db_path: Path, cohort: Cohort)

Read-only DuckDB connection, thread-safe via cursor copies.

Every cursor we hand out also has a TEMPORARY VIEW cells_v installed on it — this is the cohort-cleaned view (condition remap + exclusions) every downstream route reads. The SQL comes from :func:stellar.core.cells_v.cells_v_sql so it's driven by project config, never hard-coded.

cursor
cursor() -> duckdb.DuckDBPyConnection

Cheap per-request cursor — DuckDB cursors are session-isolated.

Reinstalls the cells_v view per cursor because TEMPORARY VIEWs live on the connection they were created on; a child cursor is effectively a new session.

StoreRegistry

stellar.core.stores.StoreRegistry

StoreRegistry(
    duck: DuckStore,
    lances: dict[str, LanceStore],
    primary: str,
)

Per-project handle holding every store the API needs.

Constructed once at startup by :func:stellar.backend.app.create_app from a :class:stellar.config.StellarConfig; routes call request.app.state.stores to reach it.

resolve_gene
resolve_gene(
    gene: str, prefer: str | None = None
) -> tuple[LanceStore, str]

Pick the matrix that holds gene (primary first, then any wide).

Returns (store, matrix_name). Raises :class:KeyError if the gene is in none of the matrices.


Orchestration

The build pipeline behind stellar ingest.

run_ingest

stellar.core.orchestrate.run_ingest

run_ingest(
    config: StellarConfig,
    project_root: Path,
    *,
    modules: Iterable[Module] = (),
    paths: BuildPaths | None = None,
    verbose: bool = True,
) -> BuildPaths

Build all data artifacts for a project.

Parameters:

Name Type Description Default
config StellarConfig

Validated :class:StellarConfig from stellar.yaml.

required
project_root Path

Directory containing stellar.yaml; all relative paths in the config are resolved against this.

required
modules Iterable[Module]

Optional iterable of :class:Module instances to run. M1 ships with none; M2+ modules will be discovered here.

()
paths BuildPaths | None

Override the default layout. Mostly for tests.

None

BuildPaths

stellar.core.orchestrate.BuildPaths dataclass

BuildPaths(
    project_root: Path,
    parquet_dir: Path,
    lance_dir: Path,
    duckdb_path: Path,
)

default_paths

stellar.core.orchestrate.default_paths

default_paths(project_root: Path) -> BuildPaths

Backend app factory

The FastAPI app behind stellar serve.

create_app

stellar.backend.app.create_app

create_app(
    config: StellarConfig,
    project_root: Path,
    *,
    modules: Iterable[Module] = (),
) -> FastAPI

Construct the FastAPI app for one project.

Parameters:

Name Type Description Default
config StellarConfig

Parsed :class:StellarConfig.

required
project_root Path

Directory containing stellar.yaml — used to resolve relative paths in the config.

required
modules Iterable[Module]

Optional iterable of module instances. Each module's :meth:Module.routes router (if non-None) is mounted under /api when the module is enabled in config.modules.

()

IO helpers

convert_rds_to_h5ad

stellar.core.io.seurat.convert_rds_to_h5ad

convert_rds_to_h5ad(
    rds_path: Path,
    *,
    cache_dir: Path | None = None,
    force: bool = False,
) -> Path

Convert a Seurat .rds to .h5ad via R + SeuratDisk/sceasy.

Caches the result next to the source (or under cache_dir if given) and short-circuits when the cache is fresher than the source.

Parameters:

Name Type Description Default
rds_path Path

Source Seurat object. Must exist.

required
cache_dir Path | None

Where to write the cached h5ad. Defaults to rds_path.parent.

None
force bool

Re-run even if a cached h5ad exists and is up-to-date.

False

Returns:

Type Description
Path

The cached .h5ad file.

Raises:

Type Description
FileNotFoundError

rds_path missing.

SeuratConversionError

Rscript not on PATH, R package missing, or the R snippet exited non-zero. The error message includes the actionable install command.

resolve_input_path

stellar.core.io.seurat.resolve_input_path

resolve_input_path(path: Path) -> Path

Normalise an input matrix path.

If path ends in .rds (case-insensitive), convert it via :func:convert_rds_to_h5ad and return the cached .h5ad. Otherwise return path unchanged.

This is what builder code (Lance + cells ingest) calls to get the h5ad they actually open. Users with Seurat objects never have to convert manually.

SeuratConversionError

stellar.core.io.seurat.SeuratConversionError

Bases: RuntimeError

Raised when an .rds → .h5ad conversion fails.


Doctor

The pre-flight validator behind stellar doctor.

run_doctor

stellar.core.doctor.run_doctor

run_doctor(
    config: StellarConfig, project_root: Path
) -> DoctorReport

Run every check; return the populated report (caller decides on exit code based on report.errors).

DoctorReport

stellar.core.doctor.DoctorReport dataclass

DoctorReport(items: list[Diagnostic] = list())

Diagnostic

stellar.core.doctor.Diagnostic dataclass

Diagnostic(severity: str, section: str, message: str)

Deploy

The rsync + snippet renderer behind stellar deploy.

run_deploy

stellar.core.deploy.run_deploy

run_deploy(
    config: StellarConfig,
    project_root: Path,
    *,
    target_dir: Path | None = None,
    dry_run: bool = False,
) -> int

Sync stellar/frontend/dist/ and the project's pre-baked static artifacts to target_dir. Render the matching nginx + systemd snippets to stdout.

Returns the rsync exit code (or 2 if any precheck fails).


Usage examples

Ingest from Python

from pathlib import Path

from stellar.config import load_config
from stellar.core.orchestrate import run_ingest
from stellar.modules.registry import builtin_modules

project_root = Path("/srv/atlases/my_atlas").resolve()
config       = load_config(project_root / "stellar.yaml")
run_ingest(
    config,
    project_root,
    modules=builtin_modules(),
    verbose=True,
)

Serve a project as an embedded FastAPI app

from pathlib import Path

import uvicorn

from stellar.backend.app import create_app
from stellar.config import load_config
from stellar.modules.registry import builtin_modules

project_root = Path("/srv/atlases/my_atlas").resolve()
config       = load_config(project_root / "stellar.yaml")
app          = create_app(config, project_root, modules=builtin_modules())

# Mount the app inside a larger uvicorn deployment, or run standalone:
if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=18901)

Run only your own modules (no built-ins)

from my_lab_modules import RegulonModule, TrajectoryModule

app = create_app(
    config,
    project_root,
    modules=[RegulonModule(), TrajectoryModule()],
)

The built-in registry is convenience, not a chokepoint — the orchestrator and app factory both accept any iterable of Module instances. See Extending for the contract.