Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Docs

on:
# The Pages workflow builds the exact commit it publishes with this workflow.
workflow_call:
inputs:
ref:
description: Commit to check out and build
type: string
required: true
pages:
description: Prepare the documentation artifact for GitHub Pages
type: boolean
default: false
pull_request:
branches:
- master
paths:
- "docs/**"
- "cytetype/**"
- "pyproject.toml"
- "uv.lock"
- "tests/test_docs_render.py"
- "tests/test_docs_structure.py"
- "tests/test_docs_execution.py"
- ".github/workflows/docs.yml"
- ".github/workflows/pages.yml"

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-26.04
steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.ref || github.sha }}
# The build reads the version and the page history from git.
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v9.0.0
with:
python-version: "3.13"
- uses: actions/setup-node@v7
with:
node-version: "24"
- name: Install documentation dependencies
run: uv sync --locked --group docs
- name: Documentation tests
run: uv run --no-sync pytest tests/test_docs_structure.py tests/test_docs_render.py tests/test_docs_execution.py
- name: Render complete documentation for GitHub Pages
if: inputs.pages
env:
BASE_URL: /CyteType
run: make -C docs html
- name: Render complete documentation and check API coverage
if: ${{ !inputs.pages }}
# Notebook execution submits real CyteType jobs and runs locally on request.
# This job runs no notebooks.
run: make -C docs html
- name: Store documentation preview
if: ${{ !inputs.pages }}
uses: actions/upload-artifact@v7
with:
name: documentation-preview
path: docs/build/site/_build/html/
if-no-files-found: error
- name: Store GitHub Pages site
if: inputs.pages
# Served at docs.nygen.io/CyteType/ by the organization site, which owns the root robots.txt.
uses: actions/upload-pages-artifact@v4
with:
path: docs/build/site/_build/html/
46 changes: 46 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: GitHub Pages

on:
workflow_dispatch:
push:
branches:
- master
paths:
- "docs/**"
- "cytetype/**"
- "pyproject.toml"
- "uv.lock"
- "tests/test_docs_render.py"
- "tests/test_docs_structure.py"
- "tests/test_docs_execution.py"
- ".github/workflows/docs.yml"
- ".github/workflows/pages.yml"

permissions:
contents: read

concurrency:
group: github-pages
cancel-in-progress: false

jobs:
build:
if: github.ref == 'refs/heads/master'
uses: ./.github/workflows/docs.yml
with:
ref: ${{ github.sha }}
pages: true

deploy:
needs: build
runs-on: ubuntu-26.04
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: https://docs.nygen.io/CyteType/
steps:
- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
9 changes: 9 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@ name: CI
on:
push:
branches: [ master ] # Or your default branch
# The Docs workflow checks documentation-only changes.
paths-ignore:
- "docs/**"
- ".github/workflows/docs.yml"
- ".github/workflows/pages.yml"
pull_request:
branches: [ master ] # Or your default branch
paths-ignore:
- "docs/**"
- ".github/workflows/docs.yml"
- ".github/workflows/pages.yml"

jobs:
test:
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ cytetype/_version.py
*ipynb_checkpoints*
*query.json
notebooks/
tmp/
tmp/

# Documentation. build/ above covers docs/build/; running MyST directly in docs/source
# writes _build/ there. The notebooks in docs/source are tracked page sources.
docs/source/_build/
!/docs/source/**/*.ipynb
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2025-2026, Nygen Analytics AB
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 changes: 0 additions & 31 deletions LICENSE.md

This file was deleted.

42 changes: 28 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@
<a href="https://pypi.org/project/cytetype/">
<img src="https://img.shields.io/pypi/v/cytetype.svg" alt="PyPI version">
</a>
<a href="https://raw.githubusercontent.com/NygenAnalytics/CyteType/refs/heads/master/LICENSE.md">
<img src="https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg" alt="License: CC BY-NC-SA 4.0">
<a href="LICENSE">
<img src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" alt="License: BSD 3-Clause">
</a>
<a href="https://pypi.org/project/cytetype/">
<img src="https://img.shields.io/pypi/dm/cytetype" alt="PyPI downloads">
</a>
</p>

**CyteType** is an end-to-end cell type annotation system for **single-cell RNA sequencing (scRNA-seq)**, designed for repeatable analysis pipelines rather than one-off prompting. It combines cluster-level marker genes, expression context, study metadata, literature retrieval, ontology mapping, and a dedicated review step in a structured workflow that operates directly on AnnData.
**CyteType** is an end-to-end cell type annotation system for **single-cell RNA sequencing (scRNA-seq)**, designed for repeatable analysis pipelines rather than one-off prompting. It combines cluster-level marker genes, expression context, study metadata, ontology mapping, and a dedicated review step in a structured workflow that operates directly on AnnData.

For Seurat workflows, use [CyteTypeR](https://github.com/NygenAnalytics/CyteTypeR).

📚 **Documentation:** [docs.nygen.io/CyteType](https://docs.nygen.io/CyteType/) covers the Python and R clients, the report, the dashboard, Chat and the MCP connector.

> [!IMPORTANT]
> Already installed CyteType? Open a terminal and run `pip install -U cytetype` to update to the latest version before you start.
>
Expand Down Expand Up @@ -55,8 +57,9 @@ cytetype login
import scanpy as sc
from cytetype import CyteType

# Assumes preprocessed AnnData with clusters and marker genes
# Assumes log-normalized AnnData with clusters in adata.obs["clusters"]
group_key = "clusters"
sc.tl.rank_genes_groups(adata, groupby=group_key, key_added=f"rank_genes_{group_key}")
annotator = CyteType(
adata,
group_key=group_key,
Expand All @@ -67,18 +70,18 @@ adata = annotator.run(study_context="Human PBMC from a healthy donor")
sc.pl.umap(adata, color="cytetype_annotation_clusters")
```

[Try CyteType in Google Colab](https://colab.research.google.com/drive/1aRLsI3mx8JR8u5BKHs48YUbLsqRsh2N7?usp=sharing).
Follow the [full quickstart](https://docs.nygen.io/CyteType/getting-started/quickstart/), or [try CyteType in Google Colab](https://colab.research.google.com/drive/1aRLsI3mx8JR8u5BKHs48YUbLsqRsh2N7?usp=sharing).

## What You Get

- **Annotations:** Cell type, subtype, and activation state for every cluster
- **Cell Ontology mapping:** Standardized CL IDs for comparison across studies
- **Confidence and quality control:** Confidence values, plus match scores against your existing labels
- **Supporting evidence:** Publications and condition-specific references behind each call
- **Confidence and quality control:** Reviewer confidence (High, Moderate, Low) and heterogeneity flags, plus match scores against your existing labels
- **Supporting evidence:** Supporting, missing and unexpected marker genes, pathways, and the reasoning behind each call

## Example Report

Each analysis generates an HTML report with annotation decisions, reviewer comments, supporting evidence, and an embedded chat interface connected to your expression data.
Each analysis generates an interactive report with annotation decisions, reviewer assessments and supporting evidence. From the report, open [Chat](https://docs.nygen.io/CyteType/platform/chat/) to ask follow-up questions about your data.

<img width="1000" alt="CyteType HTML report showing cell type annotations marker genes" src="https://github.com/user-attachments/assets/e5373fdd-7173-42db-b863-76a1e8ecfe01" />

Expand All @@ -98,10 +101,14 @@ Methods and full results are in the [preprint](https://www.biorxiv.org/content/1

## Resources

- 🔐 [CLI and Authentication](docs/cli.md): Set up API keys and manage saved credentials.
- ⚙️ [Configuration](docs/configuration.md): Customize annotation settings, LLM providers, and artifacts.
- 📋 [Output Columns](docs/results.md): Understand annotations and metadata added to AnnData.
- 🛠️ [Troubleshooting](docs/troubleshooting.md): Resolve authentication, API, artifact, and LLM issues.
- 📚 [Documentation](https://docs.nygen.io/CyteType/): Guides for the Python and R clients, the report, the dashboard, Chat and the MCP connector.
- 🔐 [Sign in and API keys](https://docs.nygen.io/CyteType/getting-started/sign-in/): Set up API keys and manage saved credentials.
- 🧬 [Prepare your data](https://docs.nygen.io/CyteType/guides/prepare-data/): Clusters, marker genes, gene symbols and other inputs.
- ⚙️ [Run, monitor and rerun jobs](https://docs.nygen.io/CyteType/guides/running-jobs/): Annotation settings, timeouts, reruns and upload files.
- 🤖 [Use your own LLM](https://docs.nygen.io/CyteType/guides/llm-configuration/): LLM providers, including local models.
- 📋 [Results in AnnData](https://docs.nygen.io/CyteType/results/results-in-your-data/): Columns and keys added to AnnData.
- 🛠️ [Troubleshooting](https://docs.nygen.io/CyteType/support/troubleshooting/): Resolve sign-in, data, upload and job issues.
- 🐍 [Python API reference](https://docs.nygen.io/CyteType/api/index.html): Every public class and function.
- 🧑‍💻 [Development](docs/development.md): Configure a local environment and contribute.
- 🎥 [Introduction video](https://vimeo.com/nygen/cytetype): Watch a quick overview of CyteType.
- 💬 [Discord community](https://discord.gg/V6QFM4AN): Ask questions and get support.
Expand All @@ -123,6 +130,13 @@ Methods and full results are in the [preprint](https://www.biorxiv.org/content/1

## License

CyteType is free for academic and non-commercial research under [CC BY-NC-SA 4.0](LICENSE.md).
The CyteType Python client is open source software released under the [BSD 3-Clause License](LICENSE) and maintained by [Nygen](https://nygen.io).

### CyteType service terms

Annotation runs on the CyteType service, which requires an API key.

- **Academic and non-commercial use** is free within the [usage limits](https://docs.nygen.io/CyteType/platform/limits/). Data generated under academic use is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/).
- **Commercial use and higher limits** require a commercial license from Nygen Analytics AB, which includes commercial rights to the generated data.

For commercial licensing, contact [contact@nygen.io](mailto:contact@nygen.io).
For licensing, contact [contact@nygen.io](mailto:contact@nygen.io).
29 changes: 29 additions & 0 deletions cytetype/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import requests

from ._version import __version__
from .api.exceptions import (
APIError,
AuthenticationError,
AuthorizationError,
ClientDisconnectedError,
CyteTypeError,
JobFailedError,
JobNotFoundError,
LLMValidationError,
NetworkError,
QuotaExceededError,
RateLimitError,
)

# Renamed so `from cytetype import *` does not shadow the builtin TimeoutError.
from .api.exceptions import TimeoutError as CyteTypeTimeoutError
from .config import logger
from .main import CyteType
from .plotting import marker_dotplot
Expand All @@ -12,6 +28,19 @@
"marker_dotplot",
"rank_genes_groups_backed",
"subsample_by_group",
# Exceptions
"CyteTypeError",
"APIError",
"AuthenticationError",
"AuthorizationError",
"RateLimitError",
"QuotaExceededError",
"JobNotFoundError",
"JobFailedError",
"LLMValidationError",
"ClientDisconnectedError",
"NetworkError",
"CyteTypeTimeoutError",
]

_PYPI_JSON_URL = "https://pypi.org/pypi/cytetype/json"
Expand Down
9 changes: 9 additions & 0 deletions cytetype/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@
JobNotFoundError,
JobFailedError,
LLMValidationError,
ClientDisconnectedError,
TimeoutError,
NetworkError,
)

# The same class under the name that `cytetype` exports, which does not shadow the
# builtin TimeoutError.
from .exceptions import TimeoutError as CyteTypeTimeoutError

__all__ = [
# Client functions
"submit_annotation_job",
Expand All @@ -51,6 +56,10 @@
"JobNotFoundError",
"JobFailedError",
"LLMValidationError",
"ClientDisconnectedError",
# `from cytetype.api import *` has always exported TimeoutError, shadowing the
# builtin, and code that catches it expects CyteType's class, so it stays.
"TimeoutError",
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
"CyteTypeTimeoutError",
"NetworkError",
]
Loading
Loading