annotuneagent 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/CHANGELOG.md +19 -3
  2. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/CITATION.cff +1 -1
  3. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/PKG-INFO +23 -7
  4. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/README.md +20 -5
  5. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/index.md +8 -0
  6. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/installation.md +10 -10
  7. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/quickstart.md +4 -0
  8. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/tutorials/index.md +12 -1
  9. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/tutorials/load_model.ipynb +8 -7
  10. annotuneagent-0.1.1/docs/tutorials/report.md +126 -0
  11. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/pyproject.toml +2 -1
  12. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/__init__.py +6 -3
  13. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/_version.py +1 -1
  14. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/decide.py +40 -7
  15. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/local_lora.py +215 -35
  16. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/workflow.py +20 -5
  17. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent.egg-info/SOURCES.txt +1 -0
  18. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_cli.py +7 -6
  19. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_decide.py +14 -0
  20. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_local_lora.py +130 -0
  21. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_version.py +2 -2
  22. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_workflow.py +43 -1
  23. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/.github/workflows/publish.yml +0 -0
  24. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/.github/workflows/tests.yml +0 -0
  25. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/.gitignore +0 -0
  26. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/.readthedocs.yaml +0 -0
  27. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/LICENSE +0 -0
  28. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/MANIFEST.in +0 -0
  29. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/SECURITY.md +0 -0
  30. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/_static/css/custom.css +0 -0
  31. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/_templates/README.md +0 -0
  32. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/api/index.md +0 -0
  33. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/conf.py +0 -0
  34. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/marker_sources.md +0 -0
  35. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/requirements.txt +0 -0
  36. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/docs/tutorials/pbmc8k_annotation.ipynb +0 -0
  37. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/setup.cfg +0 -0
  38. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/__main__.py +0 -0
  39. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/annotate.py +0 -0
  40. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/arbitrate.py +0 -0
  41. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/cli.py +0 -0
  42. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/evaluate.py +0 -0
  43. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/evidence.py +0 -0
  44. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/granularity/__init__.py +0 -0
  45. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/granularity/cl_dag.py +0 -0
  46. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/granularity/ontology_map.py +0 -0
  47. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/granularity/relations.py +0 -0
  48. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/hypotheses.py +0 -0
  49. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/marker_plot.py +0 -0
  50. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/markers.py +0 -0
  51. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/markers.tsv +0 -0
  52. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/mltools.py +0 -0
  53. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/ontology/cl_isa.json +0 -0
  54. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/ontology/granularity_policy.yaml +0 -0
  55. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/ontology/ontology_map.jsonl +0 -0
  56. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/prepare.py +0 -0
  57. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/py.typed +0 -0
  58. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/read.py +0 -0
  59. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/tokenizer/LICENSE +0 -0
  60. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/tokenizer/merges.txt +0 -0
  61. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/tokenizer/tokenizer.json +0 -0
  62. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/tokenizer/tokenizer_config.json +0 -0
  63. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/tokenizer/vocab.json +0 -0
  64. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/src/annotuneagent/votes.py +0 -0
  65. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_annotate.py +0 -0
  66. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_evaluate.py +0 -0
  67. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_evidence.py +0 -0
  68. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_granularity.py +0 -0
  69. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_markers.py +0 -0
  70. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_mltools.py +0 -0
  71. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_prepare.py +0 -0
  72. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_read.py +0 -0
  73. {annotuneagent-0.1.0 → annotuneagent-0.1.1}/tests/test_votes.py +0 -0
@@ -4,10 +4,26 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- ### Added
7
+ ## [0.1.1] - 2026-11-09
8
+
9
+ ### Fixed
10
+
11
+ - A running server is checked with `/props` and `/lora-adapters` when `/proc`
12
+ is unavailable, so Windows and macOS can reuse a server this package did
13
+ not start.
14
+ - A `TaskEndpoint` URL calls `/completion` and enables one LoRA adapter for
15
+ that request. When `maxlen` is omitted, name allows 2048 tokens and ML
16
+ allows 8192. An ML prompt does not fit in the name limit.
17
+ - A model request waits 600 seconds. `ANNOTUNE_TIMEOUT` or the `timeout`
18
+ argument overrides it. Health checks stay at 2 seconds.
19
+ - On Windows, a process check does not call `os.kill`.
20
+
21
+ ### Changed
8
22
 
9
- - PyPI publish workflow. `pip install annotuneagent` after the package is
10
- published. Publishing is manual from GitHub Actions.
23
+ - The load-model tutorial points Windows users to
24
+ [AnnoTuneLoader](https://github.com/leelieber2025/AnnoTuneLoader).
25
+ - A tutorial explains each field of the `run` report. The `Queries` paragraph
26
+ is text to paste into another model.
11
27
 
12
28
  ## [0.1.0] - 2026-11-09
13
29
 
@@ -17,7 +17,7 @@ authors:
17
17
  orcid: "https://orcid.org/0000-0002-9276-9129"
18
18
  repository-code: "https://github.com/leelieber2025/AnnoTuneAgent"
19
19
  license: Apache-2.0
20
- version: 0.1.0
20
+ version: 0.1.1
21
21
  date-released: "2026-11-09"
22
22
  keywords:
23
23
  - single-cell
@@ -1,15 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annotuneagent
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Cluster-level cell-type annotation from marker, name, and ML results
5
5
  Author-email: "Zhao Li (李钊)" <leelieber@gmail.com>
6
6
  License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/leelieber2025/AnnoTuneAgent
8
- Project-URL: Documentation, https://app.readthedocs.org/projects/annotuneagent/
8
+ Project-URL: Documentation, https://annotuneagent.readthedocs.io/
9
9
  Project-URL: Changelog, https://github.com/leelieber2025/AnnoTuneAgent/blob/main/CHANGELOG.md
10
10
  Project-URL: Source, https://github.com/leelieber2025/AnnoTuneAgent
11
11
  Project-URL: Issues, https://github.com/leelieber2025/AnnoTuneAgent/issues
12
12
  Project-URL: PyPI, https://pypi.org/project/annotuneagent/
13
+ Project-URL: Zenodo, https://doi.org/10.5281/zenodo.24092828
13
14
  Keywords: single-cell,RNA-seq,cell type annotation,marker genes,bioinformatics
14
15
  Classifier: Development Status :: 3 - Alpha
15
16
  Classifier: Intended Audience :: Science/Research
@@ -52,11 +53,15 @@ Dynamic: license-file
52
53
 
53
54
  # AnnoTuneAgent
54
55
 
56
+ [![PyPI version](https://img.shields.io/pypi/v/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
57
+ [![PyPI downloads](https://img.shields.io/pepy/dt/annotuneagent.svg?label=PyPI%20downloads)](https://pepy.tech/project/annotuneagent)
58
+ [![Python versions](https://img.shields.io/pypi/pyversions/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
59
+ [![Documentation Status](https://readthedocs.org/projects/annotuneagent/badge/?version=latest)](https://annotuneagent.readthedocs.io/en/latest/?badge=latest)
60
+ [![CI](https://github.com/leelieber2025/AnnoTuneAgent/actions/workflows/tests.yml/badge.svg)](https://github.com/leelieber2025/AnnoTuneAgent/actions/workflows/tests.yml)
55
61
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
56
- [![PyPI](https://img.shields.io/pypi/v/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
57
- [![Documentation](https://app.readthedocs.org/projects/annotuneagent/badge/?version=latest)](https://app.readthedocs.org/projects/annotuneagent/)
62
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.24092828.svg)](https://doi.org/10.5281/zenodo.24092828)
58
63
 
59
- Documentation: [Read the Docs](https://app.readthedocs.org/projects/annotuneagent/).
64
+ Documentation: [Read the Docs](https://annotuneagent.readthedocs.io/en/latest/).
60
65
 
61
66
  **Cluster-level cell-type annotation for single-cell RNA-seq, with two
62
67
  fine-tuned language models that run locally.**
@@ -127,7 +132,13 @@ Harmony, and Scanorama. If you select one that is missing, `prepare` prints
127
132
  the install command and leaves the matrix unchanged. See
128
133
  [Installation](docs/installation.md).
129
134
 
130
- From a checkout: `pip install -e '.[dev]'`.
135
+ From a checkout:
136
+
137
+ ```bash
138
+ git clone https://github.com/leelieber2025/AnnoTuneAgent.git
139
+ cd AnnoTuneAgent
140
+ pip install -e ".[dev]"
141
+ ```
131
142
 
132
143
  ## First run
133
144
 
@@ -196,7 +207,12 @@ The tokenizer is installed with the package. See
196
207
 
197
208
  ## Status
198
209
 
199
- **0.1.0 (Alpha).** Import as `import annotuneagent as ata`.
210
+ **0.1.1 (Alpha).** Import as `import annotuneagent as ata`.
211
+
212
+ ## Citation
213
+
214
+ For the software record, cite the [Zenodo DOI](https://doi.org/10.5281/zenodo.24092828).
215
+ Pin the package version used in the analysis, for example `annotuneagent==0.1.1`.
200
216
 
201
217
  ## License
202
218
 
@@ -1,10 +1,14 @@
1
1
  # AnnoTuneAgent
2
2
 
3
+ [![PyPI version](https://img.shields.io/pypi/v/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
4
+ [![PyPI downloads](https://img.shields.io/pepy/dt/annotuneagent.svg?label=PyPI%20downloads)](https://pepy.tech/project/annotuneagent)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
6
+ [![Documentation Status](https://readthedocs.org/projects/annotuneagent/badge/?version=latest)](https://annotuneagent.readthedocs.io/en/latest/?badge=latest)
7
+ [![CI](https://github.com/leelieber2025/AnnoTuneAgent/actions/workflows/tests.yml/badge.svg)](https://github.com/leelieber2025/AnnoTuneAgent/actions/workflows/tests.yml)
3
8
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
4
- [![PyPI](https://img.shields.io/pypi/v/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
5
- [![Documentation](https://app.readthedocs.org/projects/annotuneagent/badge/?version=latest)](https://app.readthedocs.org/projects/annotuneagent/)
9
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.24092828.svg)](https://doi.org/10.5281/zenodo.24092828)
6
10
 
7
- Documentation: [Read the Docs](https://app.readthedocs.org/projects/annotuneagent/).
11
+ Documentation: [Read the Docs](https://annotuneagent.readthedocs.io/en/latest/).
8
12
 
9
13
  **Cluster-level cell-type annotation for single-cell RNA-seq, with two
10
14
  fine-tuned language models that run locally.**
@@ -75,7 +79,13 @@ Harmony, and Scanorama. If you select one that is missing, `prepare` prints
75
79
  the install command and leaves the matrix unchanged. See
76
80
  [Installation](docs/installation.md).
77
81
 
78
- From a checkout: `pip install -e '.[dev]'`.
82
+ From a checkout:
83
+
84
+ ```bash
85
+ git clone https://github.com/leelieber2025/AnnoTuneAgent.git
86
+ cd AnnoTuneAgent
87
+ pip install -e ".[dev]"
88
+ ```
79
89
 
80
90
  ## First run
81
91
 
@@ -144,7 +154,12 @@ The tokenizer is installed with the package. See
144
154
 
145
155
  ## Status
146
156
 
147
- **0.1.0 (Alpha).** Import as `import annotuneagent as ata`.
157
+ **0.1.1 (Alpha).** Import as `import annotuneagent as ata`.
158
+
159
+ ## Citation
160
+
161
+ For the software record, cite the [Zenodo DOI](https://doi.org/10.5281/zenodo.24092828).
162
+ Pin the package version used in the analysis, for example `annotuneagent==0.1.1`.
148
163
 
149
164
  ## License
150
165
 
@@ -1,5 +1,12 @@
1
1
  # AnnoTuneAgent Documentation
2
2
 
3
+ [![PyPI version](https://img.shields.io/pypi/v/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
4
+ [![PyPI downloads](https://img.shields.io/pepy/dt/annotuneagent.svg?label=PyPI%20downloads)](https://pepy.tech/project/annotuneagent)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/annotuneagent.svg)](https://pypi.org/project/annotuneagent/)
6
+ [![CI](https://github.com/leelieber2025/AnnoTuneAgent/actions/workflows/tests.yml/badge.svg)](https://github.com/leelieber2025/AnnoTuneAgent/actions/workflows/tests.yml)
7
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/leelieber2025/AnnoTuneAgent/blob/main/LICENSE)
8
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.24092828.svg)](https://doi.org/10.5281/zenodo.24092828)
9
+
3
10
  AnnoTuneAgent annotates clustered scRNA-seq. `prepare` runs QC, gene
4
11
  selection, and Leiden clustering. `run` collects three results for each
5
12
  cluster: a marker score, a name-model result, and an ML-model result. A
@@ -52,6 +59,7 @@ scFair supplies the gene list and the resolution is half of its suggestion.
52
59
  | {doc}`installation`, {doc}`quickstart` | Install, then a first call |
53
60
  | {doc}`tutorials/pbmc8k_annotation` | PBMC 8k: `prepare`, then the name and ML models |
54
61
  | {doc}`tutorials/load_model` | Download the weights and run a local or remote model server |
62
+ | {doc}`tutorials/report` | What each part of the `run` report is for, including the text to paste |
55
63
  | {doc}`api/index` | Function signatures |
56
64
  | {doc}`marker_sources` | Marker reference sources and verification decisions |
57
65
 
@@ -6,14 +6,21 @@ Python 3.10–3.13.
6
6
  pip install annotuneagent
7
7
  ```
8
8
 
9
- From a checkout, `pip install -e .` installs this tree instead.
10
-
11
9
  ```python
12
10
  import annotuneagent as ata
13
11
  print(ata.__version__)
14
12
  ```
15
13
 
16
- The version is set in `src/annotuneagent/_version.py` (currently **0.1.0**).
14
+ The version is set in `src/annotuneagent/_version.py` (currently **0.1.1**).
15
+
16
+ From a checkout:
17
+
18
+ ```bash
19
+ git clone https://github.com/leelieber2025/AnnoTuneAgent.git
20
+ cd AnnoTuneAgent
21
+ pip install -e ".[dev]"
22
+ pip install -e ".[docs]"
23
+ ```
17
24
 
18
25
  ## Dependencies
19
26
 
@@ -32,13 +39,6 @@ package. These are optional:
32
39
  If you select an optional method and its package is missing, `prepare` prints
33
40
  the install command and leaves the matrix unchanged. ComBat is part of Scanpy.
34
41
 
35
- Other extras:
36
-
37
- ```bash
38
- pip install -e ".[docs]" # build this documentation from a checkout
39
- pip install -e ".[dev]" # pytest and ruff
40
- ```
41
-
42
42
  ## Model weights
43
43
 
44
44
  The GGUF weights are not in the Python package. Download them from the
@@ -184,6 +184,10 @@ panel, not to each gene separately.
184
184
  and UMAP and dotplot calls. Edit the order or labels and rerun the snippet to
185
185
  regenerate the plots.
186
186
 
187
+ {doc}`tutorials/report` explains every report field. The printed `Queries`
188
+ block is text to paste into another model. The heading is the local note.
189
+ The paragraph under it is the question.
190
+
187
191
  A Cell Ontology id is the exact map row for the written label. An unreviewed id
188
192
  is marked pending.
189
193
 
@@ -5,10 +5,11 @@ figures and the UMAP. `run` collects the marker score, the name-model result,
5
5
  and the ML-model result, and writes the title. The model server stays loaded
6
6
  between calls.
7
7
 
8
- | Notebook | Contents |
8
+ | Page | Contents |
9
9
  |----------|----------|
10
10
  | {doc}`pbmc8k_annotation` | Public PBMC 8k: `prepare`, then the name and ML models |
11
11
  | {doc}`load_model` | Download the weights, then use a local server or connect to a remote one |
12
+ | {doc}`report` | What each part of the `run` report is for, including the text to paste |
12
13
 
13
14
  {doc}`pbmc8k_annotation` uses the public 10x PBMC 8k count matrix (7,750 cells),
14
15
  not the Scanpy PBMC 3k set. The matrix contains cells only, so AmbiDose is off.
@@ -42,6 +43,15 @@ Run a local server at `http://127.0.0.1:8080`, or connect to a remote one.
42
43
  Needs GGUF files or a running server
43
44
  :::
44
45
 
46
+ :::{grid-item-card} Read the report
47
+ :link: report
48
+ :link-type: doc
49
+
50
+ The `Queries` block is text to paste into another model.
51
+ +++
52
+ No extra install
53
+ :::
54
+
45
55
  ::::
46
56
 
47
57
  ```{toctree}
@@ -50,4 +60,5 @@ Needs GGUF files or a running server
50
60
 
51
61
  pbmc8k_annotation
52
62
  load_model
63
+ report
53
64
  ```
@@ -12,7 +12,7 @@
12
12
  "| | Use it when |\n",
13
13
  "|---|---|\n",
14
14
  "| Local | You download the weights and run `llama-server` on this computer |\n",
15
- "| Remote | Name and ML each have a running HTTP endpoint |\n",
15
+ "| Remote | One `llama-server` is already running with both adapters |\n",
16
16
  "\n",
17
17
  "With `models`, the marker score, the name model, and the ML model each return a result. A judgment reads the three results and writes the title. The title is a dictionary name. The name model copies a cell-type name from the marker dictionary. The ML model is trained on machine-learning measurements of the cluster, differential expression and marker-panel statistics, and copies a candidate name. A cluster can stay unnamed. Without `models`, only the marker score is collected."
18
18
  ]
@@ -36,7 +36,9 @@
36
36
  "|---|---|\n",
37
37
  "| Linux with an NVIDIA GPU | Supported. Steps below. |\n",
38
38
  "| macOS (Apple silicon or Intel) | Supported. Use the macOS steps below. The Metal build uses the GPU automatically. |\n",
39
- "| Windows | Not supported for the local section. `import annotuneagent` works, but starting and stopping `llama-server` uses POSIX process calls. Run the server on Linux or macOS and use the Remote section, or use WSL. |\n",
39
+ "| Windows | This package does not start `llama-server`. Use [AnnoTuneLoader](https://github.com/leelieber2025/AnnoTuneLoader). |\n",
40
+ "\n",
41
+ "On Windows, download [AnnoTuneLoader.exe](https://github.com/leelieber2025/AnnoTuneLoader/raw/main/AnnoTuneLoader.exe) and double-click it. Choose the base, the name adapter, and the ML adapter. The server listens at `http://127.0.0.1:8080`. Leave the window open while you annotate. On that computer, `local_models(model_root)` reuses the server when those three files match. Another computer uses the Remote section below.\n",
40
42
  "\n",
41
43
  "**Linux, CUDA 12.8.** Unpack the server and the CUDA runtime next to each other.\n",
42
44
  "\n",
@@ -202,7 +204,7 @@
202
204
  "source": [
203
205
  "## Remote\n",
204
206
  "\n",
205
- "Use this when name and ML each have a running endpoint. Set the addresses of the two model services. No local GGUF files are required."
207
+ "Use this when one `llama-server` is already running with both adapters, including one started by [AnnoTuneLoader](https://github.com/leelieber2025/AnnoTuneLoader) on another machine. Point both tasks at that server. Each request calls `/completion` and enables one adapter. No local GGUF files are required. Name prompts must fit in 2048 tokens. ML prompts must fit in 8192. Those limits apply when `maxlen` is omitted."
206
208
  ]
207
209
  },
208
210
  {
@@ -214,11 +216,10 @@
214
216
  "source": [
215
217
  "import annotuneagent as ata\n",
216
218
  "\n",
217
- "name_url = \"http://NAME_HOST:8080\"\n",
218
- "ml_url = \"http://ML_HOST:8080\"\n",
219
+ "server = \"http://SERVER:8080\"\n",
219
220
  "models = ata.ModelChain(\n",
220
- " name=ata.TaskEndpoint(url=name_url),\n",
221
- " ml=ata.TaskEndpoint(url=ml_url),\n",
221
+ " name=ata.TaskEndpoint(url=server),\n",
222
+ " ml=ata.TaskEndpoint(url=server),\n",
222
223
  ")"
223
224
  ]
224
225
  },
@@ -0,0 +1,126 @@
1
+ # Read the report
2
+
3
+ `run` returns a dictionary and prints a `Queries` block. Each block is one
4
+ cluster that still has a question: it has no title, or the title is written
5
+ and `hint` is set. AnnoTuneAgent does not send this text to the name model,
6
+ the ML model, or any other service. Copy the paragraph under the heading and
7
+ paste it into another model.
8
+
9
+ ## The heading
10
+
11
+ ```text
12
+ cluster 0 monocyte no marker passed
13
+ ```
14
+
15
+ The three fields are the cluster id, the written title, and the hint. A
16
+ cluster with no title prints `no label` in the middle. The heading stays in
17
+ the notebook. It is not part of the question.
18
+
19
+ `no marker passed` means the title `monocyte` was written, and none of its
20
+ cited markers passed the expression check. The name stays. The dotplot still
21
+ draws the best-measured cited genes for that type.
22
+
23
+ ```text
24
+ cluster 2 CD14-positive monocyte conflict: marker neutrophil
25
+ ```
26
+
27
+ The written title is `CD14-positive monocyte`. The marker score named
28
+ `neutrophil`, and that name is not on the same Cell Ontology lineage as the
29
+ title. The hint records the other call. The same line can name the name model
30
+ or the ML model, for example `conflict: ML pericyte`. Several disagreements
31
+ are joined with commas.
32
+
33
+ ## The paragraph to paste
34
+
35
+ ```text
36
+ human, 1728 cells. What cell type is this cluster?
37
+ Enriched versus other cells: HLA-DRA (in 1.00, out 0.62, logfc 2.46); HLA-DRB1 (in 0.99, out 0.65, logfc 2.15); CD74 (in 1.00, out 0.93, logfc 1.84); LGALS2 (in 0.74, out 0.15, logfc 3.40); HLA-DPB1 (in 0.95, out 0.56, logfc 2.08); HLA-DRB5 (in 0.94, out 0.53, logfc 1.96); PID1 (in 0.70, out 0.19, logfc 2.78); CPVL (in 0.84, out 0.36, logfc 2.07); HLA-DPA1 (in 0.96, out 0.58, logfc 1.98); LYZ (in 1.00, out 0.65, logfc 3.01).
38
+ Also detected: CD14 (in 0.87, out 0.42, logfc 1.71); FCN1 (in 0.99, out 0.59, logfc 1.83); CSF1R (in 0.62, out 0.31, logfc 1.16); S100A8 (in 1.00, out 0.57, logfc 2.39); VCAN (in 0.94, out 0.50, logfc 1.59); S100A4 (in 1.00, out 0.95, logfc 0.81); ANXA1 (in 0.81, out 0.69, logfc 0.35).
39
+ ```
40
+
41
+ The first line is the species, the tissue when `run` was given one, the
42
+ number of cells, and the question. Paste from this line through `Also
43
+ detected`.
44
+
45
+ `Enriched versus other cells` lists up to 10 genes that are higher in this
46
+ cluster than in the other cells, in Wilcoxon order. `in` is the fraction of
47
+ cells in the cluster where the gene is detected. `out` is that fraction in
48
+ the other cells. `logfc` is the Scanpy log fold change.
49
+
50
+ `Also detected` lists markers of dictionary programs that are on in this
51
+ cluster, detected in at least half of its cells, and not already in the
52
+ enriched list. The list stops at 16 genes.
53
+
54
+ A reply from the other model is not written back into `adata` or the report.
55
+ Use it when you decide whether to keep or change the title in
56
+ `annotation_code`.
57
+
58
+ The same paragraph is `query` on `report["clusters"]` and on
59
+ `report["review"]`.
60
+
61
+ ## One row per cluster
62
+
63
+ `report["clusters"]` has one row per cluster. The columns stay separate. A
64
+ title is not a vote count.
65
+
66
+ | Column | What it is |
67
+ |---|---|
68
+ | `cluster` | Leiden id |
69
+ | `cells` | Cells in that cluster |
70
+ | `marker` | Name from the marker score, or none |
71
+ | `name` | First name copied by the name model. `failed` when the call failed. The action when the reply named nothing |
72
+ | `ml` | First name copied by the ML model. `escalate` when it abstains. `failed` when the call failed |
73
+ | `label` | Title written onto the cells. Empty when the cluster stays unnamed |
74
+ | `hint` | The question printed after the title. Empty when there is nothing to ask |
75
+ | `query` | The paragraph to paste. Present when the cluster is unnamed or `hint` is set |
76
+ | `reason` | Why the title was written or withheld |
77
+ | `ontology` | Cell Ontology id for the written title. `pending` means the id is not a reviewed row |
78
+
79
+ `adata.obs["annotune_label"]` is the title. `adata.obs["annotune_hint"]` is
80
+ the hint on every cell of that cluster. `adata.obs["annotune_final_status"]`
81
+ is `named`, `review`, or `escalate`. Clusters that share a title also share
82
+ one id in `adata.obs["annotune_cluster"]`. The Leiden column is kept.
83
+
84
+ ## What to read first
85
+
86
+ `report["review"]` is the clusters to read first. The order is a reading
87
+ order, not an accuracy score.
88
+
89
+ | Priority | Which clusters |
90
+ |---|---|
91
+ | 0 | No title |
92
+ | 1 | A conflict, or close marker scores |
93
+ | 2 | A model call failed |
94
+ | 3 | Fewer than 20 cells |
95
+ | 4 | Any other hint |
96
+
97
+ `report["queue"]` lists only clusters with no title. A titled cluster with a
98
+ hint is in `review`, and on `annotune_hint`, and it is not in `queue`.
99
+
100
+ ## The rest of the report
101
+
102
+ | Field | What it is |
103
+ |---|---|
104
+ | `records` | The working record for each cluster, including model rounds and the marker ranking |
105
+ | `dropped_hypotheses` | Dictionary types left out because a positive marker is missing from the matrix |
106
+ | `subclusters` | The second split of a cluster that was not settled. Empty when `subcluster=False` |
107
+ | `arbitration` | Clusters that were not given the status `named` |
108
+ | `merged` | Titles that were given one `annotune_cluster` id |
109
+ | `all_labeled` | True when every cell has a title |
110
+ | `written` | True when at least one cell has a title |
111
+ | `label_key` | The `obs` column that holds the title. `None` when nothing was written |
112
+ | `marker_dict` | Genes drawn in the dotplot, in column order |
113
+ | `marker_validation` | Measurement of each cited gene, and why it was kept or left out |
114
+ | `marker_status` | Per written type. `no_marker_passed_expression_check` is the check behind `no marker passed` |
115
+ | `marker_thresholds` | The cutoffs used for that check |
116
+ | `dotplot` | Path of the PDF when `dotplot_path` was set |
117
+ | `doublets` | Doublet summary stored by `prepare` |
118
+ | `tissue` | Tissue page passed to `run` |
119
+ | `tissues` | Tissue pages available for the species |
120
+ | `annotation_code` | Python that redraws the UMAP and the dotplot from the written titles |
121
+
122
+ `annotation_code` copies the titles into `cluster2celltype`. Clusters with no
123
+ title are left out of that map and listed under `cluster_choices` when the
124
+ record has candidate names. Edit the map or `fine_order`, then run the
125
+ snippet on the annotated object. {doc}`../quickstart` records the expression
126
+ cutoffs used for the dotplot.
@@ -71,11 +71,12 @@ annotuneagent = "annotuneagent.cli:main"
71
71
 
72
72
  [project.urls]
73
73
  Homepage = "https://github.com/leelieber2025/AnnoTuneAgent"
74
- Documentation = "https://app.readthedocs.org/projects/annotuneagent/"
74
+ Documentation = "https://annotuneagent.readthedocs.io/"
75
75
  Changelog = "https://github.com/leelieber2025/AnnoTuneAgent/blob/main/CHANGELOG.md"
76
76
  Source = "https://github.com/leelieber2025/AnnoTuneAgent"
77
77
  Issues = "https://github.com/leelieber2025/AnnoTuneAgent/issues"
78
78
  PyPI = "https://pypi.org/project/annotuneagent/"
79
+ Zenodo = "https://doi.org/10.5281/zenodo.24092828"
79
80
 
80
81
  [tool.setuptools.packages.find]
81
82
  where = ["src"]
@@ -27,9 +27,12 @@ those candidates.
27
27
  ``local_models`` takes the directory containing the Q6_K base and the name and
28
28
  ml adapters. The tokenizer is installed with the package. The server is
29
29
  ``http://127.0.0.1:8080`` unless ``url`` or ``ANNOTUNE_URL`` says otherwise.
30
- A matching local server is reused. Remote name and ML services use separate
31
- ``TaskEndpoint(url=...)`` values. ``attach`` connects to an existing service
32
- without loading weights.
30
+ A matching server is reused. When the process list is unavailable, the check
31
+ reads ``/props`` and ``/lora-adapters``. ``TaskEndpoint(url=...)`` calls
32
+ ``/completion`` on that server and enables one adapter per request.
33
+ ``attach`` connects to an existing service without loading weights.
34
+ Model requests wait 600 seconds unless ``timeout`` or ``ANNOTUNE_TIMEOUT``
35
+ says otherwise.
33
36
  """
34
37
 
35
38
  from __future__ import annotations
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  __all__ = ["__version__", "version", "version_tuple"]
9
9
 
10
- __version__ = "0.1.0"
10
+ __version__ = "0.1.1"
11
11
  version = __version__
12
12
 
13
13
  _parts: list[int | str] = []
@@ -628,10 +628,27 @@ def hit_names(payload: dict[str, object]) -> set[str]:
628
628
  return {str(item["name"]) for item in payload.get("dictionary_hits") or []}
629
629
 
630
630
 
631
- def fetch_name(url: str, payload: dict[str, object], timeout: float = 60) -> Decision:
632
- return parse_decision(
633
- post_chat(url, SYSTEM_NAME, payload, timeout, task="name"), hit_names(payload)
631
+ def fetch_name(
632
+ url: str,
633
+ payload: dict[str, object],
634
+ timeout: float | None = None,
635
+ *,
636
+ maxlen: int = 2048,
637
+ tokenizer: str | None = None,
638
+ ) -> Decision:
639
+ """Name task on ``/completion``. One adapter is enabled for this request."""
640
+ from .local_lora import complete_url
641
+
642
+ text = complete_url(
643
+ url,
644
+ "name",
645
+ SYSTEM_NAME,
646
+ payload,
647
+ maxlen=maxlen,
648
+ timeout=timeout,
649
+ tokenizer=tokenizer,
634
650
  )
651
+ return parse_decision(text, hit_names(payload))
635
652
 
636
653
 
637
654
  def candidate_names(payload: dict[str, object]) -> set[str]:
@@ -644,11 +661,27 @@ def candidate_names(payload: dict[str, object]) -> set[str]:
644
661
  return names
645
662
 
646
663
 
647
- def fetch_ml(url: str, payload: dict[str, object], timeout: float = 120) -> MlResult:
648
- return parse_ml(
649
- post_chat(url, SYSTEM_ML, payload, timeout, task="ml"),
650
- candidate_names(payload),
664
+ def fetch_ml(
665
+ url: str,
666
+ payload: dict[str, object],
667
+ timeout: float | None = None,
668
+ *,
669
+ maxlen: int = 8192,
670
+ tokenizer: str | None = None,
671
+ ) -> MlResult:
672
+ """ML task on ``/completion``. One adapter is enabled for this request."""
673
+ from .local_lora import complete_url
674
+
675
+ text = complete_url(
676
+ url,
677
+ "ml",
678
+ SYSTEM_ML,
679
+ payload,
680
+ maxlen=maxlen,
681
+ timeout=timeout,
682
+ tokenizer=tokenizer,
651
683
  )
684
+ return parse_ml(text, candidate_names(payload))
652
685
 
653
686
 
654
687
  def _negative_high(row: dict | None) -> bool: