generesearcher 0.1.0__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 (39) hide show
  1. generesearcher-0.1.0/LICENSE +25 -0
  2. generesearcher-0.1.0/PKG-INFO +229 -0
  3. generesearcher-0.1.0/README.md +198 -0
  4. generesearcher-0.1.0/generesearcher/__init__.py +19 -0
  5. generesearcher-0.1.0/generesearcher/__main__.py +3 -0
  6. generesearcher-0.1.0/generesearcher/api.py +172 -0
  7. generesearcher-0.1.0/generesearcher/cli.py +147 -0
  8. generesearcher-0.1.0/generesearcher/config.py +115 -0
  9. generesearcher-0.1.0/generesearcher/connectors/__init__.py +0 -0
  10. generesearcher-0.1.0/generesearcher/connectors/batch_manager.py +120 -0
  11. generesearcher-0.1.0/generesearcher/connectors/embedding_connector.py +397 -0
  12. generesearcher-0.1.0/generesearcher/connectors/llm_api.py +222 -0
  13. generesearcher-0.1.0/generesearcher/connectors/milvus_connector.py +1011 -0
  14. generesearcher-0.1.0/generesearcher/connectors/ncbi_connector.py +573 -0
  15. generesearcher-0.1.0/generesearcher/core/__init__.py +0 -0
  16. generesearcher-0.1.0/generesearcher/core/data_io.py +247 -0
  17. generesearcher-0.1.0/generesearcher/core/data_models.py +120 -0
  18. generesearcher-0.1.0/generesearcher/core/naming.py +6 -0
  19. generesearcher-0.1.0/generesearcher/core/parsers.py +589 -0
  20. generesearcher-0.1.0/generesearcher/logging_utils.py +67 -0
  21. generesearcher-0.1.0/generesearcher/model_installer.py +59 -0
  22. generesearcher-0.1.0/generesearcher/persistence.py +88 -0
  23. generesearcher-0.1.0/generesearcher/pipeline/__init__.py +0 -0
  24. generesearcher-0.1.0/generesearcher/pipeline/step_01_retrieval.py +668 -0
  25. generesearcher-0.1.0/generesearcher/pipeline/step_02_semantic_filter.py +493 -0
  26. generesearcher-0.1.0/generesearcher/pipeline/step_03_relevance_filter.py +1037 -0
  27. generesearcher-0.1.0/generesearcher/pipeline/step_04_synthesis.py +1301 -0
  28. generesearcher-0.1.0/generesearcher/pipeline/step_05_reporting.py +990 -0
  29. generesearcher-0.1.0/generesearcher/pipeline/step_06_report_generation.py +389 -0
  30. generesearcher-0.1.0/generesearcher/run.py +349 -0
  31. generesearcher-0.1.0/generesearcher/runtime_dependencies.py +113 -0
  32. generesearcher-0.1.0/generesearcher.egg-info/PKG-INFO +229 -0
  33. generesearcher-0.1.0/generesearcher.egg-info/SOURCES.txt +37 -0
  34. generesearcher-0.1.0/generesearcher.egg-info/dependency_links.txt +1 -0
  35. generesearcher-0.1.0/generesearcher.egg-info/entry_points.txt +2 -0
  36. generesearcher-0.1.0/generesearcher.egg-info/requires.txt +24 -0
  37. generesearcher-0.1.0/generesearcher.egg-info/top_level.txt +1 -0
  38. generesearcher-0.1.0/pyproject.toml +50 -0
  39. generesearcher-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2026, GeneResearcher contributors
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: generesearcher
3
+ Version: 0.1.0
4
+ Summary: Gene-centered biomedical literature retrieval and analysis pipeline
5
+ License-Expression: BSD-2-Clause
6
+ Requires-Python: <3.13,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: biopython<2,>=1.83
10
+ Requires-Dist: click<9,>=8
11
+ Requires-Dist: numpy<3,>=1.26
12
+ Requires-Dist: openai<3,>=1.30
13
+ Requires-Dist: pandas<4,>=2.2
14
+ Requires-Dist: pymilvus[milvus_lite]==3.0.0
15
+ Requires-Dist: milvus-lite==2.5.1
16
+ Requires-Dist: setuptools<81,>=68
17
+ Requires-Dist: python-dotenv<2,>=1.0
18
+ Requires-Dist: PyYAML<7,>=6
19
+ Requires-Dist: requests<3,>=2.31
20
+ Requires-Dist: scispacy<0.7,>=0.6
21
+ Requires-Dist: spacy<3.8,>=3.7
22
+ Requires-Dist: tqdm<5,>=4.66
23
+ Provides-Extra: dashscope
24
+ Requires-Dist: dashscope<2,>=1.20; extra == "dashscope"
25
+ Provides-Extra: local-embedding
26
+ Requires-Dist: torch>=2.2; extra == "local-embedding"
27
+ Requires-Dist: transformers<5,>=4.40; extra == "local-embedding"
28
+ Provides-Extra: dev
29
+ Requires-Dist: ruff<1,>=0.6; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # GeneResearcher
33
+
34
+ GeneResearcher searches biomedical literature for gene/function/background relationships, retrieves article text, filters evidence, synthesizes results, and writes a Markdown report.
35
+
36
+ ## Choose how to use it
37
+
38
+ | Use case | Install | Run |
39
+ | --- | --- | --- |
40
+ | Use a built package | Install the wheel, then run `python -m generesearcher install-spacy-model`. | Import `ResearchConfig` and `run_analysis` in your Python code. |
41
+ | Run or modify the source | From the project source directory, run `python -m pip install -e .`, then `python -m generesearcher install-spacy-model`. | Create a study YAML file and run `python -m generesearcher run --config study.yaml`. |
42
+
43
+ Both routes use the same `ResearchConfig` and pipeline. Users provide their own `.env` credentials. The SciSpaCy language model is a separate download, installed with `install-spacy-model` after the package dependencies are installed. The package has build metadata and a working wheel; PyPI installation is available after the project publishes a release.
44
+
45
+ ## How a study is organized
46
+
47
+ - One `project_folder` represents one research background.
48
+ - A project can contain multiple function analyses. Each run analyzes one `function_name` for one or more `genes` and writes to its own `analysis_<function>` directory.
49
+ - Re-running the same function clears that function directory's Milvus database and generated exports before starting. The Python API emits a warning if a database already exists; the pipeline also logs the directory it clears.
50
+ - CSV and TXT files are display exports. Pipeline recovery and later steps use the database as their source of truth.
51
+
52
+ The six pipeline stages run in this order:
53
+
54
+ 1. Retrieve articles and their available text.
55
+ 2. Embed article text and recall relevant sentences.
56
+ 3. Filter recalled evidence for relevance.
57
+ 4. Synthesize per-article and cross-article results.
58
+ 5. Build gene-level and study-level reporting results.
59
+ 6. Generate the final Markdown report.
60
+
61
+ ## Install and run from source
62
+
63
+ Use Python 3.10–3.12 in an isolated environment. SciSpaCy 0.6.x does not support Python 3.13. The clean installation has been verified on macOS arm64 with Python 3.11. From this directory:
64
+
65
+ ```bash
66
+ python -m pip install -e .
67
+ python -m generesearcher install-spacy-model
68
+ cp .env.example .env
69
+ ```
70
+
71
+ Edit `.env` with the required service values. Do not commit it. The default `.env` is read from the current working directory; `--env-file` can point to another file.
72
+
73
+ Step 2 uses a SciSpaCy model to split article text into sentences. The default is `en_core_sci_lg`; you may instead select `en_core_sci_sm`, `en_core_sci_md`, or `en_core_sci_scibert`. The four choices are SciSpaCy's general biomedical pipelines; GeneResearcher does not use the separate NER-only models. After installing GeneResearcher, download and validate the default model with:
74
+
75
+ ```bash
76
+ python -m generesearcher install-spacy-model
77
+ ```
78
+
79
+ To use another model, install it and set the same name in `study.yaml` under `scispacy_model`:
80
+
81
+ ```bash
82
+ python -m generesearcher install-spacy-model --model en_core_sci_sm
83
+ ```
84
+
85
+ ```yaml
86
+ scispacy_model: en_core_sci_sm
87
+ ```
88
+
89
+ The installer downloads the selected model archive from the [official SciSpaCy model list](https://github.com/allenai/scispacy/blob/main/docs/index.md). It does not retain the archive in pip's cache. The default `en_core_sci_lg` model uses about 620 MB after installation; choose `en_core_sci_sm` if you need a smaller model. GeneResearcher checks required dependencies and loads the selected model before it starts a run or clears an existing analysis database; a missing or incompatible model produces an actionable error. Model names are also saved in `params.json` with the analysis parameters.
90
+
91
+ The default install supports the configured Gitee-compatible embedding endpoint. DashScope and local Transformers are optional extras. Install the matching extra with the package:
92
+
93
+ ```bash
94
+ # Source checkout
95
+ python -m pip install -e '.[dashscope]'
96
+
97
+ # Or a built wheel
98
+ python -m pip install 'path/to/generesearcher-0.1.0-py3-none-any.whl[dashscope]'
99
+ ```
100
+
101
+ For a local Transformers embedding model, use `.[local-embedding]` instead and set `EMBEDDING_PROVIDER=local` plus `EMBEDDING_MODEL_NAME` in `.env`. PyTorch can be a large download, so install that extra only when using local embeddings.
102
+
103
+ Copy and edit the example study file, then run:
104
+
105
+ ```bash
106
+ cp study.example.yaml study.yaml
107
+ python -m generesearcher run --config study.yaml
108
+ ```
109
+
110
+ You can specify an environment file and see detailed logs with:
111
+
112
+ ```bash
113
+ python -m generesearcher run --config study.yaml --env-file .env --log-level DEBUG
114
+ python -m generesearcher run --config study.yaml --quiet
115
+ ```
116
+
117
+ The log levels follow one rule across the package:
118
+
119
+ - `DEBUG`: cache decisions, retry attempts, and low-level diagnostics.
120
+ - `INFO`: stage starts and completions, durations, paths, and record counts.
121
+ - `WARNING`: recoverable omissions, fallbacks, and partial results.
122
+ - `ERROR`: failures that stop a stage or the full run.
123
+
124
+ The default is `INFO`. Use `--log-level DEBUG` (or `--verbose`) for detail, `--log-level WARNING` to reduce routine progress messages, `--quiet` to show errors only, or `--log-level OFF` to disable logs. Fatal run failures include a traceback at the Python API boundary. Progress bars are disabled when the level is above `INFO`. Application logs do not include prompts or article text. The Python warning about clearing an existing analysis database remains visible even when logs are disabled.
125
+
126
+ Relative `project_folder` paths in YAML are resolved from the YAML file's directory. The CLI looks for `.env` beside the YAML file, then falls back to `.env` in the current working directory.
127
+
128
+ ## Use as an installed Python package
129
+
130
+ To create an installable wheel from this checkout:
131
+
132
+ ```bash
133
+ python -m pip install build
134
+ python -m build
135
+ python -m pip install dist/generesearcher-0.1.0-py3-none-any.whl
136
+ python -m generesearcher install-spacy-model
137
+ ```
138
+
139
+ PyPI publishing is a separate maintainer release step. Until then, users can install a supplied wheel directly as shown above; people working from the source checkout can use `pip install .` or `pip install -e .`.
140
+
141
+ Synchronous Python:
142
+
143
+ ```python
144
+ import logging
145
+ import os
146
+ from generesearcher import ResearchConfig, run_analysis
147
+
148
+ # Python API users control package log output with Python's logging settings.
149
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
150
+ # To suppress GeneResearcher logs in this process, use:
151
+ # logging.getLogger("generesearcher").setLevel(logging.CRITICAL + 1)
152
+
153
+ # Service credentials can be set in this process. Values already present in
154
+ # the process environment take precedence over values loaded from .env.
155
+ os.environ["ENTREZ_EMAIL"] = "your-email@example.org"
156
+ os.environ["EMBEDDING_PROVIDER"] = "dashscope"
157
+ os.environ["EMBEDDING_API_KEY"] = "your-embedding-api-key"
158
+ os.environ["LLM_MODEL_NAME"] = "your-model-name"
159
+ os.environ["LLM_API_KEY"] = "your-llm-api-key"
160
+ os.environ["LLM_BASE_URL"] = "https://your-llm-endpoint/v1"
161
+
162
+ config = ResearchConfig(
163
+ genes=["EP300", "CREBBP"],
164
+ function_name="histone acetylation",
165
+ research_background_template=(
166
+ "What is the role of {gene} in {function_name}, especially in cancer development?"
167
+ ),
168
+ project_folder="./results/example_study",
169
+ keywords=["cancer", "tumor"],
170
+ gene_aliases={"EP300": ["p300"], "CREBBP": ["CBP"]},
171
+ scispacy_model="en_core_sci_lg",
172
+ )
173
+ result = run_analysis(config)
174
+ print(result.report_path)
175
+ ```
176
+
177
+ The values above are examples; do not commit real credentials in a Python script. You can instead set the variables in the shell, a deployment secret store, or a local `.env` file. GeneResearcher reads process environment variables directly and does not overwrite them when it loads `.env`. The `.env` file is searched in the current working directory unless you pass `env_file` explicitly.
178
+
179
+ For an already-running async application, use `await run_analysis_async(config, env_file=".env")`.
180
+
181
+ ### Save and restore an analysis
182
+
183
+ The usual workflow needs no explicit save or load call. `run_analysis()` returns an `AnalysisResult` in memory. If you want to keep a separate copy or restore a previous result later, use the optional helpers:
184
+
185
+ ```python
186
+ from generesearcher import load_analysis, save_analysis
187
+
188
+ # Save the completed function analysis to a new directory.
189
+ saved_directory = save_analysis(result, "./backups/ep300_histone_acetylation")
190
+
191
+ # Restore the parameters and result tables into memory later.
192
+ restored_result = load_analysis(saved_directory)
193
+ print(restored_result.analysis_data.raw_papers_df.head())
194
+ print(restored_result.report_path)
195
+ ```
196
+
197
+ `save_analysis()` copies the function's complete analysis directory, including its Milvus Lite database, parameters, and generated files. It does not serialize through CSV. The destination directory must not already exist. `load_analysis()` restores from the database and does not call literature, embedding, or language-model services. It returns the saved result for inspection and downstream Python use; it does not rerun the pipeline. Keep the saved directory separate from the active `project_folder`, because a new full run clears the active function analysis before starting.
198
+
199
+ `ResearchConfig`, `params.json`, and Milvus use the same canonical field names. PMID is the exclusion identifier; PMCID is used only as the retrieval/full-text identifier and as the rare fallback when an article has no PMID.
200
+
201
+ ## Configuration fields
202
+
203
+ The YAML file and `ResearchConfig` accept the same fields:
204
+
205
+ | Field | Required | Meaning |
206
+ | --- | --- | --- |
207
+ | `genes` | Yes | One or more gene symbols |
208
+ | `function_name` | Yes | The function or pathway analyzed in this run |
209
+ | `research_background_template` | Yes | Background text; `{gene}` and `{function_name}` are replaced during retrieval and analysis |
210
+ | `project_folder` | No | Root folder shared by runs for the same research background |
211
+ | `keywords` | No | Literature search terms |
212
+ | `function_query` | No | Optional search wording for the function |
213
+ | `exclude_pmids` | No | PMID values to exclude |
214
+ | `publication_date_before` | No | Inclusive search cutoff in the format supported by the search code |
215
+ | `gene_aliases` | No | Additional search aliases keyed by gene symbol |
216
+ | `max_papers_per_gene` | No | Maximum search results per gene; defaults to 10 |
217
+ | `scispacy_model` | No | SciSpaCy sentence-processing model; defaults to `en_core_sci_lg` |
218
+
219
+ Credentials and provider settings belong in environment variables or `.env`, not in the study YAML. Existing process environment variables take precedence over `.env` values. Use `EMBEDDING_API_KEY` for the embedding provider key, including DashScope.
220
+
221
+ ## Naming and persisted data
222
+
223
+ Public Python entry points, configuration fields, persisted JSON keys, and Milvus fields use descriptive `snake_case` names. The project uses the canonical first-version format and does not translate alternate spellings. Research background templates use `{gene}` and `{function_name}` placeholders.
224
+
225
+ Only the `generesearcher` package is included in built distributions. Notebook checkpoints, bytecode caches, and historical duplicate scripts at the repository root are not runtime package modules.
226
+
227
+ ## License
228
+
229
+ GeneResearcher is distributed under the BSD-2-Clause License. See [LICENSE](LICENSE) for the full terms.
@@ -0,0 +1,198 @@
1
+ # GeneResearcher
2
+
3
+ GeneResearcher searches biomedical literature for gene/function/background relationships, retrieves article text, filters evidence, synthesizes results, and writes a Markdown report.
4
+
5
+ ## Choose how to use it
6
+
7
+ | Use case | Install | Run |
8
+ | --- | --- | --- |
9
+ | Use a built package | Install the wheel, then run `python -m generesearcher install-spacy-model`. | Import `ResearchConfig` and `run_analysis` in your Python code. |
10
+ | Run or modify the source | From the project source directory, run `python -m pip install -e .`, then `python -m generesearcher install-spacy-model`. | Create a study YAML file and run `python -m generesearcher run --config study.yaml`. |
11
+
12
+ Both routes use the same `ResearchConfig` and pipeline. Users provide their own `.env` credentials. The SciSpaCy language model is a separate download, installed with `install-spacy-model` after the package dependencies are installed. The package has build metadata and a working wheel; PyPI installation is available after the project publishes a release.
13
+
14
+ ## How a study is organized
15
+
16
+ - One `project_folder` represents one research background.
17
+ - A project can contain multiple function analyses. Each run analyzes one `function_name` for one or more `genes` and writes to its own `analysis_<function>` directory.
18
+ - Re-running the same function clears that function directory's Milvus database and generated exports before starting. The Python API emits a warning if a database already exists; the pipeline also logs the directory it clears.
19
+ - CSV and TXT files are display exports. Pipeline recovery and later steps use the database as their source of truth.
20
+
21
+ The six pipeline stages run in this order:
22
+
23
+ 1. Retrieve articles and their available text.
24
+ 2. Embed article text and recall relevant sentences.
25
+ 3. Filter recalled evidence for relevance.
26
+ 4. Synthesize per-article and cross-article results.
27
+ 5. Build gene-level and study-level reporting results.
28
+ 6. Generate the final Markdown report.
29
+
30
+ ## Install and run from source
31
+
32
+ Use Python 3.10–3.12 in an isolated environment. SciSpaCy 0.6.x does not support Python 3.13. The clean installation has been verified on macOS arm64 with Python 3.11. From this directory:
33
+
34
+ ```bash
35
+ python -m pip install -e .
36
+ python -m generesearcher install-spacy-model
37
+ cp .env.example .env
38
+ ```
39
+
40
+ Edit `.env` with the required service values. Do not commit it. The default `.env` is read from the current working directory; `--env-file` can point to another file.
41
+
42
+ Step 2 uses a SciSpaCy model to split article text into sentences. The default is `en_core_sci_lg`; you may instead select `en_core_sci_sm`, `en_core_sci_md`, or `en_core_sci_scibert`. The four choices are SciSpaCy's general biomedical pipelines; GeneResearcher does not use the separate NER-only models. After installing GeneResearcher, download and validate the default model with:
43
+
44
+ ```bash
45
+ python -m generesearcher install-spacy-model
46
+ ```
47
+
48
+ To use another model, install it and set the same name in `study.yaml` under `scispacy_model`:
49
+
50
+ ```bash
51
+ python -m generesearcher install-spacy-model --model en_core_sci_sm
52
+ ```
53
+
54
+ ```yaml
55
+ scispacy_model: en_core_sci_sm
56
+ ```
57
+
58
+ The installer downloads the selected model archive from the [official SciSpaCy model list](https://github.com/allenai/scispacy/blob/main/docs/index.md). It does not retain the archive in pip's cache. The default `en_core_sci_lg` model uses about 620 MB after installation; choose `en_core_sci_sm` if you need a smaller model. GeneResearcher checks required dependencies and loads the selected model before it starts a run or clears an existing analysis database; a missing or incompatible model produces an actionable error. Model names are also saved in `params.json` with the analysis parameters.
59
+
60
+ The default install supports the configured Gitee-compatible embedding endpoint. DashScope and local Transformers are optional extras. Install the matching extra with the package:
61
+
62
+ ```bash
63
+ # Source checkout
64
+ python -m pip install -e '.[dashscope]'
65
+
66
+ # Or a built wheel
67
+ python -m pip install 'path/to/generesearcher-0.1.0-py3-none-any.whl[dashscope]'
68
+ ```
69
+
70
+ For a local Transformers embedding model, use `.[local-embedding]` instead and set `EMBEDDING_PROVIDER=local` plus `EMBEDDING_MODEL_NAME` in `.env`. PyTorch can be a large download, so install that extra only when using local embeddings.
71
+
72
+ Copy and edit the example study file, then run:
73
+
74
+ ```bash
75
+ cp study.example.yaml study.yaml
76
+ python -m generesearcher run --config study.yaml
77
+ ```
78
+
79
+ You can specify an environment file and see detailed logs with:
80
+
81
+ ```bash
82
+ python -m generesearcher run --config study.yaml --env-file .env --log-level DEBUG
83
+ python -m generesearcher run --config study.yaml --quiet
84
+ ```
85
+
86
+ The log levels follow one rule across the package:
87
+
88
+ - `DEBUG`: cache decisions, retry attempts, and low-level diagnostics.
89
+ - `INFO`: stage starts and completions, durations, paths, and record counts.
90
+ - `WARNING`: recoverable omissions, fallbacks, and partial results.
91
+ - `ERROR`: failures that stop a stage or the full run.
92
+
93
+ The default is `INFO`. Use `--log-level DEBUG` (or `--verbose`) for detail, `--log-level WARNING` to reduce routine progress messages, `--quiet` to show errors only, or `--log-level OFF` to disable logs. Fatal run failures include a traceback at the Python API boundary. Progress bars are disabled when the level is above `INFO`. Application logs do not include prompts or article text. The Python warning about clearing an existing analysis database remains visible even when logs are disabled.
94
+
95
+ Relative `project_folder` paths in YAML are resolved from the YAML file's directory. The CLI looks for `.env` beside the YAML file, then falls back to `.env` in the current working directory.
96
+
97
+ ## Use as an installed Python package
98
+
99
+ To create an installable wheel from this checkout:
100
+
101
+ ```bash
102
+ python -m pip install build
103
+ python -m build
104
+ python -m pip install dist/generesearcher-0.1.0-py3-none-any.whl
105
+ python -m generesearcher install-spacy-model
106
+ ```
107
+
108
+ PyPI publishing is a separate maintainer release step. Until then, users can install a supplied wheel directly as shown above; people working from the source checkout can use `pip install .` or `pip install -e .`.
109
+
110
+ Synchronous Python:
111
+
112
+ ```python
113
+ import logging
114
+ import os
115
+ from generesearcher import ResearchConfig, run_analysis
116
+
117
+ # Python API users control package log output with Python's logging settings.
118
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
119
+ # To suppress GeneResearcher logs in this process, use:
120
+ # logging.getLogger("generesearcher").setLevel(logging.CRITICAL + 1)
121
+
122
+ # Service credentials can be set in this process. Values already present in
123
+ # the process environment take precedence over values loaded from .env.
124
+ os.environ["ENTREZ_EMAIL"] = "your-email@example.org"
125
+ os.environ["EMBEDDING_PROVIDER"] = "dashscope"
126
+ os.environ["EMBEDDING_API_KEY"] = "your-embedding-api-key"
127
+ os.environ["LLM_MODEL_NAME"] = "your-model-name"
128
+ os.environ["LLM_API_KEY"] = "your-llm-api-key"
129
+ os.environ["LLM_BASE_URL"] = "https://your-llm-endpoint/v1"
130
+
131
+ config = ResearchConfig(
132
+ genes=["EP300", "CREBBP"],
133
+ function_name="histone acetylation",
134
+ research_background_template=(
135
+ "What is the role of {gene} in {function_name}, especially in cancer development?"
136
+ ),
137
+ project_folder="./results/example_study",
138
+ keywords=["cancer", "tumor"],
139
+ gene_aliases={"EP300": ["p300"], "CREBBP": ["CBP"]},
140
+ scispacy_model="en_core_sci_lg",
141
+ )
142
+ result = run_analysis(config)
143
+ print(result.report_path)
144
+ ```
145
+
146
+ The values above are examples; do not commit real credentials in a Python script. You can instead set the variables in the shell, a deployment secret store, or a local `.env` file. GeneResearcher reads process environment variables directly and does not overwrite them when it loads `.env`. The `.env` file is searched in the current working directory unless you pass `env_file` explicitly.
147
+
148
+ For an already-running async application, use `await run_analysis_async(config, env_file=".env")`.
149
+
150
+ ### Save and restore an analysis
151
+
152
+ The usual workflow needs no explicit save or load call. `run_analysis()` returns an `AnalysisResult` in memory. If you want to keep a separate copy or restore a previous result later, use the optional helpers:
153
+
154
+ ```python
155
+ from generesearcher import load_analysis, save_analysis
156
+
157
+ # Save the completed function analysis to a new directory.
158
+ saved_directory = save_analysis(result, "./backups/ep300_histone_acetylation")
159
+
160
+ # Restore the parameters and result tables into memory later.
161
+ restored_result = load_analysis(saved_directory)
162
+ print(restored_result.analysis_data.raw_papers_df.head())
163
+ print(restored_result.report_path)
164
+ ```
165
+
166
+ `save_analysis()` copies the function's complete analysis directory, including its Milvus Lite database, parameters, and generated files. It does not serialize through CSV. The destination directory must not already exist. `load_analysis()` restores from the database and does not call literature, embedding, or language-model services. It returns the saved result for inspection and downstream Python use; it does not rerun the pipeline. Keep the saved directory separate from the active `project_folder`, because a new full run clears the active function analysis before starting.
167
+
168
+ `ResearchConfig`, `params.json`, and Milvus use the same canonical field names. PMID is the exclusion identifier; PMCID is used only as the retrieval/full-text identifier and as the rare fallback when an article has no PMID.
169
+
170
+ ## Configuration fields
171
+
172
+ The YAML file and `ResearchConfig` accept the same fields:
173
+
174
+ | Field | Required | Meaning |
175
+ | --- | --- | --- |
176
+ | `genes` | Yes | One or more gene symbols |
177
+ | `function_name` | Yes | The function or pathway analyzed in this run |
178
+ | `research_background_template` | Yes | Background text; `{gene}` and `{function_name}` are replaced during retrieval and analysis |
179
+ | `project_folder` | No | Root folder shared by runs for the same research background |
180
+ | `keywords` | No | Literature search terms |
181
+ | `function_query` | No | Optional search wording for the function |
182
+ | `exclude_pmids` | No | PMID values to exclude |
183
+ | `publication_date_before` | No | Inclusive search cutoff in the format supported by the search code |
184
+ | `gene_aliases` | No | Additional search aliases keyed by gene symbol |
185
+ | `max_papers_per_gene` | No | Maximum search results per gene; defaults to 10 |
186
+ | `scispacy_model` | No | SciSpaCy sentence-processing model; defaults to `en_core_sci_lg` |
187
+
188
+ Credentials and provider settings belong in environment variables or `.env`, not in the study YAML. Existing process environment variables take precedence over `.env` values. Use `EMBEDDING_API_KEY` for the embedding provider key, including DashScope.
189
+
190
+ ## Naming and persisted data
191
+
192
+ Public Python entry points, configuration fields, persisted JSON keys, and Milvus fields use descriptive `snake_case` names. The project uses the canonical first-version format and does not translate alternate spellings. Research background templates use `{gene}` and `{function_name}` placeholders.
193
+
194
+ Only the `generesearcher` package is included in built distributions. Notebook checkpoints, bytecode caches, and historical duplicate scripts at the repository root are not runtime package modules.
195
+
196
+ ## License
197
+
198
+ GeneResearcher is distributed under the BSD-2-Clause License. See [LICENSE](LICENSE) for the full terms.
@@ -0,0 +1,19 @@
1
+ """GeneResearcher literature-analysis package."""
2
+
3
+ import logging
4
+
5
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
6
+
7
+ from .api import AnalysisResult, ResearchConfig, run_analysis, run_analysis_async
8
+ from .persistence import load_analysis, save_analysis
9
+
10
+ __all__ = [
11
+ "AnalysisResult",
12
+ "ResearchConfig",
13
+ "load_analysis",
14
+ "run_analysis",
15
+ "run_analysis_async",
16
+ "save_analysis",
17
+ ]
18
+
19
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,172 @@
1
+ """Stable Python API for running one GeneResearcher analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import warnings
7
+ from dataclasses import dataclass, field
8
+ from datetime import date, datetime
9
+ from pathlib import Path
10
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
11
+
12
+ from .runtime_dependencies import DEFAULT_SCISPACY_MODEL, validate_scispacy_model_name
13
+ from .logging_utils import log_operation
14
+
15
+ if TYPE_CHECKING:
16
+ from .core.data_models import AnalysisData
17
+ else:
18
+ # Keep importing the public API possible before runtime packages are checked.
19
+ AnalysisData = Any
20
+
21
+
22
+ @dataclass
23
+ class ResearchConfig:
24
+ """Inputs for one function analysis within a research project.
25
+
26
+ A project folder represents one research background and may contain
27
+ multiple function analyses. Each config runs one function for one or more
28
+ genes.
29
+ """
30
+
31
+ genes: List[str]
32
+ function_name: str
33
+ research_background_template: str
34
+ project_folder: str | Path = "GeneResearch"
35
+ keywords: List[str] = field(default_factory=list)
36
+ function_query: Optional[str] = None
37
+ exclude_pmids: List[str] = field(default_factory=list)
38
+ publication_date_before: Optional[str] = None
39
+ gene_aliases: Dict[str, List[str]] = field(default_factory=dict)
40
+ max_papers_per_gene: int = 10
41
+ scispacy_model: str = DEFAULT_SCISPACY_MODEL
42
+
43
+ def __post_init__(self) -> None:
44
+ if isinstance(self.genes, str) or not self.genes:
45
+ raise ValueError("genes must be a non-empty list of gene names")
46
+ if any(not isinstance(gene, str) or not gene.strip() for gene in self.genes):
47
+ raise ValueError("genes must contain only non-empty strings")
48
+ if not isinstance(self.function_name, str) or not self.function_name.strip():
49
+ raise ValueError("function_name must be a non-empty string")
50
+ if not isinstance(self.research_background_template, str) or not self.research_background_template.strip():
51
+ raise ValueError("research_background_template must be a non-empty string")
52
+ if not isinstance(self.project_folder, (str, Path)):
53
+ raise TypeError("project_folder must be a string or pathlib.Path")
54
+ if not str(self.project_folder).strip():
55
+ raise ValueError("project_folder must be a non-empty path")
56
+ if not isinstance(self.max_papers_per_gene, int) or isinstance(self.max_papers_per_gene, bool):
57
+ raise TypeError("max_papers_per_gene must be an integer")
58
+ if self.max_papers_per_gene < 1:
59
+ raise ValueError("max_papers_per_gene must be at least 1")
60
+ if not isinstance(self.scispacy_model, str):
61
+ raise TypeError("scispacy_model must be a string")
62
+ validate_scispacy_model_name(self.scispacy_model)
63
+ if isinstance(self.keywords, str) or not isinstance(self.keywords, list):
64
+ raise TypeError("keywords must be a list of search terms")
65
+ if any(not isinstance(keyword, str) or not keyword.strip() for keyword in self.keywords):
66
+ raise ValueError("keywords must contain only non-empty strings")
67
+ if isinstance(self.exclude_pmids, str) or not isinstance(self.exclude_pmids, list):
68
+ raise TypeError("exclude_pmids must be a list of PMID values")
69
+ if any(not isinstance(pmid, str) or not pmid.strip() for pmid in self.exclude_pmids):
70
+ raise ValueError("exclude_pmids must contain only non-empty strings")
71
+ if not isinstance(self.gene_aliases, dict):
72
+ raise TypeError("gene_aliases must map gene symbols to lists of aliases")
73
+ if any(
74
+ not isinstance(gene, str)
75
+ or isinstance(aliases, str)
76
+ or not isinstance(aliases, list)
77
+ or any(not isinstance(alias, str) for alias in aliases)
78
+ for gene, aliases in self.gene_aliases.items()
79
+ ):
80
+ raise ValueError("gene_aliases must map gene symbols to lists of alias strings")
81
+ if isinstance(self.publication_date_before, datetime):
82
+ self.publication_date_before = self.publication_date_before.date().isoformat()
83
+ elif isinstance(self.publication_date_before, date):
84
+ self.publication_date_before = self.publication_date_before.isoformat()
85
+ elif self.publication_date_before is not None and not isinstance(self.publication_date_before, str):
86
+ raise TypeError("publication_date_before must be a date string or date value")
87
+
88
+ def to_analysis_data(self) -> AnalysisData:
89
+ """Map the public config onto the canonical pipeline state model."""
90
+ from .core.data_models import AnalysisData
91
+
92
+ return AnalysisData(
93
+ genes=list(self.genes),
94
+ function_name=self.function_name,
95
+ research_background_template=self.research_background_template,
96
+ keywords=list(self.keywords),
97
+ function_query=self.function_query,
98
+ exclude_pmids=list(self.exclude_pmids),
99
+ publication_date_before=self.publication_date_before,
100
+ gene_aliases={gene: list(aliases) for gene, aliases in self.gene_aliases.items()},
101
+ max_papers_per_gene=self.max_papers_per_gene,
102
+ scispacy_model=self.scispacy_model,
103
+ project_folder=str(Path(self.project_folder).expanduser()),
104
+ )
105
+
106
+
107
+ @dataclass
108
+ class AnalysisResult:
109
+ """Paths and restored state produced by a completed analysis."""
110
+
111
+ analysis_directory: Path
112
+ report_path: Path
113
+ analysis_data: AnalysisData
114
+
115
+
116
+ @log_operation("GeneResearcher analysis", log_failure=True)
117
+ async def run_analysis_async(
118
+ config: ResearchConfig,
119
+ *,
120
+ env_file: str | Path | None = None,
121
+ ) -> AnalysisResult:
122
+ """Run the complete pipeline from asynchronous Python code."""
123
+ if not isinstance(config, ResearchConfig):
124
+ raise TypeError("config must be a ResearchConfig instance")
125
+
126
+ # Validate dependencies and load the large NLP model before initializing
127
+ # services or clearing any previous analysis database.
128
+ from .runtime_dependencies import load_scispacy_model
129
+
130
+ spacy_nlp = load_scispacy_model(config.scispacy_model)
131
+ analysis_data = config.to_analysis_data()
132
+ from .core.naming import analysis_directory_name
133
+ from .run import GeneResearchPipeline
134
+
135
+ analysis_directory = Path(analysis_data.project_folder) / (
136
+ f"analysis_{analysis_directory_name(config.function_name)}"
137
+ )
138
+ database_path = analysis_directory / "step2_embeddings.db"
139
+ if database_path.exists():
140
+ warnings.warn(
141
+ f"Existing analysis data will be cleared before this full run: {database_path}",
142
+ UserWarning,
143
+ stacklevel=2,
144
+ )
145
+
146
+ pipeline = GeneResearchPipeline(
147
+ project_folder=analysis_data.project_folder,
148
+ env_file=env_file,
149
+ spacy_nlp=spacy_nlp,
150
+ )
151
+ completed_data = await pipeline.run_full_analysis(analysis_data)
152
+ return AnalysisResult(
153
+ analysis_directory=analysis_directory,
154
+ report_path=analysis_directory / "analysis_report.md",
155
+ analysis_data=completed_data,
156
+ )
157
+
158
+
159
+ def run_analysis(
160
+ config: ResearchConfig,
161
+ *,
162
+ env_file: str | Path | None = None,
163
+ ) -> AnalysisResult:
164
+ """Run the complete pipeline from regular synchronous Python code."""
165
+ try:
166
+ asyncio.get_running_loop()
167
+ except RuntimeError:
168
+ return asyncio.run(run_analysis_async(config, env_file=env_file))
169
+ raise RuntimeError(
170
+ "run_analysis() cannot run inside an active event loop; "
171
+ "use `await run_analysis_async(...)` instead."
172
+ )