GitHub View CyteType

Python client

Preprocess AnnData, run CyteType annotation, and read results in Python.

Use the Python client with Scanpy/AnnData objects.

Preprocessing your data

CyteType requires differential expression results computed per cluster before submission.

import scanpy as sc

# Assumes adata already has normalized counts in adata.X
# and cluster labels in adata.obs["leiden"]

sc.tl.rank_genes_groups(
    adata,
    groupby="leiden",
    method="wilcoxon",
    key_added="rank_genes_leiden",
    n_genes=100,
)

# If gene symbols are stored separately from var_names:
# adata.var["gene_symbols"] = adata.var_names  # or set during load

Initializing the annotator

The initialization step validates your data and precomputes expression percentages. This can take a few minutes on large datasets but only needs to run once per object.

from cytetype import CyteType

annotator = CyteType(
    adata,
    group_key="leiden",             # column in adata.obs with cluster labels
    rank_key="rank_genes_leiden",   # key in adata.uns with DE results
    gene_symbols_column="gene_symbols",  # column in adata.var with gene symbols
    n_top_genes=50,                 # top marker genes per cluster
    aggregate_metadata=True,        # include obs metadata in context
    min_percentage=10,              # min % threshold for metadata
    coordinates_key="X_umap",      # key in adata.obsm for UMAP coordinates
    max_cells_per_group=1000,       # cells sampled per cluster for visualization
    auth_token="your-token",        # Bearer token for the hosted service
)

Reviewing data before submission

The client builds obs.duckdb from the complete adata.obs table and vars.h5 from the normalized expression matrix, feature metadata, and raw counts when available. Work from a reviewed copy of the object and remove direct identifiers or observation columns that are not needed for annotation and report exploration before creating the CyteType instance.

aggregate_metadata=False disables aggregated metadata in the annotation request, but it does not remove columns from obs.duckdb. Review study_context and the metadata dictionary separately because both are submitted as written.

See Security and privacy for the complete data flow.

Running annotation

The study_context is the most important parameter for annotation quality. Describe your tissue, organism, disease state, and experimental setup in one or two sentences.

adata = annotator.run(
    study_context="Human colorectal cancer biopsy, tumor microenvironment, 10X Genomics 5' scRNA-seq, treatment-naive patients",
    metadata={
        "Study": "My TME atlas",
        "GEO": "https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE123456",
        "DOI": "https://doi.org/10.1038/example"
    },
    n_parallel_clusters=4,          # increase for faster annotation (watch rate limits)
    results_prefix="cytetype",      # prefix for result columns
    timeout_seconds=7200,
    require_artifacts=True,         # stop if an artifact build or upload fails
    show_progress=True,
)

require_artifacts=False is a failure-tolerance setting, not an upload opt-out. The client still attempts to build and upload both artifacts, and any successful artifact is submitted. Use the default value when the interactive report needs complete expression and metadata tools.

Accessing results

After a successful run, annotations are stored in your object and can be accessed immediately.

# Annotation columns added to adata.obs:
# cytetype_annotation_leiden: cell type label
# cytetype_cellOntologyTerm_leiden: Cell Ontology term name
# cytetype_cellOntologyTermID_leiden: CL:xxxxxxx ID
# cytetype_cellState_leiden: functional state

import json
results = json.loads(adata.uns["cytetype_results"]["result"])

for ann in results["annotations"]:
    print(ann["clusterId"], ann["annotation"], ann["ontologyTermID"])

# If you need to re-fetch results after disconnecting:
results = annotator.get_results()

💡 Tip: Jobs continue running on the server even after your local session disconnects. Use annotator.get_results() to retrieve results after reconnecting.

Custom LLM configuration

By default CyteType uses its own hosted model. You can bring your own LLM from any supported provider.

# Single LLM configuration
adata = annotator.run(
    study_context="...",
    llm_configs=[{
        "provider": "openai",
        "name": "gpt-4o",
        "apiKey": "sk-...",
        "baseUrl": "https://api.openai.com/v1",   # optional
        "modelSettings": {"temperature": 0.0, "max_tokens": 4096}
    }]
)

# Multiple providers (different agents can use different models)
adata = annotator.run(
    study_context="...",
    llm_configs=[
        {
            "provider": "anthropic",
            "name": "claude-3-5-sonnet-20241022",
            "apiKey": "sk-ant-...",
            "targetAgents": ["annotator", "reviewer"]
        },
        {
            "provider": "openai",
            "name": "gpt-4o-mini",
            "apiKey": "sk-...",
            "targetAgents": ["summarizer", "clinician"]
        }
    ]
)

# AWS Bedrock
adata = annotator.run(
    study_context="...",
    llm_configs=[{
        "provider": "bedrock",
        "name": "us.anthropic.claude-3-5-sonnet-20241022-v2:0",
        "awsAccessKeyId": "AKIA...",
        "awsSecretAccessKey": "...",
        "awsDefaultRegion": "us-east-1"
    }]
)

Supported providers: anthropic, bedrock, fireworks, google, groq, huggingface, mistral, openai, openrouter, vertex, xai.

Customer-supplied credentials and the relevant scientific prompts pass through the CyteType service so that it can orchestrate the workflow. A custom provider changes the inference account, but it does not bypass the CyteType backend or artifact upload.

Authentication

The hosted service requires a bearer token. Create and manage tokens in the CyteType Dashboard, and store them as secrets rather than directly in a shared script or notebook.

# Pass at initialization (applies to all run() calls)
annotator = CyteType(adata, group_key="leiden", auth_token="your-token")

# Or override at run time
adata = annotator.run(study_context="...", auth_token="your-token")

Learn more

R client

Dashboard

Understanding your report

Security and privacy