Annotates Seurat .rds objects. The Python arm in ../python/ annotates
.h5ad with the same algorithm; this arm refuses .h5ad input rather than converting it.
The step-by-step setup and the smoke test are in the repository README.
R 4.2 or later. The package is served from its own CRAN-like repository on R-universe
(https://haoran-code.r-universe.dev), which also serves presto, the genome-wide
differential-expression engine that is not on CRAN. One install.packages() brings
in the required packages, with prebuilt binaries on Windows and macOS:
options(repos = c(scmarkeragent = "https://haoran-code.r-universe.dev",
CRAN = "https://cloud.r-project.org"))
install.packages("scmarkeragent")
From a clone (after editing the code), set the same options() and run
remotes::install_local("R", dependencies = TRUE) from the repository root. Seurat,
presto and the other runtime packages are declared in Imports, so both routes install
them without further flags. Only ggrastr and png are optional (rasterized figure points
and one stitched PNG); without them the pipeline still runs and records what it skipped.
Neither route uses the GitHub API, whose anonymous quota (60 calls per hour per IP
address) is what makes remotes::install_github() fail with HTTP error 403 behind
shared addresses; the package therefore carries no Remotes: field. If R-universe is
unreachable, presto can be installed over the git protocol instead:
remotes::install_git("https://github.com/immunogenomics/presto.git", ref = "a24772a135c7895a8183b007376050556c60a05b").
If presto is missing when a run starts, annotate() stops before doing anything and
prints both lines. If a CRAN package fails to compile from source, the message names the
missing system library (libxml2, libcurl, libpng, ...); install it with your system's
package manager and rerun.
The pipeline itself, with the shared configuration, prompts, schemas and viewer assets,
ships under inst/scmarkeragent/ and runs in a fresh Rscript process.
DESCRIPTION lists the required dependencies. For a pinned dependency environment,
use inst/environment/renv.lock; set SCMA_PRESTO_SOURCE to a local checkout of the
presto revision shown above, then run:
renv::restore(lockfile = system.file("environment/renv.lock", package = "scmarkeragent")).
scmarkeragent::download_resources("~/scmarkeragent-resources")
Fetches the archive from Zenodo (about 150 MiB compressed, 760 MiB unpacked), unpacks it
and verifies every file against the shipped SHA-256 index; R's 60-second download timeout
is lifted for the transfer. verify_resources() checks an existing directory; url =
points at a mirror.
Sys.setenv(OPENAI_API_KEY = "sk-...")
Sys.setenv(OPENAI_BASE_URL = "https://...") # only for an OpenAI-compatible provider
Sys.setenv(SCMA_LLM_MODEL = "...") # optional model override
Calls are cached by model, reasoning setting and canonical prompt bytes, so rerunning the
same data replays from cache; cold calls are written to llm_cold_calls.jsonl. Without
credentials the model stages report skipped_no_credentials and never reach the network;
offline = TRUE enforces that even when credentials are present.
scmarkeragent::annotate(
input = "pbmc.rds", tag = "pbmc",
species = "Human", tissue = "blood",
counts_source = "RNA/counts",
resource_dir = "~/scmarkeragent-resources"
)
| Argument | |
|---|---|
input |
Seurat .rds with raw counts; gene symbols as rownames, unique cell ids |
counts_source |
where the raw counts are, as <assay>/<layer>. Required, never inferred |
species |
"Human", "Mouse" or "Rat"
|
tissue |
free text, resolved against UBERON; several names joined by | (e.g. urinary bladder|adipose tissue) are one operator list: the marker menu is the union of each tissue's UBERON closure, every candidate records which listed tissue admitted it, and the list is written to the run manifest |
disease |
default "Normal"; several values as a character vector |
clustering_resolution |
Leiden resolution, default 0.5. Clustering is always recomputed; a run that yields a single cluster stops with a message, because one-vs-rest differential expression is undefined for it |
resource_dir |
the downloaded marker resource, verified before the run starts |
work_dir |
cache and results root; default ./.scmarkeragent
|
offline |
deterministic stages only, no model contact |
cluster_annotation |
FALSE skips the annotating agent; the label becomes the top of the retrieval order, and every field recording how the label was produced says so |
annotate() returns the parsed run_manifest.json invisibly: run status, per-stage
outcomes and the path of every delivered file. check_input() tells which arm a file
belongs to.
One directory per run, <work_dir>/results/<tag>/:
| File | Contents |
|---|---|
cluster_summary.csv |
one row per cluster: the label, its parent and subtype, cell state, resolution status, confidence, rationale, QC outcome, key markers with detection fractions, the PMCIDs behind them, and any co-occurring identities with their own markers and PMCIDs |
marker_evidence.csv |
every measured marker of every claimed identity: polarity, detection in and out of the cluster, fold change, publication support, PMID/PMCID and source sentence |
cluster_evidence.jsonl |
audit sidecar: every retrieved candidate with its scores and complete measured panel, the agent's turns and tool calls, the exclusion and definer audits, the review verdicts and any arbitration, and the dataset-wide reading of the cluster's name |
figures/, figure_data/
|
figures and the numbers behind each one |
viewer/index.html, <tag>_results.zip
|
self-contained interactive report |
run_manifest.json, resolved_config.json
|
run status, output paths and the effective configuration |
Both arms write the same schema, so results compare field by field whichever arm produced them.
Noncommercial use only. Code: PolyForm Noncommercial 1.0.0 (LICENSE). Marker resource:
CC BY-NC 4.0; its source sentences are quoted from PubMed Central open-access articles and
every row carries its PMCID/PMID. Per-file terms are in the resource's
resource_manifest.json.