Add files using upload-large-folder tool
Browse files- .env.example +22 -0
- .gitignore +234 -0
- .pre-commit-config.yaml +41 -0
- CONTRIBUTION.md +129 -0
- DETAILS.md +364 -0
- LICENSE +201 -0
- MANIFEST.in +27 -0
- README.md +420 -0
- biomni/__init__.py +3 -0
- biomni/config.py +99 -0
- biomni/env_desc.py +221 -0
- biomni/env_desc_cm.py +219 -0
- biomni/llm.py +275 -0
- biomni/utils.py +2366 -0
- biomni/version.py +1 -0
- biomni_env/README.md +74 -0
- biomni_env/bio_env.yml +70 -0
- biomni_env/bio_env_py310.yml +19 -0
- biomni_env/cli_tools_config.json +94 -0
- biomni_env/environment.yml +39 -0
- biomni_env/fixed_env.yml +742 -0
- biomni_env/install_cli_tools.sh +912 -0
- biomni_env/install_r_packages.R +132 -0
- biomni_env/new_software_v008.sh +18 -0
- biomni_env/r_packages.yml +7 -0
- biomni_env/setup.sh +232 -0
- docs/Makefile +28 -0
- docs/building_documentation.md +36 -0
- docs/configuration.md +147 -0
- docs/generate_api_docs.py +124 -0
- docs/known_conflicts.md +34 -0
- docs/make.bat +35 -0
- docs/mcp_integration.md +347 -0
- experiments/bioagent_bench/README.md +102 -0
- experiments/bioagent_bench/README_RUN.md +208 -0
- experiments/bioagent_bench/configs/mcp_scale_100.yaml +0 -0
- experiments/bioagent_bench/configs/mcp_scale_manifest.json +1329 -0
- experiments/bioagent_bench/evaluate_bioagent_bench.py +1198 -0
- experiments/bioagent_bench/gold_tools.json +553 -0
- experiments/lab_bench/README_RUN.md +289 -0
- experiments/lab_bench/__pycache__/summarize_labbench_repeats.cpython-310.pyc +0 -0
- experiments/lab_bench/__pycache__/summarize_labbench_repeats.cpython-313.pyc +0 -0
- experiments/lab_bench/run_all_labbench_with_biomni.sh +9 -0
- experiments/lab_bench/run_dbqa_biomni_experiment.sh +129 -0
- experiments/lab_bench/run_dbqa_seqqa_biomni_experiment.sh +37 -0
- experiments/lab_bench/run_seqqa_biomni_experiment.sh +129 -0
- figs/biomni_logo.png +0 -0
- license_info.md +52 -0
- pyproject.toml +105 -0
- tutorials/biomni_101.ipynb +1070 -0
.env.example
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni Environment Configuration
|
| 2 |
+
# Copy this file to .env and fill in your actual API keys
|
| 3 |
+
|
| 4 |
+
# Required: Anthropic API Key for Claude models
|
| 5 |
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
| 6 |
+
|
| 7 |
+
# Optional: OpenAI API Key (if using OpenAI models)
|
| 8 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 9 |
+
|
| 10 |
+
# Optional: AWS Bedrock Configuration (if using AWS Bedrock models)
|
| 11 |
+
AWS_BEARER_TOKEN_BEDROCK=your_bedrock_api_key_here
|
| 12 |
+
AWS_REGION=us-east-1
|
| 13 |
+
|
| 14 |
+
# Optional: Custom model serving configuration
|
| 15 |
+
# CUSTOM_MODEL_BASE_URL=http://localhost:8000/v1
|
| 16 |
+
# CUSTOM_MODEL_API_KEY=your_custom_api_key_here
|
| 17 |
+
|
| 18 |
+
# Optional: Biomni data path (defaults to ./data)
|
| 19 |
+
# BIOMNI_DATA_PATH=/path/to/your/data
|
| 20 |
+
|
| 21 |
+
# Optional: Timeout settings
|
| 22 |
+
# BIOMNI_TIMEOUT_SECONDS=600
|
.gitignore
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.gitignore
|
| 2 |
+
.buildplan
|
| 3 |
+
.env
|
| 4 |
+
debug*
|
| 5 |
+
.*
|
| 6 |
+
# Byte-compiled / optimized / DLL files
|
| 7 |
+
biomni_release/biomni_env/biomni_tools/
|
| 8 |
+
open_source_process.ipynb
|
| 9 |
+
biomni_env/biomni_tools/*
|
| 10 |
+
.gradio/
|
| 11 |
+
*xlsx
|
| 12 |
+
scripts/*.png
|
| 13 |
+
notebooks/figures/*
|
| 14 |
+
notebooks/tmp_dir/*
|
| 15 |
+
notebooks/tmp_directory/*
|
| 16 |
+
notebooks/save_folder/*
|
| 17 |
+
notebooks/cache
|
| 18 |
+
notebooks/\[draft*
|
| 19 |
+
notebooks/\[scratch*
|
| 20 |
+
notebooks/labbench/*
|
| 21 |
+
scripts/save_folder/*
|
| 22 |
+
scripts/\[draft*
|
| 23 |
+
scripts/tmp_dir/*
|
| 24 |
+
scripts/tmp_directory/*
|
| 25 |
+
scripts/figures/*
|
| 26 |
+
*/scripts/outputs/*
|
| 27 |
+
*/scripts/wandb/*
|
| 28 |
+
*/results/*
|
| 29 |
+
bioagentos/agent/popper_agent.py
|
| 30 |
+
bioagentos/scripts/biorxiv_results
|
| 31 |
+
bioagentos/tool/data_tool/old_data_tool/*
|
| 32 |
+
examples/*.png
|
| 33 |
+
examples/*.csv
|
| 34 |
+
examples/*.fa
|
| 35 |
+
scratch/
|
| 36 |
+
packages/
|
| 37 |
+
data
|
| 38 |
+
dataset
|
| 39 |
+
logs
|
| 40 |
+
scripts/gradio
|
| 41 |
+
data_lake
|
| 42 |
+
__pycache__/
|
| 43 |
+
*.py[cod]
|
| 44 |
+
*$py.class
|
| 45 |
+
res/
|
| 46 |
+
examples/tmp*
|
| 47 |
+
bioagentos/task/lab_bench.py
|
| 48 |
+
*database/
|
| 49 |
+
.history/
|
| 50 |
+
|
| 51 |
+
temp.*
|
| 52 |
+
|
| 53 |
+
scripts/analysis/all_tools.txt
|
| 54 |
+
|
| 55 |
+
BIOMNI_GRADIO/biomni/tool/files/*
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
# C extensions
|
| 59 |
+
*.so
|
| 60 |
+
|
| 61 |
+
# Distribution / packaging
|
| 62 |
+
.Python
|
| 63 |
+
build/
|
| 64 |
+
develop-eggs/
|
| 65 |
+
dist/
|
| 66 |
+
downloads/
|
| 67 |
+
eggs/
|
| 68 |
+
.eggs/
|
| 69 |
+
lib/
|
| 70 |
+
lib64/
|
| 71 |
+
parts/
|
| 72 |
+
sdist/
|
| 73 |
+
var/
|
| 74 |
+
wheels/
|
| 75 |
+
share/python-wheels/
|
| 76 |
+
*.egg-info/
|
| 77 |
+
.installed.cfg
|
| 78 |
+
*.egg
|
| 79 |
+
MANIFEST
|
| 80 |
+
|
| 81 |
+
# PyInstaller
|
| 82 |
+
# Usually these files are written by a python script from a template
|
| 83 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 84 |
+
*.manifest
|
| 85 |
+
*.spec
|
| 86 |
+
|
| 87 |
+
# Installer logs
|
| 88 |
+
pip-log.txt
|
| 89 |
+
pip-delete-this-directory.txt
|
| 90 |
+
|
| 91 |
+
# Unit test / coverage reports
|
| 92 |
+
htmlcov/
|
| 93 |
+
.tox/
|
| 94 |
+
.nox/
|
| 95 |
+
.coverage
|
| 96 |
+
.coverage.*
|
| 97 |
+
.cache
|
| 98 |
+
nosetests.xml
|
| 99 |
+
coverage.xml
|
| 100 |
+
*.cover
|
| 101 |
+
*.py,cover
|
| 102 |
+
.hypothesis/
|
| 103 |
+
.pytest_cache/
|
| 104 |
+
cover/
|
| 105 |
+
test.ipynb
|
| 106 |
+
|
| 107 |
+
# Translations
|
| 108 |
+
*.mo
|
| 109 |
+
*.pot
|
| 110 |
+
|
| 111 |
+
# Django stuff:
|
| 112 |
+
*.log
|
| 113 |
+
local_settings.py
|
| 114 |
+
db.sqlite3
|
| 115 |
+
db.sqlite3-journal
|
| 116 |
+
|
| 117 |
+
# Flask stuff:
|
| 118 |
+
instance/
|
| 119 |
+
.webassets-cache
|
| 120 |
+
|
| 121 |
+
# Scrapy stuff:
|
| 122 |
+
.scrapy
|
| 123 |
+
|
| 124 |
+
# Sphinx documentation
|
| 125 |
+
docs/_build/
|
| 126 |
+
|
| 127 |
+
# PyBuilder
|
| 128 |
+
.pybuilder/
|
| 129 |
+
target/
|
| 130 |
+
|
| 131 |
+
# Jupyter Notebook
|
| 132 |
+
.ipynb_checkpoints
|
| 133 |
+
|
| 134 |
+
# IPython
|
| 135 |
+
profile_default/
|
| 136 |
+
ipython_config.py
|
| 137 |
+
|
| 138 |
+
# pyenv
|
| 139 |
+
# For a library or package, you might want to ignore these files since the code is
|
| 140 |
+
# intended to run in multiple environments; otherwise, check them in:
|
| 141 |
+
# .python-version
|
| 142 |
+
|
| 143 |
+
# pipenv
|
| 144 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 145 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 146 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 147 |
+
# install all needed dependencies.
|
| 148 |
+
#Pipfile.lock
|
| 149 |
+
|
| 150 |
+
# poetry
|
| 151 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 152 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 153 |
+
# commonly ignored for libraries.
|
| 154 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 155 |
+
#poetry.lock
|
| 156 |
+
|
| 157 |
+
# pdm
|
| 158 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 159 |
+
#pdm.lock
|
| 160 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
| 161 |
+
# in version control.
|
| 162 |
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
| 163 |
+
.pdm.toml
|
| 164 |
+
.pdm-python
|
| 165 |
+
.pdm-build/
|
| 166 |
+
|
| 167 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 168 |
+
__pypackages__/
|
| 169 |
+
|
| 170 |
+
# Celery stuff
|
| 171 |
+
celerybeat-schedule
|
| 172 |
+
celerybeat.pid
|
| 173 |
+
|
| 174 |
+
# SageMath parsed files
|
| 175 |
+
*.sage.py
|
| 176 |
+
|
| 177 |
+
# Environments
|
| 178 |
+
.env
|
| 179 |
+
.venv
|
| 180 |
+
env/
|
| 181 |
+
venv/
|
| 182 |
+
ENV/
|
| 183 |
+
env.bak/
|
| 184 |
+
venv.bak/
|
| 185 |
+
.envrc
|
| 186 |
+
uv.lock
|
| 187 |
+
|
| 188 |
+
# Spyder project settings
|
| 189 |
+
.spyderproject
|
| 190 |
+
.spyproject
|
| 191 |
+
|
| 192 |
+
# Rope project settings
|
| 193 |
+
.ropeproject
|
| 194 |
+
|
| 195 |
+
# mkdocs documentation
|
| 196 |
+
/site
|
| 197 |
+
|
| 198 |
+
# mypy
|
| 199 |
+
.mypy_cache/
|
| 200 |
+
.dmypy.json
|
| 201 |
+
dmypy.json
|
| 202 |
+
|
| 203 |
+
*.jsonl
|
| 204 |
+
|
| 205 |
+
# Pyre type checker
|
| 206 |
+
.pyre/
|
| 207 |
+
|
| 208 |
+
# pytype static type analyzer
|
| 209 |
+
.pytype/
|
| 210 |
+
|
| 211 |
+
# Cython debug symbols
|
| 212 |
+
cython_debug/
|
| 213 |
+
|
| 214 |
+
# macOS system files
|
| 215 |
+
.DS_Store
|
| 216 |
+
**/.DS_Store
|
| 217 |
+
|
| 218 |
+
# DDInter data files
|
| 219 |
+
biomni/tool/schema_db/ddinter_*.pkl
|
| 220 |
+
data/ddinter_raw/
|
| 221 |
+
|
| 222 |
+
# PyCharm
|
| 223 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 224 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 225 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 226 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 227 |
+
#.idea/
|
| 228 |
+
|
| 229 |
+
# VSCode
|
| 230 |
+
/.vscode
|
| 231 |
+
|
| 232 |
+
# Sphinx build
|
| 233 |
+
/docs/build/
|
| 234 |
+
/docs/source/api/
|
.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fail_fast: false
|
| 2 |
+
default_language_version:
|
| 3 |
+
python: python3
|
| 4 |
+
default_stages:
|
| 5 |
+
- pre-commit
|
| 6 |
+
- pre-push
|
| 7 |
+
minimum_pre_commit_version: 2.16.0
|
| 8 |
+
repos:
|
| 9 |
+
- repo: https://github.com/biomejs/pre-commit
|
| 10 |
+
rev: v2.3.10
|
| 11 |
+
hooks:
|
| 12 |
+
- id: biome-format
|
| 13 |
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
| 14 |
+
rev: v0.14.11
|
| 15 |
+
hooks:
|
| 16 |
+
- id: ruff-check
|
| 17 |
+
args: [--fix, --exit-non-zero-on-fix, --unsafe-fixes]
|
| 18 |
+
- id: ruff-format
|
| 19 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 20 |
+
rev: v6.0.0
|
| 21 |
+
hooks:
|
| 22 |
+
- id: detect-private-key
|
| 23 |
+
- id: check-ast
|
| 24 |
+
- id: end-of-file-fixer
|
| 25 |
+
- id: mixed-line-ending
|
| 26 |
+
args: [--fix=lf]
|
| 27 |
+
- id: trailing-whitespace
|
| 28 |
+
- id: check-case-conflict
|
| 29 |
+
- id: check-added-large-files
|
| 30 |
+
- id: check-toml
|
| 31 |
+
- id: check-yaml
|
| 32 |
+
- id: check-merge-conflict
|
| 33 |
+
- id: no-commit-to-branch
|
| 34 |
+
args: ["--branch=main"]
|
| 35 |
+
#- repo: https://github.com/pre-commit/mirrors-mypy
|
| 36 |
+
# rev: v1.16.1
|
| 37 |
+
# hooks:
|
| 38 |
+
# - id: mypy
|
| 39 |
+
# args: [--no-strict-optional, --ignore-missing-imports]
|
| 40 |
+
# additional_dependencies:
|
| 41 |
+
# ["types-setuptools", "types-requests", "types-attrs"]
|
CONTRIBUTION.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing to Biomni
|
| 2 |
+
|
| 3 |
+
Thank you for your interest in contributing to Biomni! We're building the infrastructure layer for biomedical AI agents, and we welcome contributions from the community. Contributors with significant contributions will be invited to co-author publications in top-tier journals and conferences.
|
| 4 |
+
|
| 5 |
+
## Getting Started
|
| 6 |
+
|
| 7 |
+
Before contributing, please ensure you:
|
| 8 |
+
- Have tested your changes locally
|
| 9 |
+
- Follow the existing code style and conventions
|
| 10 |
+
- Include appropriate documentation
|
| 11 |
+
|
| 12 |
+
## Types of Contributions
|
| 13 |
+
|
| 14 |
+
### 🛠️ Adding a New Tool
|
| 15 |
+
|
| 16 |
+
Tools are implemented as Python functions in `biomni/tool/XXX.py`, organized by subject area.
|
| 17 |
+
|
| 18 |
+
**Steps:**
|
| 19 |
+
1. **Implement and test** your function locally. If it requires additional software, create installation script and append it into `biomni_env/new_software_{VERSION}.sh`
|
| 20 |
+
|
| 21 |
+
2. **Choose the appropriate subject** category (e.g. database, biochemistry, etc.)
|
| 22 |
+
|
| 23 |
+
3. **Create a tool description** in `biomni/tool/tool_description/XXX.py` following the existing format
|
| 24 |
+
|
| 25 |
+
*Tip: Use this helper to auto-generate descriptions:*
|
| 26 |
+
```python
|
| 27 |
+
from biomni.utils import function_to_api_schema
|
| 28 |
+
from biomni.llm import get_llm
|
| 29 |
+
|
| 30 |
+
llm = get_llm('claude-sonnet-4-20250514')
|
| 31 |
+
desc = function_to_api_schema(function_code, llm)
|
| 32 |
+
```
|
| 33 |
+
4. **Create a test prompt** that uses your tool and verify the agent works correctly
|
| 34 |
+
5. **Submit a pull request** for review, don't forget to include your test prompt as well
|
| 35 |
+
|
| 36 |
+
### 📊 Adding New Data
|
| 37 |
+
|
| 38 |
+
If the data source has web API, follow this process:
|
| 39 |
+
|
| 40 |
+
**Steps:**
|
| 41 |
+
1. **Verify uniqueness** - ensure no overlap with existing data
|
| 42 |
+
2. **Add a new query_XX function** to `biomni/tool/database.py`, follow the format from the other functions.
|
| 43 |
+
3. **Create a tool description** in `biomni/tool/tool_description/database.py` following the existing format
|
| 44 |
+
|
| 45 |
+
If the data source has no API access, follow the process below:
|
| 46 |
+
|
| 47 |
+
**Steps:**
|
| 48 |
+
1. **Verify uniqueness** - ensure no overlap with existing data
|
| 49 |
+
2. **Prepare download link** with verified redistribution rights
|
| 50 |
+
3. **Add entry** to `data_lake_dict` in `biomni/env_desc.py`
|
| 51 |
+
4. **Submit a pull request** with the download link
|
| 52 |
+
|
| 53 |
+
Then, make a PR.
|
| 54 |
+
|
| 55 |
+
### 💻 Adding New Software
|
| 56 |
+
|
| 57 |
+
**Steps:**
|
| 58 |
+
1. **Test locally** to ensure no conflicts with existing environments
|
| 59 |
+
2. **Create installation script** and append it into `biomni_env/new_software_{VERSION}.sh`
|
| 60 |
+
3. **Add entry** to `library_content_dict` in `biomni/env_desc.py`
|
| 61 |
+
4. **Submit a pull request** including:
|
| 62 |
+
- Installation bash script
|
| 63 |
+
- Screenshot demonstrating no environment conflicts
|
| 64 |
+
|
| 65 |
+
### 🎯 Adding a New Benchmark
|
| 66 |
+
|
| 67 |
+
Create benchmarks in the `biomni/task/` folder.
|
| 68 |
+
|
| 69 |
+
**Required implementation:**
|
| 70 |
+
```python
|
| 71 |
+
class YourBenchmark:
|
| 72 |
+
def __init__(self):
|
| 73 |
+
# Initialize benchmark
|
| 74 |
+
pass
|
| 75 |
+
|
| 76 |
+
def __len__(self):
|
| 77 |
+
# Return dataset size
|
| 78 |
+
pass
|
| 79 |
+
|
| 80 |
+
def get_example(self, index):
|
| 81 |
+
# Return dataset item at index
|
| 82 |
+
pass
|
| 83 |
+
|
| 84 |
+
def evaluate(self):
|
| 85 |
+
# Evaluation logic (flexible input format)
|
| 86 |
+
pass
|
| 87 |
+
|
| 88 |
+
def output_class(self):
|
| 89 |
+
# Return expected agent output format
|
| 90 |
+
pass
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
**Steps:**
|
| 94 |
+
1. **Create benchmark file** in `biomni/task/[benchmark_name].py`
|
| 95 |
+
2. **Implement required methods** as shown above
|
| 96 |
+
3. **Provide data download link** for associated datasets
|
| 97 |
+
4. **Submit a pull request**
|
| 98 |
+
|
| 99 |
+
### 🐛 Bug Fixes & Enhancements
|
| 100 |
+
|
| 101 |
+
We welcome all bug fixes and enhancements to the existing codebase!
|
| 102 |
+
|
| 103 |
+
**Create an issue to discuss with the Biomni team first.**
|
| 104 |
+
|
| 105 |
+
**Guidelines:**
|
| 106 |
+
- Clearly describe the issue or enhancement
|
| 107 |
+
- Include tests when applicable
|
| 108 |
+
- Follow existing code patterns
|
| 109 |
+
- Update documentation if needed
|
| 110 |
+
|
| 111 |
+
## Submission Process
|
| 112 |
+
|
| 113 |
+
1. **Fork** the repository
|
| 114 |
+
2. **Create a feature branch** from `main`
|
| 115 |
+
3. **Make your changes** following the guidelines above
|
| 116 |
+
4. **Test thoroughly** in your local environment
|
| 117 |
+
5. **Submit a pull request** with a clear description
|
| 118 |
+
|
| 119 |
+
## Review Process
|
| 120 |
+
|
| 121 |
+
The Biomni team will review all pull requests promptly. We may request changes or provide feedback to ensure code quality and consistency.
|
| 122 |
+
|
| 123 |
+
## Questions?
|
| 124 |
+
|
| 125 |
+
If you have questions about contributing, please open an issue or reach out to the maintainers.
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
*Together, let's build the future of biomedical AI agents!*
|
DETAILS.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DETAILS.md
|
| 2 |
+
|
| 3 |
+
🔍 **Powered by [Detailer](https://detailer.ginylil.com)** - Context-aware codebase analysis
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 1. Project Overview
|
| 10 |
+
|
| 11 |
+
### Project Purpose & Domain
|
| 12 |
+
|
| 13 |
+
This project is a comprehensive **biomedical AI toolkit and research platform** designed to facilitate **biomedical data analysis, knowledge extraction, and AI-driven reasoning**. It integrates large language models (LLMs), domain-specific bioinformatics tools, and scientific data processing pipelines to enable:
|
| 14 |
+
|
| 15 |
+
- Automated extraction of biomedical knowledge from literature (e.g., bioRxiv papers)
|
| 16 |
+
- Querying and integration of diverse biomedical databases and APIs
|
| 17 |
+
- Execution of domain-specific computational biology and physiology analyses
|
| 18 |
+
- AI agent orchestration for complex biomedical reasoning and tool invocation
|
| 19 |
+
- Benchmarking and evaluation of biomedical tasks and datasets
|
| 20 |
+
|
| 21 |
+
### Target Users and Use Cases
|
| 22 |
+
|
| 23 |
+
- **Biomedical researchers and data scientists** seeking to automate literature mining, data retrieval, and analysis workflows.
|
| 24 |
+
- **Bioinformaticians** requiring integrated access to multiple biological databases and computational tools.
|
| 25 |
+
- **AI researchers** interested in applying LLMs and autonomous agents to biomedical problem solving.
|
| 26 |
+
- **Developers and integrators** building domain-specific AI pipelines and scientific workflows.
|
| 27 |
+
- Use cases include:
|
| 28 |
+
- Extracting structured biomedical tasks and entities from scientific papers
|
| 29 |
+
- Querying gene, protein, disease, and pathway databases via natural language prompts
|
| 30 |
+
- Running computational models of biological systems (e.g., metabolic networks, signaling)
|
| 31 |
+
- Performing image analysis and quantitative pathology workflows
|
| 32 |
+
- Orchestrating multi-step AI reasoning with tool use and self-criticism
|
| 33 |
+
|
| 34 |
+
### Core Business Logic and Domain Models
|
| 35 |
+
|
| 36 |
+
- **Biomedical domain models**: gene IDs, protein structures, pathways, disease ontologies, experimental assays.
|
| 37 |
+
- **Task abstractions**: benchmark tasks with prompt/response evaluation (e.g., humanity last exam, lab bench).
|
| 38 |
+
- **Tool metadata schemas**: declarative descriptions of biomedical tools and APIs for dynamic invocation.
|
| 39 |
+
- **AI agent workflows**: ReAct-style reasoning graphs integrating LLMs, tool calls, retrieval, and self-critique.
|
| 40 |
+
- **Data models**: structured JSON, pandas DataFrames, numpy arrays representing biological data and analysis results.
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## 2. Architecture and Structure
|
| 45 |
+
|
| 46 |
+
### High-Level Architecture
|
| 47 |
+
|
| 48 |
+
The system is organized into modular layers and components:
|
| 49 |
+
|
| 50 |
+
- **Core Library (`biomni/`)**: Contains main application logic, including:
|
| 51 |
+
- **Agent framework (`biomni/agent/`)**: Implements autonomous AI agents using LLMs and workflow graphs.
|
| 52 |
+
- **Task definitions (`biomni/task/`)**: Abstract base and concrete biomedical benchmark tasks.
|
| 53 |
+
- **Tool implementations (`biomni/tool/`)**: Domain-specific analysis functions, API clients, and computational biology workflows.
|
| 54 |
+
- **Tool metadata (`biomni/tool/tool_description/`)**: Declarative schemas describing tool APIs and parameters.
|
| 55 |
+
- **Model components (`biomni/model/`)**: AI-driven resource retriever for selecting relevant tools and data.
|
| 56 |
+
- **Utility modules (`biomni/utils.py`, `biomni/llm.py`, `biomni/env_desc.py`)**: Helpers for LLM instantiation, system commands, environment descriptions.
|
| 57 |
+
- **Versioning (`biomni/version.py`)**: Package version management.
|
| 58 |
+
|
| 59 |
+
- **Environment Setup (`biomni_env/`)**: Scripts and configuration files for reproducible environment provisioning, including:
|
| 60 |
+
- Conda environment YAMLs (`environment.yml`, `bio_env.yml`)
|
| 61 |
+
- R package specifications (`r_packages.yml`)
|
| 62 |
+
- CLI tools installer (`install_cli_tools.sh`)
|
| 63 |
+
- Shell scripts for environment setup (`setup.sh`, `setup_path.sh`)
|
| 64 |
+
|
| 65 |
+
- **Scripts (`biomni/biorxiv_scripts/`)**: Data processing pipelines for literature mining and task extraction.
|
| 66 |
+
|
| 67 |
+
- **Documentation and Configuration**:
|
| 68 |
+
- Root-level files: `README.md`, `CONTRIBUTION.md`, `pyproject.toml`, `.pre-commit-config.yaml`.
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
### Complete Repository Structure
|
| 73 |
+
|
| 74 |
+
```
|
| 75 |
+
.
|
| 76 |
+
├── biomni/ (90 items)
|
| 77 |
+
│ ├── agent/
|
| 78 |
+
│ │ ├── __init__.py
|
| 79 |
+
│ │ ├── a1.py
|
| 80 |
+
│ │ ├── env_collection.py
|
| 81 |
+
│ │ ├── qa_llm.py
|
| 82 |
+
│ │ └── react.py
|
| 83 |
+
│ ├── biorxiv_scripts/
|
| 84 |
+
│ │ ├── extract_biorxiv_tasks.py
|
| 85 |
+
│ │ ├── generate_function.py
|
| 86 |
+
│ │ └── process_all_subjects.py
|
| 87 |
+
│ ├── model/
|
| 88 |
+
│ │ ├── __init__.py
|
| 89 |
+
│ │ └── retriever.py
|
| 90 |
+
│ ├── task/
|
| 91 |
+
│ │ ├── __init__.py
|
| 92 |
+
│ │ ├── base_task.py
|
| 93 |
+
│ │ ├── hle.py
|
| 94 |
+
│ │ └── lab_bench.py
|
| 95 |
+
│ ├── tool/ (65 items)
|
| 96 |
+
│ │ ├── schema_db/ (25 items)
|
| 97 |
+
│ │ │ ├── cbioportal.pkl
|
| 98 |
+
│ │ │ ├── clinvar.pkl
|
| 99 |
+
│ │ │ ├── dbsnp.pkl
|
| 100 |
+
│ │ │ ├── emdb.pkl
|
| 101 |
+
│ │ │ ├── ensembl.pkl
|
| 102 |
+
│ │ │ ├── geo.pkl
|
| 103 |
+
│ │ │ ├── gnomad.pkl
|
| 104 |
+
│ │ │ ├── gtopdb.pkl
|
| 105 |
+
│ │ │ ├── gwas_catalog.pkl
|
| 106 |
+
│ │ │ ├── interpro.pkl
|
| 107 |
+
│ │ │ └── ... (15 more files)
|
| 108 |
+
│ │ ├── tool_description/ (18 items)
|
| 109 |
+
│ │ │ ├── biochemistry.py
|
| 110 |
+
│ │ │ ├── bioengineering.py
|
| 111 |
+
│ │ │ ├── biophysics.py
|
| 112 |
+
│ │ │ ├── cancer_biology.py
|
| 113 |
+
│ │ │ ├── cell_biology.py
|
| 114 |
+
│ │ │ ├── database.py
|
| 115 |
+
│ │ │ ├── genetics.py
|
| 116 |
+
│ │ │ ├── genomics.py
|
| 117 |
+
│ │ │ ├── immunology.py
|
| 118 |
+
│ │ │ ├── literature.py
|
| 119 |
+
│ │ │ ├── microbiology.py
|
| 120 |
+
│ │ │ ├── molecular_biology.py
|
| 121 |
+
│ │ │ ├── pathology.py
|
| 122 |
+
│ │ │ ├── pharmacology.py
|
| 123 |
+
│ │ │ ├── physiology.py
|
| 124 |
+
│ │ │ ├── support_tools.py
|
| 125 |
+
│ │ │ ├── synthetic_biology.py
|
| 126 |
+
│ │ │ └── systems_biology.py
|
| 127 |
+
│ │ ├── __init__.py
|
| 128 |
+
│ │ ├── biochemistry.py
|
| 129 |
+
│ │ ├── bioengineering.py
|
| 130 |
+
│ │ ├── biophysics.py
|
| 131 |
+
│ │ ├── cancer_biology.py
|
| 132 |
+
│ │ ├── cell_biology.py
|
| 133 |
+
│ │ ├── database.py
|
| 134 |
+
│ │ ├── genetics.py
|
| 135 |
+
│ │ └── ... (12 more files)
|
| 136 |
+
│ ├── __init__.py
|
| 137 |
+
│ ├── env_desc.py
|
| 138 |
+
│ ├── llm.py
|
| 139 |
+
│ ├── utils.py
|
| 140 |
+
│ └── version.py
|
| 141 |
+
├── biomni_env/ (9 items)
|
| 142 |
+
│ ├── README.md
|
| 143 |
+
│ ├── bio_env.yml
|
| 144 |
+
│ ├── cli_tools_config.json
|
| 145 |
+
│ ├── environment.yml
|
| 146 |
+
│ ├── install_cli_tools.sh
|
| 147 |
+
│ ├── install_r_packages.R
|
| 148 |
+
│ ├── r_packages.yml
|
| 149 |
+
│ ├── setup.sh
|
| 150 |
+
│ └── setup_path.sh
|
| 151 |
+
├── figs/
|
| 152 |
+
│ └── biomni_logo.png
|
| 153 |
+
├── tutorials/
|
| 154 |
+
│ ├── examples/
|
| 155 |
+
│ │ └── cloning.ipynb
|
| 156 |
+
│ ├── 101_biomni.ipynb
|
| 157 |
+
│ └── biomni_101.ipynb
|
| 158 |
+
├── .gitignore
|
| 159 |
+
├── .pre-commit-config.yaml
|
| 160 |
+
├── CONTRIBUTION.md
|
| 161 |
+
├── LICENSE
|
| 162 |
+
├── README.md
|
| 163 |
+
└── pyproject.toml
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
---
|
| 167 |
+
|
| 168 |
+
## 3. Technical Implementation Details
|
| 169 |
+
|
| 170 |
+
### Core Modules and Their Roles
|
| 171 |
+
|
| 172 |
+
#### `biomni/agent/`
|
| 173 |
+
|
| 174 |
+
- Implements autonomous AI agents using the **ReAct paradigm**:
|
| 175 |
+
- `react.py`: Main ReAct agent class managing reasoning, tool invocation, retrieval, and self-criticism workflows.
|
| 176 |
+
- `env_collection.py`: Environment and data retrieval utilities.
|
| 177 |
+
- `qa_llm.py`: Question-answering LLM wrappers.
|
| 178 |
+
- `a1.py`: Possibly experimental or auxiliary agent code.
|
| 179 |
+
|
| 180 |
+
- Uses **langgraph** for workflow graph orchestration and **langchain** for LLM integration.
|
| 181 |
+
|
| 182 |
+
#### `biomni/task/`
|
| 183 |
+
|
| 184 |
+
- Defines **benchmark tasks** with a common interface:
|
| 185 |
+
- `base_task.py`: Abstract base class specifying methods like `get_example()`, `evaluate()`, `output_class()`.
|
| 186 |
+
- `hle.py`: "Humanity Last Exam" task implementation.
|
| 187 |
+
- `lab_bench.py`: Lab bench dataset task.
|
| 188 |
+
|
| 189 |
+
- Tasks load data (e.g., parquet files), generate prompts, and evaluate LLM responses.
|
| 190 |
+
|
| 191 |
+
#### `biomni/tool/`
|
| 192 |
+
|
| 193 |
+
- Contains **domain-specific scientific analysis functions** organized by subdomains:
|
| 194 |
+
- `biochemistry.py`, `bioengineering.py`, `biophysics.py`, `cancer_biology.py`, `cell_biology.py`, `genetics.py`, `pathology.py`, `physiology.py`, `systems_biology.py`, etc.
|
| 195 |
+
- Each file implements multiple functions performing analyses, simulations, or data processing workflows.
|
| 196 |
+
- Functions accept input files/parameters and return detailed textual logs and output files.
|
| 197 |
+
|
| 198 |
+
- **API client modules** (e.g., `database.py`) provide facade functions to query external biomedical databases (UniProt, GWAS Catalog, Ensembl, etc.) via REST or GraphQL APIs, often using LLMs to generate query payloads from natural language prompts.
|
| 199 |
+
|
| 200 |
+
- **Tool registry (`tool_registry.py`)** manages metadata about available tools, supporting dynamic registration and lookup.
|
| 201 |
+
|
| 202 |
+
#### `biomni/tool/tool_description/`
|
| 203 |
+
|
| 204 |
+
- Contains **declarative metadata schemas** describing tool APIs:
|
| 205 |
+
- Each file exports a `description` list of dictionaries defining tool names, descriptions, required and optional parameters with types and defaults.
|
| 206 |
+
- Supports **dynamic API generation, validation, and documentation**.
|
| 207 |
+
- Organized by biological domain (e.g., genetics, immunology, pathology).
|
| 208 |
+
|
| 209 |
+
#### `biomni/model/retriever.py`
|
| 210 |
+
|
| 211 |
+
- Implements `ToolRetriever` class for **AI-driven resource selection**:
|
| 212 |
+
- Uses LLMs (OpenAI or Anthropic) to parse user queries and select relevant tools, datasets, and libraries.
|
| 213 |
+
- Encapsulates prompt formatting and response parsing logic.
|
| 214 |
+
|
| 215 |
+
#### `biomni/utils.py` and `biomni/llm.py`
|
| 216 |
+
|
| 217 |
+
- `utils.py`: Utility functions for running system commands (R, Bash), file operations, schema generation, logging, and colorized printing.
|
| 218 |
+
- `llm.py`: Factory functions to instantiate LLMs (OpenAI, Anthropic) with configurable parameters.
|
| 219 |
+
|
| 220 |
+
#### `biomni/env_desc.py`
|
| 221 |
+
|
| 222 |
+
- Contains **environment and dataset descriptions**, acting as a centralized metadata repository for datasets and experimental environments.
|
| 223 |
+
|
| 224 |
+
---
|
| 225 |
+
|
| 226 |
+
### Environment Setup (`biomni_env/`)
|
| 227 |
+
|
| 228 |
+
- `setup.sh`: Main shell script to create conda environment, install R packages, and CLI bioinformatics tools.
|
| 229 |
+
- `install_cli_tools.sh`: Automates downloading, compiling, and installing external bioinformatics command-line tools, managing PATH and verification.
|
| 230 |
+
- `r_packages.yml`: Lists R packages required.
|
| 231 |
+
- `environment.yml` and `bio_env.yml`: Conda environment specifications.
|
| 232 |
+
- `setup_path.sh`: Shell script to update environment variables for CLI tools.
|
| 233 |
+
|
| 234 |
+
---
|
| 235 |
+
|
| 236 |
+
### Entry Points and Execution Flow
|
| 237 |
+
|
| 238 |
+
- **Agent usage**: Instantiate `react` agent from `biomni.agent.react`, configure with tools and retrieval, then call `go(prompt)` to run reasoning workflows.
|
| 239 |
+
- **Task evaluation**: Use classes in `biomni.task` to load datasets, generate prompts, and evaluate LLM outputs.
|
| 240 |
+
- **Tool invocation**: Call functions in `biomni.tool` modules or use API facades in `database.py` to query external resources.
|
| 241 |
+
- **Metadata-driven tool discovery**: Use `tool_registry.py` and `tool_description` schemas to dynamically discover and validate tools.
|
| 242 |
+
- **Environment setup**: Run `biomni_env/setup.sh` to provision environment and install dependencies.
|
| 243 |
+
|
| 244 |
+
---
|
| 245 |
+
|
| 246 |
+
## 4. Development Patterns and Standards
|
| 247 |
+
|
| 248 |
+
### Code Organization Principles
|
| 249 |
+
|
| 250 |
+
- **Modular design**: Clear separation of concerns by domain and functionality (agent, task, tool, model).
|
| 251 |
+
- **Functional programming style**: Most analysis modules use standalone functions with explicit inputs and outputs.
|
| 252 |
+
- **Declarative metadata**: Tool descriptions and schemas are separated from implementation, enabling dynamic validation and UI generation.
|
| 253 |
+
- **Abstract base classes**: Used in `biomni.task.base_task` to enforce consistent task interfaces.
|
| 254 |
+
- **Factory pattern**: Used in `llm.py` to instantiate LLMs based on configuration.
|
| 255 |
+
- **Strategy pattern**: Task implementations and tool retrieval use interchangeable strategies.
|
| 256 |
+
|
| 257 |
+
### Testing and Coverage
|
| 258 |
+
|
| 259 |
+
- No explicit test files detected; testing likely manual or via notebooks (`tutorials/`).
|
| 260 |
+
- Tasks and tools return detailed logs suitable for manual verification.
|
| 261 |
+
- Metadata schemas facilitate automated validation of inputs.
|
| 262 |
+
|
| 263 |
+
### Error Handling and Logging
|
| 264 |
+
|
| 265 |
+
- Use of try-except blocks around external calls and subprocesses.
|
| 266 |
+
- Logging via custom callback handlers (`PromptLogger`, `NodeLogger`) in LLM interactions.
|
| 267 |
+
- Utilities provide colorized printing and error wrappers for robustness.
|
| 268 |
+
|
| 269 |
+
### Configuration Management
|
| 270 |
+
|
| 271 |
+
- Environment variables for API keys (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`).
|
| 272 |
+
- YAML and JSON files for environment and tool configuration.
|
| 273 |
+
- Dynamic loading of schemas from pickle files for API request generation.
|
| 274 |
+
- CLI tools and R packages installed via scripted environment setup.
|
| 275 |
+
|
| 276 |
+
---
|
| 277 |
+
|
| 278 |
+
## 5. Integration and Dependencies
|
| 279 |
+
|
| 280 |
+
### External Libraries
|
| 281 |
+
|
| 282 |
+
- **LLM & AI Frameworks**: `langchain_core`, `langchain_openai`, `langchain_anthropic`
|
| 283 |
+
- **Scientific Computing**: `numpy`, `pandas`, `scipy`, `scikit-image`, `matplotlib`, `BioPython`, `cobra`, `sklearn`
|
| 284 |
+
- **Data Processing**: `pickle`, `json`, `requests`, `PyPDF2`
|
| 285 |
+
- **System and OS**: `subprocess`, `os`, `sys`, `tempfile`, `multiprocessing`
|
| 286 |
+
- **Others**: `tqdm` (progress bars), `enum`, `ast` (code introspection)
|
| 287 |
+
|
| 288 |
+
### External APIs and Data Sources
|
| 289 |
+
|
| 290 |
+
- Biomedical databases: UniProt, GWAS Catalog, Ensembl, ClinVar, dbSNP, EMDB, GEO, GnomAD, InterPro, etc.
|
| 291 |
+
- Bioinformatics tools: PLINK, IQ-TREE, GCTA, MACS2, samtools, LUMPY, installed via CLI tools installer.
|
| 292 |
+
- R packages for statistical and bioinformatics analyses.
|
| 293 |
+
|
| 294 |
+
### Build and Deployment Dependencies
|
| 295 |
+
|
| 296 |
+
- Python 3 environment managed via Conda (`environment.yml`).
|
| 297 |
+
- R environment with specified packages (`r_packages.yml`).
|
| 298 |
+
- Shell scripts for CLI tool installation and environment setup.
|
| 299 |
+
- Pre-commit hooks for code quality and security.
|
| 300 |
+
|
| 301 |
+
---
|
| 302 |
+
|
| 303 |
+
## 6. Usage and Operational Guidance
|
| 304 |
+
|
| 305 |
+
### Getting Started
|
| 306 |
+
|
| 307 |
+
1. **Environment Setup**
|
| 308 |
+
- Run `biomni_env/setup.sh` to create the Conda environment, install R packages, and CLI tools.
|
| 309 |
+
- Source `biomni_env/setup_path.sh` or add it to your shell profile to configure PATH.
|
| 310 |
+
|
| 311 |
+
2. **API Keys**
|
| 312 |
+
- Set environment variables `OPENAI_API_KEY` and/or `ANTHROPIC_API_KEY` for LLM access.
|
| 313 |
+
|
| 314 |
+
3. **Running Agents**
|
| 315 |
+
- Import and instantiate the `react` agent from `biomni.agent.react`.
|
| 316 |
+
- Configure with desired tools and retrieval options.
|
| 317 |
+
- Call `go(prompt)` to execute reasoning workflows.
|
| 318 |
+
|
| 319 |
+
4. **Executing Tasks**
|
| 320 |
+
- Use classes in `biomni.task` to load datasets and evaluate LLM responses.
|
| 321 |
+
- Implement new tasks by subclassing `base_task` and following the interface.
|
| 322 |
+
|
| 323 |
+
5. **Querying Databases**
|
| 324 |
+
- Use `biomni.tool.database` functions (e.g., `query_uniprot`, `query_gwas_catalog`) to retrieve data via natural language or direct parameters.
|
| 325 |
+
|
| 326 |
+
6. **Extending Tools**
|
| 327 |
+
- Add new tool metadata in `biomni/tool/tool_description/` as structured dictionaries.
|
| 328 |
+
- Implement corresponding analysis functions in `biomni/tool/`.
|
| 329 |
+
- Register tools in `tool_registry.py` for discovery.
|
| 330 |
+
|
| 331 |
+
### Monitoring and Debugging
|
| 332 |
+
|
| 333 |
+
- Use logging callbacks (`PromptLogger`, `NodeLogger`) to trace LLM interactions.
|
| 334 |
+
- Check output logs returned by analysis functions for detailed execution info.
|
| 335 |
+
- Use pre-commit hooks to maintain code quality.
|
| 336 |
+
|
| 337 |
+
### Performance and Scalability
|
| 338 |
+
|
| 339 |
+
- Modular design allows parallel execution of tasks and tools.
|
| 340 |
+
- Timeout wrappers in agent tools prevent hanging executions.
|
| 341 |
+
- Use of efficient numerical libraries (`numpy`, `scipy`) for computational tasks.
|
| 342 |
+
- Large data handled via streaming and chunking (e.g., PDF text extraction).
|
| 343 |
+
|
| 344 |
+
### Security Considerations
|
| 345 |
+
|
| 346 |
+
- API keys managed via environment variables, not hardcoded.
|
| 347 |
+
- Pre-commit hooks include security checks.
|
| 348 |
+
- External tool installations verified via version commands.
|
| 349 |
+
|
| 350 |
+
### Observability
|
| 351 |
+
|
| 352 |
+
- Progress bars (`tqdm`) used in data processing scripts.
|
| 353 |
+
- Structured logs and JSON outputs facilitate downstream analysis.
|
| 354 |
+
- Agent workflows produce detailed message histories for audit.
|
| 355 |
+
|
| 356 |
+
---
|
| 357 |
+
|
| 358 |
+
## Summary
|
| 359 |
+
|
| 360 |
+
This project is a **modular, extensible biomedical AI platform** integrating **LLM-powered agents**, **domain-specific scientific tools**, and **metadata-driven APIs** to automate complex biomedical research workflows. It emphasizes **declarative tool descriptions**, **dynamic resource retrieval**, and **robust environment provisioning** to enable researchers and developers to build, evaluate, and extend AI-driven biomedical applications efficiently.
|
| 361 |
+
|
| 362 |
+
---
|
| 363 |
+
|
| 364 |
+
# End of DETAILS.md
|
LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
MANIFEST.in
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MANIFEST.in
|
| 2 |
+
|
| 3 |
+
# Include all python files from the biomni package
|
| 4 |
+
recursive-include biomni *.py
|
| 5 |
+
|
| 6 |
+
# Include the .pkl database files
|
| 7 |
+
recursive-include biomni/tool/schema_db *.pkl
|
| 8 |
+
|
| 9 |
+
# Include specific files from biomni_env, but not the biomni_tools subdirectory
|
| 10 |
+
recursive-include biomni_env *.py *.sh *.yml *.yaml *.txt *.md *.json *.R
|
| 11 |
+
|
| 12 |
+
# Exclude specific directories that are not part of the package
|
| 13 |
+
prune tutorials/data
|
| 14 |
+
prune biomni_env/biomni_tools
|
| 15 |
+
|
| 16 |
+
# Exclude build artifacts and caches
|
| 17 |
+
global-exclude *.py[co]
|
| 18 |
+
global-exclude __pycache__
|
| 19 |
+
global-exclude .ruff_cache
|
| 20 |
+
prune build
|
| 21 |
+
prune dist
|
| 22 |
+
prune *.egg-info
|
| 23 |
+
|
| 24 |
+
# Include other necessary files
|
| 25 |
+
include README.md
|
| 26 |
+
include LICENSE
|
| 27 |
+
include pyproject.toml
|
README.md
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<p align="center">
|
| 2 |
+
<img src="./figs/biomni_logo.png" alt="Biomni Logo" width="600px" />
|
| 3 |
+
</p>
|
| 4 |
+
|
| 5 |
+
<p align="center">
|
| 6 |
+
<a href="https://join.slack.com/t/biomnigroup/shared_invite/zt-3avks4913-dotMBt8D_apQnJ3mG~ak6Q">
|
| 7 |
+
<img src="https://img.shields.io/badge/Join-Slack-4A154B?style=for-the-badge&logo=slack" alt="Join Slack" />
|
| 8 |
+
</a>
|
| 9 |
+
<a href="https://biomni.stanford.edu">
|
| 10 |
+
<img src="https://img.shields.io/badge/Try-Web%20UI-blue?style=for-the-badge" alt="Web UI" />
|
| 11 |
+
</a>
|
| 12 |
+
<a href="https://x.com/ProjectBiomni">
|
| 13 |
+
<img src="https://img.shields.io/badge/Follow-on%20X-black?style=for-the-badge&logo=x" alt="Follow on X" />
|
| 14 |
+
</a>
|
| 15 |
+
<a href="https://www.linkedin.com/company/project-biomni">
|
| 16 |
+
<img src="https://img.shields.io/badge/Follow-LinkedIn-0077B5?style=for-the-badge&logo=linkedin" alt="Follow on LinkedIn" />
|
| 17 |
+
</a>
|
| 18 |
+
<a href="https://www.biorxiv.org/content/10.1101/2025.05.30.656746v1">
|
| 19 |
+
<img src="https://img.shields.io/badge/Read-Paper-green?style=for-the-badge" alt="Paper" />
|
| 20 |
+
</a>
|
| 21 |
+
</p>
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Biomni: A General-Purpose Biomedical AI Agent
|
| 26 |
+
|
| 27 |
+
## Overview
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
Biomni is a general-purpose biomedical AI agent designed to autonomously execute a wide range of research tasks across diverse biomedical subfields. By integrating cutting-edge large language model (LLM) reasoning with retrieval-augmented planning and code-based execution, Biomni helps scientists dramatically enhance research productivity and generate testable hypotheses.
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
## Quick Start
|
| 34 |
+
|
| 35 |
+
### Installation
|
| 36 |
+
|
| 37 |
+
Our software environment is massive and we provide a single setup.sh script to setup.
|
| 38 |
+
Follow this [file](biomni_env/README.md) to setup the env first.
|
| 39 |
+
|
| 40 |
+
Then activate the environment E1:
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
conda activate biomni_e1
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
then install the biomni official pip package:
|
| 47 |
+
|
| 48 |
+
```bash
|
| 49 |
+
pip install biomni --upgrade
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
For the latest update, install from the github source version, or do:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
pip install git+https://github.com/snap-stanford/Biomni.git@main
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Lastly, configure your API keys using one of the following methods:
|
| 59 |
+
|
| 60 |
+
<details>
|
| 61 |
+
<summary>Click to expand</summary>
|
| 62 |
+
|
| 63 |
+
#### Option 1: Using .env file (Recommended)
|
| 64 |
+
|
| 65 |
+
Create a `.env` file in your project directory:
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
# Copy the example file
|
| 69 |
+
cp .env.example .env
|
| 70 |
+
|
| 71 |
+
# Edit the .env file with your actual API keys
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
Your `.env` file should look like:
|
| 75 |
+
|
| 76 |
+
```env
|
| 77 |
+
# Required: Anthropic API Key for Claude models
|
| 78 |
+
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
| 79 |
+
|
| 80 |
+
# Optional: OpenAI API Key (if using OpenAI models)
|
| 81 |
+
OPENAI_API_KEY=your_openai_api_key_here
|
| 82 |
+
|
| 83 |
+
# Optional: Azure OpenAI API Key (if using Azure OpenAI models)
|
| 84 |
+
OPENAI_API_KEY=your_azure_openai_api_key
|
| 85 |
+
OPENAI_ENDPOINT=https://your-resource-name.openai.azure.com/
|
| 86 |
+
|
| 87 |
+
# Optional: AI Studio Gemini API Key (if using Gemini models)
|
| 88 |
+
GEMINI_API_KEY=your_gemini_api_key_here
|
| 89 |
+
|
| 90 |
+
# Optional: groq API Key (if using groq as model provider)
|
| 91 |
+
GROQ_API_KEY=your_groq_api_key_here
|
| 92 |
+
|
| 93 |
+
# Optional: Set the source of your LLM for example:
|
| 94 |
+
#"OpenAI", "AzureOpenAI", "Anthropic", "Ollama", "Gemini", "Bedrock", "Groq", "Custom"
|
| 95 |
+
LLM_SOURCE=your_LLM_source_here
|
| 96 |
+
|
| 97 |
+
# Optional: AWS Bedrock Configuration (if using AWS Bedrock models)
|
| 98 |
+
AWS_BEARER_TOKEN_BEDROCK=your_bedrock_api_key_here
|
| 99 |
+
AWS_REGION=us-east-1
|
| 100 |
+
|
| 101 |
+
# Optional: Custom model serving configuration
|
| 102 |
+
# CUSTOM_MODEL_BASE_URL=http://localhost:8000/v1
|
| 103 |
+
# CUSTOM_MODEL_API_KEY=your_custom_api_key_here
|
| 104 |
+
|
| 105 |
+
# Optional: Biomni data path (defaults to ./data)
|
| 106 |
+
# BIOMNI_DATA_PATH=/path/to/your/data
|
| 107 |
+
|
| 108 |
+
# Optional: Timeout settings (defaults to 600 seconds)
|
| 109 |
+
# BIOMNI_TIMEOUT_SECONDS=600
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
#### Option 2: Using shell environment variables
|
| 113 |
+
|
| 114 |
+
Alternatively, configure your API keys in bash profile `~/.bashrc`:
|
| 115 |
+
|
| 116 |
+
```bash
|
| 117 |
+
export ANTHROPIC_API_KEY="YOUR_API_KEY"
|
| 118 |
+
export OPENAI_API_KEY="YOUR_API_KEY" # optional if you just use Claude
|
| 119 |
+
export OPENAI_ENDPOINT="https://your-resource-name.openai.azure.com/" # optional unless you are using Azure
|
| 120 |
+
export AWS_BEARER_TOKEN_BEDROCK="YOUR_BEDROCK_API_KEY" # optional for AWS Bedrock models
|
| 121 |
+
export AWS_REGION="us-east-1" # optional, defaults to us-east-1 for Bedrock
|
| 122 |
+
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY" #optional if you want to use a gemini model
|
| 123 |
+
export GROQ_API_KEY="YOUR_GROQ_API_KEY" # Optional: set this to use models served by Groq
|
| 124 |
+
export LLM_SOURCE="Groq" # Optional: set this to use models served by Groq
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
```
|
| 128 |
+
</details>
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
#### ⚠️ Known Package Conflicts
|
| 132 |
+
|
| 133 |
+
Some Python packages are not installed by default in the Biomni environment due to dependency conflicts. If you need these features, you must install the packages manually and may need to uncomment relevant code in the codebase. See the up-to-date list and details in [docs/known_conflicts.md](./docs/known_conflicts.md).
|
| 134 |
+
|
| 135 |
+
### Basic Usage
|
| 136 |
+
|
| 137 |
+
Once inside the environment, you can start using Biomni:
|
| 138 |
+
|
| 139 |
+
```python
|
| 140 |
+
from biomni.agent import A1
|
| 141 |
+
|
| 142 |
+
# Initialize the agent with data path, Data lake will be automatically downloaded on first run (~11GB)
|
| 143 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514')
|
| 144 |
+
|
| 145 |
+
# Execute biomedical tasks using natural language
|
| 146 |
+
agent.go("Plan a CRISPR screen to identify genes that regulate T cell exhaustion, generate 32 genes that maximize the perturbation effect.")
|
| 147 |
+
agent.go("Perform scRNA-seq annotation at [PATH] and generate meaningful hypothesis")
|
| 148 |
+
agent.go("Predict ADMET properties for this compound: CC(C)CC1=CC=C(C=C1)C(C)C(=O)O")
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
#### Controlling Datalake Loading
|
| 152 |
+
|
| 153 |
+
By default, Biomni automatically downloads the datalake files (~11GB) when you create an agent. You can control this behavior:
|
| 154 |
+
|
| 155 |
+
```python
|
| 156 |
+
# Skip automatic datalake download (faster initialization)
|
| 157 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514', expected_data_lake_files = [])
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
This is useful for:
|
| 161 |
+
- Faster testing and development
|
| 162 |
+
- Environments with limited storage or bandwidth
|
| 163 |
+
- Cases where you only need specific tools that don't require datalake files
|
| 164 |
+
If you plan on using Azure for your model, always prefix the model name with azure- (e.g. llm='azure-gpt-4o').
|
| 165 |
+
|
| 166 |
+
### Gradio Interface
|
| 167 |
+
|
| 168 |
+
Launch an interactive web UI for Biomni:
|
| 169 |
+
|
| 170 |
+
```python
|
| 171 |
+
from biomni.agent import A1
|
| 172 |
+
|
| 173 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514')
|
| 174 |
+
agent.launch_gradio_demo()
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
**Installation:**
|
| 178 |
+
```bash
|
| 179 |
+
pip install "gradio>=5.0,<6.0"
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
**Note:** Biomni's Gradio interface currently requires Gradio 5.x due to API changes in Gradio 6.0. If you have Gradio 6.x installed, you may need to downgrade.
|
| 183 |
+
|
| 184 |
+
**Options:**
|
| 185 |
+
- `share=True` - Create a public shareable link
|
| 186 |
+
- `server_name="127.0.0.1"` - Localhost only (default: "0.0.0.0")
|
| 187 |
+
- `require_verification=True` - Require access code (default code: "Biomni2025")
|
| 188 |
+
|
| 189 |
+
The interface will be available at `http://localhost:7860`
|
| 190 |
+
|
| 191 |
+
### Configuration Management
|
| 192 |
+
|
| 193 |
+
Biomni includes a centralized configuration system that provides flexible ways to manage settings. You can configure Biomni through environment variables, runtime modifications, or direct parameters.
|
| 194 |
+
|
| 195 |
+
```python
|
| 196 |
+
from biomni.config import default_config
|
| 197 |
+
from biomni.agent import A1
|
| 198 |
+
|
| 199 |
+
# RECOMMENDED: Modify global defaults for consistency
|
| 200 |
+
default_config.llm = "gpt-4"
|
| 201 |
+
default_config.timeout_seconds = 1200
|
| 202 |
+
|
| 203 |
+
# All agents AND database queries use these defaults
|
| 204 |
+
agent = A1() # Everything uses gpt-4, 1200s timeout
|
| 205 |
+
```
|
| 206 |
+
|
| 207 |
+
**Note**: Direct parameters to `A1()` only affect that agent's reasoning, not database queries. For consistent configuration across all operations, use `default_config` or environment variables.
|
| 208 |
+
|
| 209 |
+
For detailed configuration options, see the **[Configuration Guide](docs/configuration.md)**.
|
| 210 |
+
|
| 211 |
+
### PDF Generation
|
| 212 |
+
|
| 213 |
+
Generate PDF reports of execution traces:
|
| 214 |
+
|
| 215 |
+
```python
|
| 216 |
+
from biomni.agent import A1
|
| 217 |
+
|
| 218 |
+
# Initialize agent
|
| 219 |
+
agent = A1(path='./data', llm='claude-sonnet-4-20250514')
|
| 220 |
+
|
| 221 |
+
# Run your task
|
| 222 |
+
agent.go("Your biomedical task here")
|
| 223 |
+
|
| 224 |
+
# Save conversation history as PDF
|
| 225 |
+
agent.save_conversation_history("my_analysis_results.pdf")
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
**PDF Generation Dependencies:**
|
| 229 |
+
<details>
|
| 230 |
+
<summary>Click to expand</summary>
|
| 231 |
+
For optimal PDF generation, install one of these packages:
|
| 232 |
+
|
| 233 |
+
```bash
|
| 234 |
+
# Option 1: WeasyPrint (recommended for best layout control)
|
| 235 |
+
# Conda environment (recommended)
|
| 236 |
+
conda install weasyprint
|
| 237 |
+
|
| 238 |
+
# System installation
|
| 239 |
+
brew install weasyprint # macOS
|
| 240 |
+
apt install weasyprint # Linux
|
| 241 |
+
|
| 242 |
+
# See [WeasyPrint Installation Guide](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html) for detailed instructions.
|
| 243 |
+
|
| 244 |
+
# Option 2: markdown2pdf (Rust-based, fast and reliable)
|
| 245 |
+
# macOS:
|
| 246 |
+
brew install theiskaa/tap/markdown2pdf
|
| 247 |
+
|
| 248 |
+
# Windows/Linux (using Cargo):
|
| 249 |
+
cargo install markdown2pdf
|
| 250 |
+
|
| 251 |
+
# Or download prebuilt binaries from:
|
| 252 |
+
# https://github.com/theiskaa/markdown2pdf/releases/latest
|
| 253 |
+
|
| 254 |
+
# Option 3: Pandoc (pip installation)
|
| 255 |
+
pip install pandoc
|
| 256 |
+
```
|
| 257 |
+
</details>
|
| 258 |
+
|
| 259 |
+
## MCP (Model Context Protocol) Support
|
| 260 |
+
|
| 261 |
+
Biomni supports MCP servers for external tool integration:
|
| 262 |
+
|
| 263 |
+
```python
|
| 264 |
+
from biomni.agent import A1
|
| 265 |
+
|
| 266 |
+
agent = A1()
|
| 267 |
+
agent.add_mcp(config_path="./mcp_config.yaml")
|
| 268 |
+
agent.go("Find FDA active ingredient information for ibuprofen")
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
**Built-in MCP Servers:**
|
| 272 |
+
For usage and implementation details, see the [MCP Integration Documentation](docs/mcp_integration.md) and examples in [`tutorials/examples/add_mcp_server/`](tutorials/examples/add_mcp_server/) and [`tutorials/examples/expose_biomni_server/`](tutorials/examples/expose_biomni_server/).
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
## Biomni-R0
|
| 276 |
+
|
| 277 |
+
**Biomni-R0** is our first reasoning model for biology, built on Qwen-32B with reinforcement learning from agent interaction data. It's designed to excel at tool use, multi-step reasoning, and complex biological problem-solving through iterative self-correction.
|
| 278 |
+
|
| 279 |
+
- 🤗 Model: [biomni/Biomni-R0-32B-Preview](https://huggingface.co/biomni/Biomni-R0-32B-Preview)
|
| 280 |
+
- 📝 Technical Report: [biomni.stanford.edu/blog/biomni-r0-technical-report](https://biomni.stanford.edu/blog/biomni-r0-technical-report)
|
| 281 |
+
|
| 282 |
+
To use Biomni-R0 for agent reasoning while keeping database queries on your usual provider (recommended), run a local SGLang server and pass the model to `A1()` directly.
|
| 283 |
+
|
| 284 |
+
1) Launch SGLang with Biomni-R0:
|
| 285 |
+
|
| 286 |
+
```bash
|
| 287 |
+
python -m sglang.launch_server --model-path RyanLi0802/Biomni-R0-Preview --port 30000 --host 0.0.0.0 --mem-fraction-static 0.8 --tp 2 --trust-remote-code --json-model-override-args '{"rope_scaling":{"rope_type":"yarn","factor":1.0,"original_max_position_embeddings":32768}, "max_position_embeddings": 131072}'
|
| 288 |
+
```
|
| 289 |
+
|
| 290 |
+
2) Point the agent to your SGLang endpoint for reasoning:
|
| 291 |
+
|
| 292 |
+
```python
|
| 293 |
+
from biomni.config import default_config
|
| 294 |
+
from biomni.agent import A1
|
| 295 |
+
|
| 296 |
+
# Database queries (indexes, retrieval, etc.) use default_config
|
| 297 |
+
default_config.llm = "claude-3-5-sonnet-20241022"
|
| 298 |
+
default_config.source = "Anthropic"
|
| 299 |
+
|
| 300 |
+
# Agent reasoning uses Biomni-R0 served via SGLang (OpenAI-compatible API)
|
| 301 |
+
agent = A1(
|
| 302 |
+
llm="biomni/Biomni-R0-32B-Preview",
|
| 303 |
+
source="Custom",
|
| 304 |
+
base_url="http://localhost:30000/v1",
|
| 305 |
+
api_key="EMPTY",
|
| 306 |
+
)
|
| 307 |
+
|
| 308 |
+
agent.go("Plan a CRISPR screen to identify genes regulating T cell exhaustion")
|
| 309 |
+
```
|
| 310 |
+
|
| 311 |
+
## Biomni-Eval1
|
| 312 |
+
|
| 313 |
+
**Biomni-Eval1** is a comprehensive evaluation benchmark for assessing biological reasoning capabilities across diverse tasks. It contains **433 instances** spanning **10 biological reasoning tasks**, from gene identification to disease diagnosis.
|
| 314 |
+
|
| 315 |
+
**Tasks Included:**
|
| 316 |
+
- GWAS causal gene identification (3 variants)
|
| 317 |
+
- Lab bench Q&A (2 variants)
|
| 318 |
+
- Patient gene detection
|
| 319 |
+
- Screen gene retrieval
|
| 320 |
+
- GWAS variant prioritization
|
| 321 |
+
- Rare disease diagnosis
|
| 322 |
+
- CRISPR delivery method selection
|
| 323 |
+
|
| 324 |
+
**Resources:**
|
| 325 |
+
- 🤗 Dataset: [biomni/Eval1](https://huggingface.co/datasets/biomni/Eval1)
|
| 326 |
+
- 💻 Quick Start:
|
| 327 |
+
```python
|
| 328 |
+
from biomni.eval import BiomniEval1
|
| 329 |
+
|
| 330 |
+
evaluator = BiomniEval1()
|
| 331 |
+
score = evaluator.evaluate('gwas_causal_gene_opentargets', 0, 'BRCA1')
|
| 332 |
+
```
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
## 📚 Know-How Library
|
| 336 |
+
|
| 337 |
+
Biomni includes a **Know-How Library** — a curated collection of best practices, protocols, and troubleshooting guides for biomedical techniques. These documents are automatically retrieved by the A1 agent when relevant to provide domain expertise and practical knowledge.
|
| 338 |
+
|
| 339 |
+
**Features:**
|
| 340 |
+
- Automatic retrieval based on query relevance
|
| 341 |
+
- Metadata tracking (authors, affiliations, licensing, commercial use)
|
| 342 |
+
- Compatible with commercial mode (filters non-commercial content)
|
| 343 |
+
|
| 344 |
+
### 📝 Contributing Know-How Documents
|
| 345 |
+
|
| 346 |
+
We're actively seeking community contributions to expand our Know-How Library! Share your expertise by contributing:
|
| 347 |
+
|
| 348 |
+
- **Lab protocols** (cell culture, flow cytometry, western blotting, etc.)
|
| 349 |
+
- **Analysis best practices** (NGS workflows, microscopy techniques, etc.)
|
| 350 |
+
- **Troubleshooting guides** (common issues and solutions)
|
| 351 |
+
- **Experimental design guidelines** (sample size, controls, validation)
|
| 352 |
+
- **Domain-specific knowledge** (drug formulation, animal models, clinical trials, etc.)
|
| 353 |
+
|
| 354 |
+
Know-how documents should be practical, succinct, and include proper attribution. Use [this know-how](know_how/single_cell_annotation.md) as an example.
|
| 355 |
+
|
| 356 |
+
**To contribute:** Create a markdown file following our template and submit a pull request.
|
| 357 |
+
|
| 358 |
+
## 🤝 Contributing to Biomni
|
| 359 |
+
|
| 360 |
+
Biomni is an open-science initiative that thrives on community contributions. We welcome:
|
| 361 |
+
|
| 362 |
+
- **🔧 New Tools**: Specialized analysis functions and algorithms
|
| 363 |
+
- **📊 Datasets**: Curated biomedical data and knowledge bases
|
| 364 |
+
- **💻 Software**: Integration of existing biomedical software packages
|
| 365 |
+
- **📋 Benchmarks**: Evaluation datasets and performance metrics
|
| 366 |
+
- **📚 Know-How**: Best practices, protocols, and domain expertise
|
| 367 |
+
- **📚 Misc**: Tutorials, examples, and use cases
|
| 368 |
+
- **🔧 Update existing tools**: many current tools are not optimized - fix and replacements are welcome!
|
| 369 |
+
|
| 370 |
+
Check out this **[Contributing Guide](CONTRIBUTION.md)** on how to contribute to the Biomni ecosystem.
|
| 371 |
+
|
| 372 |
+
If you have particular tool/database/software in mind that you want to add, you can also submit to [this form](https://forms.gle/nu2n1unzAYodTLVj6) and the biomni team will implement them.
|
| 373 |
+
|
| 374 |
+
## 🔬 Call for Contributors: Help Build Biomni-E2
|
| 375 |
+
|
| 376 |
+
Biomni-E1 only scratches the surface of what’s possible in the biomedical action space.
|
| 377 |
+
|
| 378 |
+
Now, we’re building **Biomni-E2** — a next-generation environment developed **with and for the community**.
|
| 379 |
+
|
| 380 |
+
We believe that by collaboratively defining and curating a shared library of standard biomedical actions, we can accelerate science for everyone.
|
| 381 |
+
|
| 382 |
+
**Join us in shaping the future of biomedical AI agent.**
|
| 383 |
+
|
| 384 |
+
- **Contributors with significant impact** (e.g., 10+ significant & integrated tool contributions or equivalent) will be **invited as co-authors** on our upcoming paper in a top-tier journal or conference.
|
| 385 |
+
- **All contributors** will be acknowledged in our publications.
|
| 386 |
+
- More contributor perks...
|
| 387 |
+
|
| 388 |
+
Let’s build it together.
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
## Tutorials and Examples
|
| 392 |
+
|
| 393 |
+
**[Biomni 101](./tutorials/biomni_101.ipynb)** - Basic concepts and first steps
|
| 394 |
+
|
| 395 |
+
More to come!
|
| 396 |
+
|
| 397 |
+
## 🌐 Web Interface
|
| 398 |
+
|
| 399 |
+
Experience Biomni through our no-code web interface at **[biomni.stanford.edu](https://biomni.stanford.edu)**.
|
| 400 |
+
|
| 401 |
+
[](https://youtu.be/E0BRvl23hLs)
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
## Important Note
|
| 405 |
+
- Security warning: Currently, Biomni executes LLM-generated code with full system privileges. If you want to use it in production, please use in isolated/sandboxed environments. The agent can access files, network, and system commands. Be careful with sensitive data or credentials.
|
| 406 |
+
- This release was frozen as of April 15 2025, so it differs from the current web platform.
|
| 407 |
+
- Biomni itself is Apache 2.0-licensed, but certain integrated tools, databases, or software may carry more restrictive commercial licenses. Review each component carefully before any commercial use.
|
| 408 |
+
|
| 409 |
+
## Cite Us
|
| 410 |
+
|
| 411 |
+
```
|
| 412 |
+
@article{huang2025biomni,
|
| 413 |
+
title={Biomni: A General-Purpose Biomedical AI Agent},
|
| 414 |
+
author={Huang, Kexin and Zhang, Serena and Wang, Hanchen and Qu, Yuanhao and Lu, Yingzhou and Roohani, Yusuf and Li, Ryan and Qiu, Lin and Zhang, Junze and Di, Yin and others},
|
| 415 |
+
journal={bioRxiv},
|
| 416 |
+
pages={2025--05},
|
| 417 |
+
year={2025},
|
| 418 |
+
publisher={Cold Spring Harbor Laboratory}
|
| 419 |
+
}
|
| 420 |
+
```
|
biomni/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .version import __version__
|
| 2 |
+
|
| 3 |
+
__all__ = ["__version__"]
|
biomni/config.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Biomni Configuration Management
|
| 3 |
+
|
| 4 |
+
Simple configuration class for centralizing common settings.
|
| 5 |
+
Maintains full backward compatibility with existing code.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
from dataclasses import dataclass
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass
|
| 13 |
+
class BiomniConfig:
|
| 14 |
+
"""Central configuration for Biomni agent.
|
| 15 |
+
|
| 16 |
+
All settings are optional and have sensible defaults.
|
| 17 |
+
API keys are still read from environment variables to maintain
|
| 18 |
+
compatibility with existing .env file structure.
|
| 19 |
+
|
| 20 |
+
Usage:
|
| 21 |
+
# Create config with defaults
|
| 22 |
+
config = BiomniConfig()
|
| 23 |
+
|
| 24 |
+
# Override specific settings
|
| 25 |
+
config = BiomniConfig(llm="gpt-4", timeout_seconds=1200)
|
| 26 |
+
|
| 27 |
+
# Modify after creation
|
| 28 |
+
config.path = "./custom_data"
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
# Data and execution settings
|
| 32 |
+
path: str = "./data"
|
| 33 |
+
timeout_seconds: int = 600
|
| 34 |
+
|
| 35 |
+
# LLM settings (API keys still from environment)
|
| 36 |
+
llm: str = "claude-sonnet-4-5"
|
| 37 |
+
temperature: float = 0.7
|
| 38 |
+
|
| 39 |
+
# Tool settings
|
| 40 |
+
use_tool_retriever: bool = True
|
| 41 |
+
|
| 42 |
+
# Data licensing settings
|
| 43 |
+
commercial_mode: bool = False # If True, excludes non-commercial datasets
|
| 44 |
+
|
| 45 |
+
# Custom model settings (for custom LLM serving)
|
| 46 |
+
base_url: str | None = None
|
| 47 |
+
api_key: str | None = None # Only for custom models, not provider API keys
|
| 48 |
+
|
| 49 |
+
# LLM source (auto-detected if None)
|
| 50 |
+
source: str | None = None
|
| 51 |
+
|
| 52 |
+
# Third-party integrations
|
| 53 |
+
protocols_io_access_token: str | None = None
|
| 54 |
+
|
| 55 |
+
def __post_init__(self):
|
| 56 |
+
"""Load any environment variable overrides if they exist."""
|
| 57 |
+
# Check for environment variable overrides (optional)
|
| 58 |
+
# Support both old and new names for backwards compatibility
|
| 59 |
+
if os.getenv("BIOMNI_PATH") or os.getenv("BIOMNI_DATA_PATH"):
|
| 60 |
+
self.path = os.getenv("BIOMNI_PATH") or os.getenv("BIOMNI_DATA_PATH")
|
| 61 |
+
if os.getenv("BIOMNI_TIMEOUT_SECONDS"):
|
| 62 |
+
self.timeout_seconds = int(os.getenv("BIOMNI_TIMEOUT_SECONDS"))
|
| 63 |
+
if os.getenv("BIOMNI_LLM") or os.getenv("BIOMNI_LLM_MODEL"):
|
| 64 |
+
self.llm = os.getenv("BIOMNI_LLM") or os.getenv("BIOMNI_LLM_MODEL")
|
| 65 |
+
if os.getenv("BIOMNI_USE_TOOL_RETRIEVER"):
|
| 66 |
+
self.use_tool_retriever = os.getenv("BIOMNI_USE_TOOL_RETRIEVER").lower() == "true"
|
| 67 |
+
if os.getenv("BIOMNI_COMMERCIAL_MODE"):
|
| 68 |
+
self.commercial_mode = os.getenv("BIOMNI_COMMERCIAL_MODE").lower() == "true"
|
| 69 |
+
if os.getenv("BIOMNI_TEMPERATURE"):
|
| 70 |
+
self.temperature = float(os.getenv("BIOMNI_TEMPERATURE"))
|
| 71 |
+
if os.getenv("BIOMNI_CUSTOM_BASE_URL"):
|
| 72 |
+
self.base_url = os.getenv("BIOMNI_CUSTOM_BASE_URL")
|
| 73 |
+
if os.getenv("BIOMNI_CUSTOM_API_KEY"):
|
| 74 |
+
self.api_key = os.getenv("BIOMNI_CUSTOM_API_KEY")
|
| 75 |
+
if os.getenv("BIOMNI_SOURCE"):
|
| 76 |
+
self.source = os.getenv("BIOMNI_SOURCE")
|
| 77 |
+
|
| 78 |
+
# Protocols.io access token (prefer specific env vars)
|
| 79 |
+
env_token = os.getenv("PROTOCOLS_IO_ACCESS_TOKEN") or os.getenv("BIOMNI_PROTOCOLS_IO_ACCESS_TOKEN")
|
| 80 |
+
if env_token:
|
| 81 |
+
self.protocols_io_access_token = env_token
|
| 82 |
+
|
| 83 |
+
def to_dict(self) -> dict:
|
| 84 |
+
"""Convert config to dictionary for easy access."""
|
| 85 |
+
return {
|
| 86 |
+
"path": self.path,
|
| 87 |
+
"timeout_seconds": self.timeout_seconds,
|
| 88 |
+
"llm": self.llm,
|
| 89 |
+
"temperature": self.temperature,
|
| 90 |
+
"use_tool_retriever": self.use_tool_retriever,
|
| 91 |
+
"commercial_mode": self.commercial_mode,
|
| 92 |
+
"base_url": self.base_url,
|
| 93 |
+
"api_key": self.api_key,
|
| 94 |
+
"source": self.source,
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
# Global default config instance (optional, for convenience)
|
| 99 |
+
default_config = BiomniConfig()
|
biomni/env_desc.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Data lake dictionary with detailed descriptions
|
| 2 |
+
data_lake_dict = {
|
| 3 |
+
"affinity_capture-ms.parquet": "Protein-protein interactions detected via affinity capture and mass spectrometry.",
|
| 4 |
+
"affinity_capture-rna.parquet": "Protein-RNA interactions detected by affinity capture.",
|
| 5 |
+
"BindingDB_All_202409.tsv": "Measured binding affinities between proteins and small molecules for drug discovery.",
|
| 6 |
+
"broad_repurposing_hub_molecule_with_smiles.parquet": "Molecules from Broad Institute's Drug Repurposing Hub with SMILES annotations.",
|
| 7 |
+
"broad_repurposing_hub_phase_moa_target_info.parquet": "Drug phases, mechanisms of action, and target information from Broad Institute.",
|
| 8 |
+
"co-fractionation.parquet": "Protein-protein interactions from co-fractionation experiments.",
|
| 9 |
+
"czi_census_datasets_v4.parquet": "Datasets from the Chan Zuckerberg Initiative's Cell Census.",
|
| 10 |
+
"DepMap_CRISPRGeneDependency.csv": "Gene dependency probability estimates for cancer cell lines, including all DepMap models.",
|
| 11 |
+
"DepMap_CRISPRGeneEffect.csv": "Genome-wide CRISPR gene effect estimates for cancer cell lines, including all DepMap models.",
|
| 12 |
+
"DepMap_Model.csv": "Metadata describing all cancer models/cell lines which are referenced by a dataset contained within the DepMap portal.",
|
| 13 |
+
"DepMap_OmicsExpressionProteinCodingGenesTPMLogp1.csv": "Gene expression in TPMs for cancer cell lines, including all DepMap models.",
|
| 14 |
+
"ddinter_alimentary_tract_metabolism.csv": "Drug-drug interactions for alimentary tract and metabolism drugs from DDInter 2.0 database.",
|
| 15 |
+
"ddinter_antineoplastic.csv": "Drug-drug interactions for antineoplastic and immunomodulating agents from DDInter 2.0 database.",
|
| 16 |
+
"ddinter_antiparasitic.csv": "Drug-drug interactions for antiparasitic products from DDInter 2.0 database.",
|
| 17 |
+
"ddinter_blood_organs.csv": "Drug-drug interactions for blood and blood forming organs drugs from DDInter 2.0 database.",
|
| 18 |
+
"ddinter_dermatological.csv": "Drug-drug interactions for dermatological drugs from DDInter 2.0 database.",
|
| 19 |
+
"ddinter_hormonal.csv": "Drug-drug interactions for systemic hormonal preparations from DDInter 2.0 database.",
|
| 20 |
+
"ddinter_respiratory.csv": "Drug-drug interactions for respiratory system drugs from DDInter 2.0 database.",
|
| 21 |
+
"ddinter_various.csv": "Drug-drug interactions for various drugs from DDInter 2.0 database.",
|
| 22 |
+
"DisGeNET.parquet": "Gene-disease associations from multiple sources.",
|
| 23 |
+
"dosage_growth_defect.parquet": "Gene dosage changes affecting growth.",
|
| 24 |
+
"enamine_cloud_library_smiles.pkl": "Compounds from Enamine REAL library with SMILES annotations.",
|
| 25 |
+
"evebio_assay_table.csv": "Assay metadata with one row per assay from EveBio pharmome mapping.",
|
| 26 |
+
"evebio_bundle_table.csv": "Target subfamily bundles used for screening-to-profiling progression.",
|
| 27 |
+
"evebio_compound_table.csv": "Compound metadata with common identifiers from EveBio screening.",
|
| 28 |
+
"evebio_control_table.csv": "Control datapoints for all screening and profiling plates.",
|
| 29 |
+
"evebio_detailed_result_table.csv": "Expanded results on evebio_summary_result_table with curve fit parameters and phase categories.",
|
| 30 |
+
"evebio_observed_points_table.csv": "Raw observed datapoints from all screening and profiling experiments.",
|
| 31 |
+
"evebio_summary_result_table.csv": "Succinct summary of results for each assay-compound combination.",
|
| 32 |
+
"evebio_target_table.csv": "Target metadata with common identifiers from EveBio screening.",
|
| 33 |
+
"genebass_missense_LC_filtered.pkl": "Filtered missense variants from GeneBass.",
|
| 34 |
+
"genebass_pLoF_filtered.pkl": "Predicted loss-of-function variants from GeneBass.",
|
| 35 |
+
"genebass_synonymous_filtered.pkl": "Filtered synonymous variants from GeneBass.",
|
| 36 |
+
"gene_info.parquet": "Comprehensive gene information.",
|
| 37 |
+
"genetic_interaction.parquet": "Genetic interactions between genes.",
|
| 38 |
+
"go-plus.json": "Gene ontology data for functional gene annotations.",
|
| 39 |
+
"gtex_tissue_gene_tpm.parquet": "Gene expression (TPM) across human tissues from GTEx.",
|
| 40 |
+
"gwas_catalog.pkl": "Genome-wide association studies (GWAS) results.",
|
| 41 |
+
"hp.obo": "Official HPO release in obographs format",
|
| 42 |
+
"kg.csv": "Precision medicine knowledge graph with 17,080 diseases and 4+ million relationships across biological scales.",
|
| 43 |
+
"marker_celltype.parquet": "Cell type marker genes for identification.",
|
| 44 |
+
"McPAS-TCR.parquet": "T-cell receptor sequences and specificity data from McPAS database.",
|
| 45 |
+
"miRDB_v6.0_results.parquet": "Predicted microRNA targets from miRDB.",
|
| 46 |
+
"miRTarBase_microRNA_target_interaction.parquet": "Experimentally validated microRNA-target interactions from miRTarBase.",
|
| 47 |
+
"miRTarBase_microRNA_target_interaction_pubmed_abtract.txt": "PubMed abstracts for microRNA-target interactions in miRTarBase.",
|
| 48 |
+
"miRTarBase_MicroRNA_Target_Sites.parquet": "Binding sites of microRNAs on target genes from miRTarBase.",
|
| 49 |
+
"mousemine_m1_positional_geneset.parquet": "Positional gene sets from MouseMine.",
|
| 50 |
+
"mousemine_m2_curated_geneset.parquet": "Curated gene sets from MouseMine.",
|
| 51 |
+
"mousemine_m3_regulatory_target_geneset.parquet": "Regulatory target gene sets from MouseMine.",
|
| 52 |
+
"mousemine_m5_ontology_geneset.parquet": "Ontology-based gene sets from MouseMine.",
|
| 53 |
+
"mousemine_m8_celltype_signature_geneset.parquet": "Cell type signature gene sets from MouseMine.",
|
| 54 |
+
"mousemine_mh_hallmark_geneset.parquet": "Hallmark gene sets from MouseMine.",
|
| 55 |
+
"msigdb_human_c1_positional_geneset.parquet": "Human positional gene sets from MSigDB.",
|
| 56 |
+
"msigdb_human_c2_curated_geneset.parquet": "Curated human gene sets from MSigDB.",
|
| 57 |
+
"msigdb_human_c3_regulatory_target_geneset.parquet": "Regulatory target gene sets from MSigDB.",
|
| 58 |
+
"msigdb_human_c3_subset_transcription_factor_targets_from_GTRD.parquet": "Transcription factor targets from GTRD/MSigDB.",
|
| 59 |
+
"msigdb_human_c4_computational_geneset.parquet": "Computationally derived gene sets from MSigDB.",
|
| 60 |
+
"msigdb_human_c5_ontology_geneset.parquet": "Ontology-based gene sets from MSigDB.",
|
| 61 |
+
"msigdb_human_c6_oncogenic_signature_geneset.parquet": "Oncogenic signatures from MSigDB.",
|
| 62 |
+
"msigdb_human_c7_immunologic_signature_geneset.parquet": "Immunologic signatures from MSigDB.",
|
| 63 |
+
"msigdb_human_c8_celltype_signature_geneset.parquet": "Cell type signatures from MSigDB.",
|
| 64 |
+
"msigdb_human_h_hallmark_geneset.parquet": "Hallmark gene sets from MSigDB.",
|
| 65 |
+
"omim.parquet": "Genetic disorders and associated genes from OMIM.",
|
| 66 |
+
"proteinatlas.tsv": "Protein expression data from Human Protein Atlas.",
|
| 67 |
+
"proximity_label-ms.parquet": "Protein interactions via proximity labeling and mass spectrometry.",
|
| 68 |
+
"reconstituted_complex.parquet": "Protein complexes reconstituted in vitro.",
|
| 69 |
+
"sgRNA_KO_SP_mouse.txt": "sgRNA knockout data for mouse.",
|
| 70 |
+
"sgRNA_KO_SP_human.txt": "sgRNA knockout data for human.",
|
| 71 |
+
"synthetic_growth_defect.parquet": "Synthetic growth defects from genetic interactions.",
|
| 72 |
+
"synthetic_lethality.parquet": "Synthetic lethal interactions.",
|
| 73 |
+
"synthetic_rescue.parquet": "Genetic interactions rescuing phenotypes.",
|
| 74 |
+
"two-hybrid.parquet": "Protein-protein interactions detected by yeast two-hybrid assays.",
|
| 75 |
+
"variant_table.parquet": "Annotated genetic variants table.",
|
| 76 |
+
"Virus-Host_PPI_P-HIPSTER_2020.parquet": "Virus-host protein-protein interactions from P-HIPSTER.",
|
| 77 |
+
"txgnn_name_mapping.pkl": "Name mapping for TXGNN.",
|
| 78 |
+
"txgnn_prediction.pkl": "Prediction data for TXGNN.",
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
# Updated library_content as a dictionary with detailed descriptions
|
| 82 |
+
library_content_dict = {
|
| 83 |
+
# === PYTHON PACKAGES ===
|
| 84 |
+
# Core Bioinformatics Libraries (Python)
|
| 85 |
+
"biopython": "[Python Package] A set of tools for biological computation including parsers for bioinformatics files, access to online services, and interfaces to common bioinformatics programs.",
|
| 86 |
+
"biom-format": "[Python Package] The Biological Observation Matrix (BIOM) format is designed for representing biological sample by observation contingency tables with associated metadata.",
|
| 87 |
+
"scanpy": "[Python Package] A scalable toolkit for analyzing single-cell gene expression data, specifically designed for large datasets using AnnData.",
|
| 88 |
+
"scikit-bio": "[Python Package] Data structures, algorithms, and educational resources for bioinformatics, including sequence analysis, phylogenetics, and ordination methods.",
|
| 89 |
+
"anndata": "[Python Package] A Python package for handling annotated data matrices in memory and on disk, primarily used for single-cell genomics data.",
|
| 90 |
+
"mudata": "[Python Package] A Python package for multimodal data storage and manipulation, extending AnnData to handle multiple modalities.",
|
| 91 |
+
"pyliftover": "[Python Package] A Python implementation of UCSC liftOver tool for converting genomic coordinates between genome assemblies.",
|
| 92 |
+
"biopandas": "[Python Package] A package that provides pandas DataFrames for working with molecular structures and biological data.",
|
| 93 |
+
"biotite": "[Python Package] A comprehensive library for computational molecular biology, providing tools for sequence analysis, structure analysis, and more.",
|
| 94 |
+
"lazyslide": "[Python Package] A Python framework that brings interoperable, reproducible whole slide image analysis, enabling seamless histopathology workflows from preprocessing to deep learning.",
|
| 95 |
+
# Genomics & Variant Analysis (Python)
|
| 96 |
+
"gget": "[Python Package] A toolkit for accessing genomic databases and retrieving sequences, annotations, and other genomic data.",
|
| 97 |
+
"lifelines": "[Python Package] A complete survival analysis library for fitting models, plotting, and statistical tests.",
|
| 98 |
+
# "scvi-tools": "[Python Package] A package for probabilistic modeling of single-cell omics data, including deep generative models.",
|
| 99 |
+
"gseapy": "[Python Package] A Python wrapper for Gene Set Enrichment Analysis (GSEA) and visualization.",
|
| 100 |
+
"scrublet": "[Python Package] A tool for detecting doublets in single-cell RNA-seq data.",
|
| 101 |
+
"cellxgene-census": "[Python Package] A tool for accessing and analyzing the CellxGene Census, a collection of single-cell datasets. To download a dataset, use the download_source_h5ad function with the dataset id as the argument (856c1b98-5727-49da-bf0f-151bdb8cb056, no .h5ad extension).",
|
| 102 |
+
"hyperopt": "[Python Package] A Python library for optimizing hyperparameters of machine learning algorithms.",
|
| 103 |
+
"scvelo": "[Python Package] A tool for RNA velocity analysis in single cells using dynamical models.",
|
| 104 |
+
"pysam": "[Python Package] A Python module for reading, manipulating and writing genomic data sets in SAM/BAM/VCF/BCF formats.",
|
| 105 |
+
"pyfaidx": "[Python Package] A Python package for efficient random access to FASTA files.",
|
| 106 |
+
"pyranges": "[Python Package] A Python package for interval manipulation with a pandas-like interface.",
|
| 107 |
+
"pybedtools": "[Python Package] A Python wrapper for Aaron Quinlan's BEDTools programs.",
|
| 108 |
+
# "panhumanpy": "A Python package for hierarchical, cross-tissue cell type annotation of human single-cell RNA-seq data",
|
| 109 |
+
# Structural Biology & Drug Discovery (Python)
|
| 110 |
+
"rdkit": "[Python Package] A collection of cheminformatics and machine learning tools for working with chemical structures and drug discovery.",
|
| 111 |
+
"deeppurpose": "[Python Package] A deep learning library for drug-target interaction prediction and virtual screening.",
|
| 112 |
+
"pyscreener": "[Python Package] A Python package for virtual screening of chemical compounds.",
|
| 113 |
+
"openbabel": "[Python Package] A chemical toolbox designed to speak the many languages of chemical data, supporting file format conversion and molecular modeling.",
|
| 114 |
+
"descriptastorus": "[Python Package] A library for computing molecular descriptors for machine learning applications in drug discovery.",
|
| 115 |
+
# "pymol": "[Python Package] A molecular visualization system for rendering and animating 3D molecular structures.",
|
| 116 |
+
"openmm": "[Python Package] A toolkit for molecular simulation using high-performance GPU computing.",
|
| 117 |
+
"pytdc": "[Python Package] A Python package for Therapeutics Data Commons, providing access to machine learning datasets for drug discovery.",
|
| 118 |
+
# Data Science & Statistical Analysis (Python)
|
| 119 |
+
"pandas": "[Python Package] A fast, powerful, and flexible data analysis and manipulation library for Python.",
|
| 120 |
+
"numpy": "[Python Package] The fundamental package for scientific computing with Python, providing support for arrays, matrices, and mathematical functions.",
|
| 121 |
+
"scipy": "[Python Package] A Python library for scientific and technical computing, including modules for optimization, linear algebra, integration, and statistics.",
|
| 122 |
+
"scikit-learn": "[Python Package] A machine learning library featuring various classification, regression, and clustering algorithms.",
|
| 123 |
+
"matplotlib": "[Python Package] A comprehensive library for creating static, animated, and interactive visualizations in Python.",
|
| 124 |
+
"seaborn": "[Python Package] A statistical data visualization library based on matplotlib with a high-level interface for drawing attractive statistical graphics.",
|
| 125 |
+
"statsmodels": "[Python Package] A Python module for statistical modeling and econometrics, including descriptive statistics and estimation of statistical models.",
|
| 126 |
+
"pymc3": "[Python Package] A Python package for Bayesian statistical modeling and probabilistic machine learning.",
|
| 127 |
+
# "pystan": "[Python Package] A Python interface to Stan, a platform for statistical modeling and high-performance statistical computation.",
|
| 128 |
+
"umap-learn": "[Python Package] Uniform Manifold Approximation and Projection, a dimension reduction technique.",
|
| 129 |
+
"faiss-cpu": "[Python Package] A library for efficient similarity search and clustering of dense vectors.",
|
| 130 |
+
"harmony-pytorch": "[Python Package] A PyTorch implementation of the Harmony algorithm for integrating single-cell data.",
|
| 131 |
+
# General Bioinformatics & Computational Utilities (Python)
|
| 132 |
+
"tiledb": "[Python Package] A powerful engine for storing and analyzing large-scale genomic data.",
|
| 133 |
+
"tiledbsoma": "[Python Package] A library for working with the SOMA (Stack of Matrices) format using TileDB.",
|
| 134 |
+
"h5py": "[Python Package] A Python interface to the HDF5 binary data format, allowing storage of large amounts of numerical data.",
|
| 135 |
+
"tqdm": "[Python Package] A fast, extensible progress bar for loops and CLI applications.",
|
| 136 |
+
"joblib": "[Python Package] A set of tools to provide lightweight pipelining in Python, including transparent disk-caching and parallel computing.",
|
| 137 |
+
"opencv-python": "[Python Package] OpenCV library for computer vision tasks, useful for image analysis in biological contexts.",
|
| 138 |
+
"PyPDF2": "[Python Package] A library for working with PDF files, useful for extracting text from scientific papers.",
|
| 139 |
+
"googlesearch-python": "[Python Package] A library for performing Google searches programmatically.",
|
| 140 |
+
"scikit-image": "[Python Package] A collection of algorithms for image processing in Python.",
|
| 141 |
+
"pymed": "[Python Package] A Python library for accessing PubMed articles.",
|
| 142 |
+
"arxiv": "[Python Package] A Python wrapper for the arXiv API, allowing access to scientific papers.",
|
| 143 |
+
"scholarly": "[Python Package] A module to retrieve author and publication information from Google Scholar.",
|
| 144 |
+
"cryosparc-tools": "[Python Package] Tools for working with cryoSPARC, a platform for cryo-EM data processing.",
|
| 145 |
+
"mageck": "[Python Package] Analysis of CRISPR screen data.",
|
| 146 |
+
"igraph": "[Python Package] Network analysis and visualization.",
|
| 147 |
+
"pyscenic": "[Python Package] Analysis of single-cell RNA-seq data and gene regulatory networks.",
|
| 148 |
+
"cooler": "[Python Package] Storage and analysis of Hi-C data.",
|
| 149 |
+
"trackpy": "[Python Package] Particle tracking in images and video.",
|
| 150 |
+
"nnunet": "[Python Package] A deep learning framework for biomedical image segmentation, providing a standardized approach to training and inference.",
|
| 151 |
+
# "flowcytometrytools": "[Python Package] Analysis and visualization of flow cytometry data.",
|
| 152 |
+
"cellpose": "[Python Package] Cell segmentation in microscopy images.",
|
| 153 |
+
"viennarna": "[Python Package] RNA secondary structure prediction.",
|
| 154 |
+
"PyMassSpec": "[Python Package] Mass spectrometry data analysis.",
|
| 155 |
+
"python-libsbml": "[Python Package] Working with SBML files for computational biology.",
|
| 156 |
+
"cobra": "[Python Package] Constraint-based modeling of metabolic networks.",
|
| 157 |
+
"reportlab": "[Python Package] Creation of PDF documents.",
|
| 158 |
+
"flowkit": "[Python Package] Toolkit for processing flow cytometry data.",
|
| 159 |
+
"hmmlearn": "[Python Package] Hidden Markov model analysis.",
|
| 160 |
+
"msprime": "[Python Package] Simulation of genetic variation.",
|
| 161 |
+
"tskit": "[Python Package] Handling tree sequences and population genetics data.",
|
| 162 |
+
"cyvcf2": "[Python Package] Fast parsing of VCF files.",
|
| 163 |
+
"pykalman": "[Python Package] Kalman filter and smoother implementation.",
|
| 164 |
+
"fanc": "[Python Package] Analysis of chromatin conformation data.",
|
| 165 |
+
"loompy": "A Python implementation of the Loom file format for efficiently storing and working with large omics datasets.",
|
| 166 |
+
"pyBigWig": "A Python library for accessing bigWig and bigBed files for genome browser track data.",
|
| 167 |
+
"pymzml": "A Python module for high-throughput bioinformatics analysis of mass spectrometry data.",
|
| 168 |
+
"optlang": "A Python package for modeling optimization problems symbolically.",
|
| 169 |
+
"FlowIO": "A Python package for reading and writing flow cytometry data files.",
|
| 170 |
+
"FlowUtils": "Utilities for processing and analyzing flow cytometry data.",
|
| 171 |
+
"arboreto": "A Python package for inferring gene regulatory networks from single-cell RNA-seq data.",
|
| 172 |
+
"pdbfixer": "A Python package for fixing problems in PDB files in preparation for molecular simulations.",
|
| 173 |
+
# === R PACKAGES ===
|
| 174 |
+
# Core R Packages for Data Analysis
|
| 175 |
+
"ggplot2": "[R Package] A system for declaratively creating graphics, based on The Grammar of Graphics. Use with subprocess.run(['Rscript', '-e', 'library(ggplot2); ...']).",
|
| 176 |
+
"dplyr": "[R Package] A grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges. Use with subprocess.",
|
| 177 |
+
"tidyr": "[R Package] A package that helps you create tidy data, where each column is a variable, each row is an observation, and each cell is a single value. Use with subprocess.",
|
| 178 |
+
"readr": "[R Package] A fast and friendly way to read rectangular data like CSV, TSV, and FWF. Use with subprocess.run(['Rscript', '-e', 'library(readr); ...']).",
|
| 179 |
+
"stringr": "[R Package] A cohesive set of functions designed to make working with strings as easy as possible. Use with subprocess calls.",
|
| 180 |
+
"Matrix": "[R Package] A package that provides classes and methods for dense and sparse matrices. Required for Seurat. Use with subprocess calls.",
|
| 181 |
+
# "Rcpp": "[R Package] Seamless R and C++ Integration, allowing R functions to call compiled C++ code. Use with subprocess calls.",
|
| 182 |
+
# "devtools": "[R Package] Tools to make developing R packages easier, including functions to install packages from GitHub. Use with subprocess calls.",
|
| 183 |
+
# "remotes": "[R Package] Install R packages from GitHub, GitLab, Bitbucket, or other remote repositories. Use with subprocess calls.",
|
| 184 |
+
# Bioinformatics R Packages
|
| 185 |
+
"DESeq2": "[R Package] Differential gene expression analysis based on the negative binomial distribution. Use with subprocess.run(['Rscript', '-e', 'library(DESeq2); ...']).",
|
| 186 |
+
"clusterProfiler": "[R Package] A package for statistical analysis and visualization of functional profiles for genes and gene clusters. Use with subprocess calls.",
|
| 187 |
+
# "DADA2": "[R Package] A package for modeling and correcting Illumina-sequenced amplicon errors. Use with subprocess calls.",
|
| 188 |
+
# "xcms": "[R Package] A package for processing and visualization of LC-MS and GC-MS data. Use with subprocess calls.",
|
| 189 |
+
# "FlowCore": "[R Package] Basic infrastructure for flow cytometry data. Use with subprocess calls.",
|
| 190 |
+
"edgeR": "[R Package] Empirical Analysis of Digital Gene Expression Data in R, for differential expression analysis. Use with subprocess calls.",
|
| 191 |
+
"limma": "[R Package] Linear Models for Microarray Data, for differential expression analysis. Use with subprocess calls.",
|
| 192 |
+
"harmony": "[R Package] A method for integrating and analyzing single-cell data across datasets. Use with subprocess calls.",
|
| 193 |
+
"WGCNA": "[R Package] Weighted Correlation Network Analysis for studying biological networks. Use with subprocess calls.",
|
| 194 |
+
# === CLI TOOLS ===
|
| 195 |
+
# Sequence Analysis Tools
|
| 196 |
+
"samtools": "[CLI Tool] A suite of programs for interacting with high-throughput sequencing data. Use with subprocess.run(['samtools', ...]).",
|
| 197 |
+
"bowtie2": "[CLI Tool] An ultrafast and memory-efficient tool for aligning sequencing reads to long reference sequences. Use with subprocess.run(['bowtie2', ...]).",
|
| 198 |
+
"bwa": "[CLI Tool] Burrows-Wheeler Aligner for mapping low-divergent sequences against a large reference genome. Use with subprocess.run(['bwa', ...]).",
|
| 199 |
+
"bedtools": "[CLI Tool] A powerful toolset for genome arithmetic, allowing operations like intersect, merge, count, and complement on genomic features. Use with subprocess.run(['bedtools', ...]).",
|
| 200 |
+
"macs2": "[CLI Tool] Model-based Analysis of ChIP-Seq data, a tool for identifying transcript factor binding sites.",
|
| 201 |
+
# Quality Control and Processing Tools
|
| 202 |
+
"fastqc": "[CLI Tool] A quality control tool for high throughput sequence data. Use with subprocess.run(['fastqc', ...]).",
|
| 203 |
+
"trimmomatic": "[CLI Tool] A flexible read trimming tool for Illumina NGS data. Use with subprocess.run(['trimmomatic', ...]).",
|
| 204 |
+
# Multiple Sequence Alignment and Phylogenetics
|
| 205 |
+
"mafft": "[CLI Tool] A multiple sequence alignment program for unix-like operating systems. Use with subprocess.run(['mafft', ...]).",
|
| 206 |
+
"Homer": "[CLI Tool] Motif discovery and next-gen sequencing analysis.",
|
| 207 |
+
"FastTree": "[CLI Tool] Phylogenetic trees from sequence alignments.",
|
| 208 |
+
"muscle": "[CLI Tool] Multiple sequence alignment tool.",
|
| 209 |
+
# Genetic Analysis Tools
|
| 210 |
+
"plink": "[CLI Tool] A comprehensive toolkit for genome association studies that can perform a range of large-scale analyses in a computationally efficient manner. Use with subprocess.run(['plink', ...]).",
|
| 211 |
+
"plink2": "[CLI Tool] A comprehensive toolkit for genome association studies that can perform a range of large-scale analyses in a computationally efficient manner. Use with subprocess.run(['plink2', ...]).",
|
| 212 |
+
"gcta64": "[CLI Tool] Genome-wide Complex Trait Analysis (GCTA) tool for estimating the proportion of phenotypic variance explained by genome-wide SNPs and analyzing genetic relationships. Use with subprocess.run(['gcta64', ...]).",
|
| 213 |
+
"iqtree2": "[CLI Tool] An efficient phylogenetic software for maximum likelihood analysis with built-in model selection and ultrafast bootstrap. Use with subprocess.run(['iqtree2', ...]).",
|
| 214 |
+
"ADFR": "AutoDock for Receptors suite for molecular docking and virtual screening. ",
|
| 215 |
+
"diamond": "A sequence aligner for protein and translated DNA searches, designed for high performance analysis of big sequence data. ",
|
| 216 |
+
"fcsparser": "A command-line tool for parsing and analyzing flow cytometry standard (FCS) files. ",
|
| 217 |
+
"plannotate": "[CLI Tool] A tool for annotating plasmid sequences with common features. ",
|
| 218 |
+
"vina": "[CLI Tool] An open-source program for molecular docking and virtual screening, known for its speed and accuracy improvements over AutoDock 4.",
|
| 219 |
+
"autosite": "[CLI Tool] A binding site detection tool used to identify potential ligand binding pockets on protein structures for molecular docking.",
|
| 220 |
+
"PyLabRobot": "[Python Package] A Python package for controlling liquid-handling robots and other lab automation equipment.",
|
| 221 |
+
}
|
biomni/env_desc_cm.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Data lake dictionary with detailed descriptions (Commercial Mode - Non-commercial datasets commented out)
|
| 2 |
+
data_lake_dict = {
|
| 3 |
+
"affinity_capture-ms.parquet": "Protein-protein interactions detected via affinity capture and mass spectrometry.",
|
| 4 |
+
"affinity_capture-rna.parquet": "Protein-RNA interactions detected by affinity capture.",
|
| 5 |
+
# "BindingDB_All_202409.tsv": "Measured binding affinities between proteins and small molecules for drug discovery.", # Requires commercial license
|
| 6 |
+
"broad_repurposing_hub_molecule_with_smiles.parquet": "Molecules from Broad Institute's Drug Repurposing Hub with SMILES annotations.",
|
| 7 |
+
"broad_repurposing_hub_phase_moa_target_info.parquet": "Drug phases, mechanisms of action, and target information from Broad Institute.",
|
| 8 |
+
"co-fractionation.parquet": "Protein-protein interactions from co-fractionation experiments.",
|
| 9 |
+
"czi_census_datasets_v4.parquet": "Datasets from the Chan Zuckerberg Initiative's Cell Census.",
|
| 10 |
+
"DepMap_CRISPRGeneDependency.csv": "Gene dependency probability estimates for cancer cell lines, including all DepMap models.",
|
| 11 |
+
"DepMap_CRISPRGeneEffect.csv": "Genome-wide CRISPR gene effect estimates for cancer cell lines, including all DepMap models.",
|
| 12 |
+
"DepMap_Model.csv": "Metadata describing all cancer models/cell lines which are referenced by a dataset contained within the DepMap portal.",
|
| 13 |
+
"DepMap_OmicsExpressionProteinCodingGenesTPMLogp1.csv": "Gene expression in TPMs for cancer cell lines, including all DepMap models.",
|
| 14 |
+
# "ddinter_alimentary_tract_metabolism.csv": "Drug-drug interactions for alimentary tract and metabolism drugs from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 15 |
+
# "ddinter_antineoplastic.csv": "Drug-drug interactions for antineoplastic and immunomodulating agents from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 16 |
+
# "ddinter_antiparasitic.csv": "Drug-drug interactions for antiparasitic products from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 17 |
+
# "ddinter_blood_organs.csv": "Drug-drug interactions for blood and blood forming organs drugs from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 18 |
+
# "ddinter_dermatological.csv": "Drug-drug interactions for dermatological drugs from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 19 |
+
# "ddinter_hormonal.csv": "Drug-drug interactions for systemic hormonal preparations from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 20 |
+
# "ddinter_respiratory.csv": "Drug-drug interactions for respiratory system drugs from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 21 |
+
# "ddinter_various.csv": "Drug-drug interactions for various drugs from DDInter 2.0 database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 22 |
+
# "DisGeNET.parquet": "Gene-disease associations from multiple sources.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 23 |
+
"dosage_growth_defect.parquet": "Gene dosage changes affecting growth.",
|
| 24 |
+
# "enamine_cloud_library_smiles.pkl": "Compounds from Enamine REAL library with SMILES annotations.", # Proprietary - Requires license
|
| 25 |
+
# "evebio_assay_table.csv": "Assay metadata with one row per assay from EveBio pharmome mapping.", # Proprietary - Requires permission
|
| 26 |
+
# "evebio_bundle_table.csv": "Target subfamily bundles used for screening-to-profiling progression.", # Proprietary - Requires permission
|
| 27 |
+
# "evebio_compound_table.csv": "Compound metadata with common identifiers from EveBio screening.", # Proprietary - Requires permission
|
| 28 |
+
# "evebio_control_table.csv": "Control datapoints for all screening and profiling plates.", # Proprietary - Requires permission
|
| 29 |
+
# "evebio_detailed_result_table.csv": "Expanded results on evebio_summary_result_table with curve fit parameters and phase categories.", # Proprietary - Requires permission
|
| 30 |
+
# "evebio_observed_points_table.csv": "Raw observed datapoints from all screening and profiling experiments.", # Proprietary - Requires permission
|
| 31 |
+
# "evebio_summary_result_table.csv": "Succinct summary of results for each assay-compound combination.", # Proprietary - Requires permission
|
| 32 |
+
# "evebio_target_table.csv": "Target metadata with common identifiers from EveBio screening.", # Proprietary - Requires permission
|
| 33 |
+
"genebass_missense_LC_filtered.pkl": "Filtered missense variants from GeneBass.",
|
| 34 |
+
"genebass_pLoF_filtered.pkl": "Predicted loss-of-function variants from GeneBass.",
|
| 35 |
+
"genebass_synonymous_filtered.pkl": "Filtered synonymous variants from GeneBass.",
|
| 36 |
+
"gene_info.parquet": "Comprehensive gene information.",
|
| 37 |
+
"genetic_interaction.parquet": "Genetic interactions between genes.",
|
| 38 |
+
"go-plus.json": "Gene ontology data for functional gene annotations.",
|
| 39 |
+
"gtex_tissue_gene_tpm.parquet": "Gene expression (TPM) across human tissues from GTEx.",
|
| 40 |
+
"gwas_catalog.pkl": "Genome-wide association studies (GWAS) results.",
|
| 41 |
+
"hp.obo": "Official HPO release in obographs format",
|
| 42 |
+
"kg.csv": "Precision medicine knowledge graph with 17,080 diseases and 4+ million relationships across biological scales.",
|
| 43 |
+
"marker_celltype.parquet": "Cell type marker genes for identification.",
|
| 44 |
+
# "McPAS-TCR.parquet": "T-cell receptor sequences and specificity data from McPAS database.", # CC BY-NC-SA 4.0 - Non-commercial only
|
| 45 |
+
# "miRDB_v6.0_results.parquet": "Predicted microRNA targets from miRDB.", # Non-commercial use only
|
| 46 |
+
# "miRTarBase_microRNA_target_interaction.parquet": "Experimentally validated microRNA-target interactions from miRTarBase.", # CC BY-NC 4.0 - Non-commercial only
|
| 47 |
+
# "miRTarBase_microRNA_target_interaction_pubmed_abtract.txt": "PubMed abstracts for microRNA-target interactions in miRTarBase.", # CC BY-NC 4.0 - Non-commercial only
|
| 48 |
+
# "miRTarBase_MicroRNA_Target_Sites.parquet": "Binding sites of microRNAs on target genes from miRTarBase.", # CC BY-NC 4.0 - Non-commercial only
|
| 49 |
+
"mousemine_m1_positional_geneset.parquet": "Positional gene sets from MouseMine.",
|
| 50 |
+
"mousemine_m2_curated_geneset.parquet": "Curated gene sets from MouseMine.",
|
| 51 |
+
"mousemine_m3_regulatory_target_geneset.parquet": "Regulatory target gene sets from MouseMine.",
|
| 52 |
+
"mousemine_m5_ontology_geneset.parquet": "Ontology-based gene sets from MouseMine.",
|
| 53 |
+
"mousemine_m8_celltype_signature_geneset.parquet": "Cell type signature gene sets from MouseMine.",
|
| 54 |
+
"mousemine_mh_hallmark_geneset.parquet": "Hallmark gene sets from MouseMine.",
|
| 55 |
+
# "msigdb_human_c1_positional_geneset.parquet": "Human positional gene sets from MSigDB.", # Requires commercial license
|
| 56 |
+
# "msigdb_human_c2_curated_geneset.parquet": "Curated human gene sets from MSigDB.", # Requires commercial license
|
| 57 |
+
# "msigdb_human_c3_regulatory_target_geneset.parquet": "Regulatory target gene sets from MSigDB.", # Requires commercial license
|
| 58 |
+
# "msigdb_human_c3_subset_transcription_factor_targets_from_GTRD.parquet": "Transcription factor targets from GTRD/MSigDB.", # Requires commercial license
|
| 59 |
+
# "msigdb_human_c4_computational_geneset.parquet": "Computationally derived gene sets from MSigDB.", # Requires commercial license
|
| 60 |
+
# "msigdb_human_c5_ontology_geneset.parquet": "Ontology-based gene sets from MSigDB.", # Requires commercial license
|
| 61 |
+
# "msigdb_human_c6_oncogenic_signature_geneset.parquet": "Oncogenic signatures from MSigDB.", # Requires commercial license
|
| 62 |
+
# "msigdb_human_c7_immunologic_signature_geneset.parquet": "Immunologic signatures from MSigDB.", # Requires commercial license
|
| 63 |
+
# "msigdb_human_c8_celltype_signature_geneset.parquet": "Cell type signatures from MSigDB.", # Requires commercial license
|
| 64 |
+
# "msigdb_human_h_hallmark_geneset.parquet": "Hallmark gene sets from MSigDB.", # Requires commercial license
|
| 65 |
+
# "omim.parquet": "Genetic disorders and associated genes from OMIM.", # Requires commercial license
|
| 66 |
+
"proteinatlas.tsv": "Protein expression data from Human Protein Atlas.",
|
| 67 |
+
"proximity_label-ms.parquet": "Protein interactions via proximity labeling and mass spectrometry.",
|
| 68 |
+
"reconstituted_complex.parquet": "Protein complexes reconstituted in vitro.",
|
| 69 |
+
"sgRNA_KO_SP_mouse.txt": "sgRNA knockout data for mouse.",
|
| 70 |
+
"sgRNA_KO_SP_human.txt": "sgRNA knockout data for human.",
|
| 71 |
+
"synthetic_growth_defect.parquet": "Synthetic growth defects from genetic interactions.",
|
| 72 |
+
"synthetic_lethality.parquet": "Synthetic lethal interactions.",
|
| 73 |
+
"synthetic_rescue.parquet": "Genetic interactions rescuing phenotypes.",
|
| 74 |
+
"two-hybrid.parquet": "Protein-protein interactions detected by yeast two-hybrid assays.",
|
| 75 |
+
"variant_table.parquet": "Annotated genetic variants table.",
|
| 76 |
+
"Virus-Host_PPI_P-HIPSTER_2020.parquet": "Virus-host protein-protein interactions from P-HIPSTER.",
|
| 77 |
+
"txgnn_name_mapping.pkl": "Name mapping for TXGNN.",
|
| 78 |
+
"txgnn_prediction.pkl": "Prediction data for TXGNN.",
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
# Updated library_content as a dictionary with detailed descriptions
|
| 82 |
+
library_content_dict = {
|
| 83 |
+
# === PYTHON PACKAGES ===
|
| 84 |
+
# Core Bioinformatics Libraries (Python)
|
| 85 |
+
"biopython": "[Python Package] A set of tools for biological computation including parsers for bioinformatics files, access to online services, and interfaces to common bioinformatics programs.",
|
| 86 |
+
"biom-format": "[Python Package] The Biological Observation Matrix (BIOM) format is designed for representing biological sample by observation contingency tables with associated metadata.",
|
| 87 |
+
"scanpy": "[Python Package] A scalable toolkit for analyzing single-cell gene expression data, specifically designed for large datasets using AnnData.",
|
| 88 |
+
"scikit-bio": "[Python Package] Data structures, algorithms, and educational resources for bioinformatics, including sequence analysis, phylogenetics, and ordination methods.",
|
| 89 |
+
"anndata": "[Python Package] A Python package for handling annotated data matrices in memory and on disk, primarily used for single-cell genomics data.",
|
| 90 |
+
"mudata": "[Python Package] A Python package for multimodal data storage and manipulation, extending AnnData to handle multiple modalities.",
|
| 91 |
+
"pyliftover": "[Python Package] A Python implementation of UCSC liftOver tool for converting genomic coordinates between genome assemblies.",
|
| 92 |
+
"biopandas": "[Python Package] A package that provides pandas DataFrames for working with molecular structures and biological data.",
|
| 93 |
+
"biotite": "[Python Package] A comprehensive library for computational molecular biology, providing tools for sequence analysis, structure analysis, and more.",
|
| 94 |
+
"lazyslide": "[Python Package] A Python framework that brings interoperable, reproducible whole slide image analysis, enabling seamless histopathology workflows from preprocessing to deep learning.",
|
| 95 |
+
# Genomics & Variant Analysis (Python)
|
| 96 |
+
"gget": "[Python Package] A toolkit for accessing genomic databases and retrieving sequences, annotations, and other genomic data.",
|
| 97 |
+
"lifelines": "[Python Package] A complete survival analysis library for fitting models, plotting, and statistical tests.",
|
| 98 |
+
# "scvi-tools": "[Python Package] A package for probabilistic modeling of single-cell omics data, including deep generative models.",
|
| 99 |
+
"gseapy": "[Python Package] A Python wrapper for Gene Set Enrichment Analysis (GSEA) and visualization.",
|
| 100 |
+
"scrublet": "[Python Package] A tool for detecting doublets in single-cell RNA-seq data.",
|
| 101 |
+
"cellxgene-census": "[Python Package] A tool for accessing and analyzing the CellxGene Census, a collection of single-cell datasets. To download a dataset, use the download_source_h5ad function with the dataset id as the argument (856c1b98-5727-49da-bf0f-151bdb8cb056, no .h5ad extension).",
|
| 102 |
+
"hyperopt": "[Python Package] A Python library for optimizing hyperparameters of machine learning algorithms.",
|
| 103 |
+
"scvelo": "[Python Package] A tool for RNA velocity analysis in single cells using dynamical models.",
|
| 104 |
+
"pysam": "[Python Package] A Python module for reading, manipulating and writing genomic data sets in SAM/BAM/VCF/BCF formats.",
|
| 105 |
+
"pyfaidx": "[Python Package] A Python package for efficient random access to FASTA files.",
|
| 106 |
+
"pyranges": "[Python Package] A Python package for interval manipulation with a pandas-like interface.",
|
| 107 |
+
"pybedtools": "[Python Package] A Python wrapper for Aaron Quinlan's BEDTools programs.",
|
| 108 |
+
# "panhumanpy": "A Python package for hierarchical, cross-tissue cell type annotation of human single-cell RNA-seq data",
|
| 109 |
+
# Structural Biology & Drug Discovery (Python)
|
| 110 |
+
"rdkit": "[Python Package] A collection of cheminformatics and machine learning tools for working with chemical structures and drug discovery.",
|
| 111 |
+
"deeppurpose": "[Python Package] A deep learning library for drug-target interaction prediction and virtual screening.",
|
| 112 |
+
"pyscreener": "[Python Package] A Python package for virtual screening of chemical compounds.",
|
| 113 |
+
"openbabel": "[Python Package] A chemical toolbox designed to speak the many languages of chemical data, supporting file format conversion and molecular modeling.",
|
| 114 |
+
"descriptastorus": "[Python Package] A library for computing molecular descriptors for machine learning applications in drug discovery.",
|
| 115 |
+
# "pymol": "[Python Package] A molecular visualization system for rendering and animating 3D molecular structures.",
|
| 116 |
+
"openmm": "[Python Package] A toolkit for molecular simulation using high-performance GPU computing.",
|
| 117 |
+
"pytdc": "[Python Package] A Python package for Therapeutics Data Commons, providing access to machine learning datasets for drug discovery.",
|
| 118 |
+
# Data Science & Statistical Analysis (Python)
|
| 119 |
+
"pandas": "[Python Package] A fast, powerful, and flexible data analysis and manipulation library for Python.",
|
| 120 |
+
"numpy": "[Python Package] The fundamental package for scientific computing with Python, providing support for arrays, matrices, and mathematical functions.",
|
| 121 |
+
"scipy": "[Python Package] A Python library for scientific and technical computing, including modules for optimization, linear algebra, integration, and statistics.",
|
| 122 |
+
"scikit-learn": "[Python Package] A machine learning library featuring various classification, regression, and clustering algorithms.",
|
| 123 |
+
"matplotlib": "[Python Package] A comprehensive library for creating static, animated, and interactive visualizations in Python.",
|
| 124 |
+
"seaborn": "[Python Package] A statistical data visualization library based on matplotlib with a high-level interface for drawing attractive statistical graphics.",
|
| 125 |
+
"statsmodels": "[Python Package] A Python module for statistical modeling and econometrics, including descriptive statistics and estimation of statistical models.",
|
| 126 |
+
"pymc3": "[Python Package] A Python package for Bayesian statistical modeling and probabilistic machine learning.",
|
| 127 |
+
# "pystan": "[Python Package] A Python interface to Stan, a platform for statistical modeling and high-performance statistical computation.",
|
| 128 |
+
"umap-learn": "[Python Package] Uniform Manifold Approximation and Projection, a dimension reduction technique.",
|
| 129 |
+
"faiss-cpu": "[Python Package] A library for efficient similarity search and clustering of dense vectors.",
|
| 130 |
+
"harmony-pytorch": "[Python Package] A PyTorch implementation of the Harmony algorithm for integrating single-cell data.",
|
| 131 |
+
# General Bioinformatics & Computational Utilities (Python)
|
| 132 |
+
"tiledb": "[Python Package] A powerful engine for storing and analyzing large-scale genomic data.",
|
| 133 |
+
"tiledbsoma": "[Python Package] A library for working with the SOMA (Stack of Matrices) format using TileDB.",
|
| 134 |
+
"h5py": "[Python Package] A Python interface to the HDF5 binary data format, allowing storage of large amounts of numerical data.",
|
| 135 |
+
"tqdm": "[Python Package] A fast, extensible progress bar for loops and CLI applications.",
|
| 136 |
+
"joblib": "[Python Package] A set of tools to provide lightweight pipelining in Python, including transparent disk-caching and parallel computing.",
|
| 137 |
+
"opencv-python": "[Python Package] OpenCV library for computer vision tasks, useful for image analysis in biological contexts.",
|
| 138 |
+
"PyPDF2": "[Python Package] A library for working with PDF files, useful for extracting text from scientific papers.",
|
| 139 |
+
"googlesearch-python": "[Python Package] A library for performing Google searches programmatically.",
|
| 140 |
+
"scikit-image": "[Python Package] A collection of algorithms for image processing in Python.",
|
| 141 |
+
"pymed": "[Python Package] A Python library for accessing PubMed articles.",
|
| 142 |
+
"arxiv": "[Python Package] A Python wrapper for the arXiv API, allowing access to scientific papers.",
|
| 143 |
+
"scholarly": "[Python Package] A module to retrieve author and publication information from Google Scholar.",
|
| 144 |
+
"cryosparc-tools": "[Python Package] Tools for working with cryoSPARC, a platform for cryo-EM data processing.",
|
| 145 |
+
"mageck": "[Python Package] Analysis of CRISPR screen data.",
|
| 146 |
+
"igraph": "[Python Package] Network analysis and visualization.",
|
| 147 |
+
"pyscenic": "[Python Package] Analysis of single-cell RNA-seq data and gene regulatory networks.",
|
| 148 |
+
"cooler": "[Python Package] Storage and analysis of Hi-C data.",
|
| 149 |
+
"trackpy": "[Python Package] Particle tracking in images and video.",
|
| 150 |
+
# "flowcytometrytools": "[Python Package] Analysis and visualization of flow cytometry data.",
|
| 151 |
+
"cellpose": "[Python Package] Cell segmentation in microscopy images.",
|
| 152 |
+
"viennarna": "[Python Package] RNA secondary structure prediction.",
|
| 153 |
+
"PyMassSpec": "[Python Package] Mass spectrometry data analysis.",
|
| 154 |
+
"python-libsbml": "[Python Package] Working with SBML files for computational biology.",
|
| 155 |
+
"cobra": "[Python Package] Constraint-based modeling of metabolic networks.",
|
| 156 |
+
"reportlab": "[Python Package] Creation of PDF documents.",
|
| 157 |
+
"flowkit": "[Python Package] Toolkit for processing flow cytometry data.",
|
| 158 |
+
"hmmlearn": "[Python Package] Hidden Markov model analysis.",
|
| 159 |
+
"msprime": "[Python Package] Simulation of genetic variation.",
|
| 160 |
+
"tskit": "[Python Package] Handling tree sequences and population genetics data.",
|
| 161 |
+
"cyvcf2": "[Python Package] Fast parsing of VCF files.",
|
| 162 |
+
"pykalman": "[Python Package] Kalman filter and smoother implementation.",
|
| 163 |
+
"fanc": "[Python Package] Analysis of chromatin conformation data.",
|
| 164 |
+
"loompy": "A Python implementation of the Loom file format for efficiently storing and working with large omics datasets.",
|
| 165 |
+
"pyBigWig": "A Python library for accessing bigWig and bigBed files for genome browser track data.",
|
| 166 |
+
"pymzml": "A Python module for high-throughput bioinformatics analysis of mass spectrometry data.",
|
| 167 |
+
"optlang": "A Python package for modeling optimization problems symbolically.",
|
| 168 |
+
"FlowIO": "A Python package for reading and writing flow cytometry data files.",
|
| 169 |
+
"FlowUtils": "Utilities for processing and analyzing flow cytometry data.",
|
| 170 |
+
"arboreto": "A Python package for inferring gene regulatory networks from single-cell RNA-seq data.",
|
| 171 |
+
"pdbfixer": "A Python package for fixing problems in PDB files in preparation for molecular simulations.",
|
| 172 |
+
# === R PACKAGES ===
|
| 173 |
+
# Core R Packages for Data Analysis
|
| 174 |
+
"ggplot2": "[R Package] A system for declaratively creating graphics, based on The Grammar of Graphics. Use with subprocess.run(['Rscript', '-e', 'library(ggplot2); ...']).",
|
| 175 |
+
"dplyr": "[R Package] A grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges. Use with subprocess.",
|
| 176 |
+
"tidyr": "[R Package] A package that helps you create tidy data, where each column is a variable, each row is an observation, and each cell is a single value. Use with subprocess.",
|
| 177 |
+
"readr": "[R Package] A fast and friendly way to read rectangular data like CSV, TSV, and FWF. Use with subprocess.run(['Rscript', '-e', 'library(readr); ...']).",
|
| 178 |
+
"stringr": "[R Package] A cohesive set of functions designed to make working with strings as easy as possible. Use with subprocess calls.",
|
| 179 |
+
"Matrix": "[R Package] A package that provides classes and methods for dense and sparse matrices. Required for Seurat. Use with subprocess calls.",
|
| 180 |
+
# "Rcpp": "[R Package] Seamless R and C++ Integration, allowing R functions to call compiled C++ code. Use with subprocess calls.",
|
| 181 |
+
# "devtools": "[R Package] Tools to make developing R packages easier, including functions to install packages from GitHub. Use with subprocess calls.",
|
| 182 |
+
# "remotes": "[R Package] Install R packages from GitHub, GitLab, Bitbucket, or other remote repositories. Use with subprocess calls.",
|
| 183 |
+
# Bioinformatics R Packages
|
| 184 |
+
"DESeq2": "[R Package] Differential gene expression analysis based on the negative binomial distribution. Use with subprocess.run(['Rscript', '-e', 'library(DESeq2); ...']).",
|
| 185 |
+
"clusterProfiler": "[R Package] A package for statistical analysis and visualization of functional profiles for genes and gene clusters. Use with subprocess calls.",
|
| 186 |
+
# "DADA2": "[R Package] A package for modeling and correcting Illumina-sequenced amplicon errors. Use with subprocess calls.",
|
| 187 |
+
# "xcms": "[R Package] A package for processing and visualization of LC-MS and GC-MS data. Use with subprocess calls.",
|
| 188 |
+
# "FlowCore": "[R Package] Basic infrastructure for flow cytometry data. Use with subprocess calls.",
|
| 189 |
+
"edgeR": "[R Package] Empirical Analysis of Digital Gene Expression Data in R, for differential expression analysis. Use with subprocess calls.",
|
| 190 |
+
"limma": "[R Package] Linear Models for Microarray Data, for differential expression analysis. Use with subprocess calls.",
|
| 191 |
+
"harmony": "[R Package] A method for integrating and analyzing single-cell data across datasets. Use with subprocess calls.",
|
| 192 |
+
"WGCNA": "[R Package] Weighted Correlation Network Analysis for studying biological networks. Use with subprocess calls.",
|
| 193 |
+
# === CLI TOOLS ===
|
| 194 |
+
# Sequence Analysis Tools
|
| 195 |
+
"samtools": "[CLI Tool] A suite of programs for interacting with high-throughput sequencing data. Use with subprocess.run(['samtools', ...]).",
|
| 196 |
+
"bowtie2": "[CLI Tool] An ultrafast and memory-efficient tool for aligning sequencing reads to long reference sequences. Use with subprocess.run(['bowtie2', ...]).",
|
| 197 |
+
"bwa": "[CLI Tool] Burrows-Wheeler Aligner for mapping low-divergent sequences against a large reference genome. Use with subprocess.run(['bwa', ...]).",
|
| 198 |
+
"bedtools": "[CLI Tool] A powerful toolset for genome arithmetic, allowing operations like intersect, merge, count, and complement on genomic features. Use with subprocess.run(['bedtools', ...]).",
|
| 199 |
+
"macs2": "[CLI Tool] Model-based Analysis of ChIP-Seq data, a tool for identifying transcript factor binding sites.",
|
| 200 |
+
# Quality Control and Processing Tools
|
| 201 |
+
"fastqc": "[CLI Tool] A quality control tool for high throughput sequence data. Use with subprocess.run(['fastqc', ...]).",
|
| 202 |
+
"trimmomatic": "[CLI Tool] A flexible read trimming tool for Illumina NGS data. Use with subprocess.run(['trimmomatic', ...]).",
|
| 203 |
+
# Multiple Sequence Alignment and Phylogenetics
|
| 204 |
+
"mafft": "[CLI Tool] A multiple sequence alignment program for unix-like operating systems. Use with subprocess.run(['mafft', ...]).",
|
| 205 |
+
"Homer": "[CLI Tool] Motif discovery and next-gen sequencing analysis.",
|
| 206 |
+
"FastTree": "[CLI Tool] Phylogenetic trees from sequence alignments.",
|
| 207 |
+
"muscle": "[CLI Tool] Multiple sequence alignment tool.",
|
| 208 |
+
# Genetic Analysis Tools
|
| 209 |
+
"plink": "[CLI Tool] A comprehensive toolkit for genome association studies that can perform a range of large-scale analyses in a computationally efficient manner. Use with subprocess.run(['plink', ...]).",
|
| 210 |
+
"plink2": "[CLI Tool] A comprehensive toolkit for genome association studies that can perform a range of large-scale analyses in a computationally efficient manner. Use with subprocess.run(['plink2', ...]).",
|
| 211 |
+
"gcta64": "[CLI Tool] Genome-wide Complex Trait Analysis (GCTA) tool for estimating the proportion of phenotypic variance explained by genome-wide SNPs and analyzing genetic relationships. Use with subprocess.run(['gcta64', ...]).",
|
| 212 |
+
"iqtree2": "[CLI Tool] An efficient phylogenetic software for maximum likelihood analysis with built-in model selection and ultrafast bootstrap. Use with subprocess.run(['iqtree2', ...]).",
|
| 213 |
+
"ADFR": "AutoDock for Receptors suite for molecular docking and virtual screening. ",
|
| 214 |
+
"diamond": "A sequence aligner for protein and translated DNA searches, designed for high performance analysis of big sequence data. ",
|
| 215 |
+
"fcsparser": "A command-line tool for parsing and analyzing flow cytometry standard (FCS) files. ",
|
| 216 |
+
"plannotate": "[CLI Tool] A tool for annotating plasmid sequences with common features. ",
|
| 217 |
+
"vina": "[CLI Tool] An open-source program for molecular docking and virtual screening, known for its speed and accuracy improvements over AutoDock 4.",
|
| 218 |
+
"autosite": "[CLI Tool] A binding site detection tool used to identify potential ligand binding pockets on protein structures for molecular docking.",
|
| 219 |
+
}
|
biomni/llm.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import TYPE_CHECKING, Literal, Optional
|
| 3 |
+
|
| 4 |
+
from langchain_core.language_models.chat_models import BaseChatModel
|
| 5 |
+
|
| 6 |
+
if TYPE_CHECKING:
|
| 7 |
+
from biomni.config import BiomniConfig
|
| 8 |
+
|
| 9 |
+
SourceType = Literal["OpenAI", "AzureOpenAI", "Anthropic", "Ollama", "Gemini", "Bedrock", "Groq", "Custom"]
|
| 10 |
+
ALLOWED_SOURCES: set[str] = set(SourceType.__args__)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def get_llm(
|
| 14 |
+
model: str | None = None,
|
| 15 |
+
temperature: float | None = None,
|
| 16 |
+
stop_sequences: list[str] | None = None,
|
| 17 |
+
source: SourceType | None = None,
|
| 18 |
+
base_url: str | None = None,
|
| 19 |
+
api_key: str | None = None,
|
| 20 |
+
config: Optional["BiomniConfig"] = None,
|
| 21 |
+
) -> BaseChatModel:
|
| 22 |
+
"""
|
| 23 |
+
Get a language model instance based on the specified model name and source.
|
| 24 |
+
This function supports models from OpenAI, Azure OpenAI, Anthropic, Ollama, Gemini, Bedrock, and custom model serving.
|
| 25 |
+
Args:
|
| 26 |
+
model (str): The model name to use
|
| 27 |
+
temperature (float): Temperature setting for generation
|
| 28 |
+
stop_sequences (list): Sequences that will stop generation
|
| 29 |
+
source (str): Source provider: "OpenAI", "AzureOpenAI", "Anthropic", "Ollama", "Gemini", "Bedrock", or "Custom"
|
| 30 |
+
If None, will attempt to auto-detect from model name
|
| 31 |
+
base_url (str): The base URL for custom model serving (e.g., "http://localhost:8000/v1"), default is None
|
| 32 |
+
api_key (str): The API key for the custom llm
|
| 33 |
+
config (BiomniConfig): Optional configuration object. If provided, unspecified parameters will use config values
|
| 34 |
+
"""
|
| 35 |
+
# Use config values for any unspecified parameters
|
| 36 |
+
if config is not None:
|
| 37 |
+
if model is None:
|
| 38 |
+
model = config.llm_model
|
| 39 |
+
if temperature is None:
|
| 40 |
+
temperature = config.temperature
|
| 41 |
+
if source is None:
|
| 42 |
+
source = config.source
|
| 43 |
+
if base_url is None:
|
| 44 |
+
base_url = config.base_url
|
| 45 |
+
if api_key is None:
|
| 46 |
+
api_key = config.api_key or "EMPTY"
|
| 47 |
+
|
| 48 |
+
# Use defaults if still not specified
|
| 49 |
+
if model is None:
|
| 50 |
+
model = "claude-3-5-sonnet-20241022"
|
| 51 |
+
if temperature is None:
|
| 52 |
+
temperature = 0.7
|
| 53 |
+
if api_key is None:
|
| 54 |
+
api_key = "EMPTY"
|
| 55 |
+
# Auto-detect source from model name if not specified
|
| 56 |
+
if source is None:
|
| 57 |
+
env_source = os.getenv("LLM_SOURCE")
|
| 58 |
+
if env_source in ALLOWED_SOURCES:
|
| 59 |
+
source = env_source
|
| 60 |
+
else:
|
| 61 |
+
if model[:7] == "claude-":
|
| 62 |
+
source = "Anthropic"
|
| 63 |
+
elif model[:7] == "gpt-oss":
|
| 64 |
+
source = "Ollama"
|
| 65 |
+
elif model[:4] == "gpt-":
|
| 66 |
+
source = "OpenAI"
|
| 67 |
+
elif model.startswith("azure-"):
|
| 68 |
+
source = "AzureOpenAI"
|
| 69 |
+
elif model[:7] == "gemini-":
|
| 70 |
+
source = "Gemini"
|
| 71 |
+
elif "groq" in model.lower():
|
| 72 |
+
source = "Groq"
|
| 73 |
+
elif base_url is not None:
|
| 74 |
+
source = "Custom"
|
| 75 |
+
elif "/" in model or any(
|
| 76 |
+
name in model.lower()
|
| 77 |
+
for name in [
|
| 78 |
+
"llama",
|
| 79 |
+
"mistral",
|
| 80 |
+
"qwen",
|
| 81 |
+
"gemma",
|
| 82 |
+
"phi",
|
| 83 |
+
"dolphin",
|
| 84 |
+
"orca",
|
| 85 |
+
"vicuna",
|
| 86 |
+
"deepseek",
|
| 87 |
+
]
|
| 88 |
+
):
|
| 89 |
+
source = "Ollama"
|
| 90 |
+
elif model.startswith(
|
| 91 |
+
("anthropic.claude-", "amazon.titan-", "meta.llama-", "mistral.", "cohere.", "ai21.", "us.")
|
| 92 |
+
):
|
| 93 |
+
source = "Bedrock"
|
| 94 |
+
else:
|
| 95 |
+
raise ValueError("Unable to determine model source. Please specify 'source' parameter.")
|
| 96 |
+
|
| 97 |
+
# Create appropriate model based on source
|
| 98 |
+
if source == "OpenAI":
|
| 99 |
+
try:
|
| 100 |
+
from langchain_openai import ChatOpenAI
|
| 101 |
+
except ImportError:
|
| 102 |
+
raise ImportError( # noqa: B904
|
| 103 |
+
"langchain-openai package is required for OpenAI models. Install with: pip install langchain-openai"
|
| 104 |
+
)
|
| 105 |
+
# Newer OpenAI models (e.g., gpt-5-*) require the Responses API and may reject
|
| 106 |
+
# legacy Chat Completions parameters like `stop`. Force Responses API when
|
| 107 |
+
# using gpt-5 models to avoid 400 errors such as: "Unsupported parameter: 'stop'".
|
| 108 |
+
use_responses = model.startswith("gpt-5")
|
| 109 |
+
|
| 110 |
+
if use_responses:
|
| 111 |
+
# Define a minimal subclass that drops the `stop` field when using the
|
| 112 |
+
# Responses API, since certain models (gpt-5-*) reject it entirely.
|
| 113 |
+
class _ChatOpenAIResponsesNoStop(ChatOpenAI):
|
| 114 |
+
def _get_request_payload(self, input_, *, stop=None, **kwargs): # type: ignore[override]
|
| 115 |
+
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
|
| 116 |
+
try:
|
| 117 |
+
# If this call will use the Responses API, drop `stop` to avoid 400s.
|
| 118 |
+
if hasattr(self, "_use_responses_api") and self._use_responses_api(payload): # type: ignore[attr-defined]
|
| 119 |
+
payload.pop("stop", None)
|
| 120 |
+
# Also drop temperature for gpt-5 models as they only support default value
|
| 121 |
+
payload.pop("temperature", None)
|
| 122 |
+
except Exception:
|
| 123 |
+
# Be conservative: if anything goes wrong, still remove `stop` and `temperature`.
|
| 124 |
+
payload.pop("stop", None)
|
| 125 |
+
payload.pop("temperature", None)
|
| 126 |
+
return payload
|
| 127 |
+
|
| 128 |
+
return _ChatOpenAIResponsesNoStop(
|
| 129 |
+
model=model,
|
| 130 |
+
temperature=1, # Set to default value for gpt-5, will be removed in payload
|
| 131 |
+
stop_sequences=stop_sequences,
|
| 132 |
+
use_responses_api=True,
|
| 133 |
+
output_version="v0",
|
| 134 |
+
)
|
| 135 |
+
else:
|
| 136 |
+
return ChatOpenAI(
|
| 137 |
+
model=model,
|
| 138 |
+
temperature=temperature,
|
| 139 |
+
stop_sequences=stop_sequences,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
elif source == "AzureOpenAI":
|
| 143 |
+
try:
|
| 144 |
+
from langchain_openai import AzureChatOpenAI
|
| 145 |
+
except ImportError:
|
| 146 |
+
raise ImportError( # noqa: B904
|
| 147 |
+
"langchain-openai package is required for Azure OpenAI models. Install with: pip install langchain-openai"
|
| 148 |
+
)
|
| 149 |
+
API_VERSION = "2024-12-01-preview"
|
| 150 |
+
model = model.replace("azure-", "")
|
| 151 |
+
return AzureChatOpenAI(
|
| 152 |
+
openai_api_key=os.getenv("OPENAI_API_KEY"),
|
| 153 |
+
azure_endpoint=os.getenv("OPENAI_ENDPOINT"),
|
| 154 |
+
azure_deployment=model,
|
| 155 |
+
openai_api_version=API_VERSION,
|
| 156 |
+
temperature=temperature,
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
elif source == "Anthropic":
|
| 160 |
+
try:
|
| 161 |
+
from langchain_anthropic import ChatAnthropic
|
| 162 |
+
except ImportError:
|
| 163 |
+
raise ImportError( # noqa: B904
|
| 164 |
+
"langchain-anthropic package is required for Anthropic models. Install with: pip install langchain-anthropic"
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
# Ensure ANTHROPIC_API_KEY is loaded from bash_profile if not in environment
|
| 168 |
+
if not os.environ.get("ANTHROPIC_API_KEY"):
|
| 169 |
+
try:
|
| 170 |
+
import subprocess
|
| 171 |
+
|
| 172 |
+
result = subprocess.run(
|
| 173 |
+
["bash", "-c", "source ~/.bash_profile 2>/dev/null && echo $ANTHROPIC_API_KEY"],
|
| 174 |
+
capture_output=True,
|
| 175 |
+
text=True,
|
| 176 |
+
timeout=5,
|
| 177 |
+
)
|
| 178 |
+
if result.stdout.strip():
|
| 179 |
+
os.environ["ANTHROPIC_API_KEY"] = result.stdout.strip()
|
| 180 |
+
print("✓ Loaded ANTHROPIC_API_KEY from ~/.bash_profile")
|
| 181 |
+
except Exception as e:
|
| 182 |
+
print(f"Note: Could not load ANTHROPIC_API_KEY from bash_profile: {e}")
|
| 183 |
+
|
| 184 |
+
return ChatAnthropic(
|
| 185 |
+
model=model,
|
| 186 |
+
temperature=temperature,
|
| 187 |
+
max_tokens=8192,
|
| 188 |
+
stop_sequences=stop_sequences,
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
elif source == "Gemini":
|
| 192 |
+
# If you want to use ChatGoogleGenerativeAI, you need to pass the stop sequences upon invoking the model.
|
| 193 |
+
# return ChatGoogleGenerativeAI(
|
| 194 |
+
# model=model,
|
| 195 |
+
# temperature=temperature,
|
| 196 |
+
# google_api_key=api_key,
|
| 197 |
+
# )
|
| 198 |
+
try:
|
| 199 |
+
from langchain_openai import ChatOpenAI
|
| 200 |
+
except ImportError:
|
| 201 |
+
raise ImportError( # noqa: B904
|
| 202 |
+
"langchain-openai package is required for Gemini models. Install with: pip install langchain-openai"
|
| 203 |
+
)
|
| 204 |
+
return ChatOpenAI(
|
| 205 |
+
model=model,
|
| 206 |
+
temperature=temperature,
|
| 207 |
+
api_key=os.getenv("GEMINI_API_KEY"),
|
| 208 |
+
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
| 209 |
+
stop_sequences=stop_sequences,
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
elif source == "Groq":
|
| 213 |
+
try:
|
| 214 |
+
from langchain_openai import ChatOpenAI
|
| 215 |
+
except ImportError:
|
| 216 |
+
raise ImportError( # noqa: B904
|
| 217 |
+
"langchain-openai package is required for Groq models. Install with: pip install langchain-openai"
|
| 218 |
+
)
|
| 219 |
+
return ChatOpenAI(
|
| 220 |
+
model=model,
|
| 221 |
+
temperature=temperature,
|
| 222 |
+
api_key=os.getenv("GROQ_API_KEY"),
|
| 223 |
+
base_url="https://api.groq.com/openai/v1",
|
| 224 |
+
stop_sequences=stop_sequences,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
elif source == "Ollama":
|
| 228 |
+
try:
|
| 229 |
+
from langchain_ollama import ChatOllama
|
| 230 |
+
except ImportError:
|
| 231 |
+
raise ImportError( # noqa: B904
|
| 232 |
+
"langchain-ollama package is required for Ollama models. Install with: pip install langchain-ollama"
|
| 233 |
+
)
|
| 234 |
+
return ChatOllama(
|
| 235 |
+
model=model,
|
| 236 |
+
temperature=temperature,
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
elif source == "Bedrock":
|
| 240 |
+
try:
|
| 241 |
+
from langchain_aws import ChatBedrock
|
| 242 |
+
except ImportError:
|
| 243 |
+
raise ImportError( # noqa: B904
|
| 244 |
+
"langchain-aws package is required for Bedrock models. Install with: pip install langchain-aws"
|
| 245 |
+
)
|
| 246 |
+
return ChatBedrock(
|
| 247 |
+
model=model,
|
| 248 |
+
temperature=temperature,
|
| 249 |
+
stop_sequences=stop_sequences,
|
| 250 |
+
region_name=os.getenv("AWS_REGION", "us-east-1"),
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
elif source == "Custom":
|
| 254 |
+
try:
|
| 255 |
+
from langchain_openai import ChatOpenAI
|
| 256 |
+
except ImportError:
|
| 257 |
+
raise ImportError( # noqa: B904
|
| 258 |
+
"langchain-openai package is required for custom models. Install with: pip install langchain-openai"
|
| 259 |
+
)
|
| 260 |
+
# Custom LLM serving such as SGLang. Must expose an openai compatible API.
|
| 261 |
+
assert base_url is not None, "base_url must be provided for customly served LLMs"
|
| 262 |
+
llm = ChatOpenAI(
|
| 263 |
+
model=model,
|
| 264 |
+
temperature=temperature,
|
| 265 |
+
max_tokens=8192,
|
| 266 |
+
stop_sequences=stop_sequences,
|
| 267 |
+
base_url=base_url,
|
| 268 |
+
api_key=api_key,
|
| 269 |
+
)
|
| 270 |
+
return llm
|
| 271 |
+
|
| 272 |
+
else:
|
| 273 |
+
raise ValueError(
|
| 274 |
+
f"Invalid source: {source}. Valid options are 'OpenAI', 'AzureOpenAI', 'Anthropic', 'Gemini', 'Groq', 'Bedrock', or 'Ollama'"
|
| 275 |
+
)
|
biomni/utils.py
ADDED
|
@@ -0,0 +1,2366 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ast
|
| 2 |
+
import enum
|
| 3 |
+
import importlib
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
import pickle
|
| 7 |
+
import subprocess
|
| 8 |
+
import tempfile
|
| 9 |
+
import traceback
|
| 10 |
+
import zipfile
|
| 11 |
+
from typing import Any, ClassVar
|
| 12 |
+
from urllib.parse import urljoin
|
| 13 |
+
|
| 14 |
+
import pandas as pd
|
| 15 |
+
import requests
|
| 16 |
+
import tqdm # Add tqdm for progress bar
|
| 17 |
+
from langchain_core.callbacks import BaseCallbackHandler
|
| 18 |
+
from langchain_core.messages.base import get_msg_title_repr
|
| 19 |
+
from langchain_core.tools import StructuredTool
|
| 20 |
+
from langchain_core.utils.interactive_env import is_interactive_env
|
| 21 |
+
from pydantic import BaseModel, Field, ValidationError
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Add these new functions for running R code and CLI commands
|
| 25 |
+
def run_r_code(code: str) -> str:
|
| 26 |
+
"""Run R code using subprocess.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
code: R code to run
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
Output of the R code
|
| 33 |
+
|
| 34 |
+
"""
|
| 35 |
+
try:
|
| 36 |
+
# Create a temporary file to store the R code
|
| 37 |
+
with tempfile.NamedTemporaryFile(suffix=".R", mode="w", delete=False) as f:
|
| 38 |
+
f.write(code)
|
| 39 |
+
temp_file = f.name
|
| 40 |
+
|
| 41 |
+
# Run the R code using Rscript
|
| 42 |
+
result = subprocess.run(["Rscript", temp_file], capture_output=True, text=True, check=False)
|
| 43 |
+
|
| 44 |
+
# Clean up the temporary file
|
| 45 |
+
os.unlink(temp_file)
|
| 46 |
+
|
| 47 |
+
# Return the output
|
| 48 |
+
if result.returncode != 0:
|
| 49 |
+
return f"Error running R code:\n{result.stderr}"
|
| 50 |
+
else:
|
| 51 |
+
return result.stdout
|
| 52 |
+
except Exception as e:
|
| 53 |
+
return f"Error running R code: {str(e)}"
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def run_bash_script(script: str) -> str:
|
| 57 |
+
"""Run a Bash script using subprocess.
|
| 58 |
+
|
| 59 |
+
Args:
|
| 60 |
+
script: Bash script to run
|
| 61 |
+
|
| 62 |
+
Returns:
|
| 63 |
+
Output of the Bash script
|
| 64 |
+
|
| 65 |
+
Example:
|
| 66 |
+
This is how to use the function
|
| 67 |
+
|
| 68 |
+
.. code-block:: python
|
| 69 |
+
|
| 70 |
+
# Example of a complex Bash script
|
| 71 |
+
script = '''
|
| 72 |
+
#!/bin/bash
|
| 73 |
+
|
| 74 |
+
# Define variables
|
| 75 |
+
DATA_DIR="/path/to/data"
|
| 76 |
+
OUTPUT_FILE="results.txt"
|
| 77 |
+
|
| 78 |
+
# Create output directory if it doesn't exist
|
| 79 |
+
mkdir -p $(dirname $OUTPUT_FILE)
|
| 80 |
+
|
| 81 |
+
# Loop through files
|
| 82 |
+
for file in $DATA_DIR/*.txt; do
|
| 83 |
+
echo "Processing $file..."
|
| 84 |
+
# Count lines in each file
|
| 85 |
+
line_count=$(wc -l < $file)
|
| 86 |
+
echo "$file: $line_count lines" >> $OUTPUT_FILE
|
| 87 |
+
done
|
| 88 |
+
|
| 89 |
+
echo "Processing complete. Results saved to $OUTPUT_FILE"
|
| 90 |
+
'''
|
| 91 |
+
result = run_bash_script(script)
|
| 92 |
+
print(result)
|
| 93 |
+
|
| 94 |
+
"""
|
| 95 |
+
try:
|
| 96 |
+
# Trim any leading/trailing whitespace
|
| 97 |
+
script = script.strip()
|
| 98 |
+
|
| 99 |
+
# If the script is empty, return an error
|
| 100 |
+
if not script:
|
| 101 |
+
return "Error: Empty script"
|
| 102 |
+
|
| 103 |
+
# Create a temporary file to store the Bash script
|
| 104 |
+
with tempfile.NamedTemporaryFile(suffix=".sh", mode="w", delete=False) as f:
|
| 105 |
+
# Add shebang if not present
|
| 106 |
+
if not script.startswith("#!/"):
|
| 107 |
+
f.write("#!/bin/bash\n")
|
| 108 |
+
# Add set -e to exit on error
|
| 109 |
+
if "set -e" not in script:
|
| 110 |
+
f.write("set -e\n")
|
| 111 |
+
f.write(script)
|
| 112 |
+
temp_file = f.name
|
| 113 |
+
|
| 114 |
+
# Make the script executable
|
| 115 |
+
os.chmod(temp_file, 0o755)
|
| 116 |
+
|
| 117 |
+
# Get current environment variables and working directory
|
| 118 |
+
env = os.environ.copy()
|
| 119 |
+
cwd = os.getcwd()
|
| 120 |
+
|
| 121 |
+
# Run the Bash script with the current environment and working directory
|
| 122 |
+
result = subprocess.run(
|
| 123 |
+
[temp_file],
|
| 124 |
+
shell=True,
|
| 125 |
+
capture_output=True,
|
| 126 |
+
text=True,
|
| 127 |
+
check=False,
|
| 128 |
+
env=env,
|
| 129 |
+
cwd=cwd,
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# Clean up the temporary file
|
| 133 |
+
os.unlink(temp_file)
|
| 134 |
+
|
| 135 |
+
# Return the output
|
| 136 |
+
if result.returncode != 0:
|
| 137 |
+
traceback.print_stack()
|
| 138 |
+
print(result)
|
| 139 |
+
return f"Error running Bash script (exit code {result.returncode}):\n{result.stderr}"
|
| 140 |
+
else:
|
| 141 |
+
return result.stdout
|
| 142 |
+
except Exception as e:
|
| 143 |
+
traceback.print_exc()
|
| 144 |
+
return f"Error running Bash script: {str(e)}"
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
# Keep the run_cli_command for backward compatibility
|
| 148 |
+
def run_cli_command(command: str) -> str:
|
| 149 |
+
"""Run a CLI command using subprocess.
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
command: CLI command to run
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
Output of the CLI command
|
| 156 |
+
|
| 157 |
+
"""
|
| 158 |
+
try:
|
| 159 |
+
# Trim any leading/trailing whitespace
|
| 160 |
+
command = command.strip()
|
| 161 |
+
|
| 162 |
+
# If the command is empty, return an error
|
| 163 |
+
if not command:
|
| 164 |
+
return "Error: Empty command"
|
| 165 |
+
|
| 166 |
+
# Split the command into a list of arguments, handling quoted arguments correctly
|
| 167 |
+
import shlex
|
| 168 |
+
|
| 169 |
+
args = shlex.split(command)
|
| 170 |
+
|
| 171 |
+
# Run the command
|
| 172 |
+
result = subprocess.run(args, capture_output=True, text=True, check=False)
|
| 173 |
+
|
| 174 |
+
# Return the output
|
| 175 |
+
if result.returncode != 0:
|
| 176 |
+
return f"Error running command '{command}':\n{result.stderr}"
|
| 177 |
+
else:
|
| 178 |
+
return result.stdout
|
| 179 |
+
except Exception as e:
|
| 180 |
+
return f"Error running command '{command}': {str(e)}"
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def run_with_timeout(func, args=None, kwargs=None, timeout=600):
|
| 184 |
+
"""Run a function with a timeout using threading instead of multiprocessing.
|
| 185 |
+
This allows variables to persist in the global namespace between function calls.
|
| 186 |
+
Returns the function result or a timeout error message.
|
| 187 |
+
"""
|
| 188 |
+
if args is None:
|
| 189 |
+
args = []
|
| 190 |
+
if kwargs is None:
|
| 191 |
+
kwargs = {}
|
| 192 |
+
|
| 193 |
+
import ctypes
|
| 194 |
+
import queue
|
| 195 |
+
import threading
|
| 196 |
+
|
| 197 |
+
result_queue = queue.Queue()
|
| 198 |
+
|
| 199 |
+
def thread_func(func, args, kwargs, result_queue):
|
| 200 |
+
"""Function to run in a separate thread."""
|
| 201 |
+
try:
|
| 202 |
+
result = func(*args, **kwargs)
|
| 203 |
+
result_queue.put(("success", result))
|
| 204 |
+
except Exception as e:
|
| 205 |
+
result_queue.put(("error", str(e)))
|
| 206 |
+
|
| 207 |
+
# Start a separate thread
|
| 208 |
+
thread = threading.Thread(target=thread_func, args=(func, args, kwargs, result_queue))
|
| 209 |
+
thread.daemon = True # Set as daemon so it will be killed when main thread exits
|
| 210 |
+
thread.start()
|
| 211 |
+
|
| 212 |
+
# Wait for the specified timeout
|
| 213 |
+
thread.join(timeout)
|
| 214 |
+
|
| 215 |
+
# Check if the thread is still running after timeout
|
| 216 |
+
if thread.is_alive():
|
| 217 |
+
print(f"TIMEOUT: Code execution timed out after {timeout} seconds")
|
| 218 |
+
|
| 219 |
+
# Unfortunately, there's no clean way to force terminate a thread in Python
|
| 220 |
+
# The recommended approach is to use daemon threads and let them be killed when main thread exits
|
| 221 |
+
# Here, we'll try to raise an exception in the thread to make it stop
|
| 222 |
+
try:
|
| 223 |
+
# Get thread ID and try to terminate it
|
| 224 |
+
thread_id = thread.ident
|
| 225 |
+
if thread_id:
|
| 226 |
+
# This is a bit dangerous and not 100% reliable
|
| 227 |
+
# It attempts to raise a SystemExit exception in the thread
|
| 228 |
+
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread_id), ctypes.py_object(SystemExit))
|
| 229 |
+
if res > 1:
|
| 230 |
+
# Oops, we raised too many exceptions
|
| 231 |
+
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread_id), None)
|
| 232 |
+
except Exception as e:
|
| 233 |
+
print(f"Error trying to terminate thread: {e}")
|
| 234 |
+
|
| 235 |
+
return f"ERROR: Code execution timed out after {timeout} seconds. Please try with simpler inputs or break your task into smaller steps."
|
| 236 |
+
|
| 237 |
+
# Get the result from the queue if available
|
| 238 |
+
try:
|
| 239 |
+
status, result = result_queue.get(block=False)
|
| 240 |
+
return result if status == "success" else f"Error in execution: {result}"
|
| 241 |
+
except queue.Empty:
|
| 242 |
+
return "Error: Execution completed but no result was returned"
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
class api_schema(BaseModel):
|
| 246 |
+
"""api schema specification."""
|
| 247 |
+
|
| 248 |
+
api_schema: str | None = Field(description="The api schema as a dictionary")
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def function_to_api_schema(function_string, llm):
|
| 252 |
+
prompt = """
|
| 253 |
+
Based on a code snippet and help me write an API docstring in the format like this:
|
| 254 |
+
|
| 255 |
+
{{'name': 'get_gene_set_enrichment',
|
| 256 |
+
'description': 'Given a list of genes, identify a pathway that is enriched for this gene set. Return a list of pathway name, p-value, z-scores.',
|
| 257 |
+
'required_parameters': [{{'name': 'genes',
|
| 258 |
+
'type': 'List[str]',
|
| 259 |
+
'description': 'List of g`ene symbols to analyze',
|
| 260 |
+
'default': None}}],
|
| 261 |
+
'optional_parameters': [{{'name': 'top_k',
|
| 262 |
+
'type': 'int',
|
| 263 |
+
'description': 'Top K pathways to return',
|
| 264 |
+
'default': 10}}, {{'name': 'database',
|
| 265 |
+
'type': 'str',
|
| 266 |
+
'description': 'Name of the database to use for enrichment analysis',
|
| 267 |
+
'default': "gene_ontology"}}]}}
|
| 268 |
+
|
| 269 |
+
Strictly follow the input from the function - don't create fake optional parameters.
|
| 270 |
+
For variable without default values, set them as None, not null.
|
| 271 |
+
For variable with boolean values, use capitalized True or False, not true or false.
|
| 272 |
+
Do not add any return type in the docstring.
|
| 273 |
+
Be as clear and succint as possible for the descriptions. Please do not make it overly verbose.
|
| 274 |
+
Here is the code snippet:
|
| 275 |
+
{code}
|
| 276 |
+
"""
|
| 277 |
+
llm = llm.with_structured_output(api_schema)
|
| 278 |
+
|
| 279 |
+
for _ in range(7):
|
| 280 |
+
try:
|
| 281 |
+
api = llm.invoke(prompt.format(code=function_string)).dict()["api_schema"]
|
| 282 |
+
return ast.literal_eval(api) # -> prefer "default": None
|
| 283 |
+
# return json.loads(api) # -> prefer "default": null
|
| 284 |
+
except Exception as e:
|
| 285 |
+
print("API string:", api)
|
| 286 |
+
print("Error parsing the API string:", e)
|
| 287 |
+
continue
|
| 288 |
+
|
| 289 |
+
return "Error: Could not parse the API schema"
|
| 290 |
+
# return
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def get_all_functions_from_file(file_path):
|
| 294 |
+
with open(file_path) as file:
|
| 295 |
+
file_content = file.read()
|
| 296 |
+
|
| 297 |
+
# Parse the file content into an AST (Abstract Syntax Tree)
|
| 298 |
+
tree = ast.parse(file_content)
|
| 299 |
+
|
| 300 |
+
# List to hold the top-level functions as strings
|
| 301 |
+
functions = []
|
| 302 |
+
|
| 303 |
+
# Walk through the AST nodes
|
| 304 |
+
for node in tree.body: # Only consider top-level nodes in the body
|
| 305 |
+
if isinstance(node, ast.FunctionDef): # Check if the node is a function definition
|
| 306 |
+
# Skip if function name starts with underscore
|
| 307 |
+
if node.name.startswith("_"):
|
| 308 |
+
continue
|
| 309 |
+
|
| 310 |
+
start_line = node.lineno - 1 # Get the starting line of the function
|
| 311 |
+
end_line = node.end_lineno # Get the ending line of the function (only available in Python 3.8+)
|
| 312 |
+
func_code = file_content.splitlines()[start_line:end_line]
|
| 313 |
+
functions.append("\n".join(func_code)) # Join lines of the function and add to the list
|
| 314 |
+
|
| 315 |
+
return functions
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def write_python_code(request: str):
|
| 319 |
+
from langchain_anthropic import ChatAnthropic
|
| 320 |
+
from langchain_core.output_parsers import StrOutputParser
|
| 321 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 322 |
+
|
| 323 |
+
model = ChatAnthropic(model="claude-3-5-sonnet-20240620")
|
| 324 |
+
template = """Write some python code to solve the user's problem.
|
| 325 |
+
|
| 326 |
+
Return only python code in Markdown format, e.g.:
|
| 327 |
+
|
| 328 |
+
```python
|
| 329 |
+
....
|
| 330 |
+
```"""
|
| 331 |
+
prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")])
|
| 332 |
+
|
| 333 |
+
def _sanitize_output(text: str):
|
| 334 |
+
_, after = text.split("```python")
|
| 335 |
+
return after.split("```")[0]
|
| 336 |
+
|
| 337 |
+
chain = prompt | model | StrOutputParser() | _sanitize_output
|
| 338 |
+
return chain.invoke({"input": "write a code that " + request})
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
def execute_graphql_query(
|
| 342 |
+
query: str,
|
| 343 |
+
variables: dict,
|
| 344 |
+
api_address: str = "https://api.genetics.opentargets.org/graphql",
|
| 345 |
+
) -> dict:
|
| 346 |
+
"""Executes a GraphQL query with variables and returns the data as a dictionary."""
|
| 347 |
+
headers = {"Content-Type": "application/json"}
|
| 348 |
+
response = requests.post(api_address, json={"query": query, "variables": variables}, headers=headers)
|
| 349 |
+
if response.status_code == 200:
|
| 350 |
+
return response.json()
|
| 351 |
+
else:
|
| 352 |
+
print(response.text)
|
| 353 |
+
response.raise_for_status()
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
def get_tool_decorated_functions(relative_path):
|
| 357 |
+
import ast
|
| 358 |
+
import importlib.util
|
| 359 |
+
import os
|
| 360 |
+
|
| 361 |
+
# Get the directory of the current file (__init__.py)
|
| 362 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 363 |
+
|
| 364 |
+
# Construct the absolute path from the relative path
|
| 365 |
+
file_path = os.path.join(current_dir, relative_path)
|
| 366 |
+
|
| 367 |
+
with open(file_path) as file:
|
| 368 |
+
tree = ast.parse(file.read(), filename=file_path)
|
| 369 |
+
|
| 370 |
+
tool_function_names = []
|
| 371 |
+
|
| 372 |
+
for node in ast.walk(tree):
|
| 373 |
+
if isinstance(node, ast.FunctionDef):
|
| 374 |
+
for decorator in node.decorator_list:
|
| 375 |
+
if (
|
| 376 |
+
isinstance(decorator, ast.Name)
|
| 377 |
+
and decorator.id == "tool"
|
| 378 |
+
or (
|
| 379 |
+
isinstance(decorator, ast.Call)
|
| 380 |
+
and isinstance(decorator.func, ast.Name)
|
| 381 |
+
and decorator.func.id == "tool"
|
| 382 |
+
)
|
| 383 |
+
):
|
| 384 |
+
tool_function_names.append(node.name)
|
| 385 |
+
|
| 386 |
+
# Calculate the module name from the relative path
|
| 387 |
+
package_path = os.path.relpath(file_path, start=current_dir)
|
| 388 |
+
module_name = package_path.replace(os.path.sep, ".").rsplit(".", 1)[0]
|
| 389 |
+
|
| 390 |
+
# Import the module and get the function objects
|
| 391 |
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
| 392 |
+
module = importlib.util.module_from_spec(spec)
|
| 393 |
+
spec.loader.exec_module(module)
|
| 394 |
+
|
| 395 |
+
tool_functions = [getattr(module, name) for name in tool_function_names]
|
| 396 |
+
|
| 397 |
+
return tool_functions
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def process_bio_retrieval_ducoment(documents_df):
|
| 401 |
+
ir_corpus = {}
|
| 402 |
+
corpus2tool = {}
|
| 403 |
+
for row in documents_df.itertuples():
|
| 404 |
+
doc = row.document_content
|
| 405 |
+
ir_corpus[row.docid] = (
|
| 406 |
+
(doc.get("name", "") or "")
|
| 407 |
+
+ ", "
|
| 408 |
+
+ (doc.get("description", "") or "")
|
| 409 |
+
+ ", "
|
| 410 |
+
+ (doc.get("url", "") or "")
|
| 411 |
+
+ ", "
|
| 412 |
+
+ ", required_params: "
|
| 413 |
+
+ json.dumps(doc.get("required_parameters", ""))
|
| 414 |
+
+ ", optional_params: "
|
| 415 |
+
+ json.dumps(doc.get("optional_parameters", ""))
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
corpus2tool[
|
| 419 |
+
(doc.get("name", "") or "")
|
| 420 |
+
+ ", "
|
| 421 |
+
+ (doc.get("description", "") or "")
|
| 422 |
+
+ ", "
|
| 423 |
+
+ (doc.get("url", "") or "")
|
| 424 |
+
+ ", "
|
| 425 |
+
+ ", required_params: "
|
| 426 |
+
+ json.dumps(doc.get("required_parameters", ""))
|
| 427 |
+
+ ", optional_params: "
|
| 428 |
+
+ json.dumps(doc.get("optional_parameters", ""))
|
| 429 |
+
] = doc["name"]
|
| 430 |
+
return ir_corpus, corpus2tool
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
def load_pickle(file):
|
| 434 |
+
import pickle
|
| 435 |
+
|
| 436 |
+
with open(file, "rb") as f:
|
| 437 |
+
return pickle.load(f)
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
def pretty_print(message, printout=True):
|
| 441 |
+
if isinstance(message, tuple):
|
| 442 |
+
title = message
|
| 443 |
+
elif isinstance(message.content, list):
|
| 444 |
+
title = get_msg_title_repr(message.type.title().upper() + " Message", bold=is_interactive_env())
|
| 445 |
+
if message.name is not None:
|
| 446 |
+
title += f"\nName: {message.name}"
|
| 447 |
+
|
| 448 |
+
for i in message.content:
|
| 449 |
+
if i["type"] == "text":
|
| 450 |
+
title += f"\n{i['text']}\n"
|
| 451 |
+
elif i["type"] == "tool_use":
|
| 452 |
+
title += f"\nTool: {i['name']}"
|
| 453 |
+
title += f"\nInput: {i['input']}"
|
| 454 |
+
if printout:
|
| 455 |
+
print(f"{title}")
|
| 456 |
+
else:
|
| 457 |
+
title = get_msg_title_repr(message.type.title() + " Message", bold=is_interactive_env())
|
| 458 |
+
if message.name is not None:
|
| 459 |
+
title += f"\nName: {message.name}"
|
| 460 |
+
title += f"\n\n{message.content}"
|
| 461 |
+
if printout:
|
| 462 |
+
print(f"{title}")
|
| 463 |
+
return title
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
class CustomBaseModel(BaseModel):
|
| 467 |
+
api_schema: ClassVar[dict] = None # Class variable to store api_schema
|
| 468 |
+
|
| 469 |
+
# Add model_config with arbitrary_types_allowed=True
|
| 470 |
+
model_config = {"arbitrary_types_allowed": True}
|
| 471 |
+
|
| 472 |
+
@classmethod
|
| 473 |
+
def set_api_schema(cls, schema: dict):
|
| 474 |
+
cls.api_schema = schema
|
| 475 |
+
|
| 476 |
+
@classmethod
|
| 477 |
+
def model_validate(cls, obj):
|
| 478 |
+
try:
|
| 479 |
+
return super().model_validate(obj)
|
| 480 |
+
except (ValidationError, AttributeError) as e:
|
| 481 |
+
if not cls.api_schema:
|
| 482 |
+
raise e # If no api_schema is set, raise original error
|
| 483 |
+
|
| 484 |
+
error_msg = "Required Parameters:\n"
|
| 485 |
+
for param in cls.api_schema["required_parameters"]:
|
| 486 |
+
error_msg += f"- {param['name']} ({param['type']}): {param['description']}\n"
|
| 487 |
+
|
| 488 |
+
error_msg += "\nErrors:\n"
|
| 489 |
+
for err in e.errors():
|
| 490 |
+
field = err["loc"][0] if err["loc"] else "input"
|
| 491 |
+
error_msg += f"- {field}: {err['msg']}\n"
|
| 492 |
+
|
| 493 |
+
if not obj:
|
| 494 |
+
error_msg += "\nNo input provided"
|
| 495 |
+
else:
|
| 496 |
+
error_msg += "\nProvided Input:\n"
|
| 497 |
+
for key, value in obj.items():
|
| 498 |
+
error_msg += f"- {key}: {value}\n"
|
| 499 |
+
|
| 500 |
+
missing_params = {param["name"] for param in cls.api_schema["required_parameters"]} - set(obj.keys())
|
| 501 |
+
if missing_params:
|
| 502 |
+
error_msg += "\nMissing Parameters:\n"
|
| 503 |
+
for param in missing_params:
|
| 504 |
+
error_msg += f"- {param}\n"
|
| 505 |
+
|
| 506 |
+
# # Create proper validation error structure
|
| 507 |
+
raise ValidationError.from_exception_data(
|
| 508 |
+
title="Validation Error",
|
| 509 |
+
line_errors=[
|
| 510 |
+
{
|
| 511 |
+
"type": "value_error",
|
| 512 |
+
"loc": ("input",),
|
| 513 |
+
"input": obj,
|
| 514 |
+
"ctx": {
|
| 515 |
+
"error": error_msg,
|
| 516 |
+
},
|
| 517 |
+
}
|
| 518 |
+
],
|
| 519 |
+
) from None
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
def safe_execute_decorator(func):
|
| 523 |
+
def wrapper(*args, **kwargs):
|
| 524 |
+
try:
|
| 525 |
+
return func(*args, **kwargs)
|
| 526 |
+
except Exception as e:
|
| 527 |
+
return str(e)
|
| 528 |
+
|
| 529 |
+
return wrapper
|
| 530 |
+
|
| 531 |
+
|
| 532 |
+
def api_schema_to_langchain_tool(api_schema, mode="generated_tool", module_name=None):
|
| 533 |
+
if mode == "generated_tool":
|
| 534 |
+
module = importlib.import_module("biomni.tool.generated_tool." + api_schema["tool_name"] + ".api")
|
| 535 |
+
elif mode == "custom_tool":
|
| 536 |
+
module = importlib.import_module(module_name)
|
| 537 |
+
|
| 538 |
+
api_function = getattr(module, api_schema["name"])
|
| 539 |
+
api_function = safe_execute_decorator(api_function)
|
| 540 |
+
|
| 541 |
+
# Define a mapping from string type names to actual Python type objects
|
| 542 |
+
type_mapping = {
|
| 543 |
+
"string": str,
|
| 544 |
+
"integer": int,
|
| 545 |
+
"boolean": bool,
|
| 546 |
+
"pandas": pd.DataFrame, # Use the imported pandas.DataFrame directly
|
| 547 |
+
"str": str,
|
| 548 |
+
"int": int,
|
| 549 |
+
"bool": bool,
|
| 550 |
+
"List[str]": list[str],
|
| 551 |
+
"List[int]": list[int],
|
| 552 |
+
"Dict": dict,
|
| 553 |
+
"Any": Any,
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
# Create the fields and annotations
|
| 557 |
+
annotations = {}
|
| 558 |
+
for param in api_schema["required_parameters"]:
|
| 559 |
+
param_type = param["type"]
|
| 560 |
+
if param_type in type_mapping:
|
| 561 |
+
annotations[param["name"]] = type_mapping[param_type]
|
| 562 |
+
else:
|
| 563 |
+
# For types not in the mapping, try a safer approach than direct eval
|
| 564 |
+
try:
|
| 565 |
+
annotations[param["name"]] = eval(param_type)
|
| 566 |
+
except (NameError, SyntaxError):
|
| 567 |
+
# Default to Any for unknown types
|
| 568 |
+
annotations[param["name"]] = Any
|
| 569 |
+
|
| 570 |
+
fields = {param["name"]: Field(description=param["description"]) for param in api_schema["required_parameters"]}
|
| 571 |
+
|
| 572 |
+
# Create the ApiInput class dynamically
|
| 573 |
+
ApiInput = type("Input", (CustomBaseModel,), {"__annotations__": annotations, **fields})
|
| 574 |
+
# Set the api_schema
|
| 575 |
+
ApiInput.set_api_schema(api_schema)
|
| 576 |
+
|
| 577 |
+
# Create the StructuredTool
|
| 578 |
+
api_tool = StructuredTool.from_function(
|
| 579 |
+
func=api_function,
|
| 580 |
+
name=api_schema["name"],
|
| 581 |
+
description=api_schema["description"],
|
| 582 |
+
args_schema=ApiInput,
|
| 583 |
+
return_direct=True,
|
| 584 |
+
)
|
| 585 |
+
|
| 586 |
+
return api_tool
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
class ID(enum.Enum):
|
| 590 |
+
ENTREZ = "Entrez"
|
| 591 |
+
ENSEMBL = "Ensembl without version" # e.g. ENSG00000123374
|
| 592 |
+
ENSEMBL_W_VERSION = "Ensembl with version" # e.g. ENSG00000123374.10 (needed for GTEx)
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
def get_gene_id(gene_symbol: str, id_type: ID):
|
| 596 |
+
"""Get the ID for a gene symbol. If no match found, returns None."""
|
| 597 |
+
if id_type == ID.ENTREZ:
|
| 598 |
+
return _get_gene_id_entrez(gene_symbol)
|
| 599 |
+
elif id_type == ID.ENSEMBL:
|
| 600 |
+
return _get_gene_id_ensembl(gene_symbol)
|
| 601 |
+
elif id_type == ID.ENSEMBL_W_VERSION:
|
| 602 |
+
return _get_gene_id_ensembl_with_version(gene_symbol)
|
| 603 |
+
else:
|
| 604 |
+
raise ValueError(f"ID type {id_type} not supported")
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
def _get_gene_id_entrez(gene_symbol: str):
|
| 608 |
+
"""Get the Entrez ID for a gene symbol. If no match found, returns None
|
| 609 |
+
e.g. 1017 (CDK2).
|
| 610 |
+
"""
|
| 611 |
+
api_call = f"https://mygene.info/v3/query?species=human&q=symbol:{gene_symbol}"
|
| 612 |
+
response = requests.get(api_call)
|
| 613 |
+
response_json = response.json()
|
| 614 |
+
|
| 615 |
+
if len(response_json["hits"]) == 0:
|
| 616 |
+
return None
|
| 617 |
+
else:
|
| 618 |
+
return response_json["hits"][0]["entrezgene"]
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
def _get_gene_id_ensembl(gene_symbol):
|
| 622 |
+
"""Get the Ensembl ID for a gene symbol. If no match found, returns None
|
| 623 |
+
e.g. ENSG00000123374.
|
| 624 |
+
"""
|
| 625 |
+
api_call = f"https://mygene.info/v3/query?species=human&fields=ensembl&q=symbol:{gene_symbol}"
|
| 626 |
+
response = requests.get(api_call)
|
| 627 |
+
response_json = response.json()
|
| 628 |
+
|
| 629 |
+
if len(response_json["hits"]) == 0:
|
| 630 |
+
return None
|
| 631 |
+
else:
|
| 632 |
+
ensembl = response_json["hits"][0]["ensembl"]
|
| 633 |
+
if isinstance(ensembl, list):
|
| 634 |
+
return ensembl[0][
|
| 635 |
+
"gene"
|
| 636 |
+
] # Sometimes returns a list, for example RNH1 (first elem is on chr11, second is on scaffold_hschr11)
|
| 637 |
+
else:
|
| 638 |
+
return ensembl["gene"]
|
| 639 |
+
|
| 640 |
+
|
| 641 |
+
def _get_gene_id_ensembl_with_version(gene_symbol):
|
| 642 |
+
"""Get the Ensembl ID for a gene symbol. If no match found, returns None
|
| 643 |
+
e.g. ENSG00000123374.10.
|
| 644 |
+
"""
|
| 645 |
+
api_base = "https://gtexportal.org/api/v2/reference/gene"
|
| 646 |
+
params = {"geneId": gene_symbol}
|
| 647 |
+
response_json = requests.get(api_base, params=params).json()
|
| 648 |
+
|
| 649 |
+
if len(response_json["data"]) == 0:
|
| 650 |
+
return None
|
| 651 |
+
else:
|
| 652 |
+
return response_json["data"][0]["gencodeId"]
|
| 653 |
+
|
| 654 |
+
|
| 655 |
+
def save_pkl(f, filename):
|
| 656 |
+
with open(filename, "wb") as file:
|
| 657 |
+
pickle.dump(f, file)
|
| 658 |
+
|
| 659 |
+
|
| 660 |
+
def load_pkl(filename):
|
| 661 |
+
with open(filename, "rb") as file:
|
| 662 |
+
return pickle.load(file)
|
| 663 |
+
|
| 664 |
+
|
| 665 |
+
_TEXT_COLOR_MAPPING = {
|
| 666 |
+
"blue": "36;1",
|
| 667 |
+
"yellow": "33;1",
|
| 668 |
+
"pink": "38;5;200",
|
| 669 |
+
"green": "32;1",
|
| 670 |
+
"red": "31;1",
|
| 671 |
+
}
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
def color_print(text, color="blue"):
|
| 675 |
+
color_str = _TEXT_COLOR_MAPPING[color]
|
| 676 |
+
print(f"\u001b[{color_str}m\033[1;3m{text}\u001b[0m")
|
| 677 |
+
|
| 678 |
+
|
| 679 |
+
class PromptLogger(BaseCallbackHandler):
|
| 680 |
+
def on_chat_model_start(self, serialized, messages, **kwargs):
|
| 681 |
+
for message in messages[0]:
|
| 682 |
+
color_print(message.pretty_repr(), color="green")
|
| 683 |
+
|
| 684 |
+
|
| 685 |
+
class NodeLogger(BaseCallbackHandler):
|
| 686 |
+
def on_llm_end(self, response, **kwargs): # response of type LLMResult
|
| 687 |
+
for generations in response.generations: # response.generations of type List[List[Generations]] becuase "each input could have multiple candidate generations"
|
| 688 |
+
for generation in generations:
|
| 689 |
+
generated_text = generation.message.content
|
| 690 |
+
# token_usage = generation.message.response_metadata["token_usage"]
|
| 691 |
+
color_print(generated_text, color="yellow")
|
| 692 |
+
|
| 693 |
+
def on_agent_action(self, action, **kwargs):
|
| 694 |
+
color_print(action.log, color="pink")
|
| 695 |
+
|
| 696 |
+
def on_agent_finish(self, finish, **kwargs):
|
| 697 |
+
color_print(finish, color="red")
|
| 698 |
+
|
| 699 |
+
def on_tool_start(self, serialized, input_str, **kwargs):
|
| 700 |
+
tool_name = serialized.get("name")
|
| 701 |
+
color_print(f"Calling {tool_name} with inputs: {input_str}", color="pink")
|
| 702 |
+
|
| 703 |
+
def on_tool_end(self, output, **kwargs):
|
| 704 |
+
output = str(output)
|
| 705 |
+
color_print(output, color="blue")
|
| 706 |
+
|
| 707 |
+
|
| 708 |
+
def check_or_create_path(path=None):
|
| 709 |
+
# Set a default path if none is provided
|
| 710 |
+
if path is None:
|
| 711 |
+
path = os.path.join(os.getcwd(), "tmp_directory")
|
| 712 |
+
|
| 713 |
+
# Check if the path exists
|
| 714 |
+
if not os.path.exists(path):
|
| 715 |
+
# If it doesn't exist, create the directory
|
| 716 |
+
os.makedirs(path)
|
| 717 |
+
print(f"Directory created at: {path}")
|
| 718 |
+
else:
|
| 719 |
+
print(f"Directory already exists at: {path}")
|
| 720 |
+
|
| 721 |
+
return path
|
| 722 |
+
|
| 723 |
+
|
| 724 |
+
def langchain_to_gradio_message(message):
|
| 725 |
+
# Build the title and content based on the message type
|
| 726 |
+
if isinstance(message.content, list):
|
| 727 |
+
# For a message with multiple content items (like text and tool use)
|
| 728 |
+
gradio_messages = []
|
| 729 |
+
for item in message.content:
|
| 730 |
+
gradio_message = {
|
| 731 |
+
"role": "user" if message.type == "human" else "assistant",
|
| 732 |
+
"content": "",
|
| 733 |
+
"metadata": {},
|
| 734 |
+
}
|
| 735 |
+
|
| 736 |
+
if item["type"] == "text":
|
| 737 |
+
item["text"] = item["text"].replace("<think>", "\n")
|
| 738 |
+
item["text"] = item["text"].replace("</think>", "\n")
|
| 739 |
+
gradio_message["content"] += f"{item['text']}\n"
|
| 740 |
+
gradio_messages.append(gradio_message)
|
| 741 |
+
elif item["type"] == "tool_use":
|
| 742 |
+
if item["name"] == "run_python_repl":
|
| 743 |
+
gradio_message["metadata"]["title"] = "🛠️ Writing code..."
|
| 744 |
+
# input = "```python {code_block}```\n".format(code_block=item['input']["command"])
|
| 745 |
+
gradio_message["metadata"]["log"] = "Executing Code block..."
|
| 746 |
+
gradio_message["content"] = f"##### Code: \n ```python \n {item['input']['command']} \n``` \n"
|
| 747 |
+
else:
|
| 748 |
+
gradio_message["metadata"]["title"] = f"🛠️ Used tool ```{item['name']}```"
|
| 749 |
+
to_print = ";".join([i + ": " + str(j) for i, j in item["input"].items()])
|
| 750 |
+
gradio_message["metadata"]["log"] = f"🔍 Input -- {to_print}\n"
|
| 751 |
+
gradio_message["metadata"]["status"] = "pending"
|
| 752 |
+
gradio_messages.append(gradio_message)
|
| 753 |
+
|
| 754 |
+
else:
|
| 755 |
+
gradio_message = {
|
| 756 |
+
"role": "user" if message.type == "human" else "assistant",
|
| 757 |
+
"content": "",
|
| 758 |
+
"metadata": {},
|
| 759 |
+
}
|
| 760 |
+
print(message)
|
| 761 |
+
content = message.content
|
| 762 |
+
content = content.replace("<think>", "\n")
|
| 763 |
+
content = content.replace("</think>", "\n")
|
| 764 |
+
content = content.replace("<solution>", "\n")
|
| 765 |
+
content = content.replace("</solution>", "\n")
|
| 766 |
+
|
| 767 |
+
gradio_message["content"] = content
|
| 768 |
+
gradio_messages = [gradio_message]
|
| 769 |
+
return gradio_messages
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
def parse_hpo_obo(file_path):
|
| 773 |
+
"""Parse the HPO OBO file and create a dictionary mapping HP IDs to phenotype descriptions.
|
| 774 |
+
|
| 775 |
+
Args:
|
| 776 |
+
file_path (str): Path to the HPO OBO file.
|
| 777 |
+
|
| 778 |
+
Returns:
|
| 779 |
+
dict: A dictionary where keys are HP IDs and values are phenotype descriptions.
|
| 780 |
+
|
| 781 |
+
"""
|
| 782 |
+
hp_dict = {}
|
| 783 |
+
current_id = None
|
| 784 |
+
current_name = None
|
| 785 |
+
|
| 786 |
+
with open(file_path) as file:
|
| 787 |
+
for line in file:
|
| 788 |
+
line = line.strip()
|
| 789 |
+
if line.startswith("[Term]"):
|
| 790 |
+
# If a new term block starts, save the previous term
|
| 791 |
+
if current_id and current_name:
|
| 792 |
+
hp_dict[current_id] = current_name
|
| 793 |
+
current_id = None
|
| 794 |
+
current_name = None
|
| 795 |
+
elif line.startswith("id: HP:"):
|
| 796 |
+
current_id = line.split(": ")[1]
|
| 797 |
+
elif line.startswith("name:"):
|
| 798 |
+
current_name = line.split(": ", 1)[1]
|
| 799 |
+
|
| 800 |
+
# Add the last term to the dictionary
|
| 801 |
+
if current_id and current_name:
|
| 802 |
+
hp_dict[current_id] = current_name
|
| 803 |
+
|
| 804 |
+
return hp_dict
|
| 805 |
+
|
| 806 |
+
|
| 807 |
+
def textify_api_dict(api_dict):
|
| 808 |
+
"""Convert a nested API dictionary to a nicely formatted string."""
|
| 809 |
+
lines = []
|
| 810 |
+
for category, methods in api_dict.items():
|
| 811 |
+
lines.append(f"Import file: {category}")
|
| 812 |
+
lines.append("=" * (len("Import file: ") + len(category)))
|
| 813 |
+
for method in methods:
|
| 814 |
+
lines.append(f"Method: {method.get('name', 'N/A')}")
|
| 815 |
+
lines.append(f" Description: {method.get('description', 'No description provided.')}")
|
| 816 |
+
|
| 817 |
+
# Process required parameters
|
| 818 |
+
req_params = method.get("required_parameters", [])
|
| 819 |
+
if req_params:
|
| 820 |
+
lines.append(" Required Parameters:")
|
| 821 |
+
for param in req_params:
|
| 822 |
+
param_name = param.get("name", "N/A")
|
| 823 |
+
param_type = param.get("type", "N/A")
|
| 824 |
+
param_desc = param.get("description", "No description")
|
| 825 |
+
param_default = param.get("default", "None")
|
| 826 |
+
lines.append(f" - {param_name} ({param_type}): {param_desc} [Default: {param_default}]")
|
| 827 |
+
|
| 828 |
+
# Process optional parameters
|
| 829 |
+
opt_params = method.get("optional_parameters", [])
|
| 830 |
+
if opt_params:
|
| 831 |
+
lines.append(" Optional Parameters:")
|
| 832 |
+
for param in opt_params:
|
| 833 |
+
param_name = param.get("name", "N/A")
|
| 834 |
+
param_type = param.get("type", "N/A")
|
| 835 |
+
param_desc = param.get("description", "No description")
|
| 836 |
+
param_default = param.get("default", "None")
|
| 837 |
+
lines.append(f" - {param_name} ({param_type}): {param_desc} [Default: {param_default}]")
|
| 838 |
+
|
| 839 |
+
lines.append("") # Empty line between methods
|
| 840 |
+
lines.append("") # Extra empty line after each category
|
| 841 |
+
|
| 842 |
+
return "\n".join(lines)
|
| 843 |
+
|
| 844 |
+
|
| 845 |
+
def read_module2api():
|
| 846 |
+
fields = [
|
| 847 |
+
"literature",
|
| 848 |
+
"biochemistry",
|
| 849 |
+
"bioimaging",
|
| 850 |
+
"bioengineering",
|
| 851 |
+
"biophysics",
|
| 852 |
+
"glycoengineering",
|
| 853 |
+
"cancer_biology",
|
| 854 |
+
"cell_biology",
|
| 855 |
+
"molecular_biology",
|
| 856 |
+
"genetics",
|
| 857 |
+
"genomics",
|
| 858 |
+
"immunology",
|
| 859 |
+
"microbiology",
|
| 860 |
+
"pathology",
|
| 861 |
+
"pharmacology",
|
| 862 |
+
"physiology",
|
| 863 |
+
"synthetic_biology",
|
| 864 |
+
"systems_biology",
|
| 865 |
+
"support_tools",
|
| 866 |
+
"database",
|
| 867 |
+
"lab_automation",
|
| 868 |
+
"protocols",
|
| 869 |
+
]
|
| 870 |
+
|
| 871 |
+
module2api = {}
|
| 872 |
+
for field in fields:
|
| 873 |
+
module_name = f"biomni.tool.tool_description.{field}"
|
| 874 |
+
module = importlib.import_module(module_name)
|
| 875 |
+
module2api[f"biomni.tool.{field}"] = module.description
|
| 876 |
+
return module2api
|
| 877 |
+
|
| 878 |
+
|
| 879 |
+
def download_and_unzip(url: str, dest_dir: str) -> str:
|
| 880 |
+
"""Download a zip file from a URL and extract it to the destination directory.
|
| 881 |
+
|
| 882 |
+
Args:
|
| 883 |
+
url: The URL to download the zip file from.
|
| 884 |
+
dest_dir: The directory to extract the contents to.
|
| 885 |
+
|
| 886 |
+
Returns:
|
| 887 |
+
The path to the extracted directory, or an error message.
|
| 888 |
+
|
| 889 |
+
"""
|
| 890 |
+
try:
|
| 891 |
+
os.makedirs(dest_dir, exist_ok=True)
|
| 892 |
+
print(f"Downloading from {url} ...")
|
| 893 |
+
with requests.get(url, stream=True) as r:
|
| 894 |
+
r.raise_for_status()
|
| 895 |
+
total_size = int(r.headers.get("content-length", 0))
|
| 896 |
+
chunk_size = 8192
|
| 897 |
+
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_file:
|
| 898 |
+
with tqdm.tqdm(
|
| 899 |
+
total=total_size / (1024**3),
|
| 900 |
+
unit="GB",
|
| 901 |
+
unit_scale=True,
|
| 902 |
+
desc="Downloading",
|
| 903 |
+
ncols=80,
|
| 904 |
+
) as pbar:
|
| 905 |
+
for chunk in r.iter_content(chunk_size=chunk_size):
|
| 906 |
+
if chunk:
|
| 907 |
+
tmp_file.write(chunk)
|
| 908 |
+
pbar.update(len(chunk) / (1024**3))
|
| 909 |
+
tmp_zip_path = tmp_file.name
|
| 910 |
+
print(f"Downloaded to {tmp_zip_path}. Extracting...")
|
| 911 |
+
with zipfile.ZipFile(tmp_zip_path, "r") as zip_ref:
|
| 912 |
+
zip_ref.extractall(dest_dir)
|
| 913 |
+
os.unlink(tmp_zip_path)
|
| 914 |
+
print(f"Extraction complete to {dest_dir}")
|
| 915 |
+
return dest_dir
|
| 916 |
+
except Exception as e:
|
| 917 |
+
print(f"Error downloading or extracting zip: {e}")
|
| 918 |
+
return f"Error: {e}"
|
| 919 |
+
|
| 920 |
+
|
| 921 |
+
def check_and_download_s3_files(
|
| 922 |
+
s3_bucket_url: str, local_data_lake_path: str, expected_files: list[str], folder: str = "data_lake"
|
| 923 |
+
) -> dict[str, bool]:
|
| 924 |
+
"""Check for missing files in the local data lake and download them from S3 bucket.
|
| 925 |
+
|
| 926 |
+
Args:
|
| 927 |
+
s3_bucket_url: Base URL of the S3 bucket (e.g., "https://biomni-release.s3.amazonaws.com")
|
| 928 |
+
local_data_lake_path: Local path to the data lake directory
|
| 929 |
+
expected_files: List of expected file names in the data lake
|
| 930 |
+
folder: S3 folder name ("data_lake" or "benchmark")
|
| 931 |
+
|
| 932 |
+
Returns:
|
| 933 |
+
Dictionary mapping file names to download success status
|
| 934 |
+
"""
|
| 935 |
+
|
| 936 |
+
os.makedirs(local_data_lake_path, exist_ok=True)
|
| 937 |
+
download_results = {}
|
| 938 |
+
|
| 939 |
+
def download_with_progress(url: str, file_path: str, desc: str) -> bool:
|
| 940 |
+
"""Download file with progress bar."""
|
| 941 |
+
try:
|
| 942 |
+
response = requests.get(url, stream=True)
|
| 943 |
+
response.raise_for_status()
|
| 944 |
+
|
| 945 |
+
total_size = int(response.headers.get("content-length", 0))
|
| 946 |
+
|
| 947 |
+
with open(file_path, "wb") as f:
|
| 948 |
+
if total_size > 0:
|
| 949 |
+
with tqdm.tqdm(total=total_size, unit="B", unit_scale=True, desc=desc, ncols=80) as pbar:
|
| 950 |
+
for chunk in response.iter_content(chunk_size=8192):
|
| 951 |
+
if chunk:
|
| 952 |
+
f.write(chunk)
|
| 953 |
+
pbar.update(len(chunk))
|
| 954 |
+
else:
|
| 955 |
+
for chunk in response.iter_content(chunk_size=8192):
|
| 956 |
+
if chunk:
|
| 957 |
+
f.write(chunk)
|
| 958 |
+
return True
|
| 959 |
+
except Exception as e:
|
| 960 |
+
print(f"✗ Failed to download {desc}: {e}")
|
| 961 |
+
if os.path.exists(file_path):
|
| 962 |
+
try:
|
| 963 |
+
os.remove(file_path)
|
| 964 |
+
except OSError:
|
| 965 |
+
pass
|
| 966 |
+
return False
|
| 967 |
+
|
| 968 |
+
def cleanup_file(file_path: str):
|
| 969 |
+
"""Clean up file if it exists."""
|
| 970 |
+
if os.path.exists(file_path):
|
| 971 |
+
try:
|
| 972 |
+
os.remove(file_path)
|
| 973 |
+
except OSError:
|
| 974 |
+
pass
|
| 975 |
+
|
| 976 |
+
# Handle benchmark folder (download as zip)
|
| 977 |
+
if folder == "benchmark":
|
| 978 |
+
print(f"Downloading entire {folder} folder structure...")
|
| 979 |
+
s3_zip_url = urljoin(s3_bucket_url + "/", folder + ".zip")
|
| 980 |
+
|
| 981 |
+
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_zip:
|
| 982 |
+
tmp_zip_path = tmp_zip.name
|
| 983 |
+
|
| 984 |
+
if download_with_progress(s3_zip_url, tmp_zip_path, f"{folder}.zip"):
|
| 985 |
+
print(f"Extracting {folder}.zip...")
|
| 986 |
+
try:
|
| 987 |
+
with zipfile.ZipFile(tmp_zip_path, "r") as zip_ref:
|
| 988 |
+
zip_ref.extractall(local_data_lake_path)
|
| 989 |
+
print(f"✓ Successfully downloaded and extracted {folder} folder")
|
| 990 |
+
download_results = dict.fromkeys(expected_files, True)
|
| 991 |
+
except Exception as e:
|
| 992 |
+
print(f"✗ Error extracting {folder}.zip: {e}")
|
| 993 |
+
download_results = dict.fromkeys(expected_files, False)
|
| 994 |
+
finally:
|
| 995 |
+
cleanup_file(tmp_zip_path)
|
| 996 |
+
else:
|
| 997 |
+
download_results = dict.fromkeys(expected_files, False)
|
| 998 |
+
|
| 999 |
+
return download_results
|
| 1000 |
+
|
| 1001 |
+
# Handle data_lake folder (download individual files)
|
| 1002 |
+
for filename in expected_files:
|
| 1003 |
+
local_file_path = os.path.join(local_data_lake_path, filename)
|
| 1004 |
+
|
| 1005 |
+
if os.path.exists(local_file_path):
|
| 1006 |
+
download_results[filename] = True
|
| 1007 |
+
continue
|
| 1008 |
+
|
| 1009 |
+
s3_file_url = urljoin(s3_bucket_url + "/" + folder + "/", filename)
|
| 1010 |
+
print(f"Downloading {filename} from {folder}...")
|
| 1011 |
+
|
| 1012 |
+
if download_with_progress(s3_file_url, local_file_path, filename):
|
| 1013 |
+
print(f"✓ Successfully downloaded: {filename}")
|
| 1014 |
+
download_results[filename] = True
|
| 1015 |
+
else:
|
| 1016 |
+
download_results[filename] = False
|
| 1017 |
+
|
| 1018 |
+
return download_results
|
| 1019 |
+
|
| 1020 |
+
|
| 1021 |
+
def clean_message_content(content: str) -> str:
|
| 1022 |
+
"""Clean message content by removing ANSI escape codes.
|
| 1023 |
+
|
| 1024 |
+
This function removes ANSI escape sequences (like color codes) from text content
|
| 1025 |
+
that might be present in terminal output or console messages. This ensures clean
|
| 1026 |
+
text for markdown generation and PDF conversion.
|
| 1027 |
+
|
| 1028 |
+
Args:
|
| 1029 |
+
content: The raw message content that may contain ANSI escape codes
|
| 1030 |
+
|
| 1031 |
+
Returns:
|
| 1032 |
+
Cleaned content with ANSI escape codes removed
|
| 1033 |
+
|
| 1034 |
+
Example:
|
| 1035 |
+
>>> clean_message_content("Hello \x1b[31mworld\x1b[0m!")
|
| 1036 |
+
"Hello world!"
|
| 1037 |
+
"""
|
| 1038 |
+
import re
|
| 1039 |
+
|
| 1040 |
+
return re.sub(r"\x1b\[[0-9;]*m", "", content)
|
| 1041 |
+
|
| 1042 |
+
|
| 1043 |
+
def should_skip_message(clean_output: str) -> bool:
|
| 1044 |
+
"""Check if message should be skipped during markdown generation.
|
| 1045 |
+
|
| 1046 |
+
This function determines whether a message should be excluded from the final
|
| 1047 |
+
markdown output. It skips empty or meaningless messages but preserves important
|
| 1048 |
+
error messages that should be displayed to users.
|
| 1049 |
+
|
| 1050 |
+
Args:
|
| 1051 |
+
clean_output: The cleaned message content to evaluate
|
| 1052 |
+
|
| 1053 |
+
Returns:
|
| 1054 |
+
True if the message should be skipped, False otherwise
|
| 1055 |
+
|
| 1056 |
+
Note:
|
| 1057 |
+
Parsing error messages are intentionally not skipped as they provide
|
| 1058 |
+
important feedback to users about conversation flow issues.
|
| 1059 |
+
"""
|
| 1060 |
+
return (
|
| 1061 |
+
clean_output.strip() in ["", "None", "null", "undefined"]
|
| 1062 |
+
# Don't skip parsing error messages - they should be displayed and increment step counter
|
| 1063 |
+
# or "There are no tags" in clean_output
|
| 1064 |
+
# or "Execution terminated due to repeated parsing errors" in clean_output
|
| 1065 |
+
)
|
| 1066 |
+
|
| 1067 |
+
|
| 1068 |
+
def has_execution_results(clean_output: str, execution_results) -> bool:
|
| 1069 |
+
"""Check if message contains code execution and has associated results.
|
| 1070 |
+
|
| 1071 |
+
This function determines whether a message contains executable code and has
|
| 1072 |
+
corresponding execution results available for display in the markdown output.
|
| 1073 |
+
|
| 1074 |
+
Args:
|
| 1075 |
+
clean_output: The cleaned message content to check for execute tags
|
| 1076 |
+
execution_results: List of execution results from the agent's execution history
|
| 1077 |
+
|
| 1078 |
+
Returns:
|
| 1079 |
+
True if the message contains <execute> tags and has execution results available
|
| 1080 |
+
"""
|
| 1081 |
+
return "<execute>" in clean_output and execution_results is not None and execution_results
|
| 1082 |
+
|
| 1083 |
+
|
| 1084 |
+
def find_matching_execution(clean_output: str, execution_results) -> dict | None:
|
| 1085 |
+
"""Find the execution result that matches the given message content.
|
| 1086 |
+
|
| 1087 |
+
This function searches through the execution results to find the one that
|
| 1088 |
+
corresponds to the current message. It matches based on the triggering message
|
| 1089 |
+
content to associate execution results with their originating AI messages.
|
| 1090 |
+
|
| 1091 |
+
Args:
|
| 1092 |
+
clean_output: The cleaned message content to match against
|
| 1093 |
+
execution_results: List of execution result dictionaries containing
|
| 1094 |
+
triggering messages and execution data
|
| 1095 |
+
|
| 1096 |
+
Returns:
|
| 1097 |
+
The matching execution result dictionary if found, None otherwise
|
| 1098 |
+
|
| 1099 |
+
Note:
|
| 1100 |
+
The matching is bidirectional - it checks if either the triggering message
|
| 1101 |
+
is contained in the current output or vice versa to handle partial matches.
|
| 1102 |
+
"""
|
| 1103 |
+
for exec_result in execution_results:
|
| 1104 |
+
if exec_result["triggering_message"] in clean_output or clean_output in exec_result["triggering_message"]:
|
| 1105 |
+
return exec_result
|
| 1106 |
+
return None
|
| 1107 |
+
|
| 1108 |
+
|
| 1109 |
+
def create_parsing_error_html() -> str:
|
| 1110 |
+
"""Create HTML markup for displaying parsing errors in markdown output.
|
| 1111 |
+
|
| 1112 |
+
This function generates a styled HTML block that displays parsing errors
|
| 1113 |
+
when the agent's response doesn't contain the required tags. The HTML
|
| 1114 |
+
uses CSS classes for consistent styling in the final PDF output.
|
| 1115 |
+
|
| 1116 |
+
Returns:
|
| 1117 |
+
HTML string containing a styled parsing error message box
|
| 1118 |
+
|
| 1119 |
+
Note:
|
| 1120 |
+
The returned HTML uses CSS classes defined in get_pdf_css_content()
|
| 1121 |
+
for consistent styling across the document.
|
| 1122 |
+
"""
|
| 1123 |
+
return """
|
| 1124 |
+
<div class="parsing-error-box">
|
| 1125 |
+
<div class="parsing-error-header">Parsing Error</div>
|
| 1126 |
+
<div class="parsing-error-content">Each response must include thinking process followed by either execute or solution tag. But there are no tags in the current response.</div>
|
| 1127 |
+
</div>
|
| 1128 |
+
"""
|
| 1129 |
+
|
| 1130 |
+
|
| 1131 |
+
def parse_tool_calls_from_code(code: str, module2api: dict, custom_functions: dict = None) -> list[str]:
|
| 1132 |
+
"""Parse code to detect imported tools by analyzing import statements.
|
| 1133 |
+
|
| 1134 |
+
This function analyzes Python code to identify which tools/functions are being
|
| 1135 |
+
imported and used. It extracts tool names from import statements and function
|
| 1136 |
+
calls, then returns a deduplicated list of detected tool names.
|
| 1137 |
+
|
| 1138 |
+
Args:
|
| 1139 |
+
code: The Python code string to analyze for tool imports
|
| 1140 |
+
module2api: Dictionary mapping module names to their available API tools
|
| 1141 |
+
custom_functions: Optional dictionary of custom functions that have been
|
| 1142 |
+
added to the agent
|
| 1143 |
+
|
| 1144 |
+
Returns:
|
| 1145 |
+
Sorted list of unique tool names detected in the code
|
| 1146 |
+
|
| 1147 |
+
Example:
|
| 1148 |
+
>>> code = "from biomni.tool import analyze_data\nimport pandas as pd"
|
| 1149 |
+
>>> parse_tool_calls_from_code(code, module2api)
|
| 1150 |
+
['analyze_data', 'pandas']
|
| 1151 |
+
"""
|
| 1152 |
+
tool_module_pairs = parse_tool_calls_with_modules(code, module2api, custom_functions)
|
| 1153 |
+
return sorted({pair[0] for pair in tool_module_pairs})
|
| 1154 |
+
|
| 1155 |
+
|
| 1156 |
+
def parse_tool_calls_with_modules(code: str, module2api: dict, custom_functions: dict = None) -> list[tuple[str, str]]:
|
| 1157 |
+
"""Parse code to detect imported tools and their associated modules.
|
| 1158 |
+
|
| 1159 |
+
This function performs detailed analysis of Python code to identify which
|
| 1160 |
+
tools/functions are being imported and which modules they belong to. It
|
| 1161 |
+
handles various import patterns including direct imports, from-imports,
|
| 1162 |
+
and module.function patterns.
|
| 1163 |
+
|
| 1164 |
+
Args:
|
| 1165 |
+
code: The Python code string to analyze for tool imports
|
| 1166 |
+
module2api: Dictionary mapping module names to their available API tools
|
| 1167 |
+
custom_functions: Optional dictionary of custom functions that have been
|
| 1168 |
+
added to the agent
|
| 1169 |
+
|
| 1170 |
+
Returns:
|
| 1171 |
+
List of tuples containing (tool_name, module_name) pairs for each
|
| 1172 |
+
detected tool and its associated module
|
| 1173 |
+
|
| 1174 |
+
Note:
|
| 1175 |
+
The function uses regex patterns to match various import statement
|
| 1176 |
+
formats and also detects direct function calls without explicit imports.
|
| 1177 |
+
"""
|
| 1178 |
+
import re
|
| 1179 |
+
|
| 1180 |
+
detected_tools = set()
|
| 1181 |
+
|
| 1182 |
+
# Get all available tools from module2api
|
| 1183 |
+
all_tools = {}
|
| 1184 |
+
for module_name, module_tools in module2api.items():
|
| 1185 |
+
for tool in module_tools:
|
| 1186 |
+
if isinstance(tool, dict) and "name" in tool:
|
| 1187 |
+
tool_name = tool["name"]
|
| 1188 |
+
if tool_name not in all_tools:
|
| 1189 |
+
all_tools[tool_name] = []
|
| 1190 |
+
all_tools[tool_name].append(module_name)
|
| 1191 |
+
|
| 1192 |
+
# Add custom tools
|
| 1193 |
+
if custom_functions:
|
| 1194 |
+
for tool_name in custom_functions.keys():
|
| 1195 |
+
if tool_name not in all_tools:
|
| 1196 |
+
all_tools[tool_name] = []
|
| 1197 |
+
all_tools[tool_name].append("custom_tools")
|
| 1198 |
+
|
| 1199 |
+
# Look for import statements in the code
|
| 1200 |
+
import_patterns = [
|
| 1201 |
+
r"from\s+([\w.]+)\s+import\s+([\w,\s]+)", # from module import tool1, tool2
|
| 1202 |
+
r"import\s+([\w.]+)", # import module
|
| 1203 |
+
]
|
| 1204 |
+
|
| 1205 |
+
for pattern in import_patterns:
|
| 1206 |
+
matches = re.findall(pattern, code)
|
| 1207 |
+
for match in matches:
|
| 1208 |
+
if len(match) == 2: # from module import tools
|
| 1209 |
+
module_name, tools_str = match
|
| 1210 |
+
# Split tools by comma and clean up
|
| 1211 |
+
tools = [tool.strip() for tool in tools_str.split(",")]
|
| 1212 |
+
|
| 1213 |
+
for tool in tools:
|
| 1214 |
+
# Check if this tool exists in any module
|
| 1215 |
+
if tool in all_tools:
|
| 1216 |
+
# Find the best matching module
|
| 1217 |
+
best_module = find_best_module_match(module_name, all_tools[tool])
|
| 1218 |
+
detected_tools.add((tool, best_module))
|
| 1219 |
+
# Also check if it's a module.function pattern
|
| 1220 |
+
elif "." in tool:
|
| 1221 |
+
parts = tool.split(".")
|
| 1222 |
+
if len(parts) == 2:
|
| 1223 |
+
module_part, func_part = parts
|
| 1224 |
+
if func_part in all_tools:
|
| 1225 |
+
best_module = find_best_module_match(module_part, all_tools[func_part])
|
| 1226 |
+
detected_tools.add((func_part, best_module))
|
| 1227 |
+
|
| 1228 |
+
elif len(match) == 1: # import module
|
| 1229 |
+
module_name = match[0]
|
| 1230 |
+
# Check if any tools from this module are used
|
| 1231 |
+
for tool_name, modules in all_tools.items():
|
| 1232 |
+
if any(module_name in mod for mod in modules):
|
| 1233 |
+
# Look for usage of this tool in the code
|
| 1234 |
+
if re.search(rf"\b{tool_name}\s*\(", code):
|
| 1235 |
+
best_module = find_best_module_match(module_name, modules)
|
| 1236 |
+
detected_tools.add((tool_name, best_module))
|
| 1237 |
+
|
| 1238 |
+
# Also look for direct function calls without imports
|
| 1239 |
+
function_call_pattern = r"(\w+)\s*\("
|
| 1240 |
+
function_calls = re.findall(function_call_pattern, code)
|
| 1241 |
+
|
| 1242 |
+
for func_call in function_calls:
|
| 1243 |
+
if func_call in all_tools:
|
| 1244 |
+
# For direct calls, use the first available module
|
| 1245 |
+
best_module = all_tools[func_call][0]
|
| 1246 |
+
detected_tools.add((func_call, best_module))
|
| 1247 |
+
|
| 1248 |
+
return sorted(detected_tools)
|
| 1249 |
+
|
| 1250 |
+
|
| 1251 |
+
def find_best_module_match(target_module: str, available_modules: list[str]) -> str:
|
| 1252 |
+
"""Find the best matching module from a list of available modules.
|
| 1253 |
+
|
| 1254 |
+
This function attempts to match a target module name against a list of
|
| 1255 |
+
available modules using various matching strategies: exact match, partial
|
| 1256 |
+
substring matches, and fallback to the first available module.
|
| 1257 |
+
|
| 1258 |
+
Args:
|
| 1259 |
+
target_module: The module name we're trying to match
|
| 1260 |
+
available_modules: List of available module names to search through
|
| 1261 |
+
|
| 1262 |
+
Returns:
|
| 1263 |
+
The best matching module name from the available modules list.
|
| 1264 |
+
Returns "unknown" if no modules are available.
|
| 1265 |
+
|
| 1266 |
+
Note:
|
| 1267 |
+
The matching strategy prioritizes exact matches, then partial matches
|
| 1268 |
+
(where either the target is contained in the module name or vice versa),
|
| 1269 |
+
and finally falls back to the first available module.
|
| 1270 |
+
"""
|
| 1271 |
+
# First try exact match
|
| 1272 |
+
if target_module in available_modules:
|
| 1273 |
+
return target_module
|
| 1274 |
+
|
| 1275 |
+
# Try partial matches
|
| 1276 |
+
for module in available_modules:
|
| 1277 |
+
if target_module in module or module in target_module:
|
| 1278 |
+
return module
|
| 1279 |
+
|
| 1280 |
+
# Return the first available module as fallback
|
| 1281 |
+
return available_modules[0] if available_modules else "unknown"
|
| 1282 |
+
|
| 1283 |
+
|
| 1284 |
+
def inject_custom_functions_to_repl(custom_functions: dict):
|
| 1285 |
+
"""Inject custom functions into the Python REPL execution environment.
|
| 1286 |
+
|
| 1287 |
+
This function makes custom tools available during code execution by injecting
|
| 1288 |
+
them into both the persistent execution namespace and the builtins module.
|
| 1289 |
+
This allows the agent to call custom functions that users have added via
|
| 1290 |
+
agent.add_tool() when executing Python code in <execute> blocks.
|
| 1291 |
+
|
| 1292 |
+
Args:
|
| 1293 |
+
custom_functions: Dictionary mapping function names to their callable objects
|
| 1294 |
+
|
| 1295 |
+
Note:
|
| 1296 |
+
The function modifies both the persistent namespace used by run_python_repl
|
| 1297 |
+
and the builtins module to ensure maximum compatibility and accessibility
|
| 1298 |
+
of custom functions during code execution.
|
| 1299 |
+
"""
|
| 1300 |
+
if custom_functions:
|
| 1301 |
+
# Access the persistent namespace used by run_python_repl
|
| 1302 |
+
from biomni.tool.support_tools import _persistent_namespace
|
| 1303 |
+
|
| 1304 |
+
# Inject all custom functions into the execution namespace
|
| 1305 |
+
for name, func in custom_functions.items():
|
| 1306 |
+
_persistent_namespace[name] = func
|
| 1307 |
+
|
| 1308 |
+
# Also make them available in builtins for broader access
|
| 1309 |
+
import builtins
|
| 1310 |
+
|
| 1311 |
+
if not hasattr(builtins, "_biomni_custom_functions"):
|
| 1312 |
+
builtins._biomni_custom_functions = {}
|
| 1313 |
+
builtins._biomni_custom_functions.update(custom_functions)
|
| 1314 |
+
|
| 1315 |
+
|
| 1316 |
+
def format_execute_tags_in_content(content: str, parse_tool_calls_with_modules_func) -> str:
|
| 1317 |
+
"""Format execute tags in content by extracting code and creating highlighted tool call blocks.
|
| 1318 |
+
|
| 1319 |
+
This function processes content that contains <execute>...</execute> tags and
|
| 1320 |
+
converts them into styled HTML blocks that display the code with syntax highlighting
|
| 1321 |
+
and information about which tools are being used.
|
| 1322 |
+
|
| 1323 |
+
Args:
|
| 1324 |
+
content: The content string that may contain <execute> tags
|
| 1325 |
+
parse_tool_calls_with_modules_func: Function to parse tool calls with modules
|
| 1326 |
+
(typically parse_tool_calls_with_modules)
|
| 1327 |
+
|
| 1328 |
+
Returns:
|
| 1329 |
+
Formatted content with execute tags converted to highlighted tool call blocks.
|
| 1330 |
+
Also processes <solution> tags in the same pass.
|
| 1331 |
+
|
| 1332 |
+
Note:
|
| 1333 |
+
The function also calls format_solution_tags_in_content() to handle
|
| 1334 |
+
solution tags in the same processing pass.
|
| 1335 |
+
"""
|
| 1336 |
+
import re
|
| 1337 |
+
|
| 1338 |
+
# Pattern to match <execute>...</execute> blocks
|
| 1339 |
+
execute_pattern = r"<execute>(.*?)</execute>"
|
| 1340 |
+
|
| 1341 |
+
def replace_execute_tag(match):
|
| 1342 |
+
code_content = match.group(1).strip()
|
| 1343 |
+
language, tool_name = detect_code_language_and_tool(code_content)
|
| 1344 |
+
code_content = clean_code_content(code_content, language)
|
| 1345 |
+
|
| 1346 |
+
# Parse tools from the code content with module information
|
| 1347 |
+
detected_tool_modules = parse_tool_calls_with_modules_func(code_content)
|
| 1348 |
+
|
| 1349 |
+
# Create the formatted block
|
| 1350 |
+
formatted_block = create_tool_call_block(code_content, language, tool_name, detected_tool_modules)
|
| 1351 |
+
return formatted_block
|
| 1352 |
+
|
| 1353 |
+
# Replace all execute tags with formatted tool call blocks
|
| 1354 |
+
formatted_content = re.sub(execute_pattern, replace_execute_tag, content, flags=re.DOTALL)
|
| 1355 |
+
|
| 1356 |
+
# Also format solution tags
|
| 1357 |
+
formatted_content = format_solution_tags_in_content(formatted_content)
|
| 1358 |
+
|
| 1359 |
+
return formatted_content
|
| 1360 |
+
|
| 1361 |
+
|
| 1362 |
+
def detect_code_language_and_tool(code_content: str) -> tuple[str, str]:
|
| 1363 |
+
"""Detect the programming language and tool name from code content.
|
| 1364 |
+
|
| 1365 |
+
This function analyzes code content to determine the programming language
|
| 1366 |
+
and appropriate tool name based on language markers at the beginning of
|
| 1367 |
+
the code block.
|
| 1368 |
+
|
| 1369 |
+
Args:
|
| 1370 |
+
code_content: The code content to analyze for language markers
|
| 1371 |
+
|
| 1372 |
+
Returns:
|
| 1373 |
+
Tuple containing (language, tool_name) where:
|
| 1374 |
+
- language: The detected programming language ("python", "r", "bash")
|
| 1375 |
+
- tool_name: The human-readable tool name for display
|
| 1376 |
+
|
| 1377 |
+
Example:
|
| 1378 |
+
>>> detect_code_language_and_tool("#!R\nlibrary(ggplot2)")
|
| 1379 |
+
("r", "R REPL")
|
| 1380 |
+
>>> detect_code_language_and_tool("#!BASH\necho 'hello'")
|
| 1381 |
+
("bash", "Bash Script")
|
| 1382 |
+
"""
|
| 1383 |
+
if code_content.startswith("#!R") or code_content.startswith("# R code") or code_content.startswith("# R script"):
|
| 1384 |
+
return "r", "R REPL"
|
| 1385 |
+
elif code_content.startswith("#!BASH") or code_content.startswith("# Bash script"):
|
| 1386 |
+
return "bash", "Bash Script"
|
| 1387 |
+
elif code_content.startswith("#!CLI"):
|
| 1388 |
+
return "bash", "CLI Command"
|
| 1389 |
+
else:
|
| 1390 |
+
return "python", "Python REPL"
|
| 1391 |
+
|
| 1392 |
+
|
| 1393 |
+
def clean_code_content(code_content: str, language: str) -> str:
|
| 1394 |
+
"""Clean code content by removing language markers.
|
| 1395 |
+
|
| 1396 |
+
This function removes language-specific markers from the beginning of code
|
| 1397 |
+
content to prepare it for display in code blocks. The markers are used
|
| 1398 |
+
internally for language detection but should not appear in the final output.
|
| 1399 |
+
|
| 1400 |
+
Args:
|
| 1401 |
+
code_content: The raw code content that may contain language markers
|
| 1402 |
+
language: The detected programming language ("python", "r", "bash")
|
| 1403 |
+
|
| 1404 |
+
Returns:
|
| 1405 |
+
Cleaned code content with language markers removed
|
| 1406 |
+
|
| 1407 |
+
Example:
|
| 1408 |
+
>>> clean_code_content("#!R\nlibrary(ggplot2)", "r")
|
| 1409 |
+
"library(ggplot2)"
|
| 1410 |
+
>>> clean_code_content("#!BASH\necho 'hello'", "bash")
|
| 1411 |
+
"echo 'hello'"
|
| 1412 |
+
"""
|
| 1413 |
+
import re
|
| 1414 |
+
|
| 1415 |
+
if language == "r":
|
| 1416 |
+
return re.sub(r"^#!R|^# R code|^# R script", "", code_content, count=1).strip()
|
| 1417 |
+
elif language == "bash":
|
| 1418 |
+
if code_content.startswith("#!BASH") or code_content.startswith("# Bash script"):
|
| 1419 |
+
return re.sub(r"^#!BASH|^# Bash script", "", code_content, count=1).strip()
|
| 1420 |
+
elif code_content.startswith("#!CLI"):
|
| 1421 |
+
return re.sub(r"^#!CLI", "", code_content, count=1).strip()
|
| 1422 |
+
return code_content
|
| 1423 |
+
|
| 1424 |
+
|
| 1425 |
+
def create_tool_call_block(code_content: str, language: str, tool_name: str, detected_tool_modules: list) -> str:
|
| 1426 |
+
"""Create the HTML block for tool call highlighting.
|
| 1427 |
+
|
| 1428 |
+
This function generates a styled HTML block that displays code execution
|
| 1429 |
+
information including the code itself, syntax highlighting, and a list of
|
| 1430 |
+
tools that were used during execution.
|
| 1431 |
+
|
| 1432 |
+
Args:
|
| 1433 |
+
code_content: The cleaned code content to display
|
| 1434 |
+
language: The programming language for syntax highlighting
|
| 1435 |
+
tool_name: The default tool name to display if no specific tools detected
|
| 1436 |
+
detected_tool_modules: List of (tool_name, module_name) tuples for tools used
|
| 1437 |
+
|
| 1438 |
+
Returns:
|
| 1439 |
+
HTML string containing a styled tool call block with code and tool information
|
| 1440 |
+
|
| 1441 |
+
Note:
|
| 1442 |
+
The HTML uses CSS classes defined in get_pdf_css_content() for styling.
|
| 1443 |
+
If no specific tools are detected, it falls back to a default tool name.
|
| 1444 |
+
"""
|
| 1445 |
+
# Create the formatted block with code and tools used
|
| 1446 |
+
formatted_block = f"""<div class="tool-call-highlight">
|
| 1447 |
+
<div class="tool-call-header">
|
| 1448 |
+
<strong>Code Execution</strong>
|
| 1449 |
+
</div>
|
| 1450 |
+
<div class="tool-call-input">
|
| 1451 |
+
```{language}
|
| 1452 |
+
{code_content}
|
| 1453 |
+
```
|
| 1454 |
+
</div>"""
|
| 1455 |
+
|
| 1456 |
+
# Add tools used section
|
| 1457 |
+
if detected_tool_modules:
|
| 1458 |
+
tools_list = format_detected_tools(detected_tool_modules)
|
| 1459 |
+
formatted_block += f"""
|
| 1460 |
+
<div class="tools-used">
|
| 1461 |
+
<strong>Tools Used:</strong> {tools_list}
|
| 1462 |
+
</div>"""
|
| 1463 |
+
else:
|
| 1464 |
+
formatted_block += format_default_tool_name(language, tool_name)
|
| 1465 |
+
|
| 1466 |
+
formatted_block += "</div>"
|
| 1467 |
+
return formatted_block
|
| 1468 |
+
|
| 1469 |
+
|
| 1470 |
+
def format_detected_tools(detected_tool_modules: list) -> str:
|
| 1471 |
+
"""Format detected tools with their modules for display.
|
| 1472 |
+
|
| 1473 |
+
This function takes a list of (tool_name, module_name) tuples and formats
|
| 1474 |
+
them into a human-readable string for display in the tool call blocks.
|
| 1475 |
+
It handles special cases for common tools and formats module names appropriately.
|
| 1476 |
+
|
| 1477 |
+
Args:
|
| 1478 |
+
detected_tool_modules: List of (tool_name, module_name) tuples
|
| 1479 |
+
|
| 1480 |
+
Returns:
|
| 1481 |
+
Comma-separated string of formatted tool descriptions
|
| 1482 |
+
|
| 1483 |
+
Example:
|
| 1484 |
+
>>> format_detected_tools([("analyze_data", "biomni.tool"), ("pandas", "pandas")])
|
| 1485 |
+
"biomni → analyze_data, pandas → pandas"
|
| 1486 |
+
"""
|
| 1487 |
+
tool_descriptions = []
|
| 1488 |
+
for tool_name, module_name in detected_tool_modules:
|
| 1489 |
+
if tool_name == "python_repl":
|
| 1490 |
+
tool_descriptions.append("Python REPL")
|
| 1491 |
+
elif tool_name == "r_repl":
|
| 1492 |
+
tool_descriptions.append("R REPL")
|
| 1493 |
+
elif "bash" in tool_name.lower():
|
| 1494 |
+
tool_descriptions.append("Bash Script")
|
| 1495 |
+
else:
|
| 1496 |
+
# Extract the last part of the module name for display
|
| 1497 |
+
display_module = module_name.split(".")[-1] if "." in module_name else module_name
|
| 1498 |
+
tool_descriptions.append(f"{display_module} → {tool_name}")
|
| 1499 |
+
|
| 1500 |
+
return ", ".join(sorted(tool_descriptions))
|
| 1501 |
+
|
| 1502 |
+
|
| 1503 |
+
def format_default_tool_name(language: str, tool_name: str) -> str:
|
| 1504 |
+
"""Format default tool name based on programming language.
|
| 1505 |
+
|
| 1506 |
+
This function generates HTML for displaying the default tool name when
|
| 1507 |
+
no specific tools are detected in the code. It maps programming languages
|
| 1508 |
+
to their appropriate default tool names.
|
| 1509 |
+
|
| 1510 |
+
Args:
|
| 1511 |
+
language: The programming language ("python", "r", "bash")
|
| 1512 |
+
tool_name: The detected tool name (used for bash CLI vs script distinction)
|
| 1513 |
+
|
| 1514 |
+
Returns:
|
| 1515 |
+
HTML string containing a styled tools-used section
|
| 1516 |
+
|
| 1517 |
+
Note:
|
| 1518 |
+
For bash, it distinguishes between CLI commands and bash scripts
|
| 1519 |
+
based on the tool_name parameter.
|
| 1520 |
+
"""
|
| 1521 |
+
if language == "r":
|
| 1522 |
+
return """
|
| 1523 |
+
<div class="tools-used">
|
| 1524 |
+
<strong>Tools Used:</strong> R REPL
|
| 1525 |
+
</div>"""
|
| 1526 |
+
elif language == "bash":
|
| 1527 |
+
if tool_name == "CLI Command":
|
| 1528 |
+
return """
|
| 1529 |
+
<div class="tools-used">
|
| 1530 |
+
<strong>Tools Used:</strong> CLI Command
|
| 1531 |
+
</div>"""
|
| 1532 |
+
else:
|
| 1533 |
+
return """
|
| 1534 |
+
<div class="tools-used">
|
| 1535 |
+
<strong>Tools Used:</strong> Bash Script
|
| 1536 |
+
</div>"""
|
| 1537 |
+
else:
|
| 1538 |
+
return """
|
| 1539 |
+
<div class="tools-used">
|
| 1540 |
+
<strong>Tools Used:</strong> Python REPL
|
| 1541 |
+
</div>"""
|
| 1542 |
+
|
| 1543 |
+
|
| 1544 |
+
def format_solution_tags_in_content(content: str) -> str:
|
| 1545 |
+
"""Format solution tags in content by extracting text and formatting as solution blocks.
|
| 1546 |
+
|
| 1547 |
+
This function processes content that contains <solution>...</solution> tags and
|
| 1548 |
+
converts them into styled HTML blocks that display solution content with appropriate
|
| 1549 |
+
formatting and CSS classes.
|
| 1550 |
+
|
| 1551 |
+
Args:
|
| 1552 |
+
content: The content string that may contain <solution> tags
|
| 1553 |
+
|
| 1554 |
+
Returns:
|
| 1555 |
+
Formatted content with solution tags converted to styled solution blocks
|
| 1556 |
+
|
| 1557 |
+
Note:
|
| 1558 |
+
The solution blocks use the "title-text summary" CSS class for consistent
|
| 1559 |
+
styling with other content blocks in the markdown output.
|
| 1560 |
+
"""
|
| 1561 |
+
import re
|
| 1562 |
+
|
| 1563 |
+
# Pattern to match <solution>...</solution> blocks
|
| 1564 |
+
solution_pattern = r"<solution>(.*?)</solution>"
|
| 1565 |
+
|
| 1566 |
+
def replace_solution_tag(match):
|
| 1567 |
+
solution_content = match.group(1).strip()
|
| 1568 |
+
# Format as regular text, not terminal
|
| 1569 |
+
return f"""<div class="title-text summary">
|
| 1570 |
+
<div class="title-text-header">
|
| 1571 |
+
<strong>Summary and Solution</strong>
|
| 1572 |
+
</div>
|
| 1573 |
+
<div class="title-text-content">
|
| 1574 |
+
{solution_content}
|
| 1575 |
+
</div>
|
| 1576 |
+
</div>"""
|
| 1577 |
+
|
| 1578 |
+
# Replace all solution tags with formatted solution blocks
|
| 1579 |
+
formatted_content = re.sub(solution_pattern, replace_solution_tag, content, flags=re.DOTALL)
|
| 1580 |
+
|
| 1581 |
+
return formatted_content
|
| 1582 |
+
|
| 1583 |
+
|
| 1584 |
+
def format_observation_as_terminal(content: str) -> str | None:
|
| 1585 |
+
"""Format observation content with terminal-like styling.
|
| 1586 |
+
|
| 1587 |
+
This function processes observation content from the agent's execution results
|
| 1588 |
+
and formats it as a styled terminal block. It handles both text and image content,
|
| 1589 |
+
with length limits to ensure the output fits within PDF page constraints.
|
| 1590 |
+
|
| 1591 |
+
Args:
|
| 1592 |
+
content: The observation content string, potentially containing <observation> tags
|
| 1593 |
+
|
| 1594 |
+
Returns:
|
| 1595 |
+
Formatted HTML content with terminal styling, or None if observation is
|
| 1596 |
+
empty, invalid, or contains only meaningless content
|
| 1597 |
+
|
| 1598 |
+
Note:
|
| 1599 |
+
- Content is limited to 10,000 characters to fit within 2 A4 pages
|
| 1600 |
+
- Handles both text and base64-encoded images
|
| 1601 |
+
- Uses CSS classes for consistent styling with other content blocks
|
| 1602 |
+
"""
|
| 1603 |
+
import re
|
| 1604 |
+
|
| 1605 |
+
# Character limit for 2 A4 pages (approximately 10,000 characters)
|
| 1606 |
+
MAX_OBSERVATION_LENGTH = 10000
|
| 1607 |
+
|
| 1608 |
+
# Remove the <observation> tags and extract the content
|
| 1609 |
+
observation_pattern = r"<observation>(.*?)</observation>"
|
| 1610 |
+
observation_match = re.search(observation_pattern, content, re.DOTALL)
|
| 1611 |
+
|
| 1612 |
+
if observation_match:
|
| 1613 |
+
observation_content = observation_match.group(1).strip()
|
| 1614 |
+
else:
|
| 1615 |
+
# Fallback if no observation tags found - check if content is meaningful
|
| 1616 |
+
if not (content.strip() and content.strip() not in ["", "None", "null", "undefined"]):
|
| 1617 |
+
return None
|
| 1618 |
+
observation_content = content.strip()
|
| 1619 |
+
|
| 1620 |
+
# Skip empty observations
|
| 1621 |
+
if not observation_content or observation_content in ["", "None", "null", "undefined"]:
|
| 1622 |
+
return None
|
| 1623 |
+
|
| 1624 |
+
# Check if observation is too long for 2 pages
|
| 1625 |
+
if len(observation_content) > MAX_OBSERVATION_LENGTH:
|
| 1626 |
+
cropped_content = observation_content[:MAX_OBSERVATION_LENGTH]
|
| 1627 |
+
truncation_notice = f"\n\n[Output truncated - content was too long to display here ({len(observation_content)} characters total)]"
|
| 1628 |
+
observation_content = cropped_content + truncation_notice
|
| 1629 |
+
|
| 1630 |
+
# Check if it contains plot data (base64 images)
|
| 1631 |
+
if "data:image/" in observation_content:
|
| 1632 |
+
content_html = process_observation_with_images(observation_content)
|
| 1633 |
+
else:
|
| 1634 |
+
# Regular text output - format as terminal output
|
| 1635 |
+
content_html = f"```terminal\n{observation_content}\n```"
|
| 1636 |
+
|
| 1637 |
+
return f"""<div class="title-text observation">
|
| 1638 |
+
<div class="title-text-header">
|
| 1639 |
+
<strong>Observation</strong>
|
| 1640 |
+
</div>
|
| 1641 |
+
<div class="title-text-content">
|
| 1642 |
+
{content_html}
|
| 1643 |
+
</div>
|
| 1644 |
+
</div>"""
|
| 1645 |
+
|
| 1646 |
+
|
| 1647 |
+
def process_observation_with_images(observation_content: str) -> str:
|
| 1648 |
+
"""Process observation content that contains both text and base64-encoded images.
|
| 1649 |
+
|
| 1650 |
+
This function handles observation content that includes both text output and
|
| 1651 |
+
base64-encoded images (typically plots from data analysis). It separates the
|
| 1652 |
+
text and image content and formats them appropriately for markdown display.
|
| 1653 |
+
|
| 1654 |
+
Args:
|
| 1655 |
+
observation_content: The observation content containing both text and images
|
| 1656 |
+
|
| 1657 |
+
Returns:
|
| 1658 |
+
HTML string containing formatted text (as terminal blocks) and images
|
| 1659 |
+
(as markdown image tags)
|
| 1660 |
+
|
| 1661 |
+
Note:
|
| 1662 |
+
The function uses "data:image/" as a delimiter to split content into
|
| 1663 |
+
text and image parts, then processes each part separately.
|
| 1664 |
+
"""
|
| 1665 |
+
# Split content into text and image parts
|
| 1666 |
+
parts = observation_content.split("data:image/")
|
| 1667 |
+
text_parts = []
|
| 1668 |
+
image_parts = []
|
| 1669 |
+
|
| 1670 |
+
for i, part in enumerate(parts):
|
| 1671 |
+
if i == 0:
|
| 1672 |
+
# First part is text only
|
| 1673 |
+
if part.strip():
|
| 1674 |
+
text_parts.append(part.strip())
|
| 1675 |
+
else:
|
| 1676 |
+
# Find the end of the base64 data
|
| 1677 |
+
end_markers = ["\n", "\r", " ", "\t", ">", "<", "]", ")", "}"]
|
| 1678 |
+
image_end = len(part)
|
| 1679 |
+
for marker in end_markers:
|
| 1680 |
+
marker_pos = part.find(marker)
|
| 1681 |
+
if marker_pos != -1 and marker_pos < image_end:
|
| 1682 |
+
image_end = marker_pos
|
| 1683 |
+
|
| 1684 |
+
# Extract image data
|
| 1685 |
+
image_data = "data:image/" + part[:image_end]
|
| 1686 |
+
image_parts.append(image_data)
|
| 1687 |
+
|
| 1688 |
+
# Extract remaining text
|
| 1689 |
+
remaining_text = part[image_end:].strip()
|
| 1690 |
+
if remaining_text:
|
| 1691 |
+
text_parts.append(remaining_text)
|
| 1692 |
+
|
| 1693 |
+
# Build the content
|
| 1694 |
+
content_html = ""
|
| 1695 |
+
if text_parts:
|
| 1696 |
+
# Add text content as terminal output
|
| 1697 |
+
text_content = "\n".join(text_parts)
|
| 1698 |
+
content_html += f"```terminal\n{text_content}\n```\n\n"
|
| 1699 |
+
|
| 1700 |
+
if image_parts:
|
| 1701 |
+
# Add image content
|
| 1702 |
+
for image_data in image_parts:
|
| 1703 |
+
content_html += f"\n\n"
|
| 1704 |
+
|
| 1705 |
+
return content_html
|
| 1706 |
+
|
| 1707 |
+
|
| 1708 |
+
def remove_emojis_from_text(text: str) -> str:
|
| 1709 |
+
"""Remove emojis from text for markdown/PDF output.
|
| 1710 |
+
|
| 1711 |
+
This function removes common emojis used in the system prompt and configuration
|
| 1712 |
+
display from text content before it's converted to markdown or PDF. This ensures
|
| 1713 |
+
clean, professional output while preserving emojis in the console display.
|
| 1714 |
+
|
| 1715 |
+
Args:
|
| 1716 |
+
text: The text content that may contain emojis
|
| 1717 |
+
|
| 1718 |
+
Returns:
|
| 1719 |
+
Text content with emojis removed
|
| 1720 |
+
|
| 1721 |
+
Note:
|
| 1722 |
+
The function targets specific emojis used in the Biomni system:
|
| 1723 |
+
- 🔧 for tools
|
| 1724 |
+
- 📊 for data
|
| 1725 |
+
- ⚙️ for software
|
| 1726 |
+
- 📋 for configuration
|
| 1727 |
+
- 🤖 for agent
|
| 1728 |
+
"""
|
| 1729 |
+
import re
|
| 1730 |
+
|
| 1731 |
+
# Remove common emojis used in the system prompt, this makes conversion simpler
|
| 1732 |
+
emoji_patterns = [
|
| 1733 |
+
r"🔧\s*", # Tool emoji
|
| 1734 |
+
r"📊\s*", # Data emoji
|
| 1735 |
+
r"⚙️\s*", # Software emoji
|
| 1736 |
+
r"📋\s*", # Config emoji
|
| 1737 |
+
r"🤖\s*", # Agent emoji
|
| 1738 |
+
]
|
| 1739 |
+
|
| 1740 |
+
for pattern in emoji_patterns:
|
| 1741 |
+
text = re.sub(pattern, "", text)
|
| 1742 |
+
|
| 1743 |
+
return text
|
| 1744 |
+
|
| 1745 |
+
|
| 1746 |
+
def format_lists_in_text(text: str) -> str:
|
| 1747 |
+
"""Format numbered lists and bullet points in text to proper markdown format.
|
| 1748 |
+
|
| 1749 |
+
This function processes text content to identify and format various types of lists,
|
| 1750 |
+
including numbered lists with checkboxes, regular lists, and plan structures.
|
| 1751 |
+
It also handles preprocessing tasks like removing bold formatting from plan titles
|
| 1752 |
+
and removing emojis for clean PDF output.
|
| 1753 |
+
|
| 1754 |
+
Args:
|
| 1755 |
+
text: The text content to process for list formatting
|
| 1756 |
+
|
| 1757 |
+
Returns:
|
| 1758 |
+
Formatted text with properly structured lists and cleaned formatting
|
| 1759 |
+
|
| 1760 |
+
Note:
|
| 1761 |
+
The function performs several preprocessing steps:
|
| 1762 |
+
- Removes bold formatting from plan titles
|
| 1763 |
+
- Removes emojis for PDF output
|
| 1764 |
+
- Identifies and formats checkbox lists
|
| 1765 |
+
- Processes regular text blocks
|
| 1766 |
+
"""
|
| 1767 |
+
import re
|
| 1768 |
+
|
| 1769 |
+
# Preprocess to remove bold formatting from plan titles
|
| 1770 |
+
# Remove **Plan:**, **Updated Plan:**, **Completed Plan:**, etc.
|
| 1771 |
+
text = re.sub(r"\*\*([Pp]lan|Updated [Pp]lan|Completed [Pp]lan|Final [Pp]lan):\*\*", r"\1:", text)
|
| 1772 |
+
# Also handle cases without colons
|
| 1773 |
+
text = re.sub(r"\*\*([Pp]lan|Updated [Pp]lan|Completed [Pp]lan|Final [Pp]lan)\*\*", r"\1", text)
|
| 1774 |
+
# Handle any other bold formatting patterns for plan titles
|
| 1775 |
+
text = re.sub(r"<strong>([Pp]lan|Updated [Pp]lan|Completed [Pp]lan|Final [Pp]lan):</strong>", r"\1:", text)
|
| 1776 |
+
text = re.sub(r"<strong>([Pp]lan|Updated [Pp]lan|Completed [Pp]lan|Final [Pp]lan)</strong>", r"\1", text)
|
| 1777 |
+
|
| 1778 |
+
# Remove emojis from the text for markdown/PDF output
|
| 1779 |
+
text = remove_emojis_from_text(text)
|
| 1780 |
+
|
| 1781 |
+
lines = text.split("\n")
|
| 1782 |
+
list_blocks = identify_list_blocks(lines)
|
| 1783 |
+
|
| 1784 |
+
# Process each block
|
| 1785 |
+
result_blocks = []
|
| 1786 |
+
for block_text, is_checkbox_list in list_blocks:
|
| 1787 |
+
if is_checkbox_list:
|
| 1788 |
+
result_blocks.append(format_single_list(block_text))
|
| 1789 |
+
else:
|
| 1790 |
+
result_blocks.append(block_text)
|
| 1791 |
+
|
| 1792 |
+
return "\n".join(result_blocks)
|
| 1793 |
+
|
| 1794 |
+
|
| 1795 |
+
def identify_list_blocks(lines: list) -> list[tuple[str, bool]]:
|
| 1796 |
+
"""Identify blocks of text that contain lists.
|
| 1797 |
+
|
| 1798 |
+
This function analyzes a list of text lines to identify contiguous blocks
|
| 1799 |
+
that contain numbered lists with checkboxes. It groups lines into blocks
|
| 1800 |
+
and marks whether each block contains a checkbox list or regular text.
|
| 1801 |
+
|
| 1802 |
+
Args:
|
| 1803 |
+
lines: List of text lines to analyze
|
| 1804 |
+
|
| 1805 |
+
Returns:
|
| 1806 |
+
List of tuples containing (block_text, is_checkbox_list) where:
|
| 1807 |
+
- block_text: The text content of the block
|
| 1808 |
+
- is_checkbox_list: True if the block contains numbered items with checkboxes
|
| 1809 |
+
|
| 1810 |
+
Note:
|
| 1811 |
+
The function looks for patterns like "1. [ ]", "2. [✓]", "3. [✗]" to
|
| 1812 |
+
identify checkbox sequences and groups them into separate blocks.
|
| 1813 |
+
"""
|
| 1814 |
+
import re
|
| 1815 |
+
|
| 1816 |
+
list_blocks = []
|
| 1817 |
+
current_block = []
|
| 1818 |
+
in_checkbox_sequence = False
|
| 1819 |
+
|
| 1820 |
+
for line in lines:
|
| 1821 |
+
line_stripped = line.strip()
|
| 1822 |
+
|
| 1823 |
+
# Check if this line starts a numbered item with checkbox
|
| 1824 |
+
if re.match(r"^\d+\.\s*\[[ ✓✗]\]", line_stripped):
|
| 1825 |
+
if not in_checkbox_sequence:
|
| 1826 |
+
# Start of a new checkbox sequence
|
| 1827 |
+
if current_block:
|
| 1828 |
+
list_blocks.append(("\n".join(current_block), False))
|
| 1829 |
+
current_block = [line]
|
| 1830 |
+
in_checkbox_sequence = True
|
| 1831 |
+
else:
|
| 1832 |
+
# Continue the sequence
|
| 1833 |
+
current_block.append(line)
|
| 1834 |
+
else:
|
| 1835 |
+
if in_checkbox_sequence:
|
| 1836 |
+
# End of checkbox sequence
|
| 1837 |
+
if current_block:
|
| 1838 |
+
list_blocks.append(("\n".join(current_block), True))
|
| 1839 |
+
current_block = []
|
| 1840 |
+
in_checkbox_sequence = False
|
| 1841 |
+
current_block.append(line)
|
| 1842 |
+
|
| 1843 |
+
# Handle the last block
|
| 1844 |
+
if current_block:
|
| 1845 |
+
if in_checkbox_sequence:
|
| 1846 |
+
list_blocks.append(("\n".join(current_block), True))
|
| 1847 |
+
else:
|
| 1848 |
+
list_blocks.append(("\n".join(current_block), False))
|
| 1849 |
+
|
| 1850 |
+
return list_blocks
|
| 1851 |
+
|
| 1852 |
+
|
| 1853 |
+
def format_single_list(text: str) -> str:
|
| 1854 |
+
"""Format a single list block with checkboxes and plan titles.
|
| 1855 |
+
|
| 1856 |
+
This function processes a text block that may contain numbered lists with
|
| 1857 |
+
checkboxes and plan titles. It converts checkbox symbols to HTML list items
|
| 1858 |
+
and wraps the content in a styled container with appropriate CSS classes.
|
| 1859 |
+
|
| 1860 |
+
Args:
|
| 1861 |
+
text: The text block to format, potentially containing numbered lists
|
| 1862 |
+
|
| 1863 |
+
Returns:
|
| 1864 |
+
HTML string containing either a formatted list with plan title or
|
| 1865 |
+
regular text if no list items are found
|
| 1866 |
+
|
| 1867 |
+
Note:
|
| 1868 |
+
The function recognizes plan titles like "Plan", "Updated Plan", "Completed Plan"
|
| 1869 |
+
and converts checkbox symbols (✓, ✗) to HTML format ([x], [ ]).
|
| 1870 |
+
"""
|
| 1871 |
+
import re
|
| 1872 |
+
|
| 1873 |
+
lines = text.split("\n")
|
| 1874 |
+
list_items = []
|
| 1875 |
+
has_list_items = False
|
| 1876 |
+
plan_title = "Plan" # Default title
|
| 1877 |
+
|
| 1878 |
+
for line in lines:
|
| 1879 |
+
line = line.strip()
|
| 1880 |
+
if not line:
|
| 1881 |
+
continue
|
| 1882 |
+
|
| 1883 |
+
# Check for plan title patterns
|
| 1884 |
+
if re.match(r"^(Plan|Updated Plan|Completed Plan)$", line, re.IGNORECASE):
|
| 1885 |
+
plan_title = line
|
| 1886 |
+
continue
|
| 1887 |
+
|
| 1888 |
+
# Check for numbered lists with checkboxes (1. [ ] or 1. [✓] or 1. [✗])
|
| 1889 |
+
if re.match(r"^\d+\.\s*\[[ ✓✗]\]", line):
|
| 1890 |
+
has_list_items = True
|
| 1891 |
+
# Extract the content after the checkbox
|
| 1892 |
+
content = re.sub(r"^\d+\.\s*\[[ ✓✗]\]\s*", "", line)
|
| 1893 |
+
|
| 1894 |
+
# Replace checkbox symbols with text format
|
| 1895 |
+
if "[✓]" in line:
|
| 1896 |
+
list_items.append(f"<li><strong>[x]</strong> {content}</li>")
|
| 1897 |
+
elif "[✗]" in line:
|
| 1898 |
+
list_items.append(f"<li><strong>[ ]</strong> {content}</li>")
|
| 1899 |
+
else:
|
| 1900 |
+
list_items.append(f"<li><strong>[ ]</strong> {content}</li>")
|
| 1901 |
+
else:
|
| 1902 |
+
# Regular text - add as is (don't convert to list items)
|
| 1903 |
+
list_items.append(line)
|
| 1904 |
+
|
| 1905 |
+
if has_list_items and list_items:
|
| 1906 |
+
# This is a list - return with container div and styled title
|
| 1907 |
+
return f"""<div class="title-text plan">
|
| 1908 |
+
<div class="title-text-header">
|
| 1909 |
+
<span class="plan-title">{plan_title}</span>
|
| 1910 |
+
</div>
|
| 1911 |
+
<div class="title-text-content">
|
| 1912 |
+
<ul>
|
| 1913 |
+
{chr(10).join(list_items)}
|
| 1914 |
+
</ul>
|
| 1915 |
+
</div>
|
| 1916 |
+
</div>"""
|
| 1917 |
+
else:
|
| 1918 |
+
# Regular text
|
| 1919 |
+
return "\n".join(list_items)
|
| 1920 |
+
|
| 1921 |
+
|
| 1922 |
+
def convert_markdown_to_pdf(markdown_path: str, pdf_path: str) -> None:
|
| 1923 |
+
"""Convert markdown file to PDF using weasyprint or fallback libraries.
|
| 1924 |
+
|
| 1925 |
+
This function converts a markdown file to PDF format using multiple fallback
|
| 1926 |
+
strategies. It prioritizes weasyprint for better layout control, then falls back
|
| 1927 |
+
to markdown2pdf and finally pandoc if the preferred libraries are not available.
|
| 1928 |
+
|
| 1929 |
+
Args:
|
| 1930 |
+
markdown_path: Path to the input markdown file
|
| 1931 |
+
pdf_path: Path where the output PDF file should be saved
|
| 1932 |
+
|
| 1933 |
+
Raises:
|
| 1934 |
+
ImportError: If no PDF conversion library is available
|
| 1935 |
+
Exception: If PDF conversion fails for any other reason
|
| 1936 |
+
|
| 1937 |
+
Note:
|
| 1938 |
+
The function uses minimal markdown extensions for better performance
|
| 1939 |
+
and applies custom CSS styling for consistent formatting.
|
| 1940 |
+
"""
|
| 1941 |
+
try:
|
| 1942 |
+
# Try weasyprint first (better for complex layouts)
|
| 1943 |
+
from weasyprint import HTML
|
| 1944 |
+
from weasyprint.text.fonts import FontConfiguration
|
| 1945 |
+
|
| 1946 |
+
# Read markdown content
|
| 1947 |
+
with open(markdown_path, encoding="utf-8") as f:
|
| 1948 |
+
markdown_content = f.read()
|
| 1949 |
+
|
| 1950 |
+
# Convert markdown to HTML with minimal extensions for better performance
|
| 1951 |
+
import markdown
|
| 1952 |
+
|
| 1953 |
+
# Use minimal extensions to improve performance
|
| 1954 |
+
html_content = markdown.markdown(
|
| 1955 |
+
markdown_content,
|
| 1956 |
+
extensions=["fenced_code"], # Removed codehilite for better performance
|
| 1957 |
+
)
|
| 1958 |
+
|
| 1959 |
+
# Add CSS styling
|
| 1960 |
+
css_content = get_pdf_css_content()
|
| 1961 |
+
|
| 1962 |
+
# Create HTML document
|
| 1963 |
+
html_doc = f"""
|
| 1964 |
+
<!DOCTYPE html>
|
| 1965 |
+
<html>
|
| 1966 |
+
<head>
|
| 1967 |
+
<meta charset="utf-8">
|
| 1968 |
+
<title>Biomni Conversation History</title>
|
| 1969 |
+
<style>{css_content}</style>
|
| 1970 |
+
</head>
|
| 1971 |
+
<body>
|
| 1972 |
+
{html_content}
|
| 1973 |
+
</body>
|
| 1974 |
+
</html>
|
| 1975 |
+
"""
|
| 1976 |
+
|
| 1977 |
+
# Convert to PDF with performance optimizations
|
| 1978 |
+
font_config = FontConfiguration()
|
| 1979 |
+
html_obj = HTML(string=html_doc)
|
| 1980 |
+
html_obj.write_pdf(pdf_path, font_config=font_config, optimize_images=True)
|
| 1981 |
+
|
| 1982 |
+
except ImportError:
|
| 1983 |
+
# Fallback to markdown2pdf if weasyprint is not available
|
| 1984 |
+
try:
|
| 1985 |
+
from markdown2pdf import markdown2pdf
|
| 1986 |
+
|
| 1987 |
+
markdown2pdf(markdown_path, pdf_path)
|
| 1988 |
+
except ImportError:
|
| 1989 |
+
# Final fallback - try using pandoc if available
|
| 1990 |
+
import subprocess
|
| 1991 |
+
|
| 1992 |
+
try:
|
| 1993 |
+
subprocess.run(["pandoc", markdown_path, "-o", pdf_path], check=True)
|
| 1994 |
+
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
| 1995 |
+
raise ImportError(
|
| 1996 |
+
"No PDF conversion library available. Please install weasyprint, markdown2pdf, or pandoc."
|
| 1997 |
+
) from e
|
| 1998 |
+
except Exception as e:
|
| 1999 |
+
raise Exception(f"PDF conversion failed: {e}") from e
|
| 2000 |
+
|
| 2001 |
+
|
| 2002 |
+
def get_pdf_css_content() -> str:
|
| 2003 |
+
"""Get the CSS content for PDF generation.
|
| 2004 |
+
|
| 2005 |
+
This function returns a comprehensive CSS stylesheet designed specifically
|
| 2006 |
+
for PDF generation from markdown content. It includes styling for all
|
| 2007 |
+
HTML elements that may appear in the converted markdown, with optimized
|
| 2008 |
+
typography, spacing, and layout for print media.
|
| 2009 |
+
|
| 2010 |
+
Returns:
|
| 2011 |
+
CSS string containing all styles needed for PDF generation
|
| 2012 |
+
|
| 2013 |
+
Note:
|
| 2014 |
+
The CSS includes styles for:
|
| 2015 |
+
- Typography and font families
|
| 2016 |
+
- Headings and text formatting
|
| 2017 |
+
- Code blocks and syntax highlighting
|
| 2018 |
+
- Tables and lists
|
| 2019 |
+
- Custom classes for tool calls, observations, and plans
|
| 2020 |
+
- Print-optimized spacing and layout
|
| 2021 |
+
"""
|
| 2022 |
+
return """
|
| 2023 |
+
body {
|
| 2024 |
+
/* Previously: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Twemoji', 'EmojiOne Color' */
|
| 2025 |
+
font-family: sans-serif;
|
| 2026 |
+
font-size: 9pt;
|
| 2027 |
+
line-height: 1.4;
|
| 2028 |
+
max-width: 800px;
|
| 2029 |
+
margin: 0 auto;
|
| 2030 |
+
padding: 15px;
|
| 2031 |
+
color: #333;
|
| 2032 |
+
}
|
| 2033 |
+
h1, h2, h3, h4, h5, h6 {
|
| 2034 |
+
/* Previously: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Twemoji', 'EmojiOne Color' */
|
| 2035 |
+
font-family: sans-serif;
|
| 2036 |
+
color: #2c3e50;
|
| 2037 |
+
margin-top: 1em;
|
| 2038 |
+
margin-bottom: 0.5em;
|
| 2039 |
+
}
|
| 2040 |
+
h1 {
|
| 2041 |
+
border-bottom: 2px solid #3498db;
|
| 2042 |
+
padding-bottom: 8px;
|
| 2043 |
+
font-size: 16pt;
|
| 2044 |
+
}
|
| 2045 |
+
h2 {
|
| 2046 |
+
border-bottom: 1px solid #bdc3c7;
|
| 2047 |
+
padding-bottom: 3px;
|
| 2048 |
+
font-size: 14pt;
|
| 2049 |
+
}
|
| 2050 |
+
h3 {
|
| 2051 |
+
font-size: 12pt;
|
| 2052 |
+
}
|
| 2053 |
+
h4 {
|
| 2054 |
+
font-size: 10pt;
|
| 2055 |
+
margin-top: 0.8em;
|
| 2056 |
+
margin-bottom: 0.3em;
|
| 2057 |
+
}
|
| 2058 |
+
h5, h6 {
|
| 2059 |
+
font-size: 9pt;
|
| 2060 |
+
margin-top: 0.6em;
|
| 2061 |
+
margin-bottom: 0.2em;
|
| 2062 |
+
}
|
| 2063 |
+
code {
|
| 2064 |
+
background-color: #f8f9fa;
|
| 2065 |
+
padding: 1px 3px;
|
| 2066 |
+
border-radius: 2px;
|
| 2067 |
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
| 2068 |
+
font-size: 8pt;
|
| 2069 |
+
white-space: pre-wrap;
|
| 2070 |
+
word-wrap: break-word;
|
| 2071 |
+
}
|
| 2072 |
+
pre {
|
| 2073 |
+
background-color: #f8f9fa;
|
| 2074 |
+
padding: 10px;
|
| 2075 |
+
border-radius: 3px;
|
| 2076 |
+
overflow-x: auto;
|
| 2077 |
+
border-left: 3px solid #3498db;
|
| 2078 |
+
white-space: pre-wrap;
|
| 2079 |
+
word-wrap: break-word;
|
| 2080 |
+
font-size: 8pt;
|
| 2081 |
+
margin: 0.5em 0;
|
| 2082 |
+
}
|
| 2083 |
+
pre code {
|
| 2084 |
+
background-color: transparent;
|
| 2085 |
+
padding: 0;
|
| 2086 |
+
border-radius: 0;
|
| 2087 |
+
font-size: 8pt;
|
| 2088 |
+
}
|
| 2089 |
+
/* Code header styling */
|
| 2090 |
+
strong {
|
| 2091 |
+
font-size: 9pt;
|
| 2092 |
+
font-weight: normal;
|
| 2093 |
+
color: #6c757d;
|
| 2094 |
+
font-style: italic;
|
| 2095 |
+
}
|
| 2096 |
+
blockquote {
|
| 2097 |
+
border-left: 3px solid #bdc3c7;
|
| 2098 |
+
margin: 0.5em 0;
|
| 2099 |
+
padding-left: 15px;
|
| 2100 |
+
color: #7f8c8d;
|
| 2101 |
+
font-size: 8pt;
|
| 2102 |
+
}
|
| 2103 |
+
table {
|
| 2104 |
+
border-collapse: collapse;
|
| 2105 |
+
width: 100%;
|
| 2106 |
+
margin: 0.5em 0;
|
| 2107 |
+
font-size: 8pt;
|
| 2108 |
+
}
|
| 2109 |
+
th, td {
|
| 2110 |
+
border: 1px solid #bdc3c7;
|
| 2111 |
+
padding: 4px 8px;
|
| 2112 |
+
text-align: left;
|
| 2113 |
+
}
|
| 2114 |
+
th {
|
| 2115 |
+
background-color: #ecf0f1;
|
| 2116 |
+
font-weight: bold;
|
| 2117 |
+
}
|
| 2118 |
+
img {
|
| 2119 |
+
max-width: 100%;
|
| 2120 |
+
height: auto;
|
| 2121 |
+
display: block;
|
| 2122 |
+
margin: 10px auto;
|
| 2123 |
+
border: 1px solid #ddd;
|
| 2124 |
+
border-radius: 3px;
|
| 2125 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 2126 |
+
}
|
| 2127 |
+
p {
|
| 2128 |
+
/* Previously: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Twemoji', 'EmojiOne Color' */
|
| 2129 |
+
font-family: sans-serif;
|
| 2130 |
+
margin: 0.3em 0;
|
| 2131 |
+
}
|
| 2132 |
+
/* Tool call highlighting - matching observation and code formatting */
|
| 2133 |
+
.tool-call-highlight {
|
| 2134 |
+
background-color: #f8f9fa;
|
| 2135 |
+
border: 1px solid #e9ecef;
|
| 2136 |
+
border-radius: 3px;
|
| 2137 |
+
padding: 0;
|
| 2138 |
+
margin: 10px 0;
|
| 2139 |
+
overflow: hidden;
|
| 2140 |
+
}
|
| 2141 |
+
.tool-call-header {
|
| 2142 |
+
background-color: #e9ecef;
|
| 2143 |
+
color: #495057;
|
| 2144 |
+
padding: 8px 12px;
|
| 2145 |
+
margin: 0;
|
| 2146 |
+
font-weight: normal;
|
| 2147 |
+
font-size: 9pt;
|
| 2148 |
+
font-style: italic;
|
| 2149 |
+
border-bottom: 1px solid #dee2e6;
|
| 2150 |
+
}
|
| 2151 |
+
.tool-call-input {
|
| 2152 |
+
background-color: #f8f9fa;
|
| 2153 |
+
border: none;
|
| 2154 |
+
border-radius: 0;
|
| 2155 |
+
padding: 10px 12px;
|
| 2156 |
+
margin: 0;
|
| 2157 |
+
color: #333;
|
| 2158 |
+
font-size: 8pt;
|
| 2159 |
+
line-height: 1.4;
|
| 2160 |
+
}
|
| 2161 |
+
.tool-call-input strong {
|
| 2162 |
+
color: #495057;
|
| 2163 |
+
font-weight: normal;
|
| 2164 |
+
font-size: 8pt;
|
| 2165 |
+
font-style: italic;
|
| 2166 |
+
}
|
| 2167 |
+
.tool-call-input pre {
|
| 2168 |
+
background-color: #f8f9fa;
|
| 2169 |
+
border: 1px solid #e9ecef;
|
| 2170 |
+
border-radius: 3px;
|
| 2171 |
+
padding: 10px;
|
| 2172 |
+
margin: 0;
|
| 2173 |
+
font-size: 8pt;
|
| 2174 |
+
line-height: 1.4;
|
| 2175 |
+
overflow-x: auto;
|
| 2176 |
+
white-space: pre-wrap;
|
| 2177 |
+
word-wrap: break-word;
|
| 2178 |
+
}
|
| 2179 |
+
.tool-call-input code {
|
| 2180 |
+
background-color: transparent;
|
| 2181 |
+
padding: 0;
|
| 2182 |
+
border-radius: 0;
|
| 2183 |
+
font-size: 8pt;
|
| 2184 |
+
color: #2c3e50;
|
| 2185 |
+
}
|
| 2186 |
+
.tools-used {
|
| 2187 |
+
background-color: #f8f9fa;
|
| 2188 |
+
border-top: 1px solid #dee2e6;
|
| 2189 |
+
padding: 8px 12px;
|
| 2190 |
+
margin: 0;
|
| 2191 |
+
font-size: 8pt;
|
| 2192 |
+
color: #6c757d;
|
| 2193 |
+
}
|
| 2194 |
+
.tools-used strong {
|
| 2195 |
+
color: #6c757d;
|
| 2196 |
+
font-weight: normal;
|
| 2197 |
+
font-size: 8pt;
|
| 2198 |
+
font-style: italic;
|
| 2199 |
+
}
|
| 2200 |
+
/* Title-text styling - unified for observations, plans, and solutions */
|
| 2201 |
+
.title-text {
|
| 2202 |
+
background-color: #f8f9fa;
|
| 2203 |
+
border: 1px solid #e9ecef;
|
| 2204 |
+
border-radius: 3px;
|
| 2205 |
+
padding: 0;
|
| 2206 |
+
margin: 10px 0;
|
| 2207 |
+
overflow: hidden;
|
| 2208 |
+
}
|
| 2209 |
+
.title-text-header {
|
| 2210 |
+
background-color: #e9ecef;
|
| 2211 |
+
color: #495057;
|
| 2212 |
+
padding: 8px 12px;
|
| 2213 |
+
margin: 0;
|
| 2214 |
+
font-weight: normal;
|
| 2215 |
+
font-size: 9pt;
|
| 2216 |
+
font-style: italic;
|
| 2217 |
+
border-bottom: 1px solid #dee2e6;
|
| 2218 |
+
}
|
| 2219 |
+
.title-text-header strong {
|
| 2220 |
+
color: #495057;
|
| 2221 |
+
font-weight: normal;
|
| 2222 |
+
font-size: 9pt;
|
| 2223 |
+
font-style: italic;
|
| 2224 |
+
}
|
| 2225 |
+
.title-text-content {
|
| 2226 |
+
background-color: #f8f9fa;
|
| 2227 |
+
border: none;
|
| 2228 |
+
border-radius: 0;
|
| 2229 |
+
padding: 10px 12px;
|
| 2230 |
+
margin: 0;
|
| 2231 |
+
color: #333;
|
| 2232 |
+
font-size: 8pt;
|
| 2233 |
+
line-height: 1.4;
|
| 2234 |
+
}
|
| 2235 |
+
/* Plan-specific styling - soft blue pastel */
|
| 2236 |
+
.title-text.plan {
|
| 2237 |
+
background-color: #e3f2fd;
|
| 2238 |
+
border-color: #bbdefb;
|
| 2239 |
+
}
|
| 2240 |
+
.title-text.plan .title-text-header {
|
| 2241 |
+
background-color: #bbdefb;
|
| 2242 |
+
color: #1976d2;
|
| 2243 |
+
}
|
| 2244 |
+
.title-text.plan .title-text-content {
|
| 2245 |
+
background-color: #e3f2fd;
|
| 2246 |
+
}
|
| 2247 |
+
.plan-title {
|
| 2248 |
+
font-style: italic;
|
| 2249 |
+
font-weight: normal;
|
| 2250 |
+
color: #1565c0;
|
| 2251 |
+
text-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
| 2252 |
+
}
|
| 2253 |
+
.plan-title strong {
|
| 2254 |
+
font-weight: normal;
|
| 2255 |
+
}
|
| 2256 |
+
/* Code execution-specific styling - matching title-text styling */
|
| 2257 |
+
.tool-call-highlight {
|
| 2258 |
+
background-color: #f8f9fa;
|
| 2259 |
+
border-color: #e9ecef;
|
| 2260 |
+
}
|
| 2261 |
+
.tool-call-header {
|
| 2262 |
+
background-color: #e9ecef;
|
| 2263 |
+
color: #495057;
|
| 2264 |
+
}
|
| 2265 |
+
.tool-call-input {
|
| 2266 |
+
background-color: #f8f9fa;
|
| 2267 |
+
color: #333;
|
| 2268 |
+
}
|
| 2269 |
+
/* Observation-specific styling - soft purple pastel */
|
| 2270 |
+
.title-text.observation {
|
| 2271 |
+
background-color: #f3e5f5;
|
| 2272 |
+
border-color: #e1bee7;
|
| 2273 |
+
}
|
| 2274 |
+
.title-text.observation .title-text-header {
|
| 2275 |
+
background-color: #e1bee7;
|
| 2276 |
+
color: #7b1fa2;
|
| 2277 |
+
}
|
| 2278 |
+
.title-text.observation .title-text-content {
|
| 2279 |
+
background-color: #f3e5f5;
|
| 2280 |
+
}
|
| 2281 |
+
/* Summary and solution-specific styling - soft orange pastel, no overlay */
|
| 2282 |
+
.title-text.summary {
|
| 2283 |
+
background-color: #fff3e0;
|
| 2284 |
+
border-color: #ffcc02;
|
| 2285 |
+
}
|
| 2286 |
+
.title-text.summary .title-text-header {
|
| 2287 |
+
background-color: #ffcc02;
|
| 2288 |
+
color: #f57c00;
|
| 2289 |
+
}
|
| 2290 |
+
.title-text.summary .title-text-content {
|
| 2291 |
+
background-color: #fff3e0;
|
| 2292 |
+
}
|
| 2293 |
+
.title-text-content ul {
|
| 2294 |
+
background-color: transparent;
|
| 2295 |
+
border: none;
|
| 2296 |
+
border-radius: 0;
|
| 2297 |
+
padding: 0;
|
| 2298 |
+
margin: 0;
|
| 2299 |
+
color: #333;
|
| 2300 |
+
font-size: 8pt;
|
| 2301 |
+
line-height: 1.4;
|
| 2302 |
+
}
|
| 2303 |
+
.title-text-content li {
|
| 2304 |
+
margin: 3px 0;
|
| 2305 |
+
color: #333;
|
| 2306 |
+
}
|
| 2307 |
+
.title-text-content li strong {
|
| 2308 |
+
color: #495057;
|
| 2309 |
+
font-weight: normal;
|
| 2310 |
+
font-size: 8pt;
|
| 2311 |
+
font-style: italic;
|
| 2312 |
+
}
|
| 2313 |
+
.title-text-content li code {
|
| 2314 |
+
background-color: #e9ecef;
|
| 2315 |
+
color: #333;
|
| 2316 |
+
padding: 1px 3px;
|
| 2317 |
+
border-radius: 2px;
|
| 2318 |
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
| 2319 |
+
font-size: 7pt;
|
| 2320 |
+
}
|
| 2321 |
+
.title-text-content pre {
|
| 2322 |
+
background-color: #f8f9fa;
|
| 2323 |
+
border: 1px solid #e9ecef;
|
| 2324 |
+
border-radius: 3px;
|
| 2325 |
+
padding: 10px;
|
| 2326 |
+
margin: 0;
|
| 2327 |
+
font-size: 8pt;
|
| 2328 |
+
line-height: 1.4;
|
| 2329 |
+
overflow-x: auto;
|
| 2330 |
+
white-space: pre-wrap;
|
| 2331 |
+
word-wrap: break-word;
|
| 2332 |
+
}
|
| 2333 |
+
.title-text-content code {
|
| 2334 |
+
background-color: transparent;
|
| 2335 |
+
padding: 0;
|
| 2336 |
+
border-radius: 0;
|
| 2337 |
+
font-size: 8pt;
|
| 2338 |
+
color: #2c3e50;
|
| 2339 |
+
}
|
| 2340 |
+
/* Parsing error display styling */
|
| 2341 |
+
.parsing-error-box {
|
| 2342 |
+
background-color: #ffebee;
|
| 2343 |
+
border: 1px solid #f44336;
|
| 2344 |
+
border-radius: 4px;
|
| 2345 |
+
padding: 8px 12px;
|
| 2346 |
+
margin: 8px 0;
|
| 2347 |
+
font-size: 9pt;
|
| 2348 |
+
color: #c62828;
|
| 2349 |
+
box-shadow: 0 2px 4px rgba(244, 67, 54, 0.1);
|
| 2350 |
+
}
|
| 2351 |
+
.parsing-error-header {
|
| 2352 |
+
font-weight: bold;
|
| 2353 |
+
margin-bottom: 4px;
|
| 2354 |
+
color: #d32f2f;
|
| 2355 |
+
}
|
| 2356 |
+
.parsing-error-content {
|
| 2357 |
+
font-family: 'Courier New', monospace;
|
| 2358 |
+
background-color: #ffcdd2;
|
| 2359 |
+
padding: 4px 6px;
|
| 2360 |
+
border-radius: 2px;
|
| 2361 |
+
margin-top: 4px;
|
| 2362 |
+
font-size: 8pt;
|
| 2363 |
+
white-space: pre-wrap;
|
| 2364 |
+
word-wrap: break-word;
|
| 2365 |
+
}
|
| 2366 |
+
"""
|
biomni/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
__version__ = "0.0.8" # pragma: no cover
|
biomni_env/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni Environment Setup
|
| 2 |
+
|
| 3 |
+
This directory contains scripts and configuration files to set up a comprehensive bioinformatics environment with various tools and packages.
|
| 4 |
+
|
| 5 |
+
1. Clone the repository:
|
| 6 |
+
```bash
|
| 7 |
+
git clone https://github.com/snap-stanford/Biomni.git
|
| 8 |
+
cd Biomni/biomni_env
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
2. Setting up the environment:
|
| 12 |
+
- (a) If you want to use or try out the basic agent without the full E1 or install your own softwares, run the following script:
|
| 13 |
+
|
| 14 |
+
```bash
|
| 15 |
+
conda env create -f environment.yml
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
- (b) If you want to use the full environment E1, run the setup script (this script takes > 10 hours to setup, and requires a disk of at least 30 GB quota). Follow the prompts to install the desired components.
|
| 19 |
+
|
| 20 |
+
```bash
|
| 21 |
+
bash setup.sh
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
If you already installed the base version, and just wants to add the additional packages in the new release, you can simply do:
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
bash new_software_v005.sh
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
Note: we have only tested this setup.sh script with Ubuntu 22.04, 64 bit.
|
| 31 |
+
|
| 32 |
+
- (c) If you want to use a reduced conda environment without R or CLI tools, run the following script:
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
conda env create -f fixed_env.yml
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
This contains most of the packages from environment.yml and bio_env.yml, and requires a disk of at elast 13GB quota.
|
| 39 |
+
|
| 40 |
+
- (d) **Python 3.10 Environment for Copy Number Analysis**: If you specifically need to use the `analyze_copy_number_purity_ploidy_and_focal_events` function, we provide a Python 3.10 environment option. This function has specific dependency requirements that are best met with Python 3.10. To set up this environment:
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
conda env create -f bio_env_py310.yml
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
This environment is optimized for copy number variation analysis and includes the necessary packages for purity, ploidy, and focal event detection.
|
| 47 |
+
|
| 48 |
+
3. Lastly, to activate the biomni environment:
|
| 49 |
+
```bash
|
| 50 |
+
conda activate biomni_e1
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
For the Python 3.10 environment specifically:
|
| 54 |
+
```bash
|
| 55 |
+
conda activate biomni_py310
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
### 📦 Langchain Package Support
|
| 59 |
+
|
| 60 |
+
The Biomni environment comes with a minimal set of langchain packages by default:
|
| 61 |
+
- `langchain-openai` - for OpenAI model support
|
| 62 |
+
- `langchain-anthropic` - for Anthropic model support
|
| 63 |
+
- `langchain-ollama` - for Ollama model support
|
| 64 |
+
|
| 65 |
+
If you need support for other external models or services, you'll need to install additional langchain packages manually. For example:
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
# For AWS Bedrock support
|
| 69 |
+
pip install langchain-aws
|
| 70 |
+
|
| 71 |
+
# For Google Gemini support
|
| 72 |
+
pip install langchain-google-genai
|
| 73 |
+
|
| 74 |
+
```
|
biomni_env/bio_env.yml
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: biomni_e1
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
- bioconda
|
| 5 |
+
- defaults
|
| 6 |
+
dependencies:
|
| 7 |
+
- blast
|
| 8 |
+
- samtools
|
| 9 |
+
- bowtie2
|
| 10 |
+
- bwa
|
| 11 |
+
- bedtools
|
| 12 |
+
- fastqc
|
| 13 |
+
- trimmomatic
|
| 14 |
+
- mafft
|
| 15 |
+
- gseapy
|
| 16 |
+
- mageck
|
| 17 |
+
- plannotate
|
| 18 |
+
- pip:
|
| 19 |
+
- scanpy
|
| 20 |
+
- biopython
|
| 21 |
+
- biopandas
|
| 22 |
+
- biotite
|
| 23 |
+
- opencv-python
|
| 24 |
+
- rdkit
|
| 25 |
+
- macs2
|
| 26 |
+
- pymc3
|
| 27 |
+
- cryosparc-tools
|
| 28 |
+
- lifelines
|
| 29 |
+
- scikit-image
|
| 30 |
+
- googlesearch-python
|
| 31 |
+
- PyPDF2
|
| 32 |
+
- gget
|
| 33 |
+
- faiss-cpu
|
| 34 |
+
- cellxgene-census
|
| 35 |
+
- scikit-bio
|
| 36 |
+
- pymed
|
| 37 |
+
- arxiv
|
| 38 |
+
- scholarly
|
| 39 |
+
- umap-learn
|
| 40 |
+
- scrublet
|
| 41 |
+
- scvelo
|
| 42 |
+
- scvi-tools
|
| 43 |
+
- harmony-pytorch
|
| 44 |
+
- pyliftover
|
| 45 |
+
- pytdc==0.4.0
|
| 46 |
+
- pysam
|
| 47 |
+
- pyfaidx
|
| 48 |
+
- pyranges
|
| 49 |
+
- pybedtools
|
| 50 |
+
- openmm
|
| 51 |
+
- pystan
|
| 52 |
+
- igraph
|
| 53 |
+
- pyscenic
|
| 54 |
+
- cooler
|
| 55 |
+
- trackpy
|
| 56 |
+
- flowcytometrytools
|
| 57 |
+
- cellpose
|
| 58 |
+
- viennarna
|
| 59 |
+
- PyMassSpec
|
| 60 |
+
- python-libsbml
|
| 61 |
+
- cobra
|
| 62 |
+
- reportlab
|
| 63 |
+
#- flowkit
|
| 64 |
+
- hmmlearn
|
| 65 |
+
- msprime
|
| 66 |
+
- tskit
|
| 67 |
+
- cyvcf2
|
| 68 |
+
- pykalman
|
| 69 |
+
- community
|
| 70 |
+
- fanc
|
biomni_env/bio_env_py310.yml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: bio_env_py310
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
- bioconda
|
| 5 |
+
- defaults
|
| 6 |
+
dependencies:
|
| 7 |
+
- python=3.10
|
| 8 |
+
- cnvkit
|
| 9 |
+
- samtools
|
| 10 |
+
- bedtools
|
| 11 |
+
- pip
|
| 12 |
+
|
| 13 |
+
# Purpose: Python 3.10 compatibility environment for tools not yet supporting newer Python versions.
|
| 14 |
+
# Automaticlly setup on setup.sh execution.
|
| 15 |
+
# Contains tools that require Python 3.10 or earlier versions.
|
| 16 |
+
# Can be create with:
|
| 17 |
+
# micromamba create -f bio_env_py310.yml OR conda env create -f bio_env_py310.yml
|
| 18 |
+
# Use tools via:
|
| 19 |
+
# conda run -n bio_env_py310 <command> ...
|
biomni_env/cli_tools_config.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tools": [
|
| 3 |
+
{
|
| 4 |
+
"name": "PLINK 2.0",
|
| 5 |
+
"function_name": "install_plink2",
|
| 6 |
+
"description": "Whole genome association analysis toolset",
|
| 7 |
+
"website": "https://www.cog-genomics.org/plink/2.0/",
|
| 8 |
+
"downloads": {
|
| 9 |
+
"linux": "https://s3.amazonaws.com/plink2-assets/alpha6/plink2_linux_avx2_20250129.zip",
|
| 10 |
+
"macos_intel": "https://s3.amazonaws.com/plink2-assets/alpha6/plink2_mac_avx2_20250129.zip",
|
| 11 |
+
"macos_arm64": "https://s3.amazonaws.com/plink2-assets/alpha6/plink2_mac_arm64_20250129.zip"
|
| 12 |
+
},
|
| 13 |
+
"binary_path": "plink2",
|
| 14 |
+
"version_command": "--version"
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"name": "IQ-TREE",
|
| 18 |
+
"function_name": "install_iqtree",
|
| 19 |
+
"description": "Efficient phylogenetic software by maximum likelihood",
|
| 20 |
+
"website": "http://www.iqtree.org/",
|
| 21 |
+
"downloads": {
|
| 22 |
+
"linux": "https://github.com/iqtree/iqtree2/releases/download/v2.2.2.7/iqtree-2.2.2.7-Linux.tar.gz",
|
| 23 |
+
"macos_intel": "https://github.com/iqtree/iqtree2/releases/download/v2.2.2.7/iqtree-2.2.2.7-MacOSX.zip",
|
| 24 |
+
"macos_arm64": "https://github.com/iqtree/iqtree2/releases/download/v2.2.2.7/iqtree-2.2.2.7-MacOSX.zip"
|
| 25 |
+
},
|
| 26 |
+
"binary_path": "bin/iqtree2",
|
| 27 |
+
"version_command": "--version"
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"name": "GCTA",
|
| 31 |
+
"function_name": "install_gcta",
|
| 32 |
+
"description": "Genome-wide Complex Trait Analysis",
|
| 33 |
+
"website": "https://yanglab.westlake.edu.cn/software/gcta/",
|
| 34 |
+
"downloads": {
|
| 35 |
+
"linux": "https://yanglab.westlake.edu.cn/software/gcta/bin/gcta-1.94.4-linux-kernel-3-x86_64.zip",
|
| 36 |
+
"macos_intel": "https://yanglab.westlake.edu.cn/software/gcta/bin/gcta-1.94.1-macOS-x86_64.zip",
|
| 37 |
+
"macos_arm64": "https://yanglab.westlake.edu.cn/software/gcta/bin/gcta-1.94.3-macOS-arm64.zip"
|
| 38 |
+
},
|
| 39 |
+
"binary_path": "gcta64",
|
| 40 |
+
"version_command": ""
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"name": "BWA",
|
| 44 |
+
"function_name": "install_bwa",
|
| 45 |
+
"description": "Burrow-Wheeler Aligner for short-read alignment",
|
| 46 |
+
"website": "https://github.com/lh3/bwa",
|
| 47 |
+
"downloads": {
|
| 48 |
+
"linux": "https://github.com/lh3/bwa.git",
|
| 49 |
+
"macos_intel": "https://github.com/lh3/bwa.git",
|
| 50 |
+
"macos_arm64": "https://github.com/lh3/bwa.git"
|
| 51 |
+
},
|
| 52 |
+
"binary_path": "bwa",
|
| 53 |
+
"version_command": ""
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"name": "FastTree",
|
| 57 |
+
"function_name": "install_fasttree",
|
| 58 |
+
"description": "Approximately-Maximum-Likelihood phylogenetic trees from alignments",
|
| 59 |
+
"website": "https://morgannprice.github.io/fasttree/",
|
| 60 |
+
"downloads": {
|
| 61 |
+
"linux": "https://morgannprice.github.io/fasttree/FastTree.c",
|
| 62 |
+
"macos_intel": "https://morgannprice.github.io/fasttree/FastTree.c",
|
| 63 |
+
"macos_arm64": "https://morgannprice.github.io/fasttree/FastTree.c"
|
| 64 |
+
},
|
| 65 |
+
"binary_path": "FastTree",
|
| 66 |
+
"version_command": "-help | head -n 2"
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"name": "MUSCLE",
|
| 70 |
+
"function_name": "install_muscle",
|
| 71 |
+
"description": "Multiple sequence alignment with high accuracy and performance",
|
| 72 |
+
"website": "https://github.com/rcedgar/muscle",
|
| 73 |
+
"downloads": {
|
| 74 |
+
"linux": "https://github.com/rcedgar/muscle/releases/download/v5.3/muscle-linux-x86.v5.3",
|
| 75 |
+
"macos_arm64": "https://github.com/rcedgar/muscle/releases/download/v5.3/muscle-osx-arm64.v5.3"
|
| 76 |
+
},
|
| 77 |
+
"binary_path": "muscle",
|
| 78 |
+
"version_command": "-version"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"name": "HOMER",
|
| 82 |
+
"function_name": "install_homer",
|
| 83 |
+
"description": "Software for motif discovery and next-gen sequencing analysis",
|
| 84 |
+
"website": "http://homer.ucsd.edu/homer/",
|
| 85 |
+
"downloads": {
|
| 86 |
+
"linux": "http://homer.ucsd.edu/homer/configureHomer.pl",
|
| 87 |
+
"macos_intel": "http://homer.ucsd.edu/homer/configureHomer.pl",
|
| 88 |
+
"macos_arm64": "http://homer.ucsd.edu/homer/configureHomer.pl"
|
| 89 |
+
},
|
| 90 |
+
"binary_path": "bin/findMotifs.pl",
|
| 91 |
+
"version_command": "-h"
|
| 92 |
+
}
|
| 93 |
+
]
|
| 94 |
+
}
|
biomni_env/environment.yml
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: biomni_e1
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
- defaults
|
| 5 |
+
- bioconda
|
| 6 |
+
dependencies:
|
| 7 |
+
- python=3.11
|
| 8 |
+
- pip
|
| 9 |
+
- pip:
|
| 10 |
+
- numpy==2.1
|
| 11 |
+
- pandas
|
| 12 |
+
- matplotlib
|
| 13 |
+
- scipy
|
| 14 |
+
- statsmodels
|
| 15 |
+
- scikit-learn
|
| 16 |
+
- gradio
|
| 17 |
+
- langchain
|
| 18 |
+
- langgraph==0.3.18
|
| 19 |
+
- langchain-openai
|
| 20 |
+
- langchain-anthropic
|
| 21 |
+
- langchain-ollama
|
| 22 |
+
- langchain-community
|
| 23 |
+
- openai
|
| 24 |
+
- beautifulsoup4
|
| 25 |
+
- lxml
|
| 26 |
+
- tqdm
|
| 27 |
+
- seaborn
|
| 28 |
+
- networkx
|
| 29 |
+
- requests
|
| 30 |
+
- pyyaml
|
| 31 |
+
- jupyter
|
| 32 |
+
- notebook
|
| 33 |
+
- ipykernel
|
| 34 |
+
- pytest
|
| 35 |
+
- transformers
|
| 36 |
+
- sentencepiece
|
| 37 |
+
- mcp
|
| 38 |
+
- tooluniverse
|
| 39 |
+
- python-dotenv
|
biomni_env/fixed_env.yml
ADDED
|
@@ -0,0 +1,742 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: biomni_e1
|
| 2 |
+
channels:
|
| 3 |
+
- bioconda
|
| 4 |
+
- conda-forge
|
| 5 |
+
- defaults
|
| 6 |
+
dependencies:
|
| 7 |
+
- altair=5.5.0
|
| 8 |
+
- asttokens=3.0.0
|
| 9 |
+
- attrs=25.3.0
|
| 10 |
+
- aws-c-auth=0.9.0
|
| 11 |
+
- aws-c-cal=0.9.2
|
| 12 |
+
- aws-c-common=0.12.4
|
| 13 |
+
- aws-c-compression=0.3.1
|
| 14 |
+
- aws-c-event-stream=0.5.5
|
| 15 |
+
- aws-c-http=0.10.4
|
| 16 |
+
- aws-c-io=0.21.2
|
| 17 |
+
- aws-c-mqtt=0.13.3
|
| 18 |
+
- aws-c-s3=0.8.6
|
| 19 |
+
- aws-c-sdkutils=0.2.4
|
| 20 |
+
- aws-checksums=0.2.7
|
| 21 |
+
- aws-crt-cpp=0.33.1
|
| 22 |
+
- aws-sdk-cpp=1.11.606
|
| 23 |
+
- azure-core-cpp=1.16.0
|
| 24 |
+
- azure-identity-cpp=1.12.0
|
| 25 |
+
- azure-storage-blobs-cpp=12.14.0
|
| 26 |
+
- azure-storage-common-cpp=12.10.0
|
| 27 |
+
- azure-storage-files-datalake-cpp=12.12.0
|
| 28 |
+
- bedtools=2.31.1
|
| 29 |
+
- biopython=1.85
|
| 30 |
+
- blast=2.16.0
|
| 31 |
+
- blinker=1.9.0
|
| 32 |
+
- bokeh=3.7.3
|
| 33 |
+
- bowtie2=2.5.4
|
| 34 |
+
- brotli-bin=1.1.0
|
| 35 |
+
- brotli-python=1.1.0
|
| 36 |
+
- bwa=0.7.19
|
| 37 |
+
- bzip2=1.0.8
|
| 38 |
+
- c-ares=1.34.5
|
| 39 |
+
- ca-certificates=2025.8.3
|
| 40 |
+
- cairo=1.18.4
|
| 41 |
+
- certifi=2025.8.3
|
| 42 |
+
- cffi=1.17.1
|
| 43 |
+
- charset-normalizer=3.4.2
|
| 44 |
+
- contourpy=1.3.3
|
| 45 |
+
- curl=8.14.1
|
| 46 |
+
- cycler=0.12.1
|
| 47 |
+
- decorator=5.2.1
|
| 48 |
+
- diamond=2.1.13
|
| 49 |
+
- entrez-direct=22.4
|
| 50 |
+
- exceptiongroup=1.3.0
|
| 51 |
+
- executing=2.2.0
|
| 52 |
+
- fastqc=0.12.1
|
| 53 |
+
- font-ttf-dejavu-sans-mono=2.37
|
| 54 |
+
- font-ttf-inconsolata=3.000
|
| 55 |
+
- font-ttf-source-code-pro=2.038
|
| 56 |
+
- font-ttf-ubuntu=0.83
|
| 57 |
+
- fontconfig=2.15.0
|
| 58 |
+
- fonts-conda-ecosystem=1
|
| 59 |
+
- fonts-conda-forge=1
|
| 60 |
+
- fonttools=4.59.0
|
| 61 |
+
- freetype=2.13.3
|
| 62 |
+
- gflags=2.2.2
|
| 63 |
+
- giflib=5.2.2
|
| 64 |
+
- gitdb=4.0.12
|
| 65 |
+
- gitpython=3.1.45
|
| 66 |
+
- glog=0.7.1
|
| 67 |
+
- gmp=6.3.0
|
| 68 |
+
- graphite2=1.3.14
|
| 69 |
+
- gseapy=1.1.9
|
| 70 |
+
- h2=4.2.0
|
| 71 |
+
- harfbuzz=11.3.3
|
| 72 |
+
- hpack=4.1.0
|
| 73 |
+
- htslib=1.22.1
|
| 74 |
+
- hyperframe=6.1.0
|
| 75 |
+
- icu=75.1
|
| 76 |
+
- idna=3.10
|
| 77 |
+
- importlib-metadata=8.7.0
|
| 78 |
+
- infernal=1.1.5
|
| 79 |
+
- ipython=9.4.0
|
| 80 |
+
- ipython_pygments_lexers=1.1.1
|
| 81 |
+
- jedi=0.19.2
|
| 82 |
+
- jinja2=3.1.6
|
| 83 |
+
- jsonschema=4.25.0
|
| 84 |
+
- jsonschema-specifications=2025.4.1
|
| 85 |
+
- kiwisolver=1.4.8
|
| 86 |
+
- krb5=1.21.3
|
| 87 |
+
- lcms2=2.17
|
| 88 |
+
- lerc=4.0.0
|
| 89 |
+
- libabseil=20250512.1
|
| 90 |
+
- libarrow=21.0.0
|
| 91 |
+
- libarrow-acero=21.0.0
|
| 92 |
+
- libarrow-compute=21.0.0
|
| 93 |
+
- libarrow-dataset=21.0.0
|
| 94 |
+
- libarrow-substrait=21.0.0
|
| 95 |
+
- libasprintf=0.25.1
|
| 96 |
+
- libblas=3.9.0
|
| 97 |
+
- libbrotlicommon=1.1.0
|
| 98 |
+
- libbrotlidec=1.1.0
|
| 99 |
+
- libbrotlienc=1.1.0
|
| 100 |
+
- libcblas=3.9.0
|
| 101 |
+
- libcrc32c=1.1.2
|
| 102 |
+
- libcurl=8.14.1
|
| 103 |
+
- libdeflate=1.22
|
| 104 |
+
- libedit=3.1.20250104
|
| 105 |
+
- libev=4.33
|
| 106 |
+
- libevent=2.1.12
|
| 107 |
+
- libexpat=2.7.1
|
| 108 |
+
- libffi=3.4.6
|
| 109 |
+
- libfreetype=2.13.3
|
| 110 |
+
- libfreetype6=2.13.3
|
| 111 |
+
- libgettextpo=0.25.1
|
| 112 |
+
- libgfortran=15.1.0
|
| 113 |
+
- libgfortran5=15.1.0
|
| 114 |
+
- libglib=2.84.2
|
| 115 |
+
- libgoogle-cloud=2.39.0
|
| 116 |
+
- libgoogle-cloud-storage=2.39.0
|
| 117 |
+
- libgrpc=1.73.1
|
| 118 |
+
- libiconv=1.18
|
| 119 |
+
- libidn2=2.3.8
|
| 120 |
+
- libjpeg-turbo=3.1.0
|
| 121 |
+
- liblapack=3.9.0
|
| 122 |
+
- liblzma=5.8.1
|
| 123 |
+
- libnghttp2=1.64.0
|
| 124 |
+
- libopenblas=0.3.30
|
| 125 |
+
- libopentelemetry-cpp=1.21.0
|
| 126 |
+
- libopentelemetry-cpp-headers=1.21.0
|
| 127 |
+
- libparquet=21.0.0
|
| 128 |
+
- libpng=1.6.50
|
| 129 |
+
- libprotobuf=6.31.1
|
| 130 |
+
- libre2-11=2025.07.22
|
| 131 |
+
- libsqlite=3.50.4
|
| 132 |
+
- libssh2=1.11.1
|
| 133 |
+
- libthrift=0.22.0
|
| 134 |
+
- libtiff=4.7.0
|
| 135 |
+
- libunistring=0.9.10
|
| 136 |
+
- libutf8proc=2.10.0
|
| 137 |
+
- libwebp-base=1.6.0
|
| 138 |
+
- libxml2=2.13.8
|
| 139 |
+
- libzlib=1.3.1
|
| 140 |
+
- lz4-c=1.10.0
|
| 141 |
+
- mafft=7.526
|
| 142 |
+
# - mageck=0.5.9.5
|
| 143 |
+
- markupsafe=3.0.2
|
| 144 |
+
- matplotlib-base=3.10.5
|
| 145 |
+
- matplotlib-inline=0.1.7
|
| 146 |
+
- mpfr=4.2.1
|
| 147 |
+
- munkres=1.1.4
|
| 148 |
+
- narwhals=2.0.1
|
| 149 |
+
- ncbi-vdb=3.2.1
|
| 150 |
+
- ncurses=6.5
|
| 151 |
+
- nlohmann_json=3.12.0
|
| 152 |
+
- openjdk=23.0.2
|
| 153 |
+
- openjpeg=2.5.3
|
| 154 |
+
- openssl=3.5.1
|
| 155 |
+
- orc=2.1.3
|
| 156 |
+
- packaging=25.0
|
| 157 |
+
- pandas=2.3.1
|
| 158 |
+
- parso=0.8.4
|
| 159 |
+
- pcre2=10.45
|
| 160 |
+
- perl=5.32.1
|
| 161 |
+
- perl-archive-tar=3.04
|
| 162 |
+
- perl-carp=1.50
|
| 163 |
+
- perl-common-sense=3.75
|
| 164 |
+
- perl-compress-raw-bzip2=2.201
|
| 165 |
+
- perl-compress-raw-zlib=2.202
|
| 166 |
+
- perl-encode=3.21
|
| 167 |
+
- perl-exporter=5.74
|
| 168 |
+
- perl-exporter-tiny=1.002002
|
| 169 |
+
- perl-extutils-makemaker=7.70
|
| 170 |
+
- perl-io-compress=2.201
|
| 171 |
+
- perl-io-zlib=1.15
|
| 172 |
+
- perl-json=4.10
|
| 173 |
+
- perl-json-xs=4.03
|
| 174 |
+
- perl-list-moreutils=0.430
|
| 175 |
+
- perl-list-moreutils-xs=0.430
|
| 176 |
+
- perl-parent=0.243
|
| 177 |
+
- perl-pathtools=3.75
|
| 178 |
+
- perl-scalar-list-utils=1.70
|
| 179 |
+
- perl-storable=3.15
|
| 180 |
+
- perl-types-serialiser=1.01
|
| 181 |
+
- pexpect=4.9.0
|
| 182 |
+
- pickleshare=0.7.5
|
| 183 |
+
- pillow=11.3.0
|
| 184 |
+
- pip=25.2
|
| 185 |
+
- pixman=0.46.4
|
| 186 |
+
# - plannotate=1.2.0
|
| 187 |
+
- prometheus-cpp=1.3.0
|
| 188 |
+
- prompt-toolkit=3.0.51
|
| 189 |
+
- protobuf=6.31.1
|
| 190 |
+
- ptyprocess=0.7.0
|
| 191 |
+
- pure_eval=0.2.3
|
| 192 |
+
- pyarrow=21.0.0
|
| 193 |
+
- pyarrow-core=21.0.0
|
| 194 |
+
- pycparser=2.22
|
| 195 |
+
- pydeck=0.9.1
|
| 196 |
+
- pygments=2.19.2
|
| 197 |
+
- pyopengl=3.1.9
|
| 198 |
+
- pyparsing=3.2.3
|
| 199 |
+
- pysocks=1.7.1
|
| 200 |
+
- python=3.11.13
|
| 201 |
+
- python-dateutil=2.9.0.post0
|
| 202 |
+
- python-tzdata=2025.2
|
| 203 |
+
- python_abi=3.11
|
| 204 |
+
- pytz=2025.2
|
| 205 |
+
- pyyaml=6.0.2
|
| 206 |
+
- qhull=2020.2
|
| 207 |
+
- re2=2025.07.22
|
| 208 |
+
- readline=8.2
|
| 209 |
+
- referencing=0.36.2
|
| 210 |
+
- requests=2.32.4
|
| 211 |
+
- ripgrep=14.1.1
|
| 212 |
+
- rpds-py=0.26.0
|
| 213 |
+
- rpsbproc=0.5.0
|
| 214 |
+
- s2n=1.5.23
|
| 215 |
+
- samtools=1.22.1
|
| 216 |
+
- setuptools=80.9.0
|
| 217 |
+
- six=1.17.0
|
| 218 |
+
- smmap=5.0.2
|
| 219 |
+
- snappy=1.2.2
|
| 220 |
+
- stack_data=0.6.3
|
| 221 |
+
- streamlit=1.47.1
|
| 222 |
+
- tabulate=0.9.0
|
| 223 |
+
- tenacity=9.1.2
|
| 224 |
+
- tk=8.6.13
|
| 225 |
+
- toml=0.10.2
|
| 226 |
+
- tornado=6.5.1
|
| 227 |
+
- traitlets=5.14.3
|
| 228 |
+
- trimmomatic=0.39
|
| 229 |
+
- trnascan-se=2.0.12
|
| 230 |
+
- typing-extensions=4.14.1
|
| 231 |
+
- typing_extensions=4.14.1
|
| 232 |
+
- tzdata=2025b
|
| 233 |
+
- unicodedata2=16.0.0
|
| 234 |
+
- urllib3=2.5.0
|
| 235 |
+
- watchdog=6.0.0
|
| 236 |
+
- wcwidth=0.2.13
|
| 237 |
+
- wget=1.21.4
|
| 238 |
+
- wheel=0.45.1
|
| 239 |
+
- xyzservices=2025.4.0
|
| 240 |
+
- yaml=0.2.5
|
| 241 |
+
- zipp=3.23.0
|
| 242 |
+
- zlib=1.3.1
|
| 243 |
+
- zstd=1.5.7
|
| 244 |
+
- pip:
|
| 245 |
+
- absl-py==2.3.1
|
| 246 |
+
- adjusttext==1.3.0
|
| 247 |
+
- aiobotocore
|
| 248 |
+
- aiofiles==24.1.0
|
| 249 |
+
- aiohappyeyeballs==2.6.1
|
| 250 |
+
- aiohttp==3.12.15
|
| 251 |
+
- aioitertools==0.12.0
|
| 252 |
+
- aiosignal==1.4.0
|
| 253 |
+
- airr==1.5.1
|
| 254 |
+
- alabaster==1.0.0
|
| 255 |
+
- anndata==0.12.1
|
| 256 |
+
- annotated-types==0.7.0
|
| 257 |
+
- annoy==1.17.3
|
| 258 |
+
- anthropic==0.60.0
|
| 259 |
+
- anyio==4.9.0
|
| 260 |
+
- appdirs==1.4.4
|
| 261 |
+
- arboreto==0.1.6
|
| 262 |
+
- argon2-cffi==25.1.0
|
| 263 |
+
- argon2-cffi-bindings==25.1.0
|
| 264 |
+
- array-api-compat==1.12.0
|
| 265 |
+
- arrow==1.3.0
|
| 266 |
+
- arviz==0.22.0
|
| 267 |
+
- arxiv==2.2.0
|
| 268 |
+
- asciitree==0.3.3
|
| 269 |
+
- async-lru==2.0.5
|
| 270 |
+
- authlib==1.6.1
|
| 271 |
+
- autograd==1.8.0
|
| 272 |
+
- autograd-gamma==0.5.0
|
| 273 |
+
- awkward==2.8.6
|
| 274 |
+
- awkward-cpp==48
|
| 275 |
+
- babel==2.17.0
|
| 276 |
+
- beautifulsoup4==4.13.4
|
| 277 |
+
- bibtexparser==1.4.3
|
| 278 |
+
- biom-format==2.1.16
|
| 279 |
+
# - biomni==0.0.8
|
| 280 |
+
- biopandas==0.5.1
|
| 281 |
+
- biotite==1.4.0
|
| 282 |
+
- biotraj==1.2.2
|
| 283 |
+
- bleach==6.2.0
|
| 284 |
+
- blitzgsea==1.3.54
|
| 285 |
+
- blosc2==3.6.1
|
| 286 |
+
- boltons==25.0.0
|
| 287 |
+
- boto3
|
| 288 |
+
- botocore
|
| 289 |
+
- brotli==1.1.0
|
| 290 |
+
- cachetools==5.5.2
|
| 291 |
+
- cellpose==4.0.6
|
| 292 |
+
- cellxgene-census==1.17.0
|
| 293 |
+
- cftime==1.6.4.post1
|
| 294 |
+
- chex==0.1.90
|
| 295 |
+
- click==8.2.1
|
| 296 |
+
- clikit==0.6.2
|
| 297 |
+
- cloudpickle==3.1.1
|
| 298 |
+
- cobra==0.29.1
|
| 299 |
+
- colorcet==3.1.0
|
| 300 |
+
- coloredlogs==15.0.1
|
| 301 |
+
- comm==0.2.3
|
| 302 |
+
- community==1.0.0b1
|
| 303 |
+
- cooler==0.10.4
|
| 304 |
+
- crashtest==0.3.1
|
| 305 |
+
- cryosparc-tools==4.7.1
|
| 306 |
+
- cryptography==45.0.5
|
| 307 |
+
- ctxcore==0.2.0
|
| 308 |
+
- cyclopts==3.22.5
|
| 309 |
+
- cython==0.29.37
|
| 310 |
+
- cytoolz==1.0.1
|
| 311 |
+
- cyvcf2==0.31.1
|
| 312 |
+
- dask==2024.11.2
|
| 313 |
+
- dask-expr==1.1.19
|
| 314 |
+
- dask-image==2024.5.3
|
| 315 |
+
- dataclasses==0.6
|
| 316 |
+
- dataclasses-json==0.6.7
|
| 317 |
+
- datashader==0.18.1
|
| 318 |
+
- debugpy==1.8.15
|
| 319 |
+
- decoupler==2.1.1
|
| 320 |
+
- defusedxml==0.7.1
|
| 321 |
+
- demes==0.2.3
|
| 322 |
+
- depinfo==2.2.0
|
| 323 |
+
- deprecated==1.2.18
|
| 324 |
+
- deprecation==2.1.0
|
| 325 |
+
- dill==0.4.0
|
| 326 |
+
- diskcache==5.6.3
|
| 327 |
+
- distributed==2024.11.2
|
| 328 |
+
- distro==1.9.0
|
| 329 |
+
- dnspython==2.7.0
|
| 330 |
+
- docrep==0.3.2
|
| 331 |
+
- docstring-parser==0.17.0
|
| 332 |
+
- docutils==0.21.2
|
| 333 |
+
- domdf-python-tools==3.10.0
|
| 334 |
+
- drmaa==0.7.9
|
| 335 |
+
- email-validator==2.2.0
|
| 336 |
+
- enum-tools==0.13.0
|
| 337 |
+
- equinox==0.13.0
|
| 338 |
+
- et-xmlfile==2.0.0
|
| 339 |
+
- etils==1.13.0
|
| 340 |
+
- faiss-cpu==1.11.0.post1
|
| 341 |
+
- fake-useragent==2.2.0
|
| 342 |
+
- fanc==0.9.28
|
| 343 |
+
- fast-array-utils==1.2.1
|
| 344 |
+
- fastapi==0.116.1
|
| 345 |
+
- fasteners==0.19
|
| 346 |
+
- fastjsonschema==2.21.1
|
| 347 |
+
- fastmcp==2.11.0
|
| 348 |
+
- fastprogress==1.0.3
|
| 349 |
+
- fastremap==1.17.2
|
| 350 |
+
- fcsparser==0.2.4
|
| 351 |
+
- feedparser==6.0.11
|
| 352 |
+
- ffmpy==0.6.1
|
| 353 |
+
- filelock==3.18.0
|
| 354 |
+
- filetype==1.2.0
|
| 355 |
+
- fill-voids==2.1.0
|
| 356 |
+
- flask==3.1.1
|
| 357 |
+
- flax==0.11.0
|
| 358 |
+
- flowcytometrytools==0.5.1
|
| 359 |
+
- formulaic==1.2.0
|
| 360 |
+
- formulaic-contrasts==1.0.0
|
| 361 |
+
- fqdn==1.5.1
|
| 362 |
+
- free-proxy==1.1.3
|
| 363 |
+
- frozendict==2.4.6
|
| 364 |
+
- frozenlist==1.7.0
|
| 365 |
+
- fsspec==2025.7.0
|
| 366 |
+
- future==1.0.0
|
| 367 |
+
- fuzzywuzzy==0.18.0
|
| 368 |
+
- genomic-regions==0.0.10
|
| 369 |
+
- geopandas==1.1.1
|
| 370 |
+
- gget==0.29.2
|
| 371 |
+
- google-ai-generativelanguage==0.6.18
|
| 372 |
+
- google-api-core==2.25.1
|
| 373 |
+
- google-auth==2.40.3
|
| 374 |
+
- googleapis-common-protos==1.70.0
|
| 375 |
+
- googlesearch-python==1.3.0
|
| 376 |
+
- gradio==5.39.0
|
| 377 |
+
- gradio-client==1.11.0
|
| 378 |
+
- graphql-core==3.2.6
|
| 379 |
+
- greenlet==3.2.3
|
| 380 |
+
- gridmap==0.15.0
|
| 381 |
+
- groovy==0.1.2
|
| 382 |
+
- grpcio==1.74.0
|
| 383 |
+
- grpcio-status==1.74.0
|
| 384 |
+
- h11==0.16.0
|
| 385 |
+
- h5netcdf==1.6.3
|
| 386 |
+
- h5py==3.14.0
|
| 387 |
+
- harmony-pytorch==0.1.8
|
| 388 |
+
- hf-xet==1.1.5
|
| 389 |
+
- hmmlearn==0.3.3
|
| 390 |
+
- httpcore==1.0.9
|
| 391 |
+
# - httpstan==4.13.0
|
| 392 |
+
- httpx==0.28.1
|
| 393 |
+
- httpx-sse==0.4.1
|
| 394 |
+
- huggingface-hub==0.34.3
|
| 395 |
+
- humanfriendly==10.0
|
| 396 |
+
- humanize==4.12.3
|
| 397 |
+
- igraph==0.11.9
|
| 398 |
+
- imagecodecs==2025.8.2
|
| 399 |
+
- imageio==2.37.0
|
| 400 |
+
- imagesize==1.4.1
|
| 401 |
+
- importlib-resources==6.5.2
|
| 402 |
+
- inflect==7.5.0
|
| 403 |
+
- iniconfig==2.1.0
|
| 404 |
+
- interface-meta==1.3.0
|
| 405 |
+
- interlap==0.2.7
|
| 406 |
+
- intervaltree==3.1.0
|
| 407 |
+
- ipykernel==6.30.0
|
| 408 |
+
- ipywidgets==8.1.7
|
| 409 |
+
- isodate==0.7.2
|
| 410 |
+
- isoduration==20.11.0
|
| 411 |
+
- itsdangerous==2.2.0
|
| 412 |
+
- jax==0.6.2
|
| 413 |
+
- jaxlib==0.6.2
|
| 414 |
+
- jaxopt==0.8.5
|
| 415 |
+
- jaxtyping==0.3.2
|
| 416 |
+
- jiter==0.10.0
|
| 417 |
+
- jmespath==1.0.1
|
| 418 |
+
- joblib==1.5.1
|
| 419 |
+
- json5==0.12.0
|
| 420 |
+
- jsonpatch==1.33
|
| 421 |
+
- jsonpointer==3.0.0
|
| 422 |
+
- jsonschema-path==0.3.4
|
| 423 |
+
- jupyter==1.1.1
|
| 424 |
+
- jupyter-client==8.6.3
|
| 425 |
+
- jupyter-console==6.6.3
|
| 426 |
+
- jupyter-core==5.8.1
|
| 427 |
+
- jupyter-events==0.12.0
|
| 428 |
+
- jupyter-lsp==2.2.6
|
| 429 |
+
- jupyter-server==2.16.0
|
| 430 |
+
- jupyter-server-terminals==0.5.3
|
| 431 |
+
- jupyterlab==4.4.5
|
| 432 |
+
- jupyterlab-pygments==0.3.0
|
| 433 |
+
- jupyterlab-server==2.27.3
|
| 434 |
+
- jupyterlab-widgets==3.0.15
|
| 435 |
+
- lamin-utils==0.15.0
|
| 436 |
+
- langchain==0.3.27
|
| 437 |
+
- langchain-anthropic==0.3.18
|
| 438 |
+
- langchain-community==0.3.27
|
| 439 |
+
- langchain-core==0.3.72
|
| 440 |
+
# - langchain-google-genai==2.1.8
|
| 441 |
+
- langchain-ollama==0.3.6
|
| 442 |
+
- langchain-openai==0.3.28
|
| 443 |
+
- langchain-text-splitters==0.3.9
|
| 444 |
+
- langgraph==0.3.18
|
| 445 |
+
- langgraph-checkpoint==2.1.1
|
| 446 |
+
- langgraph-prebuilt==0.1.8
|
| 447 |
+
- langgraph-sdk==0.1.74
|
| 448 |
+
- langsmith==0.4.10
|
| 449 |
+
- lark==1.2.2
|
| 450 |
+
- lazy-loader==0.4
|
| 451 |
+
- lazy-object-proxy==1.11.0
|
| 452 |
+
- legacy-api-wrap==1.4.1
|
| 453 |
+
- legendkit==0.3.6
|
| 454 |
+
- levenshtein==0.27.1
|
| 455 |
+
- lifelines==0.30.0
|
| 456 |
+
- lightning==2.5.2
|
| 457 |
+
- lightning-utilities==0.15.0
|
| 458 |
+
- lineax==0.0.8
|
| 459 |
+
- llvmlite==0.44.0
|
| 460 |
+
- locket==1.0.0
|
| 461 |
+
- logomaker==0.8.7
|
| 462 |
+
- loompy==3.0.8
|
| 463 |
+
- looseversion==1.1.2
|
| 464 |
+
- lxml==6.0.0
|
| 465 |
+
- lz4==4.4.4
|
| 466 |
+
- macs2==2.2.9.1
|
| 467 |
+
- markdown==3.8.2
|
| 468 |
+
- markdown-it-py==3.0.0
|
| 469 |
+
- marshmallow==3.26.1
|
| 470 |
+
- marsilea==0.5.4
|
| 471 |
+
- matplotlib-scalebar==0.9.0
|
| 472 |
+
- mcp==1.12.3
|
| 473 |
+
- mdurl==0.1.2
|
| 474 |
+
- mistune==3.1.3
|
| 475 |
+
- ml-collections==1.1.0
|
| 476 |
+
- ml-dtypes==0.5.3
|
| 477 |
+
- mmtf-python==1.1.3
|
| 478 |
+
- more-itertools==10.7.0
|
| 479 |
+
- mpmath==1.3.0
|
| 480 |
+
- msgpack==1.1.1
|
| 481 |
+
- msgpack-numpy==0.4.8
|
| 482 |
+
- msprime==1.3.4
|
| 483 |
+
- mudata==0.3.2
|
| 484 |
+
- multidict==6.6.3
|
| 485 |
+
- multipledispatch==1.0.0
|
| 486 |
+
- multiprocess==0.70.18
|
| 487 |
+
- multiprocessing-on-dill==3.5.0a4
|
| 488 |
+
- multiscale-spatial-image==2.0.2
|
| 489 |
+
- muon==0.1.7
|
| 490 |
+
- mypy-extensions==1.1.0
|
| 491 |
+
- mysql-connector-python==9.4.0
|
| 492 |
+
- natsort==8.4.0
|
| 493 |
+
- nbclient==0.10.2
|
| 494 |
+
- nbconvert==7.16.6
|
| 495 |
+
- nbformat==5.10.4
|
| 496 |
+
- ncls==0.0.70
|
| 497 |
+
- ndindex==1.10.0
|
| 498 |
+
- nest-asyncio==1.6.0
|
| 499 |
+
- netcdf4==1.7.2
|
| 500 |
+
- networkx==3.5
|
| 501 |
+
- newick==1.10.0
|
| 502 |
+
- notebook==7.4.4
|
| 503 |
+
- notebook-shim==0.2.4
|
| 504 |
+
- numba==0.61.2
|
| 505 |
+
- numcodecs==0.15.1
|
| 506 |
+
- numexpr==2.11.0
|
| 507 |
+
- numpy==2.1.0
|
| 508 |
+
- numpy-groupies==0.11.3
|
| 509 |
+
- numpyro==0.18.0
|
| 510 |
+
# - nvidia-cublas-cu12==12.6.4.1
|
| 511 |
+
# - nvidia-cuda-cupti-cu12==12.6.80
|
| 512 |
+
# - nvidia-cuda-nvrtc-cu12==12.6.77
|
| 513 |
+
# - nvidia-cuda-runtime-cu12==12.6.77
|
| 514 |
+
# - nvidia-cudnn-cu12==9.5.1.17
|
| 515 |
+
# - nvidia-cufft-cu12==11.3.0.4
|
| 516 |
+
# - nvidia-cufile-cu12==1.11.1.6
|
| 517 |
+
# - nvidia-curand-cu12==10.3.7.77
|
| 518 |
+
# - nvidia-cusolver-cu12==11.7.1.2
|
| 519 |
+
# - nvidia-cusparse-cu12==12.5.4.2
|
| 520 |
+
# - nvidia-cusparselt-cu12==0.6.3
|
| 521 |
+
# - nvidia-nccl-cu12==2.26.2
|
| 522 |
+
# - nvidia-nvjitlink-cu12==12.6.85
|
| 523 |
+
# - nvidia-nvtx-cu12==12.6.77
|
| 524 |
+
- ollama==0.5.1
|
| 525 |
+
- ome-zarr==0.11.1
|
| 526 |
+
- omnipath==1.0.12
|
| 527 |
+
- openai==1.98.0
|
| 528 |
+
- openapi-core==0.19.5
|
| 529 |
+
- openapi-pydantic==0.5.1
|
| 530 |
+
- openapi-schema-validator==0.6.3
|
| 531 |
+
- openapi-spec-validator==0.7.2
|
| 532 |
+
- opencv-python==4.12.0.88
|
| 533 |
+
- opencv-python-headless==4.12.0.88
|
| 534 |
+
- openmm==8.3.1
|
| 535 |
+
- openpyxl==3.1.5
|
| 536 |
+
- opt-einsum==3.4.0
|
| 537 |
+
- optax==0.2.5
|
| 538 |
+
- optlang==1.8.3
|
| 539 |
+
- orbax-checkpoint==0.11.20
|
| 540 |
+
- orjson==3.11.1
|
| 541 |
+
- ormsgpack==1.10.0
|
| 542 |
+
- ott-jax==0.5.1
|
| 543 |
+
- outcome==1.3.0.post0
|
| 544 |
+
- overrides==7.7.0
|
| 545 |
+
- pandocfilters==1.5.1
|
| 546 |
+
- param==2.2.1
|
| 547 |
+
- parse==1.20.2
|
| 548 |
+
- partd==1.4.2
|
| 549 |
+
- pastel==0.2.1
|
| 550 |
+
- pathable==0.4.4
|
| 551 |
+
- patsy==1.0.1
|
| 552 |
+
- pertpy==1.0.1
|
| 553 |
+
- pims==0.7
|
| 554 |
+
- platformdirs==4.3.8
|
| 555 |
+
- pluggy==1.6.0
|
| 556 |
+
- ply==3.11
|
| 557 |
+
- pooch==1.8.2
|
| 558 |
+
- progressbar2==4.5.0
|
| 559 |
+
- prometheus-client==0.22.1
|
| 560 |
+
- propcache==0.3.2
|
| 561 |
+
- proto-plus==1.26.1
|
| 562 |
+
- psutil==7.0.0
|
| 563 |
+
- pubchempy==1.0.4
|
| 564 |
+
- py-cpuinfo==9.0.0
|
| 565 |
+
- pyarrow-hotfix==0.7
|
| 566 |
+
- pyasn1==0.6.1
|
| 567 |
+
- pyasn1-modules==0.4.2
|
| 568 |
+
- pybedtools==0.12.0
|
| 569 |
+
- pybigwig==0.3.24
|
| 570 |
+
- pyct==0.5.0
|
| 571 |
+
- pydantic==2.11.7
|
| 572 |
+
- pydantic-core==2.33.2
|
| 573 |
+
- pydantic-settings==2.10.1
|
| 574 |
+
- pydeseq2==0.5.2
|
| 575 |
+
- pydub==0.25.1
|
| 576 |
+
- pyfaidx==0.8.1.4
|
| 577 |
+
- pykalman==0.10.1
|
| 578 |
+
- pylev==1.4.0
|
| 579 |
+
- pyliftover==0.4.1
|
| 580 |
+
- pymassspec==2.6.0.post1
|
| 581 |
+
- pymc3==3.11.4
|
| 582 |
+
- pymed==0.8.9
|
| 583 |
+
- pymzml==2.5.11
|
| 584 |
+
- pynndescent==0.5.13
|
| 585 |
+
- pyogrio==0.11.1
|
| 586 |
+
- pyomo==6.9.2
|
| 587 |
+
- pypdf2==3.0.1
|
| 588 |
+
- pyperclip==1.9.0
|
| 589 |
+
- pyproj==3.7.1
|
| 590 |
+
- pyranges==0.1.4
|
| 591 |
+
- pyro-api==0.1.2
|
| 592 |
+
- pyro-ppl==1.9.1
|
| 593 |
+
- pysam==0.23.3
|
| 594 |
+
- pyscenic==0.12.1
|
| 595 |
+
- pysimdjson==6.0.2
|
| 596 |
+
# - pystan==3.10.0
|
| 597 |
+
- pytdc==0.4.0
|
| 598 |
+
- pytest==8.4.1
|
| 599 |
+
- python-dotenv==1.1.1
|
| 600 |
+
- python-json-logger==3.3.0
|
| 601 |
+
- python-levenshtein==0.27.1
|
| 602 |
+
- python-libsbml==5.20.5
|
| 603 |
+
- python-multipart==0.0.20
|
| 604 |
+
- python-utils==3.9.1
|
| 605 |
+
- pytorch-lightning==2.5.2
|
| 606 |
+
- pyzmq==27.0.1
|
| 607 |
+
- rapidfuzz==3.13.0
|
| 608 |
+
- rdkit==2025.3.5
|
| 609 |
+
- rdkit-pypi==2022.9.5
|
| 610 |
+
- regex==2025.7.34
|
| 611 |
+
- reportlab==4.4.3
|
| 612 |
+
- requests-toolbelt==1.0.0
|
| 613 |
+
- rfc3339-validator==0.1.4
|
| 614 |
+
- rfc3986-validator==0.1.1
|
| 615 |
+
- rfc3987-syntax==1.1.0
|
| 616 |
+
- rich==14.1.0
|
| 617 |
+
- rich-rst==1.3.1
|
| 618 |
+
- roifile==2025.5.10
|
| 619 |
+
- roman-numerals-py==3.1.0
|
| 620 |
+
- rsa==4.9.1
|
| 621 |
+
- ruamel-yaml==0.18.14
|
| 622 |
+
- ruamel-yaml-clib==0.2.12
|
| 623 |
+
- ruff==0.12.7
|
| 624 |
+
- s3fs==2025.7.0
|
| 625 |
+
- s3transfer==0.13.1
|
| 626 |
+
- safehttpx==0.1.6
|
| 627 |
+
- safetensors==0.5.3
|
| 628 |
+
- scanpy==1.11.4
|
| 629 |
+
- scholarly==1.7.11
|
| 630 |
+
- scikit-base==0.12.4
|
| 631 |
+
- scikit-bio==0.7.0
|
| 632 |
+
- scikit-image==0.25.2
|
| 633 |
+
- scikit-learn==1.7.1
|
| 634 |
+
- scikit-misc==0.5.1
|
| 635 |
+
- scipy==1.15.0
|
| 636 |
+
- scirpy==0.22.1
|
| 637 |
+
- scrublet==0.2.3
|
| 638 |
+
- scvelo==0.3.3
|
| 639 |
+
- scvi-tools==1.3.3
|
| 640 |
+
- sdjson==0.5.0
|
| 641 |
+
- seaborn==0.13.2
|
| 642 |
+
- segment-anything==1.0
|
| 643 |
+
- selenium==4.34.2
|
| 644 |
+
- semantic-version==2.10.0
|
| 645 |
+
- semver==3.0.4
|
| 646 |
+
- send2trash==1.8.3
|
| 647 |
+
- sentencepiece==0.2.0
|
| 648 |
+
- session-info==1.0.1
|
| 649 |
+
- session-info2==0.2
|
| 650 |
+
- sgmllib3k==1.0.0
|
| 651 |
+
- shapely==2.1.1
|
| 652 |
+
- shellingham==1.5.4
|
| 653 |
+
- simplejson==3.20.1
|
| 654 |
+
- slicerator==1.1.0
|
| 655 |
+
- sniffio==1.3.1
|
| 656 |
+
- snowballstemmer==3.0.1
|
| 657 |
+
- somacore==1.0.28
|
| 658 |
+
- sorted-nearest==0.0.39
|
| 659 |
+
- sortedcontainers==2.4.0
|
| 660 |
+
- soupsieve==2.7
|
| 661 |
+
- sparse==0.17.0
|
| 662 |
+
- sparsecca==0.3.1
|
| 663 |
+
- spatial-image==1.2.2
|
| 664 |
+
- spatialdata==0.4.0
|
| 665 |
+
- sphinx==8.2.3
|
| 666 |
+
- sphinx-rtd-theme==3.0.2
|
| 667 |
+
- sphinxcontrib-applehelp==2.0.0
|
| 668 |
+
- sphinxcontrib-devhelp==2.0.0
|
| 669 |
+
- sphinxcontrib-htmlhelp==2.1.0
|
| 670 |
+
- sphinxcontrib-jquery==4.1
|
| 671 |
+
- sphinxcontrib-jsmath==1.0.1
|
| 672 |
+
- sphinxcontrib-qthelp==2.0.0
|
| 673 |
+
- sphinxcontrib-serializinghtml==2.0.0
|
| 674 |
+
- sqlalchemy==2.0.42
|
| 675 |
+
- squarify==0.4.4
|
| 676 |
+
- squidpy==1.6.5
|
| 677 |
+
- sse-starlette==3.0.2
|
| 678 |
+
- starlette==0.47.2
|
| 679 |
+
- statsmodels==0.14.5
|
| 680 |
+
- stdlib-list==0.11.1
|
| 681 |
+
- swiglpk==5.0.12
|
| 682 |
+
- sympy==1.14.0
|
| 683 |
+
- tables==3.10.2
|
| 684 |
+
- tblib==3.1.0
|
| 685 |
+
- tensorboard==2.20.0
|
| 686 |
+
- tensorboard-data-server==0.7.2
|
| 687 |
+
- tensorstore==0.1.76
|
| 688 |
+
- terminado==0.18.1
|
| 689 |
+
- texttable==1.7.0
|
| 690 |
+
- theano-pymc==1.1.2
|
| 691 |
+
- threadpoolctl==3.6.0
|
| 692 |
+
- tifffile==2025.6.11
|
| 693 |
+
- tiktoken==0.9.0
|
| 694 |
+
- tiledbsoma==1.17.1
|
| 695 |
+
- tinycss2==1.4.0
|
| 696 |
+
- tokenizers==0.21.4
|
| 697 |
+
- tomlkit==0.13.3
|
| 698 |
+
- tooluniverse==0.2.0
|
| 699 |
+
- toolz==1.0.0
|
| 700 |
+
- torch==2.7.1
|
| 701 |
+
- torchmetrics==1.8.0
|
| 702 |
+
- torchvision==0.22.1
|
| 703 |
+
- tqdm==4.67.1
|
| 704 |
+
- trackpy==0.7
|
| 705 |
+
- transformers==4.54.1
|
| 706 |
+
- treescope==0.1.9
|
| 707 |
+
- trio==0.30.0
|
| 708 |
+
- trio-websocket==0.12.2
|
| 709 |
+
# - triton==3.3.1
|
| 710 |
+
- tskit==0.6.4
|
| 711 |
+
- typeguard==4.4.4
|
| 712 |
+
- typer==0.16.0
|
| 713 |
+
- types-python-dateutil==2.9.0.20250708
|
| 714 |
+
- typing-inspect==0.9.0
|
| 715 |
+
- typing-inspection==0.4.1
|
| 716 |
+
- umap-learn==0.5.9.post2
|
| 717 |
+
- uri-template==1.3.0
|
| 718 |
+
- uvicorn==0.35.0
|
| 719 |
+
- validators==0.35.0
|
| 720 |
+
- viennarna==2.7.0
|
| 721 |
+
- wadler-lindig==0.1.7
|
| 722 |
+
- webargs==8.7.0
|
| 723 |
+
- webcolors==24.11.1
|
| 724 |
+
- webencodings==0.5.1
|
| 725 |
+
- websocket-client==1.8.0
|
| 726 |
+
- websockets==15.0.1
|
| 727 |
+
- werkzeug==3.1.1
|
| 728 |
+
- widgetsnbextension==4.0.14
|
| 729 |
+
- wrapt==1.17.2
|
| 730 |
+
- wsproto==1.2.0
|
| 731 |
+
- xarray==2024.11.0
|
| 732 |
+
- xarray-dataclass==3.0.0
|
| 733 |
+
- xarray-dataclasses==1.9.1
|
| 734 |
+
- xarray-einstats==0.9.1
|
| 735 |
+
- xarray-schema==0.0.3
|
| 736 |
+
- xarray-spatial==0.4.0
|
| 737 |
+
- yamlordereddictloader==0.4.2
|
| 738 |
+
- yarl==1.20.1
|
| 739 |
+
- zarr==2.18.7
|
| 740 |
+
- zict==3.0.0
|
| 741 |
+
- zstandard==0.23.0
|
| 742 |
+
- mi-googlesearch-python==1.3.0.post1
|
biomni_env/install_cli_tools.sh
ADDED
|
@@ -0,0 +1,912 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# Script to install command-line bioinformatics tools
|
| 4 |
+
|
| 5 |
+
# Set up colors for output
|
| 6 |
+
GREEN='\033[0;32m'
|
| 7 |
+
RED='\033[0;31m'
|
| 8 |
+
YELLOW='\033[1;33m'
|
| 9 |
+
BLUE='\033[0;34m'
|
| 10 |
+
NC='\033[0m' # No Color
|
| 11 |
+
|
| 12 |
+
# Check for wget or fallback to curl
|
| 13 |
+
if command -v wget &> /dev/null; then
|
| 14 |
+
DOWNLOADER="wget -v -O"
|
| 15 |
+
elif command -v curl &> /dev/null; then
|
| 16 |
+
DOWNLOADER="curl -L -o"
|
| 17 |
+
echo -e "${YELLOW}Warning: wget not found, using curl instead.${NC}"
|
| 18 |
+
else
|
| 19 |
+
echo -e "${RED}Error: Neither wget nor curl is installed.${NC}"
|
| 20 |
+
echo -e "${YELLOW}Please install one of them (e.g., brew install wget on macOS).${NC}"
|
| 21 |
+
exit 1
|
| 22 |
+
fi
|
| 23 |
+
|
| 24 |
+
# Check if jq is installed
|
| 25 |
+
if ! command -v jq &> /dev/null; then
|
| 26 |
+
echo -e "${YELLOW}jq is not installed. Installing jq for JSON parsing...${NC}"
|
| 27 |
+
# Use conda to install jq (no sudo required)
|
| 28 |
+
conda install -y -c conda-forge jq
|
| 29 |
+
|
| 30 |
+
if [ $? -ne 0 ]; then
|
| 31 |
+
echo -e "${RED}Failed to install jq with conda. Please install it manually.${NC}"
|
| 32 |
+
echo "Visit: https://stedolan.github.io/jq/download/"
|
| 33 |
+
exit 1
|
| 34 |
+
fi
|
| 35 |
+
fi
|
| 36 |
+
|
| 37 |
+
# Function to clean up old installations
|
| 38 |
+
cleanup_old_installations() {
|
| 39 |
+
echo -e "${YELLOW}Checking for old installations...${NC}"
|
| 40 |
+
|
| 41 |
+
# Check for old installation in AFS
|
| 42 |
+
local old_afs_dir="/afs/cs.stanford.edu/u/$(whoami)/biomni_tools"
|
| 43 |
+
if [ -d "$old_afs_dir" ]; then
|
| 44 |
+
echo -e "${YELLOW}Found old installation in $old_afs_dir${NC}"
|
| 45 |
+
echo -e "${YELLOW}Removing old bin directory...${NC}"
|
| 46 |
+
rm -rf "$old_afs_dir/bin"
|
| 47 |
+
echo -e "${GREEN}Old bin directory removed.${NC}"
|
| 48 |
+
fi
|
| 49 |
+
|
| 50 |
+
# Check for old installation in HOME
|
| 51 |
+
local old_home_dir="$HOME/biomni_tools"
|
| 52 |
+
if [ -d "$old_home_dir" ] && [ "$old_home_dir" != "$TOOLS_DIR" ]; then
|
| 53 |
+
echo -e "${YELLOW}Found old installation in $old_home_dir${NC}"
|
| 54 |
+
echo -e "${YELLOW}Removing old bin directory...${NC}"
|
| 55 |
+
rm -rf "$old_home_dir/bin"
|
| 56 |
+
echo -e "${GREEN}Old bin directory removed.${NC}"
|
| 57 |
+
fi
|
| 58 |
+
|
| 59 |
+
# Clean up PATH
|
| 60 |
+
echo -e "${YELLOW}Cleaning up PATH...${NC}"
|
| 61 |
+
PATH=$(echo $PATH | tr ':' '\n' | grep -v "biomni_tools/bin" | tr '\n' ':' | sed 's/:$//')
|
| 62 |
+
export PATH="$TOOLS_DIR/bin:$PATH"
|
| 63 |
+
echo -e "${GREEN}PATH cleaned up.${NC}"
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
# Create a directory for CLI tools if it doesn't exist
|
| 67 |
+
if [ -n "$BIOMNI_TOOLS_DIR" ]; then
|
| 68 |
+
TOOLS_DIR="$BIOMNI_TOOLS_DIR"
|
| 69 |
+
else
|
| 70 |
+
TOOLS_DIR="$(pwd)/biomni_tools"
|
| 71 |
+
fi
|
| 72 |
+
|
| 73 |
+
# Clean up any existing installation if it exists
|
| 74 |
+
if [ -d "$TOOLS_DIR" ]; then
|
| 75 |
+
echo -e "${YELLOW}Cleaning up existing installation in $TOOLS_DIR...${NC}"
|
| 76 |
+
# Remove bin directory to clean up symlinks
|
| 77 |
+
rm -rf "$TOOLS_DIR/bin"
|
| 78 |
+
fi
|
| 79 |
+
|
| 80 |
+
# Clean up old installations
|
| 81 |
+
cleanup_old_installations
|
| 82 |
+
|
| 83 |
+
# Create fresh directories
|
| 84 |
+
mkdir -p "$TOOLS_DIR"
|
| 85 |
+
mkdir -p "$TOOLS_DIR/bin"
|
| 86 |
+
|
| 87 |
+
# Add the tools bin directory to PATH in the current session
|
| 88 |
+
# Remove any old paths first to avoid duplicates
|
| 89 |
+
PATH=$(echo $PATH | tr ':' '\n' | grep -v "biomni_tools/bin" | tr '\n' ':' | sed 's/:$//')
|
| 90 |
+
export PATH="$TOOLS_DIR/bin:$PATH"
|
| 91 |
+
|
| 92 |
+
# Clear the shell's command hash table to force it to re-search the PATH
|
| 93 |
+
hash -r 2>/dev/null || rehash 2>/dev/null || true
|
| 94 |
+
|
| 95 |
+
# Create a setup_path.sh file in the tools directory
|
| 96 |
+
echo "#!/bin/bash" > "$TOOLS_DIR/setup_path.sh"
|
| 97 |
+
echo "# Added by biomni setup" >> "$TOOLS_DIR/setup_path.sh"
|
| 98 |
+
echo "# Remove any old paths first to avoid duplicates" >> "$TOOLS_DIR/setup_path.sh"
|
| 99 |
+
echo "PATH=\$(echo \$PATH | tr ':' '\n' | grep -v \"biomni_tools/bin\" | tr '\n' ':' | sed 's/:$//')" >> "$TOOLS_DIR/setup_path.sh"
|
| 100 |
+
echo "export PATH=\"$TOOLS_DIR/bin:\$PATH\"" >> "$TOOLS_DIR/setup_path.sh"
|
| 101 |
+
echo "# Clear the shell's command hash table to force it to re-search the PATH" >> "$TOOLS_DIR/setup_path.sh"
|
| 102 |
+
echo "hash -r 2>/dev/null || rehash 2>/dev/null || true" >> "$TOOLS_DIR/setup_path.sh"
|
| 103 |
+
chmod +x "$TOOLS_DIR/setup_path.sh"
|
| 104 |
+
|
| 105 |
+
# Config file path
|
| 106 |
+
CONFIG_FILE="cli_tools_config.json"
|
| 107 |
+
|
| 108 |
+
# Check if config file exists
|
| 109 |
+
if [ ! -f "$CONFIG_FILE" ]; then
|
| 110 |
+
echo -e "${RED}Configuration file $CONFIG_FILE not found.${NC}"
|
| 111 |
+
exit 1
|
| 112 |
+
fi
|
| 113 |
+
|
| 114 |
+
# Function to download and install a tool
|
| 115 |
+
install_tool() {
|
| 116 |
+
local tool_name=$1
|
| 117 |
+
local download_url=$2
|
| 118 |
+
local binary_path=$3
|
| 119 |
+
local version_cmd=$4
|
| 120 |
+
local tool_dir_name=$(echo "$tool_name" | tr '[:upper:]' '[:lower:]' | tr ' .' '_')
|
| 121 |
+
local binary_name=$(basename "$binary_path")
|
| 122 |
+
|
| 123 |
+
echo -e "\n${BLUE}=== Installing $tool_name ===${NC}"
|
| 124 |
+
|
| 125 |
+
# Check if the tool is already installed
|
| 126 |
+
if [ -f "$TOOLS_DIR/bin/$binary_name" ]; then
|
| 127 |
+
echo -e "${GREEN}$tool_name is already installed at $TOOLS_DIR/bin/$binary_name${NC}"
|
| 128 |
+
echo -e "${YELLOW}Testing installation...${NC}"
|
| 129 |
+
|
| 130 |
+
if [ -n "$version_cmd" ]; then
|
| 131 |
+
echo -e "${YELLOW}Running version command: $TOOLS_DIR/bin/$binary_name $version_cmd${NC}"
|
| 132 |
+
$TOOLS_DIR/bin/$binary_name $version_cmd
|
| 133 |
+
fi
|
| 134 |
+
|
| 135 |
+
echo -e "${GREEN}Skipping download and installation.${NC}"
|
| 136 |
+
return 0
|
| 137 |
+
fi
|
| 138 |
+
|
| 139 |
+
# Create directory for the tool
|
| 140 |
+
mkdir -p "$TOOLS_DIR/$tool_dir_name"
|
| 141 |
+
|
| 142 |
+
# Special handling for HOMER
|
| 143 |
+
if [ "$tool_name" = "HOMER" ]; then
|
| 144 |
+
echo -e "${YELLOW}Installing HOMER via Perl script...${NC}"
|
| 145 |
+
|
| 146 |
+
# Download the configuration script directly to the bin directory
|
| 147 |
+
$DOWNLOADER "$TOOLS_DIR/bin/configureHomer.pl" "$download_url"
|
| 148 |
+
|
| 149 |
+
if [ $? -ne 0 ]; then
|
| 150 |
+
echo -e "${RED}Failed to download HOMER configuration script from $download_url${NC}"
|
| 151 |
+
return 1
|
| 152 |
+
fi
|
| 153 |
+
|
| 154 |
+
# Make the script executable
|
| 155 |
+
chmod +x "$TOOLS_DIR/bin/configureHomer.pl"
|
| 156 |
+
|
| 157 |
+
# Create a HOMER installation directory
|
| 158 |
+
mkdir -p "$TOOLS_DIR/$tool_dir_name/homer"
|
| 159 |
+
|
| 160 |
+
# Run the configuration script
|
| 161 |
+
echo -e "${YELLOW}Running HOMER configuration script...${NC}"
|
| 162 |
+
echo -e "${YELLOW}This will install HOMER to $TOOLS_DIR/$tool_dir_name/homer${NC}"
|
| 163 |
+
|
| 164 |
+
# Install HOMER with batch mode ("-b" flag for basic installation)
|
| 165 |
+
# Use -local to specify the absolute installation directory
|
| 166 |
+
"$TOOLS_DIR/bin/configureHomer.pl" -install -local "$TOOLS_DIR/$tool_dir_name/homer" -b
|
| 167 |
+
|
| 168 |
+
if [ $? -ne 0 ]; then
|
| 169 |
+
echo -e "${RED}Failed to install HOMER.${NC}"
|
| 170 |
+
return 1
|
| 171 |
+
fi
|
| 172 |
+
|
| 173 |
+
# Create symlinks to HOMER binaries
|
| 174 |
+
echo -e "${YELLOW}Creating symlinks to HOMER binaries...${NC}"
|
| 175 |
+
for homer_bin in "$TOOLS_DIR/$tool_dir_name/homer/bin/"*; do
|
| 176 |
+
if [ -f "$homer_bin" ] && [ -x "$homer_bin" ]; then
|
| 177 |
+
ln -sf "$homer_bin" "$TOOLS_DIR/bin/$(basename "$homer_bin")"
|
| 178 |
+
fi
|
| 179 |
+
done
|
| 180 |
+
|
| 181 |
+
# Create a sourceable environment setup file
|
| 182 |
+
echo "#!/bin/bash" > "$TOOLS_DIR/$tool_dir_name/homer_env.sh"
|
| 183 |
+
echo "export PATH=\"$TOOLS_DIR/$tool_dir_name/homer/bin:\$PATH\"" >> "$TOOLS_DIR/$tool_dir_name/homer_env.sh"
|
| 184 |
+
echo "export HOMER=\"$TOOLS_DIR/$tool_dir_name/homer\"" >> "$TOOLS_DIR/$tool_dir_name/homer_env.sh"
|
| 185 |
+
chmod +x "$TOOLS_DIR/$tool_dir_name/homer_env.sh"
|
| 186 |
+
|
| 187 |
+
# Add the HOMER environment to the global setup_path.sh
|
| 188 |
+
echo "# HOMER environment" >> "$TOOLS_DIR/setup_path.sh"
|
| 189 |
+
echo "export PATH=\"$TOOLS_DIR/$tool_dir_name/homer/bin:\$PATH\"" >> "$TOOLS_DIR/setup_path.sh"
|
| 190 |
+
echo "export HOMER=\"$TOOLS_DIR/$tool_dir_name/homer\"" >> "$TOOLS_DIR/setup_path.sh"
|
| 191 |
+
|
| 192 |
+
echo -e "${GREEN}HOMER installed successfully!${NC}"
|
| 193 |
+
echo -e "${YELLOW}To use HOMER, you may need to source the environment file:${NC}"
|
| 194 |
+
echo -e "${GREEN}source $TOOLS_DIR/$tool_dir_name/homer_env.sh${NC}"
|
| 195 |
+
echo -e "${YELLOW}Or you can run configureHomer.pl directly from:${NC}"
|
| 196 |
+
echo -e "${GREEN}$TOOLS_DIR/bin/configureHomer.pl${NC}"
|
| 197 |
+
return 0
|
| 198 |
+
|
| 199 |
+
# Special handling for FastTree (requires compilation)
|
| 200 |
+
elif [ "$tool_name" = "FastTree" ]; then
|
| 201 |
+
echo -e "${YELLOW}Installing FastTree from source...${NC}"
|
| 202 |
+
|
| 203 |
+
# Download the source
|
| 204 |
+
$DOWNLOADER "$TOOLS_DIR/$tool_dir_name/FastTree.c" "$download_url"
|
| 205 |
+
|
| 206 |
+
if [ $? -ne 0 ]; then
|
| 207 |
+
echo -e "${RED}Failed to download FastTree source from $download_url${NC}"
|
| 208 |
+
return 1
|
| 209 |
+
fi
|
| 210 |
+
|
| 211 |
+
# Compile FastTree
|
| 212 |
+
echo -e "${YELLOW}Compiling FastTree...${NC}"
|
| 213 |
+
echo -e "${YELLOW}This may take a few minutes.${NC}"
|
| 214 |
+
|
| 215 |
+
# Try to compile with SSE support first
|
| 216 |
+
(cd "$TOOLS_DIR/$tool_dir_name" && gcc -O3 -finline-functions -funroll-loops -Wall -o FastTree FastTree.c -lm)
|
| 217 |
+
|
| 218 |
+
# If compilation fails, try without SSE support
|
| 219 |
+
if [ $? -ne 0 ]; then
|
| 220 |
+
echo -e "${YELLOW}Compilation with SSE failed, trying without SSE...${NC}"
|
| 221 |
+
(cd "$TOOLS_DIR/$tool_dir_name" && gcc -DNO_SSE -O3 -finline-functions -funroll-loops -Wall -o FastTree FastTree.c -lm)
|
| 222 |
+
|
| 223 |
+
if [ $? -ne 0 ]; then
|
| 224 |
+
echo -e "${RED}Failed to compile FastTree.${NC}"
|
| 225 |
+
return 1
|
| 226 |
+
fi
|
| 227 |
+
fi
|
| 228 |
+
|
| 229 |
+
# Create symlink
|
| 230 |
+
ln -sf "$TOOLS_DIR/$tool_dir_name/FastTree" "$TOOLS_DIR/bin/FastTree"
|
| 231 |
+
|
| 232 |
+
echo -e "${GREEN}FastTree installed successfully!${NC}"
|
| 233 |
+
|
| 234 |
+
# Test installation
|
| 235 |
+
if [ -f "$TOOLS_DIR/bin/FastTree" ]; then
|
| 236 |
+
echo -e "${GREEN}FastTree installed successfully!${NC}"
|
| 237 |
+
if [ -n "$version_cmd" ]; then
|
| 238 |
+
echo -e "${YELLOW}Running version command: $TOOLS_DIR/bin/FastTree $version_cmd | head -n 5${NC}"
|
| 239 |
+
"$TOOLS_DIR/bin/FastTree" $version_cmd | head -n 5
|
| 240 |
+
fi
|
| 241 |
+
else
|
| 242 |
+
echo -e "${RED}FastTree installation failed.${NC}"
|
| 243 |
+
echo -e "${YELLOW}Binary not found at: $TOOLS_DIR/bin/FastTree${NC}"
|
| 244 |
+
return 1
|
| 245 |
+
fi
|
| 246 |
+
|
| 247 |
+
return 0
|
| 248 |
+
|
| 249 |
+
# Special handling for BWA (requires compilation)
|
| 250 |
+
elif [ "$tool_name" = "BWA" ]; then
|
| 251 |
+
echo -e "${YELLOW}Installing BWA...${NC}"
|
| 252 |
+
|
| 253 |
+
# Check if git is installed
|
| 254 |
+
if ! command -v git &> /dev/null; then
|
| 255 |
+
echo -e "${RED}Git is not installed. Please install git to continue.${NC}"
|
| 256 |
+
return 1
|
| 257 |
+
fi
|
| 258 |
+
|
| 259 |
+
# Clone the repository
|
| 260 |
+
echo -e "${YELLOW}Cloning BWA repository from $download_url...${NC}"
|
| 261 |
+
mkdir -p "$TOOLS_DIR/$tool_dir_name"
|
| 262 |
+
git clone "$download_url" "$TOOLS_DIR/$tool_dir_name"
|
| 263 |
+
|
| 264 |
+
if [ $? -ne 0 ]; then
|
| 265 |
+
echo -e "${RED}Failed to clone BWA repository from $download_url${NC}"
|
| 266 |
+
return 1
|
| 267 |
+
fi
|
| 268 |
+
|
| 269 |
+
# Compile BWA
|
| 270 |
+
echo -e "${YELLOW}Compiling BWA...${NC}"
|
| 271 |
+
(cd "$TOOLS_DIR/$tool_dir_name" && make)
|
| 272 |
+
|
| 273 |
+
if [ $? -ne 0 ]; then
|
| 274 |
+
echo -e "${RED}Failed to compile BWA.${NC}"
|
| 275 |
+
return 1
|
| 276 |
+
fi
|
| 277 |
+
|
| 278 |
+
# Create symlink
|
| 279 |
+
ln -sf "$TOOLS_DIR/$tool_dir_name/bwa" "$TOOLS_DIR/bin/bwa"
|
| 280 |
+
|
| 281 |
+
# Test installation
|
| 282 |
+
if [ -f "$TOOLS_DIR/bin/bwa" ]; then
|
| 283 |
+
echo -e "${GREEN}BWA installed successfully!${NC}"
|
| 284 |
+
echo -e "${YELLOW}Running version check:${NC}"
|
| 285 |
+
"$TOOLS_DIR/bin/bwa" 2>&1 | head -n 3
|
| 286 |
+
else
|
| 287 |
+
echo -e "${RED}BWA installation failed.${NC}"
|
| 288 |
+
echo -e "${YELLOW}Binary not found at: $TOOLS_DIR/bin/bwa${NC}"
|
| 289 |
+
return 1
|
| 290 |
+
fi
|
| 291 |
+
|
| 292 |
+
return 0
|
| 293 |
+
fi
|
| 294 |
+
|
| 295 |
+
# Download the tool
|
| 296 |
+
echo -e "${YELLOW}Downloading $tool_name from: $download_url${NC}"
|
| 297 |
+
|
| 298 |
+
# Determine file extension
|
| 299 |
+
if [[ "$download_url" == *".zip" ]]; then
|
| 300 |
+
# Use verbose output to help diagnose issues
|
| 301 |
+
$DOWNLOADER "$TOOLS_DIR/$tool_dir_name.zip" "$download_url"
|
| 302 |
+
|
| 303 |
+
if [ $? -ne 0 ]; then
|
| 304 |
+
echo -e "${RED}Failed to download $tool_name from $download_url${NC}"
|
| 305 |
+
echo -e "${YELLOW}Please check your internet connection and try again.${NC}"
|
| 306 |
+
echo -e "${YELLOW}If the problem persists, the download URL may be incorrect or the server may be down.${NC}"
|
| 307 |
+
return 1
|
| 308 |
+
fi
|
| 309 |
+
|
| 310 |
+
echo -e "${YELLOW}Extracting $tool_name...${NC}"
|
| 311 |
+
unzip -q -o "$TOOLS_DIR/$tool_dir_name.zip" -d "$TOOLS_DIR/$tool_dir_name"
|
| 312 |
+
|
| 313 |
+
if [ $? -ne 0 ]; then
|
| 314 |
+
echo -e "${RED}Failed to extract $tool_name.${NC}"
|
| 315 |
+
return 1
|
| 316 |
+
fi
|
| 317 |
+
|
| 318 |
+
# Clean up
|
| 319 |
+
rm "$TOOLS_DIR/$tool_dir_name.zip"
|
| 320 |
+
elif [[ "$download_url" == *".tar.gz" ]]; then
|
| 321 |
+
# Use verbose output to help diagnose issues
|
| 322 |
+
$DOWNLOADER "$TOOLS_DIR/$tool_dir_name.tar.gz" "$download_url"
|
| 323 |
+
|
| 324 |
+
if [ $? -ne 0 ]; then
|
| 325 |
+
echo -e "${RED}Failed to download $tool_name from $download_url${NC}"
|
| 326 |
+
echo -e "${YELLOW}Please check your internet connection and try again.${NC}"
|
| 327 |
+
echo -e "${YELLOW}If the problem persists, the download URL may be incorrect or the server may be down.${NC}"
|
| 328 |
+
return 1
|
| 329 |
+
fi
|
| 330 |
+
|
| 331 |
+
echo -e "${YELLOW}Extracting $tool_name...${NC}"
|
| 332 |
+
mkdir -p "$TOOLS_DIR/$tool_dir_name"
|
| 333 |
+
tar -xzf "$TOOLS_DIR/$tool_dir_name.tar.gz" -C "$TOOLS_DIR/$tool_dir_name" --strip-components=1
|
| 334 |
+
|
| 335 |
+
if [ $? -ne 0 ]; then
|
| 336 |
+
echo -e "${RED}Failed to extract $tool_name.${NC}"
|
| 337 |
+
return 1
|
| 338 |
+
fi
|
| 339 |
+
|
| 340 |
+
# Clean up
|
| 341 |
+
rm "$TOOLS_DIR/$tool_dir_name.tar.gz"
|
| 342 |
+
# Handle executable files directly (for MUSCLE)
|
| 343 |
+
elif [[ "$tool_name" = "MUSCLE" ]]; then
|
| 344 |
+
echo -e "${YELLOW}Downloading $tool_name binary...${NC}"
|
| 345 |
+
$DOWNLOADER "$TOOLS_DIR/$tool_dir_name/$binary_name" "$download_url"
|
| 346 |
+
|
| 347 |
+
if [ $? -ne 0 ]; then
|
| 348 |
+
echo -e "${RED}Failed to download $tool_name from $download_url${NC}"
|
| 349 |
+
return 1
|
| 350 |
+
fi
|
| 351 |
+
|
| 352 |
+
# Make executable
|
| 353 |
+
chmod +x "$TOOLS_DIR/$tool_dir_name/$binary_name"
|
| 354 |
+
|
| 355 |
+
# Create symlink
|
| 356 |
+
ln -sf "$TOOLS_DIR/$tool_dir_name/$binary_name" "$TOOLS_DIR/bin/$binary_name"
|
| 357 |
+
|
| 358 |
+
# Test installation
|
| 359 |
+
if [ -f "$TOOLS_DIR/bin/$binary_name" ]; then
|
| 360 |
+
echo -e "${GREEN}$tool_name installed successfully!${NC}"
|
| 361 |
+
if [ -n "$version_cmd" ]; then
|
| 362 |
+
echo -e "${YELLOW}Running version command: $TOOLS_DIR/bin/$binary_name $version_cmd${NC}"
|
| 363 |
+
"$TOOLS_DIR/bin/$binary_name" $version_cmd
|
| 364 |
+
fi
|
| 365 |
+
else
|
| 366 |
+
echo -e "${RED}$tool_name installation failed.${NC}"
|
| 367 |
+
echo -e "${YELLOW}Binary not found at: $TOOLS_DIR/bin/$binary_name${NC}"
|
| 368 |
+
return 1
|
| 369 |
+
fi
|
| 370 |
+
|
| 371 |
+
return 0
|
| 372 |
+
else
|
| 373 |
+
echo -e "${RED}Unsupported file format for $tool_name.${NC}"
|
| 374 |
+
return 1
|
| 375 |
+
fi
|
| 376 |
+
|
| 377 |
+
# Find the binary path
|
| 378 |
+
local full_binary_path=$(find "$TOOLS_DIR/$tool_dir_name" -name "$(basename "$binary_path")" | head -n 1)
|
| 379 |
+
|
| 380 |
+
if [ -z "$full_binary_path" ]; then
|
| 381 |
+
echo -e "${RED}Could not find binary for $tool_name.${NC}"
|
| 382 |
+
echo -e "${YELLOW}Looking for binary at expected path: $TOOLS_DIR/$tool_dir_name/$binary_path${NC}"
|
| 383 |
+
full_binary_path="$TOOLS_DIR/$tool_dir_name/$binary_path"
|
| 384 |
+
fi
|
| 385 |
+
|
| 386 |
+
# Make the binary executable
|
| 387 |
+
chmod +x "$full_binary_path"
|
| 388 |
+
|
| 389 |
+
# Create symlink in bin directory
|
| 390 |
+
ln -sf "$full_binary_path" "$TOOLS_DIR/bin/$binary_name"
|
| 391 |
+
|
| 392 |
+
# Test installation
|
| 393 |
+
if [ -f "$TOOLS_DIR/bin/$binary_name" ]; then
|
| 394 |
+
echo -e "${GREEN}$tool_name installed successfully!${NC}"
|
| 395 |
+
if [ -n "$version_cmd" ]; then
|
| 396 |
+
echo -e "${YELLOW}Running version command: $TOOLS_DIR/bin/$binary_name $version_cmd${NC}"
|
| 397 |
+
"$TOOLS_DIR/bin/$binary_name" $version_cmd
|
| 398 |
+
fi
|
| 399 |
+
else
|
| 400 |
+
echo -e "${RED}$tool_name installation failed.${NC}"
|
| 401 |
+
echo -e "${YELLOW}Binary not found at: $TOOLS_DIR/bin/$binary_name${NC}"
|
| 402 |
+
return 1
|
| 403 |
+
fi
|
| 404 |
+
|
| 405 |
+
return 0
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
# Function to install a tool from the config
|
| 409 |
+
install_tool_from_config() {
|
| 410 |
+
local tool_index=$1
|
| 411 |
+
local auto_install=${2:-0}
|
| 412 |
+
|
| 413 |
+
# Get tool information from config
|
| 414 |
+
local tool_name=$(jq -r ".tools[$tool_index].name" "$CONFIG_FILE")
|
| 415 |
+
local tool_desc=$(jq -r ".tools[$tool_index].description" "$CONFIG_FILE")
|
| 416 |
+
|
| 417 |
+
# Determine the appropriate download URL based on the system
|
| 418 |
+
local download_url=""
|
| 419 |
+
if [[ "$(uname)" == "Darwin" ]]; then
|
| 420 |
+
if [[ "$(uname -m)" == "arm64" ]]; then
|
| 421 |
+
# macOS M1
|
| 422 |
+
download_url=$(jq -r ".tools[$tool_index].downloads.macos_arm64" "$CONFIG_FILE")
|
| 423 |
+
else
|
| 424 |
+
# macOS Intel
|
| 425 |
+
download_url=$(jq -r ".tools[$tool_index].downloads.macos_intel" "$CONFIG_FILE")
|
| 426 |
+
fi
|
| 427 |
+
else
|
| 428 |
+
# Linux
|
| 429 |
+
download_url=$(jq -r ".tools[$tool_index].downloads.linux" "$CONFIG_FILE")
|
| 430 |
+
fi
|
| 431 |
+
|
| 432 |
+
local binary_path=$(jq -r ".tools[$tool_index].binary_path" "$CONFIG_FILE")
|
| 433 |
+
local version_cmd=$(jq -r ".tools[$tool_index].version_command" "$CONFIG_FILE")
|
| 434 |
+
|
| 435 |
+
# Install the tool
|
| 436 |
+
echo -e "\n${YELLOW}$tool_name: $tool_desc${NC}"
|
| 437 |
+
|
| 438 |
+
# If auto_install is enabled, install without asking
|
| 439 |
+
if [ "$auto_install" -eq 1 ]; then
|
| 440 |
+
install_tool "$tool_name" "$download_url" "$binary_path" "$version_cmd"
|
| 441 |
+
return $?
|
| 442 |
+
fi
|
| 443 |
+
|
| 444 |
+
# Otherwise, ask for confirmation
|
| 445 |
+
read -p "Install $tool_name? (y/n) " -n 1 -r
|
| 446 |
+
echo
|
| 447 |
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
| 448 |
+
install_tool "$tool_name" "$download_url" "$binary_path" "$version_cmd"
|
| 449 |
+
return $?
|
| 450 |
+
fi
|
| 451 |
+
|
| 452 |
+
return 0
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
# Function to add a new tool to the config
|
| 456 |
+
add_new_tool() {
|
| 457 |
+
echo -e "\n${BLUE}=== Add a New Tool ===${NC}"
|
| 458 |
+
|
| 459 |
+
read -p "Tool name: " tool_name
|
| 460 |
+
read -p "Tool description: " tool_desc
|
| 461 |
+
read -p "Tool website: " tool_website
|
| 462 |
+
read -p "Linux download URL: " linux_url
|
| 463 |
+
read -p "macOS Intel download URL: " macos_intel_url
|
| 464 |
+
read -p "macOS ARM64 download URL: " macos_arm64_url
|
| 465 |
+
read -p "Binary path (relative to extraction): " binary_path
|
| 466 |
+
read -p "Version command (e.g., --version): " version_cmd
|
| 467 |
+
|
| 468 |
+
# Generate function name
|
| 469 |
+
function_name="install_$(echo "$tool_name" | tr '[:upper:]' '[:lower:]' | tr ' .' '_')"
|
| 470 |
+
|
| 471 |
+
# Create new tool JSON
|
| 472 |
+
new_tool=$(cat <<EOF
|
| 473 |
+
{
|
| 474 |
+
"name": "$tool_name",
|
| 475 |
+
"function_name": "$function_name",
|
| 476 |
+
"description": "$tool_desc",
|
| 477 |
+
"website": "$tool_website",
|
| 478 |
+
"downloads": {
|
| 479 |
+
"linux": "$linux_url",
|
| 480 |
+
"macos_intel": "$macos_intel_url",
|
| 481 |
+
"macos_arm64": "$macos_arm64_url"
|
| 482 |
+
},
|
| 483 |
+
"binary_path": "$binary_path",
|
| 484 |
+
"version_command": "$version_cmd"
|
| 485 |
+
}
|
| 486 |
+
EOF
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
# Add to config file
|
| 490 |
+
jq ".tools += [$new_tool]" "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
|
| 491 |
+
|
| 492 |
+
echo -e "${GREEN}Tool added to configuration!${NC}"
|
| 493 |
+
|
| 494 |
+
# Ask if user wants to install the tool now
|
| 495 |
+
read -p "Install $tool_name now? (y/n) " -n 1 -r
|
| 496 |
+
echo
|
| 497 |
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
| 498 |
+
local tool_index=$(jq '.tools | length - 1' "$CONFIG_FILE")
|
| 499 |
+
install_tool_from_config "$tool_index"
|
| 500 |
+
fi
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
# Function to install all tools
|
| 504 |
+
install_all_tools() {
|
| 505 |
+
local auto_install=${1:-0}
|
| 506 |
+
local num_tools=$(jq '.tools | length' "$CONFIG_FILE")
|
| 507 |
+
|
| 508 |
+
echo -e "\n${BLUE}Installing all command-line tools...${NC}"
|
| 509 |
+
|
| 510 |
+
for (( i=0; i<$num_tools; i++ )); do
|
| 511 |
+
install_tool_from_config "$i" "$auto_install"
|
| 512 |
+
done
|
| 513 |
+
|
| 514 |
+
return 0
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
# Function to add PATH to shell profile
|
| 518 |
+
add_path_to_profile() {
|
| 519 |
+
local force_profile=${1:-""}
|
| 520 |
+
|
| 521 |
+
# Try to detect the shell profile file
|
| 522 |
+
local profile_file=""
|
| 523 |
+
local shell_name=$(basename "$SHELL")
|
| 524 |
+
|
| 525 |
+
# Create a sourceable file in the tools directory with PATH cleanup
|
| 526 |
+
echo "#!/bin/bash" > "$TOOLS_DIR/setup_path.sh"
|
| 527 |
+
echo "# Added by biomni setup" >> "$TOOLS_DIR/setup_path.sh"
|
| 528 |
+
echo "# Remove any old paths first to avoid duplicates" >> "$TOOLS_DIR/setup_path.sh"
|
| 529 |
+
echo "PATH=\$(echo \$PATH | tr ':' '\n' | grep -v \"biomni_tools/bin\" | tr '\n' ':' | sed 's/:$//')" >> "$TOOLS_DIR/setup_path.sh"
|
| 530 |
+
echo "export PATH=\"$TOOLS_DIR/bin:\$PATH\"" >> "$TOOLS_DIR/setup_path.sh"
|
| 531 |
+
echo "# Clear the shell's command hash table to force it to re-search the PATH" >> "$TOOLS_DIR/setup_path.sh"
|
| 532 |
+
echo "hash -r 2>/dev/null || rehash 2>/dev/null || true" >> "$TOOLS_DIR/setup_path.sh"
|
| 533 |
+
chmod +x "$TOOLS_DIR/setup_path.sh"
|
| 534 |
+
echo -e "${GREEN}Created sourceable file at $TOOLS_DIR/setup_path.sh${NC}"
|
| 535 |
+
echo -e "${YELLOW}You can add this to your PATH by running:${NC}"
|
| 536 |
+
echo -e "${GREEN}source $TOOLS_DIR/setup_path.sh${NC}"
|
| 537 |
+
|
| 538 |
+
# If a specific profile is forced, use that
|
| 539 |
+
if [ -n "$force_profile" ]; then
|
| 540 |
+
profile_file="$HOME/$force_profile"
|
| 541 |
+
echo -e "${YELLOW}Using specified profile file: $profile_file${NC}"
|
| 542 |
+
else
|
| 543 |
+
# Auto-detect based on current shell
|
| 544 |
+
case "$shell_name" in
|
| 545 |
+
bash)
|
| 546 |
+
if [ -f "$HOME/.bash_profile" ]; then
|
| 547 |
+
profile_file="$HOME/.bash_profile"
|
| 548 |
+
elif [ -f "$HOME/.profile" ]; then
|
| 549 |
+
profile_file="$HOME/.profile"
|
| 550 |
+
elif [ -f "$HOME/.bashrc" ]; then
|
| 551 |
+
profile_file="$HOME/.bashrc"
|
| 552 |
+
fi
|
| 553 |
+
;;
|
| 554 |
+
zsh)
|
| 555 |
+
profile_file="$HOME/.zshrc"
|
| 556 |
+
;;
|
| 557 |
+
fish)
|
| 558 |
+
# Fish has a different configuration structure
|
| 559 |
+
mkdir -p "$HOME/.config/fish"
|
| 560 |
+
profile_file="$HOME/.config/fish/config.fish"
|
| 561 |
+
;;
|
| 562 |
+
*)
|
| 563 |
+
# For other shells, try common profile files
|
| 564 |
+
if [ -f "$HOME/.profile" ]; then
|
| 565 |
+
profile_file="$HOME/.profile"
|
| 566 |
+
elif [ -f "$HOME/.bashrc" ]; then
|
| 567 |
+
profile_file="$HOME/.bashrc"
|
| 568 |
+
elif [ -f "$HOME/.zshrc" ]; then
|
| 569 |
+
profile_file="$HOME/.zshrc"
|
| 570 |
+
fi
|
| 571 |
+
;;
|
| 572 |
+
esac
|
| 573 |
+
fi
|
| 574 |
+
|
| 575 |
+
# If we found a profile file and it's writable, add the PATH
|
| 576 |
+
if [ -n "$profile_file" ]; then
|
| 577 |
+
# Create the file if it doesn't exist
|
| 578 |
+
if [ ! -f "$profile_file" ]; then
|
| 579 |
+
echo -e "${YELLOW}Creating new profile file: $profile_file${NC}"
|
| 580 |
+
touch "$profile_file"
|
| 581 |
+
fi
|
| 582 |
+
|
| 583 |
+
if [ -w "$profile_file" ]; then
|
| 584 |
+
# Check if the PATH is already in the profile
|
| 585 |
+
if ! grep -q "export PATH=\"$TOOLS_DIR/bin:\$PATH\"" "$profile_file"; then
|
| 586 |
+
# Remove any old biomni_tools paths first
|
| 587 |
+
if grep -q "biomni_tools/bin" "$profile_file"; then
|
| 588 |
+
echo -e "${YELLOW}Removing old biomni_tools paths from $profile_file...${NC}"
|
| 589 |
+
sed -i '/biomni_tools\/bin/d' "$profile_file"
|
| 590 |
+
fi
|
| 591 |
+
|
| 592 |
+
echo "" >> "$profile_file"
|
| 593 |
+
echo "# Added by biomni setup" >> "$profile_file"
|
| 594 |
+
echo "# Remove any old paths first to avoid duplicates" >> "$profile_file"
|
| 595 |
+
echo "PATH=\$(echo \$PATH | tr ':' '\n' | grep -v \"biomni_tools/bin\" | tr '\n' ':' | sed 's/:$//')" >> "$profile_file"
|
| 596 |
+
|
| 597 |
+
# Use the appropriate syntax for the shell
|
| 598 |
+
if [ "$shell_name" = "fish" ]; then
|
| 599 |
+
echo "set -gx PATH $TOOLS_DIR/bin \$PATH" >> "$profile_file"
|
| 600 |
+
else
|
| 601 |
+
echo "export PATH=\"$TOOLS_DIR/bin:\$PATH\"" >> "$profile_file"
|
| 602 |
+
fi
|
| 603 |
+
|
| 604 |
+
echo -e "${GREEN}Added tools directory to PATH in $profile_file${NC}"
|
| 605 |
+
echo -e "${YELLOW}Note: You may need to restart your shell or run 'source $profile_file' for changes to take effect.${NC}"
|
| 606 |
+
else
|
| 607 |
+
echo -e "${GREEN}PATH already configured in $profile_file${NC}"
|
| 608 |
+
fi
|
| 609 |
+
else
|
| 610 |
+
echo -e "${RED}Profile file $profile_file is not writable.${NC}"
|
| 611 |
+
echo -e "${YELLOW}Please add the following line to your shell profile manually:${NC}"
|
| 612 |
+
|
| 613 |
+
if [ "$shell_name" = "fish" ]; then
|
| 614 |
+
echo -e "${GREEN}set -gx PATH $TOOLS_DIR/bin \$PATH${NC}"
|
| 615 |
+
else
|
| 616 |
+
echo -e "${GREEN}export PATH=\"$TOOLS_DIR/bin:\$PATH\"${NC}"
|
| 617 |
+
fi
|
| 618 |
+
|
| 619 |
+
echo -e "${YELLOW}Or source the setup file we created:${NC}"
|
| 620 |
+
echo -e "${GREEN}source $TOOLS_DIR/setup_path.sh${NC}"
|
| 621 |
+
fi
|
| 622 |
+
else
|
| 623 |
+
# If we couldn't find a profile file, just print instructions
|
| 624 |
+
echo -e "${YELLOW}Could not determine appropriate shell profile file.${NC}"
|
| 625 |
+
echo -e "${YELLOW}Please add the following line to your shell profile manually:${NC}"
|
| 626 |
+
|
| 627 |
+
if [ "$shell_name" = "fish" ]; then
|
| 628 |
+
echo -e "${GREEN}set -gx PATH $TOOLS_DIR/bin \$PATH${NC}"
|
| 629 |
+
else
|
| 630 |
+
echo -e "${GREEN}export PATH=\"$TOOLS_DIR/bin:\$PATH\"${NC}"
|
| 631 |
+
fi
|
| 632 |
+
|
| 633 |
+
echo -e "${YELLOW}Or source the setup file we created:${NC}"
|
| 634 |
+
echo -e "${GREEN}source $TOOLS_DIR/setup_path.sh${NC}"
|
| 635 |
+
fi
|
| 636 |
+
|
| 637 |
+
# Always export PATH for the current session
|
| 638 |
+
# Remove any old paths first to avoid duplicates
|
| 639 |
+
PATH=$(echo $PATH | tr ':' '\n' | grep -v "biomni_tools/bin" | tr '\n' ':' | sed 's/:$//')
|
| 640 |
+
export PATH="$TOOLS_DIR/bin:$PATH"
|
| 641 |
+
|
| 642 |
+
# Clear the shell's command hash table to force it to re-search the PATH
|
| 643 |
+
hash -r 2>/dev/null || rehash 2>/dev/null || true
|
| 644 |
+
|
| 645 |
+
# Create a test script to verify the tools are in the PATH
|
| 646 |
+
echo "#!/bin/bash" > "$TOOLS_DIR/test_tools.sh"
|
| 647 |
+
echo "echo \"Testing if tools are in the PATH...\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 648 |
+
echo "echo \"Current PATH: \$PATH\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 649 |
+
echo "echo \"\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 650 |
+
echo "echo \"Looking for tools in: $TOOLS_DIR/bin\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 651 |
+
echo "ls -la \"$TOOLS_DIR/bin\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 652 |
+
echo "echo \"\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 653 |
+
echo "echo \"Checking for path caching issues...\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 654 |
+
echo "for tool in \$(ls \"$TOOLS_DIR/bin\"); do" >> "$TOOLS_DIR/test_tools.sh"
|
| 655 |
+
echo " which \$tool 2>/dev/null | grep -q \"/afs/cs.stanford.edu\" && {" >> "$TOOLS_DIR/test_tools.sh"
|
| 656 |
+
echo " echo \"WARNING: \$tool is still pointing to the old AFS location!\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 657 |
+
echo " echo \"Run 'hash -r' (bash) or 'rehash' (zsh) to clear the command cache.\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 658 |
+
echo " break" >> "$TOOLS_DIR/test_tools.sh"
|
| 659 |
+
echo " }" >> "$TOOLS_DIR/test_tools.sh"
|
| 660 |
+
echo "done" >> "$TOOLS_DIR/test_tools.sh"
|
| 661 |
+
echo "echo \"\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 662 |
+
echo "for tool in \$(ls \"$TOOLS_DIR/bin\"); do" >> "$TOOLS_DIR/test_tools.sh"
|
| 663 |
+
echo " if command -v \$tool &> /dev/null; then" >> "$TOOLS_DIR/test_tools.sh"
|
| 664 |
+
echo " echo \"\$tool: \$(which \$tool)\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 665 |
+
echo " else" >> "$TOOLS_DIR/test_tools.sh"
|
| 666 |
+
echo " echo \"\$tool: NOT FOUND IN PATH\"" >> "$TOOLS_DIR/test_tools.sh"
|
| 667 |
+
echo " fi" >> "$TOOLS_DIR/test_tools.sh"
|
| 668 |
+
echo "done" >> "$TOOLS_DIR/test_tools.sh"
|
| 669 |
+
chmod +x "$TOOLS_DIR/test_tools.sh"
|
| 670 |
+
|
| 671 |
+
echo -e "${YELLOW}Created test script at $TOOLS_DIR/test_tools.sh${NC}"
|
| 672 |
+
echo -e "${YELLOW}You can run it to verify the tools are in your PATH:${NC}"
|
| 673 |
+
echo -e "${GREEN}$TOOLS_DIR/test_tools.sh${NC}"
|
| 674 |
+
|
| 675 |
+
# Create a quick fix script for path caching issues
|
| 676 |
+
echo "#!/bin/bash" > "$TOOLS_DIR/fix_path.sh"
|
| 677 |
+
echo "# Script to fix path caching issues" >> "$TOOLS_DIR/fix_path.sh"
|
| 678 |
+
echo "echo \"Fixing path caching issues...\"" >> "$TOOLS_DIR/fix_path.sh"
|
| 679 |
+
echo "# Remove any old paths first to avoid duplicates" >> "$TOOLS_DIR/fix_path.sh"
|
| 680 |
+
echo "PATH=\$(echo \$PATH | tr ':' '\n' | grep -v \"biomni_tools/bin\" | tr '\n' ':' | sed 's/:$//')" >> "$TOOLS_DIR/fix_path.sh"
|
| 681 |
+
echo "export PATH=\"$TOOLS_DIR/bin:\$PATH\"" >> "$TOOLS_DIR/fix_path.sh"
|
| 682 |
+
echo "# Clear the shell's command hash table to force it to re-search the PATH" >> "$TOOLS_DIR/fix_path.sh"
|
| 683 |
+
echo "hash -r 2>/dev/null || rehash 2>/dev/null || true" >> "$TOOLS_DIR/fix_path.sh"
|
| 684 |
+
echo "echo \"Path fixed. Try running your command again.\"" >> "$TOOLS_DIR/fix_path.sh"
|
| 685 |
+
chmod +x "$TOOLS_DIR/fix_path.sh"
|
| 686 |
+
|
| 687 |
+
echo -e "${YELLOW}Created fix script at $TOOLS_DIR/fix_path.sh${NC}"
|
| 688 |
+
echo -e "${YELLOW}If you encounter 'No such file or directory' errors, run:${NC}"
|
| 689 |
+
echo -e "${GREEN}source $TOOLS_DIR/fix_path.sh${NC}"
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
+
# Function to verify installation
|
| 693 |
+
verify_installation() {
|
| 694 |
+
echo -e "\n${BLUE}=== Verifying Installation ===${NC}"
|
| 695 |
+
echo -e "${YELLOW}Tools directory: $TOOLS_DIR${NC}"
|
| 696 |
+
echo -e "${YELLOW}Bin directory: $TOOLS_DIR/bin${NC}"
|
| 697 |
+
|
| 698 |
+
# Check if bin directory exists
|
| 699 |
+
if [ ! -d "$TOOLS_DIR/bin" ]; then
|
| 700 |
+
echo -e "${RED}Bin directory does not exist!${NC}"
|
| 701 |
+
return 1
|
| 702 |
+
fi
|
| 703 |
+
|
| 704 |
+
# Check if there are any tools in the bin directory
|
| 705 |
+
local tool_count=$(ls -1 "$TOOLS_DIR/bin" 2>/dev/null | wc -l)
|
| 706 |
+
if [ "$tool_count" -eq 0 ]; then
|
| 707 |
+
echo -e "${RED}No tools found in bin directory!${NC}"
|
| 708 |
+
return 1
|
| 709 |
+
fi
|
| 710 |
+
|
| 711 |
+
echo -e "${GREEN}Found $tool_count tools in bin directory.${NC}"
|
| 712 |
+
|
| 713 |
+
# List all tools
|
| 714 |
+
echo -e "${YELLOW}Installed tools:${NC}"
|
| 715 |
+
ls -la "$TOOLS_DIR/bin"
|
| 716 |
+
|
| 717 |
+
# Check if tools are in PATH
|
| 718 |
+
echo -e "\n${YELLOW}Checking if tools are in PATH...${NC}"
|
| 719 |
+
echo -e "${YELLOW}Current PATH: $PATH${NC}"
|
| 720 |
+
|
| 721 |
+
# Check if TOOLS_DIR/bin is in PATH
|
| 722 |
+
if [[ "$PATH" == *"$TOOLS_DIR/bin"* ]]; then
|
| 723 |
+
echo -e "${GREEN}Tools directory is in PATH.${NC}"
|
| 724 |
+
else
|
| 725 |
+
echo -e "${RED}Tools directory is NOT in PATH!${NC}"
|
| 726 |
+
echo -e "${YELLOW}Please run: source $TOOLS_DIR/setup_path.sh${NC}"
|
| 727 |
+
return 1
|
| 728 |
+
fi
|
| 729 |
+
|
| 730 |
+
# Check if each tool is accessible
|
| 731 |
+
echo -e "\n${YELLOW}Testing tool accessibility:${NC}"
|
| 732 |
+
for tool in $(ls "$TOOLS_DIR/bin"); do
|
| 733 |
+
if command -v "$tool" &> /dev/null; then
|
| 734 |
+
echo -e "${GREEN}$tool: $(which $tool)${NC}"
|
| 735 |
+
else
|
| 736 |
+
echo -e "${RED}$tool: NOT FOUND IN PATH${NC}"
|
| 737 |
+
fi
|
| 738 |
+
done
|
| 739 |
+
|
| 740 |
+
echo -e "\n${GREEN}Installation verification completed.${NC}"
|
| 741 |
+
echo -e "${YELLOW}If you encounter any issues, please run:${NC}"
|
| 742 |
+
echo -e "${GREEN}source $TOOLS_DIR/setup_path.sh${NC}"
|
| 743 |
+
echo -e "${YELLOW}And then run the test script:${NC}"
|
| 744 |
+
echo -e "${GREEN}$TOOLS_DIR/test_tools.sh${NC}"
|
| 745 |
+
|
| 746 |
+
echo -e "\n${YELLOW}IMPORTANT: If you see 'No such file or directory' errors when running tools,${NC}"
|
| 747 |
+
echo -e "${YELLOW}your shell may be using cached paths to old tool locations.${NC}"
|
| 748 |
+
echo -e "${YELLOW}To fix this, run:${NC}"
|
| 749 |
+
echo -e "${GREEN}source $TOOLS_DIR/fix_path.sh${NC}"
|
| 750 |
+
echo -e "${YELLOW}Or run one of these commands:${NC}"
|
| 751 |
+
echo -e "${GREEN}hash -r${NC} (for bash/sh)"
|
| 752 |
+
echo -e "${GREEN}rehash${NC} (for zsh/csh)"
|
| 753 |
+
echo -e "${YELLOW}Or simply start a new terminal session.${NC}"
|
| 754 |
+
|
| 755 |
+
return 0
|
| 756 |
+
}
|
| 757 |
+
|
| 758 |
+
# Main installation function
|
| 759 |
+
install_cli_tools() {
|
| 760 |
+
echo -e "${YELLOW}=== Installing Command-Line Bioinformatics Tools ===${NC}"
|
| 761 |
+
|
| 762 |
+
# Try to add the tools bin directory to PATH
|
| 763 |
+
add_path_to_profile
|
| 764 |
+
|
| 765 |
+
# Check if auto-install mode is enabled
|
| 766 |
+
if [ -n "$BIOMNI_AUTO_INSTALL" ]; then
|
| 767 |
+
install_all_tools 1
|
| 768 |
+
|
| 769 |
+
echo -e "\n${GREEN}CLI tools installation completed!${NC}"
|
| 770 |
+
echo -e "The tools are installed in: ${YELLOW}$TOOLS_DIR${NC}"
|
| 771 |
+
echo -e "Binaries are symlinked in: ${YELLOW}$TOOLS_DIR/bin${NC}"
|
| 772 |
+
echo -e "Tools have been added to your PATH for the current session."
|
| 773 |
+
|
| 774 |
+
# Verify installation
|
| 775 |
+
verify_installation
|
| 776 |
+
|
| 777 |
+
return 0
|
| 778 |
+
fi
|
| 779 |
+
|
| 780 |
+
# Get the number of tools in the config
|
| 781 |
+
local num_tools=$(jq '.tools | length' "$CONFIG_FILE")
|
| 782 |
+
|
| 783 |
+
# Display menu
|
| 784 |
+
echo -e "\n${BLUE}Available tools:${NC}"
|
| 785 |
+
for (( i=0; i<$num_tools; i++ )); do
|
| 786 |
+
local tool_name=$(jq -r ".tools[$i].name" "$CONFIG_FILE")
|
| 787 |
+
local tool_desc=$(jq -r ".tools[$i].description" "$CONFIG_FILE")
|
| 788 |
+
echo -e "${YELLOW}$((i+1)). $tool_name${NC} - $tool_desc"
|
| 789 |
+
done
|
| 790 |
+
echo -e "${YELLOW}$((num_tools+1)). Add a new tool${NC}"
|
| 791 |
+
echo -e "${YELLOW}$((num_tools+2)). Install all tools${NC}"
|
| 792 |
+
echo -e "${YELLOW}$((num_tools+3)). Add tools directory to shell profile${NC}"
|
| 793 |
+
echo -e "${YELLOW}0. Exit${NC}"
|
| 794 |
+
|
| 795 |
+
# Get user choice
|
| 796 |
+
read -p "Enter your choice (0-$((num_tools+3))): " choice
|
| 797 |
+
|
| 798 |
+
if [[ "$choice" -eq 0 ]]; then
|
| 799 |
+
echo -e "${YELLOW}Exiting...${NC}"
|
| 800 |
+
return 0
|
| 801 |
+
elif [[ "$choice" -eq $((num_tools+1)) ]]; then
|
| 802 |
+
add_new_tool
|
| 803 |
+
elif [[ "$choice" -eq $((num_tools+2)) ]]; then
|
| 804 |
+
# Install all tools
|
| 805 |
+
install_all_tools
|
| 806 |
+
elif [[ "$choice" -eq $((num_tools+3)) ]]; then
|
| 807 |
+
# Add to shell profile
|
| 808 |
+
echo -e "\n${BLUE}=== Adding to Shell Profile ===${NC}"
|
| 809 |
+
echo -e "1. Auto-detect profile (recommended)"
|
| 810 |
+
echo -e "2. Specify profile file"
|
| 811 |
+
read -p "Enter your choice (1-2): " profile_choice
|
| 812 |
+
|
| 813 |
+
if [[ "$profile_choice" -eq 1 ]]; then
|
| 814 |
+
add_path_to_profile
|
| 815 |
+
elif [[ "$profile_choice" -eq 2 ]]; then
|
| 816 |
+
echo -e "\nCommon profile files:"
|
| 817 |
+
echo -e "- .bash_profile (for Bash on macOS/login shells)"
|
| 818 |
+
echo -e "- .bashrc (for Bash on Linux/non-login shells)"
|
| 819 |
+
echo -e "- .zshrc (for Zsh)"
|
| 820 |
+
echo -e "- .config/fish/config.fish (for Fish)"
|
| 821 |
+
echo -e "- .profile (generic)"
|
| 822 |
+
read -p "Enter profile filename (without $HOME/ prefix): " profile_file
|
| 823 |
+
add_path_to_profile "$profile_file"
|
| 824 |
+
else
|
| 825 |
+
echo -e "${RED}Invalid choice.${NC}"
|
| 826 |
+
fi
|
| 827 |
+
elif [[ "$choice" -ge 1 ]] && [[ "$choice" -le "$num_tools" ]]; then
|
| 828 |
+
# Install selected tool
|
| 829 |
+
install_tool_from_config "$((choice-1))"
|
| 830 |
+
else
|
| 831 |
+
echo -e "${RED}Invalid choice.${NC}"
|
| 832 |
+
return 1
|
| 833 |
+
fi
|
| 834 |
+
|
| 835 |
+
echo -e "\n${GREEN}CLI tools installation completed!${NC}"
|
| 836 |
+
echo -e "The tools are installed in: ${YELLOW}$TOOLS_DIR${NC}"
|
| 837 |
+
echo -e "Binaries are symlinked in: ${YELLOW}$TOOLS_DIR/bin${NC}"
|
| 838 |
+
echo -e "Tools have been added to your PATH for the current session."
|
| 839 |
+
|
| 840 |
+
# Ask if user wants to install more tools or configure PATH
|
| 841 |
+
read -p "Install more tools or configure PATH? (y/n) " -n 1 -r
|
| 842 |
+
echo
|
| 843 |
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
| 844 |
+
install_cli_tools
|
| 845 |
+
fi
|
| 846 |
+
}
|
| 847 |
+
|
| 848 |
+
# Function to display help message
|
| 849 |
+
show_help() {
|
| 850 |
+
echo "Usage: $0 [OPTION]"
|
| 851 |
+
echo "Install command-line bioinformatics tools for BioAgentOS."
|
| 852 |
+
echo
|
| 853 |
+
echo "Options:"
|
| 854 |
+
echo " --auto Automatically install all tools without prompting"
|
| 855 |
+
echo " --profile PROFILE Add tools directory to the specified shell profile file"
|
| 856 |
+
echo " Example: $0 --profile .zshrc"
|
| 857 |
+
echo " --help Display this help message and exit"
|
| 858 |
+
echo
|
| 859 |
+
echo "Without options, the script runs in interactive mode."
|
| 860 |
+
}
|
| 861 |
+
|
| 862 |
+
# Check command-line arguments
|
| 863 |
+
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
| 864 |
+
show_help
|
| 865 |
+
exit 0
|
| 866 |
+
elif [ "$1" = "--auto" ]; then
|
| 867 |
+
# Set BIOMNI_AUTO_INSTALL if it's not already set
|
| 868 |
+
export BIOMNI_AUTO_INSTALL=1
|
| 869 |
+
|
| 870 |
+
# Run in auto-install mode without showing the menu
|
| 871 |
+
install_all_tools 1
|
| 872 |
+
|
| 873 |
+
# Add to profile automatically
|
| 874 |
+
add_path_to_profile
|
| 875 |
+
|
| 876 |
+
# Verify installation
|
| 877 |
+
verify_installation
|
| 878 |
+
|
| 879 |
+
echo -e "\n${GREEN}CLI tools installation completed!${NC}"
|
| 880 |
+
echo -e "The tools are installed in: ${YELLOW}$TOOLS_DIR${NC}"
|
| 881 |
+
echo -e "Binaries are symlinked in: ${YELLOW}$TOOLS_DIR/bin${NC}"
|
| 882 |
+
echo -e "Tools have been added to your PATH for the current session."
|
| 883 |
+
|
| 884 |
+
exit 0
|
| 885 |
+
elif [ "$1" = "--profile" ] && [ -n "$2" ]; then
|
| 886 |
+
# Add to a specific profile file
|
| 887 |
+
add_path_to_profile "$2"
|
| 888 |
+
exit 0
|
| 889 |
+
elif [ -n "$BIOMNI_AUTO_INSTALL" ]; then
|
| 890 |
+
# Run in auto-install mode without showing the menu
|
| 891 |
+
install_all_tools 1
|
| 892 |
+
|
| 893 |
+
# Add to profile automatically
|
| 894 |
+
add_path_to_profile
|
| 895 |
+
|
| 896 |
+
# Verify installation
|
| 897 |
+
verify_installation
|
| 898 |
+
|
| 899 |
+
echo -e "\n${GREEN}CLI tools installation completed!${NC}"
|
| 900 |
+
echo -e "The tools are installed in: ${YELLOW}$TOOLS_DIR${NC}"
|
| 901 |
+
echo -e "Binaries are symlinked in: ${YELLOW}$TOOLS_DIR/bin${NC}"
|
| 902 |
+
echo -e "Tools have been added to your PATH for the current session."
|
| 903 |
+
|
| 904 |
+
exit 0
|
| 905 |
+
elif [ -n "$1" ]; then
|
| 906 |
+
echo -e "${RED}Unknown option: $1${NC}"
|
| 907 |
+
show_help
|
| 908 |
+
exit 1
|
| 909 |
+
else
|
| 910 |
+
# Run the interactive installation
|
| 911 |
+
install_cli_tools
|
| 912 |
+
fi
|
biomni_env/install_r_packages.R
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env Rscript
|
| 2 |
+
|
| 3 |
+
# Script to install R packages that might be missing from the conda environment
|
| 4 |
+
# Run this script after creating the conda environment if you encounter issues with R packages
|
| 5 |
+
|
| 6 |
+
# Set repository
|
| 7 |
+
options(repos = c(CRAN = "https://cran.rstudio.com/"))
|
| 8 |
+
|
| 9 |
+
# Function to install a package if it's not already installed
|
| 10 |
+
install_if_missing <- function(package_name, bioconductor = FALSE) {
|
| 11 |
+
if (!require(package_name, character.only = TRUE, quietly = TRUE)) {
|
| 12 |
+
cat(sprintf("Installing package: %s\n", package_name))
|
| 13 |
+
|
| 14 |
+
if (bioconductor) {
|
| 15 |
+
if (!require("BiocManager", quietly = TRUE)) {
|
| 16 |
+
install.packages("BiocManager", dependencies = TRUE)
|
| 17 |
+
}
|
| 18 |
+
BiocManager::install(package_name, update = FALSE, ask = FALSE, dependencies = TRUE)
|
| 19 |
+
} else {
|
| 20 |
+
install.packages(package_name, dependencies = TRUE)
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
# Check if installation was successful
|
| 24 |
+
if (require(package_name, character.only = TRUE, quietly = TRUE)) {
|
| 25 |
+
cat(sprintf("✓ Successfully installed %s\n", package_name))
|
| 26 |
+
} else {
|
| 27 |
+
cat(sprintf("✗ Failed to install %s\n", package_name))
|
| 28 |
+
}
|
| 29 |
+
} else {
|
| 30 |
+
cat(sprintf("✓ Package %s is already installed\n", package_name))
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
# Install BiocManager first with dependencies
|
| 35 |
+
cat("Installing BiocManager...\n")
|
| 36 |
+
if (!require("BiocManager", quietly = TRUE)) {
|
| 37 |
+
install.packages("BiocManager", dependencies = TRUE)
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# Make sure BiocManager is up to date
|
| 41 |
+
BiocManager::install(version = BiocManager::version(), update = TRUE, ask = FALSE)
|
| 42 |
+
|
| 43 |
+
# Install system dependencies for Bioconductor packages
|
| 44 |
+
cat("\nInstalling system dependencies for Bioconductor packages...\n")
|
| 45 |
+
|
| 46 |
+
# Install CRAN packages
|
| 47 |
+
cran_packages <- c(
|
| 48 |
+
"ggplot2",
|
| 49 |
+
"lme4",
|
| 50 |
+
"dplyr",
|
| 51 |
+
"tidyr",
|
| 52 |
+
"readr",
|
| 53 |
+
"stringr",
|
| 54 |
+
"Matrix", # Required for Seurat
|
| 55 |
+
"Rcpp", # Required for many packages
|
| 56 |
+
"devtools", # Useful for package installation
|
| 57 |
+
"remotes", # Required for GitHub installations
|
| 58 |
+
"harmony"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
# Install Bioconductor packages
|
| 62 |
+
bioc_packages <- c(
|
| 63 |
+
"DESeq2",
|
| 64 |
+
"dada2",
|
| 65 |
+
"xcms",
|
| 66 |
+
"flowCore",
|
| 67 |
+
"edgeR",
|
| 68 |
+
"limma"
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# Install CRAN packages
|
| 72 |
+
cat("\nInstalling CRAN packages...\n")
|
| 73 |
+
for (pkg in cran_packages) {
|
| 74 |
+
install_if_missing(pkg)
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
# Install Bioconductor packages one by one with specific handling
|
| 78 |
+
cat("\nInstalling Bioconductor packages...\n")
|
| 79 |
+
|
| 80 |
+
# Install DESeq2 with dependencies
|
| 81 |
+
cat("\nInstalling DESeq2 and dependencies...\n")
|
| 82 |
+
if (!require("DESeq2", quietly = TRUE)) {
|
| 83 |
+
BiocManager::install("DESeq2", dependencies = TRUE, update = FALSE, ask = FALSE)
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
# Install WGCNA (from CRAN, not Bioconductor)
|
| 87 |
+
# Following official instructions from https://cran.r-project.org/web/packages/WGCNA/index.html
|
| 88 |
+
cat("\nInstalling WGCNA and dependencies...\n")
|
| 89 |
+
if (!require("WGCNA", quietly = TRUE)) {
|
| 90 |
+
# Install WGCNA dependencies first
|
| 91 |
+
wgcna_deps <- c("dynamicTreeCut", "fastcluster", "matrixStats", "Hmisc", "foreach", "doParallel")
|
| 92 |
+
for (dep in wgcna_deps) {
|
| 93 |
+
install_if_missing(dep)
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
# Install Bioconductor dependencies for WGCNA
|
| 97 |
+
bioc_deps <- c("impute", "preprocessCore", "GO.db", "AnnotationDbi")
|
| 98 |
+
for (dep in bioc_deps) {
|
| 99 |
+
if (!require(dep, character.only = TRUE, quietly = TRUE)) {
|
| 100 |
+
BiocManager::install(dep, update = FALSE, ask = FALSE)
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
# Install WGCNA from CRAN
|
| 105 |
+
install.packages("WGCNA", dependencies = TRUE)
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
# Install clusterProfiler
|
| 109 |
+
cat("\nInstalling clusterProfiler and dependencies...\n")
|
| 110 |
+
if (!require("clusterProfiler", quietly = TRUE)) {
|
| 111 |
+
BiocManager::install("clusterProfiler", dependencies = TRUE, update = FALSE, ask = FALSE)
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
# Install remaining Bioconductor packages
|
| 115 |
+
remaining_bioc <- setdiff(bioc_packages, c("DESeq2", "clusterProfiler"))
|
| 116 |
+
for (pkg in remaining_bioc) {
|
| 117 |
+
install_if_missing(pkg, bioconductor = TRUE)
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# Verify installations
|
| 121 |
+
cat("\nVerifying installations...\n")
|
| 122 |
+
all_packages <- c(cran_packages, bioc_packages, "WGCNA")
|
| 123 |
+
for (pkg in unique(all_packages)) {
|
| 124 |
+
if (require(pkg, character.only = TRUE, quietly = TRUE)) {
|
| 125 |
+
cat(sprintf("✓ Package %s is successfully installed\n", pkg))
|
| 126 |
+
} else {
|
| 127 |
+
cat(sprintf("✗ Package %s is NOT installed\n", pkg))
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
cat("\nPackage installation completed!\n")
|
| 132 |
+
cat("If you still encounter issues with specific packages, please install them manually.\n")
|
biomni_env/new_software_v008.sh
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni v0.0.8 - Incremental Software Installation
|
| 2 |
+
# Add any new packages/software introduced in version 0.0.8 below
|
| 3 |
+
pip install transformers sentencepiece langchain-google-genai langchain_ollama mcp
|
| 4 |
+
pip install lazyslide
|
| 5 |
+
pip install "git+https://github.com/YosefLab/popV.git@refs/pull/100/head"
|
| 6 |
+
pip install uv
|
| 7 |
+
sudo apt-get install git-lfs # or brew install git-lfs if you are on macOS
|
| 8 |
+
git lfs install
|
| 9 |
+
pip install pybiomart
|
| 10 |
+
pip install fair-esm
|
| 11 |
+
pip install uv
|
| 12 |
+
uv pip install transcriptformer
|
| 13 |
+
pip install "zarr>=2.0,<3.0" #this resolved transcripformer download isses
|
| 14 |
+
uv tool install arc-state
|
| 15 |
+
pip install nnunet nibabel nilearn
|
| 16 |
+
pip install mi-googlesearch-python
|
| 17 |
+
pip install git+https://github.com/pylabrobot/pylabrobot.git
|
| 18 |
+
conda install weasyprint
|
biomni_env/r_packages.yml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: biomni_e1
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
- defaults
|
| 5 |
+
dependencies:
|
| 6 |
+
- r-essentials
|
| 7 |
+
- r-base>=4.4
|
biomni_env/setup.sh
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
# BioAgentOS - Biomni Environment Setup Script
|
| 4 |
+
# This script sets up a comprehensive bioinformatics environment with various tools and packages
|
| 5 |
+
|
| 6 |
+
# Set up colors for output
|
| 7 |
+
GREEN='\033[0;32m'
|
| 8 |
+
RED='\033[0;31m'
|
| 9 |
+
YELLOW='\033[1;33m'
|
| 10 |
+
BLUE='\033[0;34m'
|
| 11 |
+
NC='\033[0m' # No Color
|
| 12 |
+
|
| 13 |
+
# Default tools directory is the current directory
|
| 14 |
+
DEFAULT_TOOLS_DIR="$(pwd)/biomni_tools"
|
| 15 |
+
TOOLS_DIR=""
|
| 16 |
+
|
| 17 |
+
echo -e "${YELLOW}=== Biomni Environment Setup ===${NC}"
|
| 18 |
+
echo -e "${BLUE}This script will set up a comprehensive bioinformatics environment with various tools and packages.${NC}"
|
| 19 |
+
|
| 20 |
+
# Check if conda is installed
|
| 21 |
+
if ! command -v conda &> /dev/null && ! command -v micromamba &> /dev/null; then
|
| 22 |
+
echo -e "${RED}Error: Conda is not installed or not in PATH.${NC}"
|
| 23 |
+
echo "Please install Miniconda or Anaconda first."
|
| 24 |
+
echo "Visit: https://docs.conda.io/en/latest/miniconda.html"
|
| 25 |
+
exit 1
|
| 26 |
+
fi
|
| 27 |
+
|
| 28 |
+
# redirect to micromamba if needed
|
| 29 |
+
if ! command -v conda &> /dev/null && command -v micromamba &> /dev/null; then
|
| 30 |
+
conda() {
|
| 31 |
+
micromamba "$@"
|
| 32 |
+
}
|
| 33 |
+
export -f conda
|
| 34 |
+
fi
|
| 35 |
+
|
| 36 |
+
# Function to handle errors
|
| 37 |
+
handle_error() {
|
| 38 |
+
local exit_code=$1
|
| 39 |
+
local error_message=$2
|
| 40 |
+
local optional=${3:-false}
|
| 41 |
+
|
| 42 |
+
if [ $exit_code -ne 0 ]; then
|
| 43 |
+
echo -e "${RED}Error: $error_message${NC}"
|
| 44 |
+
if [ "$optional" = true ]; then
|
| 45 |
+
echo -e "${YELLOW}Continuing with setup as this component is optional.${NC}"
|
| 46 |
+
return 0
|
| 47 |
+
else
|
| 48 |
+
if [ -z "$NON_INTERACTIVE" ]; then
|
| 49 |
+
read -p "Continue with setup? (y/n) " -n 1 -r
|
| 50 |
+
echo
|
| 51 |
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
| 52 |
+
echo -e "${RED}Setup aborted.${NC}"
|
| 53 |
+
exit 1
|
| 54 |
+
fi
|
| 55 |
+
else
|
| 56 |
+
echo -e "${YELLOW}Non-interactive mode: continuing despite error.${NC}"
|
| 57 |
+
fi
|
| 58 |
+
fi
|
| 59 |
+
fi
|
| 60 |
+
return $exit_code
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
# Function to install a specific environment file
|
| 64 |
+
install_env_file() {
|
| 65 |
+
local env_file=$1
|
| 66 |
+
local description=$2
|
| 67 |
+
local optional=${3:-false}
|
| 68 |
+
|
| 69 |
+
echo -e "\n${BLUE}=== Installing $description ===${NC}"
|
| 70 |
+
|
| 71 |
+
if [ "$optional" = true ]; then
|
| 72 |
+
if [ -z "$NON_INTERACTIVE" ]; then
|
| 73 |
+
read -p "Do you want to install $description? (y/n) " -n 1 -r
|
| 74 |
+
echo
|
| 75 |
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
| 76 |
+
echo -e "${YELLOW}Skipping $description installation.${NC}"
|
| 77 |
+
return 0
|
| 78 |
+
fi
|
| 79 |
+
else
|
| 80 |
+
echo -e "${YELLOW}Non-interactive mode: automatically installing $description.${NC}"
|
| 81 |
+
fi
|
| 82 |
+
fi
|
| 83 |
+
|
| 84 |
+
echo -e "${YELLOW}Installing $description from $env_file...${NC}"
|
| 85 |
+
conda env update -f $env_file
|
| 86 |
+
handle_error $? "Failed to install $description." $optional
|
| 87 |
+
|
| 88 |
+
if [ $? -eq 0 ]; then
|
| 89 |
+
echo -e "${GREEN}Successfully installed $description!${NC}"
|
| 90 |
+
fi
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
# Function to install CLI tools
|
| 94 |
+
install_cli_tools() {
|
| 95 |
+
echo -e "\n${BLUE}=== Installing Command-Line Bioinformatics Tools ===${NC}"
|
| 96 |
+
|
| 97 |
+
# Ask user for the directory to install CLI tools
|
| 98 |
+
if [ -z "$NON_INTERACTIVE" ]; then
|
| 99 |
+
echo -e "${YELLOW}Where would you like to install the command-line tools?${NC}"
|
| 100 |
+
echo -e "${BLUE}Default: $DEFAULT_TOOLS_DIR${NC}"
|
| 101 |
+
read -p "Enter directory path (or press Enter for default): " user_tools_dir
|
| 102 |
+
else
|
| 103 |
+
user_tools_dir=""
|
| 104 |
+
echo -e "${YELLOW}Non-interactive mode: using default directory $DEFAULT_TOOLS_DIR for CLI tools.${NC}"
|
| 105 |
+
fi
|
| 106 |
+
|
| 107 |
+
if [ -z "$user_tools_dir" ]; then
|
| 108 |
+
TOOLS_DIR="$DEFAULT_TOOLS_DIR"
|
| 109 |
+
else
|
| 110 |
+
TOOLS_DIR="$user_tools_dir"
|
| 111 |
+
fi
|
| 112 |
+
|
| 113 |
+
# Export the tools directory for the CLI tools installer
|
| 114 |
+
export BIOMNI_TOOLS_DIR="$TOOLS_DIR"
|
| 115 |
+
|
| 116 |
+
echo -e "${YELLOW}Installing command-line tools (PLINK, IQ-TREE, GCTA, etc.) to $TOOLS_DIR...${NC}"
|
| 117 |
+
|
| 118 |
+
# Set environment variable to skip prompts in the CLI tools installer
|
| 119 |
+
export BIOMNI_AUTO_INSTALL=1
|
| 120 |
+
|
| 121 |
+
# Run the CLI tools installer
|
| 122 |
+
bash install_cli_tools.sh
|
| 123 |
+
handle_error $? "Failed to install CLI tools." true
|
| 124 |
+
|
| 125 |
+
if [ $? -eq 0 ]; then
|
| 126 |
+
echo -e "${GREEN}Successfully installed command-line tools!${NC}"
|
| 127 |
+
|
| 128 |
+
# Create a setup_path.sh file in the current directory
|
| 129 |
+
echo "#!/bin/bash" > setup_path.sh
|
| 130 |
+
echo "# Added by biomni setup" >> setup_path.sh
|
| 131 |
+
echo "# Remove any old paths first to avoid duplicates" >> setup_path.sh
|
| 132 |
+
echo "PATH=\$(echo \$PATH | tr ':' '\n' | grep -v \"biomni_tools/bin\" | tr '\n' ':' | sed 's/:$//')" >> setup_path.sh
|
| 133 |
+
echo "export PATH=\"$TOOLS_DIR/bin:\$PATH\"" >> setup_path.sh
|
| 134 |
+
chmod +x setup_path.sh
|
| 135 |
+
|
| 136 |
+
echo -e "${GREEN}Created setup_path.sh in the current directory.${NC}"
|
| 137 |
+
echo -e "${YELLOW}You can add the tools to your PATH by running:${NC}"
|
| 138 |
+
echo -e "${GREEN}source $(pwd)/setup_path.sh${NC}"
|
| 139 |
+
|
| 140 |
+
# Also add to the current session
|
| 141 |
+
# Remove any old paths first to avoid duplicates
|
| 142 |
+
PATH=$(echo $PATH | tr ':' '\n' | grep -v "biomni_tools/bin" | tr '\n' ':' | sed 's/:$//')
|
| 143 |
+
export PATH="$TOOLS_DIR/bin:$PATH"
|
| 144 |
+
fi
|
| 145 |
+
|
| 146 |
+
# Unset the environment variables
|
| 147 |
+
unset BIOMNI_AUTO_INSTALL
|
| 148 |
+
unset BIOMNI_TOOLS_DIR
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
# Main installation process
|
| 152 |
+
main() {
|
| 153 |
+
# Step 1: Create base conda environment
|
| 154 |
+
echo -e "\n${YELLOW}Step 1: Creating base environment from environment.yml...${NC}"
|
| 155 |
+
conda env create -n biomni_e1 -f environment.yml
|
| 156 |
+
handle_error $? "Failed to create base conda environment."
|
| 157 |
+
|
| 158 |
+
# Step 2: Activate the environment
|
| 159 |
+
echo -e "\n${YELLOW}Step 2: Activating conda environment...${NC}"
|
| 160 |
+
if command -v micromamba &> /dev/null; then
|
| 161 |
+
eval "$("$MAMBA_EXE" shell hook --shell bash)"
|
| 162 |
+
micromamba activate biomni_e1
|
| 163 |
+
else
|
| 164 |
+
eval "$(conda shell.bash hook)"
|
| 165 |
+
conda activate biomni_e1
|
| 166 |
+
fi
|
| 167 |
+
handle_error $? "Failed to activate biomni_e1 environment."
|
| 168 |
+
|
| 169 |
+
# Step 3: Install core bioinformatics tools (including QIIME2)
|
| 170 |
+
echo -e "\n${YELLOW}Step 3: Installing core bioinformatics tools (including QIIME2)...${NC}"
|
| 171 |
+
install_env_file "bio_env.yml" "core bioinformatics tools"
|
| 172 |
+
|
| 173 |
+
# Step 4: Install R packages
|
| 174 |
+
echo -e "\n${YELLOW}Step 4: Installing R packages...${NC}"
|
| 175 |
+
install_env_file "r_packages.yml" "core R packages"
|
| 176 |
+
|
| 177 |
+
# Step 5: Install additional R packages through R's package manager
|
| 178 |
+
echo -e "\n${YELLOW}Step 5: Installing additional R packages through R's package manager...${NC}"
|
| 179 |
+
Rscript install_r_packages.R
|
| 180 |
+
handle_error $? "Failed to install additional R packages." true
|
| 181 |
+
|
| 182 |
+
# Step 6: Install CLI tools
|
| 183 |
+
echo -e "\n${YELLOW}Step 6: Installing command-line bioinformatics tools...${NC}"
|
| 184 |
+
install_cli_tools
|
| 185 |
+
|
| 186 |
+
# Setup completed
|
| 187 |
+
echo -e "\n${GREEN}=== Biomni Environment Setup Completed! ===${NC}"
|
| 188 |
+
echo -e "You can now run the example analysis with: ${YELLOW}python bio_analysis_example.py${NC}"
|
| 189 |
+
echo -e "To activate this environment in the future, run: ${YELLOW}conda activate biomni_e1${NC}"
|
| 190 |
+
echo -e "To use BioAgentOS, navigate to the BioAgentOS directory and follow the instructions in the README."
|
| 191 |
+
|
| 192 |
+
# Display CLI tools setup instructions
|
| 193 |
+
if [ -n "$TOOLS_DIR" ]; then
|
| 194 |
+
echo -e "\n${BLUE}=== Command-Line Tools Setup ===${NC}"
|
| 195 |
+
echo -e "The command-line tools are installed in: ${YELLOW}$TOOLS_DIR${NC}"
|
| 196 |
+
echo -e "To add these tools to your PATH, run: ${YELLOW}source $(pwd)/setup_path.sh${NC}"
|
| 197 |
+
echo -e "You can also add this line to your shell profile for permanent access:"
|
| 198 |
+
echo -e "${GREEN}export PATH=\"$TOOLS_DIR/bin:\$PATH\"${NC}"
|
| 199 |
+
|
| 200 |
+
# Test if tools are accessible
|
| 201 |
+
echo -e "\n${BLUE}=== Testing CLI Tools ===${NC}"
|
| 202 |
+
if command -v plink2 &> /dev/null; then
|
| 203 |
+
echo -e "${GREEN}PLINK2 is accessible in the current PATH${NC}"
|
| 204 |
+
echo -e "PLINK2 location: $(which plink2)"
|
| 205 |
+
else
|
| 206 |
+
echo -e "${RED}PLINK2 is not accessible in the current PATH${NC}"
|
| 207 |
+
echo -e "Please run: ${YELLOW}source $(pwd)/setup_path.sh${NC} to update your PATH"
|
| 208 |
+
fi
|
| 209 |
+
|
| 210 |
+
if command -v gcta64 &> /dev/null; then
|
| 211 |
+
echo -e "${GREEN}GCTA is accessible in the current PATH${NC}"
|
| 212 |
+
echo -e "GCTA location: $(which gcta64)"
|
| 213 |
+
else
|
| 214 |
+
echo -e "${RED}GCTA is not accessible in the current PATH${NC}"
|
| 215 |
+
echo -e "Please run: ${YELLOW}source $(pwd)/setup_path.sh${NC} to update your PATH"
|
| 216 |
+
fi
|
| 217 |
+
|
| 218 |
+
if command -v iqtree2 &> /dev/null; then
|
| 219 |
+
echo -e "${GREEN}IQ-TREE is accessible in the current PATH${NC}"
|
| 220 |
+
echo -e "IQ-TREE location: $(which iqtree2)"
|
| 221 |
+
else
|
| 222 |
+
echo -e "${RED}IQ-TREE is not accessible in the current PATH${NC}"
|
| 223 |
+
echo -e "Please run: ${YELLOW}source $(pwd)/setup_path.sh${NC} to update your PATH"
|
| 224 |
+
fi
|
| 225 |
+
fi
|
| 226 |
+
|
| 227 |
+
PATH=$(echo $PATH | tr ':' '\n' | grep -v "biomni_tools/bin" | tr '\n' ':' | sed 's/:$//')
|
| 228 |
+
export PATH="$(pwd)/biomni_tools/bin:$PATH"
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
# Run the main installation process
|
| 232 |
+
main
|
docs/Makefile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Minimal makefile for Sphinx documentation
|
| 2 |
+
#
|
| 3 |
+
|
| 4 |
+
# You can set these variables from the command line, and also
|
| 5 |
+
# from the environment for the first two.
|
| 6 |
+
SPHINXOPTS ?=
|
| 7 |
+
SPHINXBUILD ?= sphinx-build
|
| 8 |
+
SOURCEDIR = source
|
| 9 |
+
BUILDDIR = build
|
| 10 |
+
|
| 11 |
+
# Put it first so that "make" without argument is like "make help".
|
| 12 |
+
help:
|
| 13 |
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
| 14 |
+
|
| 15 |
+
.PHONY: help Makefile
|
| 16 |
+
|
| 17 |
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
| 18 |
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
| 19 |
+
|
| 20 |
+
html:
|
| 21 |
+
@echo "Step 1: Generating API documentation..."
|
| 22 |
+
@python generate_api_docs.py
|
| 23 |
+
|
| 24 |
+
@echo "Step 2: Building HTML documentation with Sphinx..."
|
| 25 |
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
| 26 |
+
|
| 27 |
+
%: Makefile
|
| 28 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
docs/building_documentation.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# Building Project Documentation
|
| 3 |
+
|
| 4 |
+
To build the project's documentation locally, use the `Makefile` located in the `docs` directory.
|
| 5 |
+
|
| 6 |
+
## Prerequisites
|
| 7 |
+
|
| 8 |
+
First, ensure you have activated the `biomni_e1` conda environment. Then, install the required dependencies:
|
| 9 |
+
|
| 10 |
+
```bash
|
| 11 |
+
pip install sphinx sphinx-rtd-theme
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
## Build the Documentation
|
| 16 |
+
|
| 17 |
+
1. Navigate to the `docs` directory:
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
cd docs
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
2. Run the `make html` command:
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
make html
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
This command will automatically generate the API documentation and build all HTML files.
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
## View the Documentation
|
| 33 |
+
|
| 34 |
+
Once the build is complete, you can find the generated documentation in the `docs/build/html` directory.
|
| 35 |
+
|
| 36 |
+
Open the `index.html` file in your browser to view it.
|
docs/configuration.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni Configuration Guide
|
| 2 |
+
|
| 3 |
+
## Quick Start
|
| 4 |
+
|
| 5 |
+
**Recommended approach**: Use environment variables or modify `default_config` for consistent behavior across your entire application.
|
| 6 |
+
|
| 7 |
+
```python
|
| 8 |
+
from biomni.config import default_config
|
| 9 |
+
from biomni.agent import A1
|
| 10 |
+
|
| 11 |
+
# Option 1: Modify global defaults (affects everything)
|
| 12 |
+
default_config.llm = "gpt-4"
|
| 13 |
+
default_config.timeout_seconds = 1200
|
| 14 |
+
|
| 15 |
+
# Option 2: Use environment variables (set in .env file)
|
| 16 |
+
# BIOMNI_LLM=gpt-4
|
| 17 |
+
# BIOMNI_TIMEOUT_SECONDS=1200
|
| 18 |
+
|
| 19 |
+
agent = A1() # Uses your configuration
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
## Configuration Methods
|
| 23 |
+
|
| 24 |
+
### 1. Environment Variables (Recommended for Production)
|
| 25 |
+
|
| 26 |
+
Create a `.env` file in your project:
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
# Required API Keys (at least one)
|
| 30 |
+
ANTHROPIC_API_KEY=your_key
|
| 31 |
+
OPENAI_API_KEY=your_key
|
| 32 |
+
|
| 33 |
+
# Optional Settings
|
| 34 |
+
BIOMNI_LLM=claude-3-5-sonnet-20241022
|
| 35 |
+
BIOMNI_TIMEOUT_SECONDS=1200
|
| 36 |
+
BIOMNI_PATH=/path/to/data
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
### 2. Runtime Configuration (Recommended for Scripts)
|
| 40 |
+
|
| 41 |
+
```python
|
| 42 |
+
from biomni.config import default_config
|
| 43 |
+
|
| 44 |
+
# Changes apply to all agents and database queries
|
| 45 |
+
default_config.llm = "gpt-4"
|
| 46 |
+
default_config.timeout_seconds = 1200
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### 3. Direct Parameters (Use with Caution)
|
| 50 |
+
|
| 51 |
+
```python
|
| 52 |
+
# ⚠️ Only affects this agent's reasoning, NOT database queries
|
| 53 |
+
agent = A1(llm="claude-3-5-sonnet-20241022")
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
## Common Examples
|
| 57 |
+
|
| 58 |
+
### Using Different Models
|
| 59 |
+
|
| 60 |
+
```python
|
| 61 |
+
# Use GPT-4 everywhere
|
| 62 |
+
default_config.llm = "gpt-4"
|
| 63 |
+
agent = A1()
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
### Cost Optimization (Different Models for Agent vs Database)
|
| 67 |
+
|
| 68 |
+
```python
|
| 69 |
+
# Cheaper model for database queries
|
| 70 |
+
default_config.llm = "claude-3-5-haiku-20241022"
|
| 71 |
+
|
| 72 |
+
# More powerful model for agent reasoning
|
| 73 |
+
agent = A1(llm="claude-3-5-sonnet-20241022")
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
### Custom/Local Models
|
| 77 |
+
|
| 78 |
+
```python
|
| 79 |
+
default_config.source = "Custom"
|
| 80 |
+
default_config.base_url = "http://localhost:8000/v1"
|
| 81 |
+
default_config.api_key = "local_key"
|
| 82 |
+
default_config.llm = "local-llama-70b"
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
## All Available Settings
|
| 86 |
+
|
| 87 |
+
### Environment Variables
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
# API Keys
|
| 91 |
+
ANTHROPIC_API_KEY=your_key
|
| 92 |
+
OPENAI_API_KEY=your_key
|
| 93 |
+
GEMINI_API_KEY=your_key
|
| 94 |
+
GROQ_API_KEY=your_key
|
| 95 |
+
AWS_BEARER_TOKEN_BEDROCK=your_key
|
| 96 |
+
AWS_REGION=us-east-1
|
| 97 |
+
|
| 98 |
+
# Azure OpenAI
|
| 99 |
+
OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
|
| 100 |
+
|
| 101 |
+
# Biomni Settings
|
| 102 |
+
BIOMNI_PATH=/path/to/data # Default: ./data
|
| 103 |
+
BIOMNI_TIMEOUT_SECONDS=1200 # Default: 600
|
| 104 |
+
BIOMNI_LLM=model_name # Default: claude-sonnet-4-20250514
|
| 105 |
+
BIOMNI_TEMPERATURE=0.7 # Default: 0.7
|
| 106 |
+
BIOMNI_USE_TOOL_RETRIEVER=true # Default: true
|
| 107 |
+
BIOMNI_SOURCE=Anthropic # Auto-detected if not set
|
| 108 |
+
BIOMNI_CUSTOM_BASE_URL=http://localhost:8000/v1
|
| 109 |
+
BIOMNI_CUSTOM_API_KEY=custom_key
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
### Python Configuration
|
| 113 |
+
|
| 114 |
+
```python
|
| 115 |
+
from biomni.config import default_config
|
| 116 |
+
|
| 117 |
+
# All available settings
|
| 118 |
+
default_config.path = "./data"
|
| 119 |
+
default_config.timeout_seconds = 600
|
| 120 |
+
default_config.llm = "claude-sonnet-4-20250514"
|
| 121 |
+
default_config.temperature = 0.7
|
| 122 |
+
default_config.use_tool_retriever = True
|
| 123 |
+
default_config.source = None # Auto-detected
|
| 124 |
+
default_config.base_url = None # For custom models
|
| 125 |
+
default_config.api_key = None # For custom models
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
## Important Notes
|
| 129 |
+
|
| 130 |
+
- **For pip-installed packages**: You can't edit the package files, but you can still use environment variables or modify `default_config` at runtime
|
| 131 |
+
- **Configuration consistency**: Database queries always use `default_config`, regardless of agent parameters
|
| 132 |
+
- **Priority order**: Direct params > Runtime config > Env vars > Defaults
|
| 133 |
+
|
| 134 |
+
## Troubleshooting
|
| 135 |
+
|
| 136 |
+
**API Key Not Found**:
|
| 137 |
+
- Check `.env` file exists in your working directory
|
| 138 |
+
- Verify with: `echo $ANTHROPIC_API_KEY`
|
| 139 |
+
|
| 140 |
+
**Configuration Not Applied**:
|
| 141 |
+
- Changes to `default_config` only affect agents created after the change
|
| 142 |
+
- Direct parameters only affect that specific agent, not database queries
|
| 143 |
+
|
| 144 |
+
**Model Not Found**:
|
| 145 |
+
- Check spelling of model name
|
| 146 |
+
- For Azure, prefix with "azure-" (e.g., "azure-gpt-4o")
|
| 147 |
+
- Ensure you have the right API key for that provider
|
docs/generate_api_docs.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# generate_api_docs.py
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def generate_api_docs(base_path, output_dir, package_name, exclude_list=None):
|
| 7 |
+
"""
|
| 8 |
+
Automatically generates .rst files for Sphinx API documentation.
|
| 9 |
+
|
| 10 |
+
Args:
|
| 11 |
+
base_path (str): The root directory of the Python package (e.g., 'biomni').
|
| 12 |
+
output_dir (str): The directory where the .rst files will be generated (e.g., 'source/api').
|
| 13 |
+
package_name (str): The name of the Python package.
|
| 14 |
+
exclude_list (list): A list of paths (directories or files) to exclude from documentation.
|
| 15 |
+
Paths should be relative to the base_path.
|
| 16 |
+
"""
|
| 17 |
+
if exclude_list is None:
|
| 18 |
+
exclude_list = []
|
| 19 |
+
|
| 20 |
+
if not os.path.exists(output_dir):
|
| 21 |
+
os.makedirs(output_dir)
|
| 22 |
+
|
| 23 |
+
# Walk through the package directory
|
| 24 |
+
for root, dirs, files in os.walk(base_path):
|
| 25 |
+
# Exclude directories if they are in the exclude list
|
| 26 |
+
dirs[:] = [d for d in dirs if os.path.join(os.path.relpath(root, base_path), d) not in exclude_list]
|
| 27 |
+
|
| 28 |
+
for file in files:
|
| 29 |
+
file_path_relative = os.path.relpath(os.path.join(root, file), base_path)
|
| 30 |
+
|
| 31 |
+
# Check if the file should be excluded
|
| 32 |
+
if file_path_relative in exclude_list:
|
| 33 |
+
print(f"Skipping excluded file: {file_path_relative}")
|
| 34 |
+
continue
|
| 35 |
+
|
| 36 |
+
if file.endswith(".py") and file != "__init__.py":
|
| 37 |
+
# Construct the full module name (e.g., biomni.agent.a1)
|
| 38 |
+
rel_path = os.path.relpath(root, base_path)
|
| 39 |
+
module_name = f"{package_name}.{rel_path.replace(os.path.sep, '.')}.{os.path.splitext(file)[0]}"
|
| 40 |
+
|
| 41 |
+
# Clean up the module name if it starts with the package name
|
| 42 |
+
if module_name.startswith(f"{package_name}.{package_name}."):
|
| 43 |
+
module_name = f"{package_name}.{module_name[len(f'{package_name}.{package_name}.') :]}"
|
| 44 |
+
|
| 45 |
+
# Create the directory structure in the output folder
|
| 46 |
+
output_path_dir = os.path.join(output_dir, rel_path)
|
| 47 |
+
if not os.path.exists(output_path_dir):
|
| 48 |
+
os.makedirs(output_path_dir)
|
| 49 |
+
|
| 50 |
+
# Define the output .rst file path
|
| 51 |
+
output_file = os.path.join(output_path_dir, f"{os.path.splitext(file)[0]}.rst")
|
| 52 |
+
|
| 53 |
+
# Generate the RST content
|
| 54 |
+
rst_content = f"""{module_name}
|
| 55 |
+
{"=" * len(module_name)}
|
| 56 |
+
|
| 57 |
+
.. automodule:: {module_name}
|
| 58 |
+
:members:
|
| 59 |
+
:undoc-members:
|
| 60 |
+
:show-inheritance:
|
| 61 |
+
"""
|
| 62 |
+
# Write the content to the .rst file
|
| 63 |
+
with open(output_file, "w", encoding="utf-8") as f:
|
| 64 |
+
f.write(rst_content)
|
| 65 |
+
print(f"Generated {output_file}")
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def create_index_rst(output_dir, package_name):
|
| 69 |
+
"""
|
| 70 |
+
Creates or updates the main index file that lists all generated .rst files.
|
| 71 |
+
"""
|
| 72 |
+
output_dir = os.path.join(output_dir, "../")
|
| 73 |
+
index_path = os.path.join(output_dir, "index.rst")
|
| 74 |
+
rst_files = []
|
| 75 |
+
|
| 76 |
+
# Find all generated rst files
|
| 77 |
+
for root, _dirs, files in os.walk(output_dir):
|
| 78 |
+
for file in files:
|
| 79 |
+
if file.endswith(".rst") and file != "index.rst":
|
| 80 |
+
rel_path = os.path.relpath(os.path.join(root, file), output_dir)
|
| 81 |
+
rst_files.append(os.path.splitext(rel_path)[0])
|
| 82 |
+
|
| 83 |
+
rst_files.sort()
|
| 84 |
+
|
| 85 |
+
index_content = f"""{package_name} API Reference
|
| 86 |
+
{"=" * (len(package_name) + len(" API Reference"))}
|
| 87 |
+
|
| 88 |
+
.. toctree::
|
| 89 |
+
:maxdepth: 2
|
| 90 |
+
:caption: API Contents:
|
| 91 |
+
|
| 92 |
+
"""
|
| 93 |
+
for file in rst_files:
|
| 94 |
+
index_content += f" {file}\n"
|
| 95 |
+
|
| 96 |
+
with open(index_path, "w", encoding="utf-8") as f:
|
| 97 |
+
f.write(index_content)
|
| 98 |
+
print(f"Updated {index_path} with module links.")
|
| 99 |
+
|
| 100 |
+
# update ../index.rst to include api/index
|
| 101 |
+
# index_path = os.path.join(output_dir, '../index.rst')
|
| 102 |
+
# with open(index_path, 'w', encoding='utf-8') as f:
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
if __name__ == "__main__":
|
| 106 |
+
package_to_document = "../biomni"
|
| 107 |
+
api_docs_path = "./source/api"
|
| 108 |
+
package_name = "biomni"
|
| 109 |
+
|
| 110 |
+
os.makedirs(api_docs_path, exist_ok=True)
|
| 111 |
+
|
| 112 |
+
exclude_list = [
|
| 113 |
+
"llm.py",
|
| 114 |
+
"env_desc.py",
|
| 115 |
+
"version.py",
|
| 116 |
+
"tool/tool_description",
|
| 117 |
+
"tool/example_mcp_tools",
|
| 118 |
+
]
|
| 119 |
+
|
| 120 |
+
generate_api_docs(package_to_document, api_docs_path, package_name, exclude_list)
|
| 121 |
+
create_index_rst(api_docs_path, package_name)
|
| 122 |
+
|
| 123 |
+
print("\nAPI documentation source files have been generated.")
|
| 124 |
+
print("Now run 'make html' from your project's root directory to build the documentation.")
|
docs/known_conflicts.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Known Package Conflicts in Biomni
|
| 2 |
+
|
| 3 |
+
This file lists Python packages that are known to have dependency conflicts with the default Biomni environment. These packages are not installed by default. If you require their functionality, you must install them manually and may need to uncomment relevant code in the codebase.
|
| 4 |
+
|
| 5 |
+
## Packages
|
| 6 |
+
|
| 7 |
+
### 1. hyperimpute
|
| 8 |
+
- Not installed by default due to dependency conflicts with the main environment.
|
| 9 |
+
- If you need imputation tools that require this package, install it manually in a separate environment or with caution.
|
| 10 |
+
|
| 11 |
+
### 2. langchain_aws
|
| 12 |
+
- Needed for Amazon Bedrock support.
|
| 13 |
+
- Amazon Bedrock support is present in the codebase, but due to package dependency conflicts, you should install `langchain_aws` only when you need Bedrock support.
|
| 14 |
+
- You must also uncomment the relevant Bedrock support code sections in the codebase to enable this feature.
|
| 15 |
+
|
| 16 |
+
### 3. cnvkit
|
| 17 |
+
- **Environment Requirement**: Requires Python 3.10 environment (`bio_env_py310.yml`)
|
| 18 |
+
- **Function**: Supports the `analyze_copy_number_purity_ploidy_and_focal_events` function
|
| 19 |
+
- **Why Separate Environment**: cnvkit has strict dependency requirements that conflict with newer Python versions and other packages in the main Biomni environment. Python 3.10 provides the optimal compatibility for cnvkit and its dependencies, ensuring reliable copy number variation analysis, purity estimation, ploidy detection, and focal event identification.
|
| 20 |
+
- **Installation**: Use `conda env create -f bio_env_py310.yml` to create the dedicated environment
|
| 21 |
+
|
| 22 |
+
### 4. panhumanpy
|
| 23 |
+
- **Environment Requirement**: Requires its own dedicated environment due to specific dependency constraints
|
| 24 |
+
- **Function**: Supports the `annotate_celltype_with_panhumanpy` function for automated cell type annotation in scRNA-seq data
|
| 25 |
+
- **Why Separate Environment**: panhumanpy has strict version requirements including:
|
| 26 |
+
- TensorFlow 2.17 (specific version)
|
| 27 |
+
- scikit-learn 1.6.0 (specific version)
|
| 28 |
+
- Python >=3.9 but optimized for Python 3.12
|
| 29 |
+
- These version constraints conflict with the main Biomni environment and other packages
|
| 30 |
+
- **Installation**: Install in a separate conda environment with the exact versions specified in the package requirements
|
| 31 |
+
|
| 32 |
+
---
|
| 33 |
+
|
| 34 |
+
If you encounter other package conflicts, please add them to this file or open an issue.
|
docs/make.bat
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@ECHO OFF
|
| 2 |
+
|
| 3 |
+
pushd %~dp0
|
| 4 |
+
|
| 5 |
+
REM Command file for Sphinx documentation
|
| 6 |
+
|
| 7 |
+
if "%SPHINXBUILD%" == "" (
|
| 8 |
+
set SPHINXBUILD=sphinx-build
|
| 9 |
+
)
|
| 10 |
+
set SOURCEDIR=source
|
| 11 |
+
set BUILDDIR=build
|
| 12 |
+
|
| 13 |
+
%SPHINXBUILD% >NUL 2>NUL
|
| 14 |
+
if errorlevel 9009 (
|
| 15 |
+
echo.
|
| 16 |
+
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
| 17 |
+
echo.installed, then set the SPHINXBUILD environment variable to point
|
| 18 |
+
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
| 19 |
+
echo.may add the Sphinx directory to PATH.
|
| 20 |
+
echo.
|
| 21 |
+
echo.If you don't have Sphinx installed, grab it from
|
| 22 |
+
echo.https://www.sphinx-doc.org/
|
| 23 |
+
exit /b 1
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
if "%1" == "" goto help
|
| 27 |
+
|
| 28 |
+
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
| 29 |
+
goto end
|
| 30 |
+
|
| 31 |
+
:help
|
| 32 |
+
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
| 33 |
+
|
| 34 |
+
:end
|
| 35 |
+
popd
|
docs/mcp_integration.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MCP (Model Context Protocol) Integration in Biomni
|
| 2 |
+
|
| 3 |
+
Biomni provides comprehensive support for the Model Context Protocol (MCP), allowing you to both integrate external MCP servers and expose Biomni tools as MCP servers. This enables seamless interoperability with a wide ecosystem of AI tools and services.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
MCP (Model Context Protocol) is a standard protocol for AI applications to communicate with external tools and services. Biomni supports two main MCP integration patterns:
|
| 8 |
+
|
| 9 |
+
1. **Adding External MCP Servers**: Import tools from external MCP servers into Biomni
|
| 10 |
+
2. **Exposing Biomni as MCP Server**: Make Biomni tools available to other MCP clients
|
| 11 |
+
|
| 12 |
+
## Adding External MCP Servers to Biomni
|
| 13 |
+
|
| 14 |
+
### Configuration File Format
|
| 15 |
+
|
| 16 |
+
MCP servers are configured using a YAML file that defines server connections and their tools. The `command` field specifies how to start the MCP server, which varies depending on how the server is packaged and distributed.
|
| 17 |
+
|
| 18 |
+
```yaml
|
| 19 |
+
mcp_servers:
|
| 20 |
+
server_name:
|
| 21 |
+
enabled: true # Optional, defaults to true
|
| 22 |
+
command: ["docker", "run", "-i", "--rm", "-e", "ENV_VAR", "image:tag"] # Docker-based server
|
| 23 |
+
# OR
|
| 24 |
+
command: ["npx", "-y", "@modelcontextprotocol/server-name"] # NPM-based server
|
| 25 |
+
# OR
|
| 26 |
+
command: ["python", "-m", "my_mcp_server"] # Python-based server
|
| 27 |
+
# OR
|
| 28 |
+
command: ["./my-mcp-server", "--config", "config.json"] # Binary server
|
| 29 |
+
env:
|
| 30 |
+
API_KEY: "${OPENAI_API_KEY}" # Environment variable substitution
|
| 31 |
+
CUSTOM_VAR: "static_value"
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
**Note**: The exact command format depends on the MCP server. Check the server's documentation for the correct command to use.
|
| 35 |
+
|
| 36 |
+
### Example Configuration
|
| 37 |
+
|
| 38 |
+
```yaml
|
| 39 |
+
mcp_servers:
|
| 40 |
+
# GitHub MCP Server (Docker approach - recommended)
|
| 41 |
+
github:
|
| 42 |
+
command: ["docker", "run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"]
|
| 43 |
+
enabled: true
|
| 44 |
+
description: "Official GitHub MCP server for repository and issue management"
|
| 45 |
+
env:
|
| 46 |
+
GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}"
|
| 47 |
+
|
| 48 |
+
# Custom Local MCP Server - Only add tools necessary
|
| 49 |
+
pubmed:
|
| 50 |
+
command: ["python", "-m", "biomni.tool.mcp_tools.pubmed_mcp"]
|
| 51 |
+
enabled: true
|
| 52 |
+
tools:
|
| 53 |
+
- biomni_name: search_pubmed
|
| 54 |
+
description: "Search PubMed"
|
| 55 |
+
parameters:
|
| 56 |
+
query: {type: str, required: true, description: "PubMed search term"}
|
| 57 |
+
max_results: {type: int, required: false, default: 10, description: "Maximum number of hits"}
|
| 58 |
+
- biomni_name: get_article_abstract
|
| 59 |
+
description: "Fetch PubMed abstract"
|
| 60 |
+
parameters:
|
| 61 |
+
pmid: {type: str, required: true, description: "PubMed ID"}
|
| 62 |
+
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
You can also create custom MCP servers and define your own tools by implementing the MCP protocol and adding them to your configuration file.
|
| 66 |
+
|
| 67 |
+
### Using the `add_mcp` Method
|
| 68 |
+
|
| 69 |
+
```python
|
| 70 |
+
from biomni.agent import A1
|
| 71 |
+
|
| 72 |
+
# Initialize Biomni agent
|
| 73 |
+
agent = A1()
|
| 74 |
+
|
| 75 |
+
# Optional: Set email for PubMed
|
| 76 |
+
# import os
|
| 77 |
+
# os.environ.setdefault("NCBI_EMAIL", "Your email address")
|
| 78 |
+
|
| 79 |
+
# Add MCP servers from configuration file
|
| 80 |
+
agent.add_mcp(config_path="./mcp_config.yaml")
|
| 81 |
+
|
| 82 |
+
# Now you can use the MCP tools in your queries
|
| 83 |
+
result = agent.go("Please list all repositories in the github account of the user.")
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
### How It Works
|
| 87 |
+
|
| 88 |
+
1. **Tool Discovery**: Biomni automatically discovers available tools from each MCP server
|
| 89 |
+
2. **Async-to-Sync Wrapping**: MCP tools are wrapped to work with Biomni's synchronous execution model
|
| 90 |
+
3. **Integration**: Tools are registered in Biomni's tool registry and made available for retrieval
|
| 91 |
+
4. **Module Organization**: Each MCP server gets its own module namespace (e.g., `mcp_servers.github`)
|
| 92 |
+
|
| 93 |
+
### Tool Registration Process
|
| 94 |
+
|
| 95 |
+
When you call `add_mcp()`, Biomni:
|
| 96 |
+
|
| 97 |
+
1. Loads the configuration file
|
| 98 |
+
2. For each enabled server:
|
| 99 |
+
- Establishes a connection to the MCP server
|
| 100 |
+
- Discovers available tools
|
| 101 |
+
- Creates synchronous wrapper functions
|
| 102 |
+
- Registers tools in the tool registry
|
| 103 |
+
- Adds tools to the module2api mapping
|
| 104 |
+
- Stores tools in custom functions registry
|
| 105 |
+
|
| 106 |
+
### Environment Variable Substitution
|
| 107 |
+
|
| 108 |
+
The configuration supports environment variable substitution using `${VARIABLE_NAME}` syntax:
|
| 109 |
+
|
| 110 |
+
```yaml
|
| 111 |
+
mcp_servers:
|
| 112 |
+
my_server:
|
| 113 |
+
command: ["my-server"]
|
| 114 |
+
env:
|
| 115 |
+
API_KEY: "${MY_API_KEY}" # Will be replaced with os.getenv("MY_API_KEY")
|
| 116 |
+
DEBUG: "true" # Static value
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
You will need to properly set your environment variables (e.g., `GITHUB_TOKEN`) using a `.env` file or shell exports before running the agent.
|
| 120 |
+
|
| 121 |
+
## Exposing Biomni Tools as MCP Server
|
| 122 |
+
|
| 123 |
+
### Using the `create_mcp_server` Method
|
| 124 |
+
|
| 125 |
+
Biomni can expose its internal tools as an MCP server, making them available to other MCP clients:
|
| 126 |
+
|
| 127 |
+
```python
|
| 128 |
+
from biomni.agent.a1 import A1
|
| 129 |
+
|
| 130 |
+
# Create the agent
|
| 131 |
+
agent = A1()
|
| 132 |
+
|
| 133 |
+
# Create the MCP server with specific modules
|
| 134 |
+
mcp = agent.create_mcp_server(tool_modules=["biomni.tool.database"])
|
| 135 |
+
|
| 136 |
+
if __name__ == "__main__":
|
| 137 |
+
# Run the server using stdio transport
|
| 138 |
+
print("Starting Biomni MCP server...")
|
| 139 |
+
mcp.run(transport="stdio")
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
### Server Configuration
|
| 143 |
+
|
| 144 |
+
The MCP server can be configured with various options:
|
| 145 |
+
|
| 146 |
+
```python
|
| 147 |
+
# Create server with specific modules
|
| 148 |
+
mcp = agent.create_mcp_server(tool_modules=[
|
| 149 |
+
"biomni.tool.genetics",
|
| 150 |
+
"biomni.tool.database",
|
| 151 |
+
"biomni.tool.cell_biology"
|
| 152 |
+
])
|
| 153 |
+
|
| 154 |
+
# The server will expose all tools from these modules
|
| 155 |
+
# Tools are automatically wrapped with proper parameter validation
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
## Best Practices
|
| 159 |
+
|
| 160 |
+
### Configuration Management
|
| 161 |
+
|
| 162 |
+
1. **Environment Variables**: Use environment variables for sensitive data like API keys
|
| 163 |
+
2. **Docker Images**: Use official Docker images for MCP servers when available
|
| 164 |
+
3. **Server Validation**: Test server connections before adding them to production
|
| 165 |
+
4. **Tool Naming**: Ensure unique tool names across different servers to avoid conflicts
|
| 166 |
+
|
| 167 |
+
### Performance Considerations
|
| 168 |
+
|
| 169 |
+
1. **Connection Management**: MCP servers are created on-demand for each tool call
|
| 170 |
+
2. **Tool Discovery**: Tool discovery happens once during `add_mcp()` call
|
| 171 |
+
3. **Error Handling**: Failed tool calls are properly handled and reported
|
| 172 |
+
4. **Docker Overhead**: Containerized servers may have additional startup time
|
| 173 |
+
|
| 174 |
+
### Security
|
| 175 |
+
|
| 176 |
+
1. **Environment Variables**: Never hardcode sensitive information in configuration files
|
| 177 |
+
2. **Docker Security**: Use trusted Docker images and consider security implications
|
| 178 |
+
3. **Server Permissions**: Ensure MCP servers have appropriate permissions for their operations
|
| 179 |
+
4. **Network Access**: Be aware of network access requirements for external MCP servers
|
| 180 |
+
|
| 181 |
+
## Troubleshooting
|
| 182 |
+
|
| 183 |
+
### Common Issues
|
| 184 |
+
|
| 185 |
+
1. **Docker Not Running**: Ensure Docker is running for containerized MCP servers
|
| 186 |
+
2. **Environment Variables**: Verify that required environment variables are set (e.g., `GITHUB_TOKEN`, `NCBI_EMAIL`)
|
| 187 |
+
3. **Tool Discovery Failures**: Check server logs for connection or authentication issues
|
| 188 |
+
4. **Naming Conflicts**: Tools with duplicate names will be skipped with a warning
|
| 189 |
+
|
| 190 |
+
### Debugging
|
| 191 |
+
|
| 192 |
+
```python
|
| 193 |
+
# Test MCP connections without adding tools
|
| 194 |
+
test_results = agent.test_mcp_connection("./mcp_config.yaml")
|
| 195 |
+
print(test_results)
|
| 196 |
+
|
| 197 |
+
# List all MCP servers and their tools
|
| 198 |
+
servers = agent.list_mcp_servers()
|
| 199 |
+
print(servers)
|
| 200 |
+
|
| 201 |
+
# Remove specific MCP tools
|
| 202 |
+
agent.remove_mcp_tools("server_name")
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
### Testing MCP Server
|
| 206 |
+
|
| 207 |
+
Use the provided test script to verify your MCP server is working:
|
| 208 |
+
|
| 209 |
+
```bash
|
| 210 |
+
cd tutorials/examples/expose_biomni_server
|
| 211 |
+
python test_mcp_server.py
|
| 212 |
+
```
|
| 213 |
+
|
| 214 |
+
The test script allows you to:
|
| 215 |
+
- Test individual tools with custom parameters
|
| 216 |
+
- View available tools and their schemas
|
| 217 |
+
- Debug connection issues
|
| 218 |
+
- Validate tool responses
|
| 219 |
+
|
| 220 |
+
## Example Use Cases
|
| 221 |
+
|
| 222 |
+
### Adding External MCP Servers
|
| 223 |
+
|
| 224 |
+
```python
|
| 225 |
+
from biomni.agent import A1
|
| 226 |
+
|
| 227 |
+
# Initialize agent and add MCP servers
|
| 228 |
+
agent = A1()
|
| 229 |
+
agent.add_mcp(config_path="./mcp_config.yaml")
|
| 230 |
+
|
| 231 |
+
# Use GitHub tools alongside Biomni tools
|
| 232 |
+
result = agent.go("""
|
| 233 |
+
Please list all repositories in the github account of the user.
|
| 234 |
+
Then use Biomni's genetics tools to analyze any bioinformatics repositories found.
|
| 235 |
+
""")
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
### Exposing Biomni as MCP Server
|
| 239 |
+
|
| 240 |
+
```python
|
| 241 |
+
from biomni.agent.a1 import A1
|
| 242 |
+
|
| 243 |
+
# Create agent and MCP server
|
| 244 |
+
agent = A1()
|
| 245 |
+
mcp = agent.create_mcp_server(tool_modules=["biomni.tool.database"])
|
| 246 |
+
|
| 247 |
+
# Run the server
|
| 248 |
+
if __name__ == "__main__":
|
| 249 |
+
print("Starting Biomni MCP server...")
|
| 250 |
+
mcp.run(transport="stdio")
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
## Integration with Other Systems
|
| 254 |
+
|
| 255 |
+
### Available Examples
|
| 256 |
+
|
| 257 |
+
The Biomni repository includes complete examples in the `tutorials/examples/` directory:
|
| 258 |
+
|
| 259 |
+
#### Adding MCP Servers
|
| 260 |
+
- **Location**: `tutorials/examples/add_mcp_server/`
|
| 261 |
+
- **Files**:
|
| 262 |
+
- `mcp_config.yaml` - Example configuration file
|
| 263 |
+
- `mcp_example.ipynb` - Jupyter notebook demonstrating usage
|
| 264 |
+
|
| 265 |
+
#### Exposing Biomni as MCP Server
|
| 266 |
+
- **Location**: `tutorials/examples/expose_biomni_server/`
|
| 267 |
+
- **Files**:
|
| 268 |
+
- `run_mcp_server.py` - Script to run Biomni MCP server
|
| 269 |
+
- `test_mcp_server.py` - Comprehensive test script for MCP tools
|
| 270 |
+
|
| 271 |
+
To run the examples:
|
| 272 |
+
|
| 273 |
+
```bash
|
| 274 |
+
# Add MCP servers
|
| 275 |
+
cd tutorials/examples/add_mcp_server
|
| 276 |
+
jupyter notebook mcp_example.ipynb
|
| 277 |
+
|
| 278 |
+
# Expose Biomni as MCP server
|
| 279 |
+
cd tutorials/examples/expose_biomni_server
|
| 280 |
+
python run_mcp_server.py
|
| 281 |
+
|
| 282 |
+
# Test the server
|
| 283 |
+
python test_mcp_server.py
|
| 284 |
+
```
|
| 285 |
+
|
| 286 |
+
### Testing MCP Server
|
| 287 |
+
|
| 288 |
+
You can test the Biomni MCP server using the provided test script:
|
| 289 |
+
|
| 290 |
+
```python
|
| 291 |
+
#!/usr/bin/env python3
|
| 292 |
+
"""
|
| 293 |
+
Simple test script for testing a single Biomni MCP tool.
|
| 294 |
+
"""
|
| 295 |
+
|
| 296 |
+
import asyncio
|
| 297 |
+
import json
|
| 298 |
+
import sys
|
| 299 |
+
|
| 300 |
+
from mcp import ClientSession
|
| 301 |
+
from mcp.client.stdio import StdioServerParameters, stdio_client
|
| 302 |
+
|
| 303 |
+
# Configuration - Change these to test different tools
|
| 304 |
+
TOOL_TO_TEST = "query_uniprot" # Change this to the tool you want to test
|
| 305 |
+
TEST_ARGS = {"prompt": "Find information about human insulin protein"}
|
| 306 |
+
|
| 307 |
+
async def test_single_tool():
|
| 308 |
+
"""Test a single tool in the Biomni MCP server."""
|
| 309 |
+
|
| 310 |
+
# Set up the server parameters
|
| 311 |
+
import os
|
| 312 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 313 |
+
server_script = os.path.join(current_dir, "run_mcp_server.py")
|
| 314 |
+
server_params = StdioServerParameters(command="python", args=[server_script])
|
| 315 |
+
|
| 316 |
+
try:
|
| 317 |
+
print("🔌 Connecting to MCP server...")
|
| 318 |
+
|
| 319 |
+
# Connect to the server
|
| 320 |
+
async with stdio_client(server_params) as (reader, writer):
|
| 321 |
+
async with ClientSession(reader, writer) as session:
|
| 322 |
+
# Initialize the session
|
| 323 |
+
await session.initialize()
|
| 324 |
+
print("✅ Connected to MCP server")
|
| 325 |
+
|
| 326 |
+
# List available tools
|
| 327 |
+
response = await session.list_tools()
|
| 328 |
+
tools = response.tools
|
| 329 |
+
print(f"✅ Found {len(tools)} tools")
|
| 330 |
+
|
| 331 |
+
# Test the tool
|
| 332 |
+
result = await session.call_tool(TOOL_TO_TEST, TEST_ARGS)
|
| 333 |
+
print("✅ Tool call successful!")
|
| 334 |
+
print(f"📄 Result: {result.content[0].text}")
|
| 335 |
+
|
| 336 |
+
except Exception as e:
|
| 337 |
+
print(f"❌ Failed to connect to MCP server: {e}")
|
| 338 |
+
return False
|
| 339 |
+
|
| 340 |
+
if __name__ == "__main__":
|
| 341 |
+
success = asyncio.run(test_single_tool())
|
| 342 |
+
sys.exit(0 if success else 1)
|
| 343 |
+
```
|
| 344 |
+
|
| 345 |
+
## Conclusion
|
| 346 |
+
|
| 347 |
+
Biomni's MCP integration provides a powerful way to extend its capabilities with external tools and services, while also making Biomni tools available to the broader MCP ecosystem. This enables seamless interoperability and allows you to build sophisticated AI workflows that combine the best of multiple tool ecosystems.
|
experiments/bioagent_bench/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni MCP Tool Scaling Experiment
|
| 2 |
+
|
| 3 |
+
This experiment studies how Biomni behaves when the MCP tool background grows from
|
| 4 |
+
about 100 tools to about 500, 1000, 1500, and 2000 tools on bioagent-bench.
|
| 5 |
+
|
| 6 |
+
## Generated Configs
|
| 7 |
+
|
| 8 |
+
The configs are generated from `/225040511/project/Biomni/mcp_generated` and can be
|
| 9 |
+
registered directly with Biomni:
|
| 10 |
+
|
| 11 |
+
```python
|
| 12 |
+
from biomni.agent import A1
|
| 13 |
+
|
| 14 |
+
agent = A1(use_tool_retriever=True)
|
| 15 |
+
agent.add_mcp(
|
| 16 |
+
"/225040511/project/Biomni/experiments/bioagent_bench/configs/mcp_scale_100.yaml"
|
| 17 |
+
)
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
Generated files:
|
| 21 |
+
|
| 22 |
+
- `configs/mcp_scale_100.yaml`: 10 MCP servers, 107 tools
|
| 23 |
+
- `configs/mcp_scale_500.yaml`: 96 MCP servers, 504 tools
|
| 24 |
+
- `configs/mcp_scale_1000.yaml`: 276 MCP servers, 1003 tools
|
| 25 |
+
- `configs/mcp_scale_1500.yaml`: 378 MCP servers, 1527 tools
|
| 26 |
+
- `configs/mcp_scale_2000.yaml`: 526 MCP servers, 2000 tools
|
| 27 |
+
- `configs/mcp_scale_manifest.json`: exact server/tool counts and selected server order
|
| 28 |
+
- `configs/mcp_server_inventory.json`: parseable MCP inventory
|
| 29 |
+
|
| 30 |
+
The generator writes explicit `tools` metadata into YAML, so `A1.add_mcp()` does
|
| 31 |
+
not have to start every server just to discover schemas. The runtime command for
|
| 32 |
+
each tool still points to the corresponding generated MCP server file.
|
| 33 |
+
|
| 34 |
+
## Regenerate
|
| 35 |
+
|
| 36 |
+
```bash
|
| 37 |
+
cd /225040511/project/Biomni
|
| 38 |
+
python experiments/bioagent_bench/scripts/generate_mcp_scale_configs.py
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
The generator prioritizes benchmark-relevant tools first, then fills each tier by
|
| 42 |
+
whole MCP server units until the target tool count is reached.
|
| 43 |
+
|
| 44 |
+
## Benchmark Design
|
| 45 |
+
|
| 46 |
+
Use the tasks in `/225040511/project/bioagent-bench/src/task_metadata.json`.
|
| 47 |
+
For each task, run Biomni once per tool tier:
|
| 48 |
+
|
| 49 |
+
1. Create a fresh run directory named like:
|
| 50 |
+
`runs/<tier>/<task_id>_<timestamp>/`
|
| 51 |
+
2. Initialize Biomni with `use_tool_retriever=True`.
|
| 52 |
+
3. Register exactly one tier config with `agent.add_mcp(config_path=...)`.
|
| 53 |
+
4. Run the bioagent-bench task prompt with the same data policy used in your
|
| 54 |
+
existing runs.
|
| 55 |
+
5. Save `retrieval_plan.json`, `execution_log.json`, `execution_log.txt`,
|
| 56 |
+
`run_metadata.json`, `run_summary.json`, `output_validation.json`, and final
|
| 57 |
+
deliverables in the run directory.
|
| 58 |
+
|
| 59 |
+
Use identical model, decoding settings, timeout, conda environment, and benchmark
|
| 60 |
+
data policy across tiers. Repeat each task at least 3 times if you want confidence
|
| 61 |
+
intervals, because LLM tool retrieval and planning can vary.
|
| 62 |
+
|
| 63 |
+
## Metrics
|
| 64 |
+
|
| 65 |
+
Metric definitions used here:
|
| 66 |
+
|
| 67 |
+
- `Retrieval Recall@k`: fraction of task gold tools/servers present in the first
|
| 68 |
+
`k` retrieved candidates from `retrieval_plan.json`. The table export uses a
|
| 69 |
+
primary `k` value, defaulting to `10`.
|
| 70 |
+
- `Workflow Validity`: whether observed tool/log evidence covers the task's gold
|
| 71 |
+
workflow stages in order.
|
| 72 |
+
- `Execution Success Rate`: whether benchmark output validation or required
|
| 73 |
+
output paths indicate the pipeline completed.
|
| 74 |
+
- `Context Tokens`: rough planning-context token count from
|
| 75 |
+
`retrieval_plan.json/planning_context_text`.
|
| 76 |
+
- `Planning Latency`: retrieval/planning latency recorded by the agent during
|
| 77 |
+
tool selection.
|
| 78 |
+
- `Hallucinated Tool Rate`: fraction of executed MCP tools mentioned in the
|
| 79 |
+
execution trace that were not present in the retrieved candidate tool set
|
| 80 |
+
persisted in `retrieval_plan.json`.
|
| 81 |
+
- `Data-Type Mismatch Rate`: heuristic rate of execution attempts that emitted
|
| 82 |
+
argument/schema/type mismatch errors, estimated from execution-log patterns.
|
| 83 |
+
- `Biological Constraint Error Rate`: heuristic rate of execution attempts that
|
| 84 |
+
violated benchmark biological/data constraints, such as sibling-task data
|
| 85 |
+
access, truth/results access, or external installs/downloads.
|
| 86 |
+
|
| 87 |
+
Gold tool and workflow definitions are in `gold_tools.json`. They are deliberately
|
| 88 |
+
pattern based, because MCP wrapper names differ across generated servers.
|
| 89 |
+
|
| 90 |
+
## Aggregate Existing Runs
|
| 91 |
+
|
| 92 |
+
```bash
|
| 93 |
+
python experiments/bioagent_bench/scripts/evaluate_mcp_scaling_runs.py \
|
| 94 |
+
--runs-root /path/to/runs/scale_100 \
|
| 95 |
+
--out experiments/bioagent_bench/results/scale_100_metrics.json
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
Run this once per tier, then compare the aggregate blocks or the exported table:
|
| 99 |
+
|
| 100 |
+
- `results/scale_<N>_mcp_metrics.json`
|
| 101 |
+
- `results/experiment1_scaling_table.json`
|
| 102 |
+
- `results/experiment1_scaling_table.csv`
|
experiments/bioagent_bench/README_RUN.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# BioAgent Bench MCP Scaling 新手运行指南
|
| 2 |
+
|
| 3 |
+
这份说明用于继续运行 Biomni 在 BioAgent Bench 上的 MCP tool scale 实验。当前实验目录是:
|
| 4 |
+
|
| 5 |
+
```bash
|
| 6 |
+
/225040511/project/Biomni/experiments/bioagent_bench
|
| 7 |
+
```
|
| 8 |
+
|
| 9 |
+
## 1. 进入项目目录
|
| 10 |
+
|
| 11 |
+
```bash
|
| 12 |
+
cd /225040511/project/Biomni
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
## 2. 准备 LLM API Key
|
| 16 |
+
|
| 17 |
+
实验需要至少设置下面任意一种 key:
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
export ANTHROPIC_API_KEY="你的 Anthropic key"
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
或者:
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
export OPENAI_API_KEY="你的 OpenAI key"
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
或者使用 DeepSeek:
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
export DEEPSEEK_API_KEY="你的 DeepSeek key"
|
| 33 |
+
export DEEPSEEK_MODEL_NAME="deepseek-chat"
|
| 34 |
+
export DEEPSEEK_BASE_URL="https://api.deepseek.com/v1"
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
脚本会自动把 `DEEPSEEK_API_KEY` 转成 Biomni 使用的 `BIOMNI_CUSTOM_API_KEY`。
|
| 38 |
+
|
| 39 |
+
可以用下面命令确认 key 是否已经设置,不会打印 key 内容:
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
for k in ANTHROPIC_API_KEY OPENAI_API_KEY BIOMNI_CUSTOM_API_KEY DEEPSEEK_API_KEY; do
|
| 43 |
+
if [ -n "${!k:-}" ]; then echo "$k=set"; else echo "$k=unset"; fi
|
| 44 |
+
done
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
## 3. 检查 MCP 配置
|
| 48 |
+
|
| 49 |
+
新增 scale 的配置已经生成在:
|
| 50 |
+
|
| 51 |
+
```bash
|
| 52 |
+
ls -lh experiments/bioagent_bench/configs/mcp_scale_1500.yaml
|
| 53 |
+
ls -lh experiments/bioagent_bench/configs/mcp_scale_2000.yaml
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
当前 manifest 在:
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
experiments/bioagent_bench/configs/mcp_scale_manifest.json
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
其中:
|
| 63 |
+
|
| 64 |
+
- `scale_1500`: 378 个 MCP servers,1527 个 tools
|
| 65 |
+
- `scale_2000`: 526 个 MCP servers,2000 个 tools
|
| 66 |
+
|
| 67 |
+
如果以后需要重新生成配置:
|
| 68 |
+
|
| 69 |
+
```bash
|
| 70 |
+
/225040511/miniconda3/envs/biomni_e1/bin/python \
|
| 71 |
+
experiments/bioagent_bench/scripts/generate_mcp_scale_configs.py
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
## 4. 后台运行 1500 和 2000 scale
|
| 75 |
+
|
| 76 |
+
推荐后台跑,因为完整实验可能需要很久:
|
| 77 |
+
|
| 78 |
+
```bash
|
| 79 |
+
experiments/bioagent_bench/scripts/run_biomni_scaling_experiment.sh \
|
| 80 |
+
--background \
|
| 81 |
+
--scale 1500 \
|
| 82 |
+
--scale 2000
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
脚本启动后会打印:
|
| 86 |
+
|
| 87 |
+
- `PID`: 后台进程号
|
| 88 |
+
- `Log`: 日志文件路径
|
| 89 |
+
- `Monitor with`: 查看日志的命令
|
| 90 |
+
|
| 91 |
+
## 5. 查看运行日志
|
| 92 |
+
|
| 93 |
+
把下面的路径替换成脚本启动时打印的 log 路径:
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
tail -f experiments/bioagent_bench/results/logs/run_biomni_scaling_experiment_YYYYMMDD_HHMMSS.log
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
如果日志里出现:
|
| 100 |
+
|
| 101 |
+
```text
|
| 102 |
+
No LLM API key found.
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
说明第 2 步的 API key 没有在当前 shell 里生效,需要重新 `export` 后再启动。
|
| 106 |
+
|
| 107 |
+
## 6. 结果输出位置
|
| 108 |
+
|
| 109 |
+
每个 scale 的原始运行结果会写到:
|
| 110 |
+
|
| 111 |
+
```bash
|
| 112 |
+
experiments/bioagent_bench/runs/scale_1500
|
| 113 |
+
experiments/bioagent_bench/runs/scale_2000
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
+
每个 task 会生成自己的子目录,例如:
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
experiments/bioagent_bench/runs/scale_1500/alzheimer-mouse_YYYYMMDD_HHMMSS
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
常见文件包括:
|
| 123 |
+
|
| 124 |
+
- `run_metadata.json`
|
| 125 |
+
- `retrieval_plan.json`
|
| 126 |
+
- `execution_log.json`
|
| 127 |
+
- `execution_log.txt`
|
| 128 |
+
- `output_validation.json`
|
| 129 |
+
- `final_answer.txt`
|
| 130 |
+
- task 对应的最终输出文件
|
| 131 |
+
|
| 132 |
+
汇总指标会写到:
|
| 133 |
+
|
| 134 |
+
```bash
|
| 135 |
+
experiments/bioagent_bench/results/scale_1500_mcp_metrics.json
|
| 136 |
+
experiments/bioagent_bench/results/scale_1500_task_metrics.json
|
| 137 |
+
experiments/bioagent_bench/results/scale_1500_task_metrics.csv
|
| 138 |
+
experiments/bioagent_bench/results/scale_1500_bioagent_bench_rule_eval.json
|
| 139 |
+
|
| 140 |
+
experiments/bioagent_bench/results/scale_2000_mcp_metrics.json
|
| 141 |
+
experiments/bioagent_bench/results/scale_2000_task_metrics.json
|
| 142 |
+
experiments/bioagent_bench/results/scale_2000_task_metrics.csv
|
| 143 |
+
experiments/bioagent_bench/results/scale_2000_bioagent_bench_rule_eval.json
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
总表会更新到:
|
| 147 |
+
|
| 148 |
+
```bash
|
| 149 |
+
experiments/bioagent_bench/results/experiment1_scaling_table.json
|
| 150 |
+
experiments/bioagent_bench/results/experiment1_scaling_table.csv
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
## 7. 断点续跑
|
| 154 |
+
|
| 155 |
+
runner 默认带 `--skip-completed`,所以中途失败后可以直接重新运行同一条命令。已经完成的 task 会被跳过,未完成的 task 会继续跑。
|
| 156 |
+
|
| 157 |
+
```bash
|
| 158 |
+
experiments/bioagent_bench/scripts/run_biomni_scaling_experiment.sh \
|
| 159 |
+
--background \
|
| 160 |
+
--scale 1500 \
|
| 161 |
+
--scale 2000
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
## 8. 只跑单个 scale
|
| 165 |
+
|
| 166 |
+
例如只跑 1500:
|
| 167 |
+
|
| 168 |
+
```bash
|
| 169 |
+
experiments/bioagent_bench/scripts/run_biomni_scaling_experiment.sh \
|
| 170 |
+
--background \
|
| 171 |
+
--scale 1500
|
| 172 |
+
```
|
| 173 |
+
|
| 174 |
+
只跑 2000:
|
| 175 |
+
|
| 176 |
+
```bash
|
| 177 |
+
experiments/bioagent_bench/scripts/run_biomni_scaling_experiment.sh \
|
| 178 |
+
--background \
|
| 179 |
+
--scale 2000
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
## 9. 前台调试
|
| 183 |
+
|
| 184 |
+
如果想直接在终端里看报错,可以用前台模式:
|
| 185 |
+
|
| 186 |
+
```bash
|
| 187 |
+
experiments/bioagent_bench/scripts/run_biomni_scaling_experiment.sh \
|
| 188 |
+
--foreground \
|
| 189 |
+
--scale 1500
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
前台模式会占住当前终端,适合调试,不适合长时间完整实验。
|
| 193 |
+
|
| 194 |
+
## 10. 快速检查是否完成
|
| 195 |
+
|
| 196 |
+
查看每个 scale 是否有 batch summary:
|
| 197 |
+
|
| 198 |
+
```bash
|
| 199 |
+
ls -lh experiments/bioagent_bench/runs/scale_1500/latest_batch_summary.json
|
| 200 |
+
ls -lh experiments/bioagent_bench/runs/scale_2000/latest_batch_summary.json
|
| 201 |
+
```
|
| 202 |
+
|
| 203 |
+
查看汇总表是否更新:
|
| 204 |
+
|
| 205 |
+
```bash
|
| 206 |
+
ls -lh experiments/bioagent_bench/results/experiment1_scaling_table.csv
|
| 207 |
+
```
|
| 208 |
+
|
experiments/bioagent_bench/configs/mcp_scale_100.yaml
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
experiments/bioagent_bench/configs/mcp_scale_manifest.json
ADDED
|
@@ -0,0 +1,1329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"mcp_root": "/225040511/project/Biomni/mcp_generated",
|
| 3 |
+
"execution_python": "/225040511/miniconda3/envs/biomni_e1/bin/python",
|
| 4 |
+
"available_server_count": 793,
|
| 5 |
+
"available_tool_count": 3189,
|
| 6 |
+
"targets": {
|
| 7 |
+
"100": {
|
| 8 |
+
"config": "/225040511/project/Biomni/experiments/bioagent_bench/configs/mcp_scale_100.yaml",
|
| 9 |
+
"server_count": 10,
|
| 10 |
+
"tool_count": 107,
|
| 11 |
+
"servers": [
|
| 12 |
+
"mcp_kallisto",
|
| 13 |
+
"mcp_kraken2",
|
| 14 |
+
"mcp_csvtk",
|
| 15 |
+
"mcp_megahit",
|
| 16 |
+
"mcp_kaiju",
|
| 17 |
+
"mcp_fastp",
|
| 18 |
+
"mcp_spades",
|
| 19 |
+
"mcp_scanpy",
|
| 20 |
+
"mcp_scanpy-cli",
|
| 21 |
+
"mcp_scanpy-scripts"
|
| 22 |
+
]
|
| 23 |
+
},
|
| 24 |
+
"500": {
|
| 25 |
+
"config": "/225040511/project/Biomni/experiments/bioagent_bench/configs/mcp_scale_500.yaml",
|
| 26 |
+
"server_count": 96,
|
| 27 |
+
"tool_count": 504,
|
| 28 |
+
"servers": [
|
| 29 |
+
"mcp_kallisto",
|
| 30 |
+
"mcp_kraken2",
|
| 31 |
+
"mcp_csvtk",
|
| 32 |
+
"mcp_megahit",
|
| 33 |
+
"mcp_kaiju",
|
| 34 |
+
"mcp_fastp",
|
| 35 |
+
"mcp_spades",
|
| 36 |
+
"mcp_scanpy",
|
| 37 |
+
"mcp_scanpy-cli",
|
| 38 |
+
"mcp_scanpy-scripts",
|
| 39 |
+
"mcp_seurat-scripts",
|
| 40 |
+
"mcp_bioconductor-deseq2",
|
| 41 |
+
"mcp_bioconductor-edger",
|
| 42 |
+
"mcp_bioconductor-limma",
|
| 43 |
+
"mcp_bioconductor-clusterprofiler",
|
| 44 |
+
"mcp_star",
|
| 45 |
+
"mcp_hisat2",
|
| 46 |
+
"mcp_subread",
|
| 47 |
+
"mcp_bcftools",
|
| 48 |
+
"mcp_bwa",
|
| 49 |
+
"mcp_gatk4",
|
| 50 |
+
"mcp_gatk",
|
| 51 |
+
"mcp_snpeff",
|
| 52 |
+
"mcp_freebayes",
|
| 53 |
+
"mcp_orthofinder",
|
| 54 |
+
"mcp_blast",
|
| 55 |
+
"mcp_blast-legacy",
|
| 56 |
+
"mcp_diamond",
|
| 57 |
+
"mcp_gffread",
|
| 58 |
+
"mcp_seqkit",
|
| 59 |
+
"mcp_multiqc",
|
| 60 |
+
"mcp_fastqc",
|
| 61 |
+
"mcp_trimmomatic",
|
| 62 |
+
"mcp_bioconductor-keggrest",
|
| 63 |
+
"mcp_snpsift",
|
| 64 |
+
"mcp_bioconductor-rsubread",
|
| 65 |
+
"mcp_bioconductor-tximport",
|
| 66 |
+
"mcp_bioconductor-variantannotation",
|
| 67 |
+
"mcp_celltypist",
|
| 68 |
+
"mcp_abnumber",
|
| 69 |
+
"mcp_abricate",
|
| 70 |
+
"mcp_abundancebin",
|
| 71 |
+
"mcp_abyss",
|
| 72 |
+
"mcp_adapterremoval",
|
| 73 |
+
"mcp_alfred",
|
| 74 |
+
"mcp_anansescanpy",
|
| 75 |
+
"mcp_anarci",
|
| 76 |
+
"mcp_aragorn",
|
| 77 |
+
"mcp_aria2",
|
| 78 |
+
"mcp_ariba",
|
| 79 |
+
"mcp_arriba",
|
| 80 |
+
"mcp_art",
|
| 81 |
+
"mcp_arvados-cwl-runner",
|
| 82 |
+
"mcp_arvados-python-client",
|
| 83 |
+
"mcp_atropos",
|
| 84 |
+
"mcp_augustus",
|
| 85 |
+
"mcp_auspice",
|
| 86 |
+
"mcp_bactopia",
|
| 87 |
+
"mcp_bamtools",
|
| 88 |
+
"mcp_barrnap",
|
| 89 |
+
"mcp_bbmap",
|
| 90 |
+
"mcp_bcbio-gff",
|
| 91 |
+
"mcp_bcbio-nextgen",
|
| 92 |
+
"mcp_bedops",
|
| 93 |
+
"mcp_bedtools",
|
| 94 |
+
"mcp_bin2cell",
|
| 95 |
+
"mcp_bioawk",
|
| 96 |
+
"mcp_biobambam",
|
| 97 |
+
"mcp_bioconductor-affy",
|
| 98 |
+
"mcp_bioconductor-affyio",
|
| 99 |
+
"mcp_bioconductor-alabaster",
|
| 100 |
+
"mcp_bioconductor-alabaster.sfe",
|
| 101 |
+
"mcp_bioconductor-alabaster.spatial",
|
| 102 |
+
"mcp_bioconductor-annotate",
|
| 103 |
+
"mcp_bioconductor-annotationdbi",
|
| 104 |
+
"mcp_bioconductor-annotationfilter",
|
| 105 |
+
"mcp_bioconductor-apeglm",
|
| 106 |
+
"mcp_bioconductor-apl",
|
| 107 |
+
"mcp_bioconductor-awaggregator",
|
| 108 |
+
"mcp_bioconductor-banksy",
|
| 109 |
+
"mcp_bioconductor-beachmat",
|
| 110 |
+
"mcp_bioconductor-benchdamic",
|
| 111 |
+
"mcp_bioconductor-biobase",
|
| 112 |
+
"mcp_bioconductor-biocbaseutils",
|
| 113 |
+
"mcp_bioconductor-biocfilecache",
|
| 114 |
+
"mcp_bioconductor-biocgenerics",
|
| 115 |
+
"mcp_bioconductor-biocio",
|
| 116 |
+
"mcp_bioconductor-biocneighbors",
|
| 117 |
+
"mcp_bioconductor-biocparallel",
|
| 118 |
+
"mcp_bioconductor-biocsingular",
|
| 119 |
+
"mcp_bioconductor-biomart",
|
| 120 |
+
"mcp_bioconductor-biomformat",
|
| 121 |
+
"mcp_bioconductor-biostrings",
|
| 122 |
+
"mcp_bioconductor-biovizbase",
|
| 123 |
+
"mcp_bioconductor-blase",
|
| 124 |
+
"mcp_bioconductor-bluster"
|
| 125 |
+
]
|
| 126 |
+
},
|
| 127 |
+
"1000": {
|
| 128 |
+
"config": "/225040511/project/Biomni/experiments/bioagent_bench/configs/mcp_scale_1000.yaml",
|
| 129 |
+
"server_count": 276,
|
| 130 |
+
"tool_count": 1003,
|
| 131 |
+
"servers": [
|
| 132 |
+
"mcp_kallisto",
|
| 133 |
+
"mcp_kraken2",
|
| 134 |
+
"mcp_csvtk",
|
| 135 |
+
"mcp_megahit",
|
| 136 |
+
"mcp_kaiju",
|
| 137 |
+
"mcp_fastp",
|
| 138 |
+
"mcp_spades",
|
| 139 |
+
"mcp_scanpy",
|
| 140 |
+
"mcp_scanpy-cli",
|
| 141 |
+
"mcp_scanpy-scripts",
|
| 142 |
+
"mcp_seurat-scripts",
|
| 143 |
+
"mcp_bioconductor-deseq2",
|
| 144 |
+
"mcp_bioconductor-edger",
|
| 145 |
+
"mcp_bioconductor-limma",
|
| 146 |
+
"mcp_bioconductor-clusterprofiler",
|
| 147 |
+
"mcp_star",
|
| 148 |
+
"mcp_hisat2",
|
| 149 |
+
"mcp_subread",
|
| 150 |
+
"mcp_bcftools",
|
| 151 |
+
"mcp_bwa",
|
| 152 |
+
"mcp_gatk4",
|
| 153 |
+
"mcp_gatk",
|
| 154 |
+
"mcp_snpeff",
|
| 155 |
+
"mcp_freebayes",
|
| 156 |
+
"mcp_orthofinder",
|
| 157 |
+
"mcp_blast",
|
| 158 |
+
"mcp_blast-legacy",
|
| 159 |
+
"mcp_diamond",
|
| 160 |
+
"mcp_gffread",
|
| 161 |
+
"mcp_seqkit",
|
| 162 |
+
"mcp_multiqc",
|
| 163 |
+
"mcp_fastqc",
|
| 164 |
+
"mcp_trimmomatic",
|
| 165 |
+
"mcp_bioconductor-keggrest",
|
| 166 |
+
"mcp_snpsift",
|
| 167 |
+
"mcp_bioconductor-rsubread",
|
| 168 |
+
"mcp_bioconductor-tximport",
|
| 169 |
+
"mcp_bioconductor-variantannotation",
|
| 170 |
+
"mcp_celltypist",
|
| 171 |
+
"mcp_abnumber",
|
| 172 |
+
"mcp_abricate",
|
| 173 |
+
"mcp_abundancebin",
|
| 174 |
+
"mcp_abyss",
|
| 175 |
+
"mcp_adapterremoval",
|
| 176 |
+
"mcp_alfred",
|
| 177 |
+
"mcp_anansescanpy",
|
| 178 |
+
"mcp_anarci",
|
| 179 |
+
"mcp_aragorn",
|
| 180 |
+
"mcp_aria2",
|
| 181 |
+
"mcp_ariba",
|
| 182 |
+
"mcp_arriba",
|
| 183 |
+
"mcp_art",
|
| 184 |
+
"mcp_arvados-cwl-runner",
|
| 185 |
+
"mcp_arvados-python-client",
|
| 186 |
+
"mcp_atropos",
|
| 187 |
+
"mcp_augustus",
|
| 188 |
+
"mcp_auspice",
|
| 189 |
+
"mcp_bactopia",
|
| 190 |
+
"mcp_bamtools",
|
| 191 |
+
"mcp_barrnap",
|
| 192 |
+
"mcp_bbmap",
|
| 193 |
+
"mcp_bcbio-gff",
|
| 194 |
+
"mcp_bcbio-nextgen",
|
| 195 |
+
"mcp_bedops",
|
| 196 |
+
"mcp_bedtools",
|
| 197 |
+
"mcp_bin2cell",
|
| 198 |
+
"mcp_bioawk",
|
| 199 |
+
"mcp_biobambam",
|
| 200 |
+
"mcp_bioconductor-affy",
|
| 201 |
+
"mcp_bioconductor-affyio",
|
| 202 |
+
"mcp_bioconductor-alabaster",
|
| 203 |
+
"mcp_bioconductor-alabaster.sfe",
|
| 204 |
+
"mcp_bioconductor-alabaster.spatial",
|
| 205 |
+
"mcp_bioconductor-annotate",
|
| 206 |
+
"mcp_bioconductor-annotationdbi",
|
| 207 |
+
"mcp_bioconductor-annotationfilter",
|
| 208 |
+
"mcp_bioconductor-apeglm",
|
| 209 |
+
"mcp_bioconductor-apl",
|
| 210 |
+
"mcp_bioconductor-awaggregator",
|
| 211 |
+
"mcp_bioconductor-banksy",
|
| 212 |
+
"mcp_bioconductor-beachmat",
|
| 213 |
+
"mcp_bioconductor-benchdamic",
|
| 214 |
+
"mcp_bioconductor-biobase",
|
| 215 |
+
"mcp_bioconductor-biocbaseutils",
|
| 216 |
+
"mcp_bioconductor-biocfilecache",
|
| 217 |
+
"mcp_bioconductor-biocgenerics",
|
| 218 |
+
"mcp_bioconductor-biocio",
|
| 219 |
+
"mcp_bioconductor-biocneighbors",
|
| 220 |
+
"mcp_bioconductor-biocparallel",
|
| 221 |
+
"mcp_bioconductor-biocsingular",
|
| 222 |
+
"mcp_bioconductor-biomart",
|
| 223 |
+
"mcp_bioconductor-biomformat",
|
| 224 |
+
"mcp_bioconductor-biostrings",
|
| 225 |
+
"mcp_bioconductor-biovizbase",
|
| 226 |
+
"mcp_bioconductor-blase",
|
| 227 |
+
"mcp_bioconductor-bluster",
|
| 228 |
+
"mcp_bioconductor-bulksignalr",
|
| 229 |
+
"mcp_bioconductor-cardspa",
|
| 230 |
+
"mcp_bioconductor-catscradle",
|
| 231 |
+
"mcp_bioconductor-cdi",
|
| 232 |
+
"mcp_bioconductor-cellhashr",
|
| 233 |
+
"mcp_bioconductor-cellid",
|
| 234 |
+
"mcp_bioconductor-cellmigration",
|
| 235 |
+
"mcp_bioconductor-clusterfoldsimilarity",
|
| 236 |
+
"mcp_bioconductor-clustifyr",
|
| 237 |
+
"mcp_bioconductor-clustsignal",
|
| 238 |
+
"mcp_bioconductor-complexheatmap",
|
| 239 |
+
"mcp_bioconductor-concordexr",
|
| 240 |
+
"mcp_bioconductor-cotan",
|
| 241 |
+
"mcp_bioconductor-csoa",
|
| 242 |
+
"mcp_bioconductor-ctsv",
|
| 243 |
+
"mcp_bioconductor-curatedatlasqueryr",
|
| 244 |
+
"mcp_bioconductor-cytomapper",
|
| 245 |
+
"mcp_bioconductor-dada2",
|
| 246 |
+
"mcp_bioconductor-data-packages",
|
| 247 |
+
"mcp_bioconductor-decipher",
|
| 248 |
+
"mcp_bioconductor-decontam",
|
| 249 |
+
"mcp_bioconductor-decontx",
|
| 250 |
+
"mcp_bioconductor-deconvobuddies",
|
| 251 |
+
"mcp_bioconductor-delayedmatrixstats",
|
| 252 |
+
"mcp_bioconductor-despace",
|
| 253 |
+
"mcp_bioconductor-diffbind",
|
| 254 |
+
"mcp_bioconductor-dino",
|
| 255 |
+
"mcp_bioconductor-dirichletmultinomial",
|
| 256 |
+
"mcp_bioconductor-dnacopy",
|
| 257 |
+
"mcp_bioconductor-dose",
|
| 258 |
+
"mcp_bioconductor-ensembldb",
|
| 259 |
+
"mcp_bioconductor-erma",
|
| 260 |
+
"mcp_bioconductor-escher",
|
| 261 |
+
"mcp_bioconductor-experimentsubset",
|
| 262 |
+
"mcp_bioconductor-fgsea",
|
| 263 |
+
"mcp_bioconductor-genefilter",
|
| 264 |
+
"mcp_bioconductor-geneplotter",
|
| 265 |
+
"mcp_bioconductor-genomeinfodb",
|
| 266 |
+
"mcp_bioconductor-genomeinfodbdata",
|
| 267 |
+
"mcp_bioconductor-genomicalignments",
|
| 268 |
+
"mcp_bioconductor-genomicfeatures",
|
| 269 |
+
"mcp_bioconductor-genomicranges",
|
| 270 |
+
"mcp_bioconductor-geomxtools",
|
| 271 |
+
"mcp_bioconductor-ggsc",
|
| 272 |
+
"mcp_bioconductor-ggspavis",
|
| 273 |
+
"mcp_bioconductor-ggtree",
|
| 274 |
+
"mcp_bioconductor-glmgampoi",
|
| 275 |
+
"mcp_bioconductor-go.db",
|
| 276 |
+
"mcp_bioconductor-gosemsim",
|
| 277 |
+
"mcp_bioconductor-graph",
|
| 278 |
+
"mcp_bioconductor-gsva",
|
| 279 |
+
"mcp_bioconductor-hcatonsildata",
|
| 280 |
+
"mcp_bioconductor-hdf5array",
|
| 281 |
+
"mcp_bioconductor-hoodscanr",
|
| 282 |
+
"mcp_bioconductor-humanhippocampus2024",
|
| 283 |
+
"mcp_bioconductor-imcdatasets",
|
| 284 |
+
"mcp_bioconductor-imcrtools",
|
| 285 |
+
"mcp_bioconductor-impute",
|
| 286 |
+
"mcp_bioconductor-infercnv",
|
| 287 |
+
"mcp_bioconductor-interactivedisplaybase",
|
| 288 |
+
"mcp_bioconductor-iranges",
|
| 289 |
+
"mcp_bioconductor-irisfgm",
|
| 290 |
+
"mcp_bioconductor-jazzpanda",
|
| 291 |
+
"mcp_bioconductor-lisaclust",
|
| 292 |
+
"mcp_bioconductor-mastr",
|
| 293 |
+
"mcp_bioconductor-matrixgenerics",
|
| 294 |
+
"mcp_bioconductor-merfishdata",
|
| 295 |
+
"mcp_bioconductor-metabolomicsworkbenchr",
|
| 296 |
+
"mcp_bioconductor-metapod",
|
| 297 |
+
"mcp_bioconductor-mia",
|
| 298 |
+
"mcp_bioconductor-moleculeexperiment",
|
| 299 |
+
"mcp_bioconductor-mosim",
|
| 300 |
+
"mcp_bioconductor-mousegastrulationdata",
|
| 301 |
+
"mcp_bioconductor-mspurity",
|
| 302 |
+
"mcp_bioconductor-multiassayexperiment",
|
| 303 |
+
"mcp_bioconductor-multtest",
|
| 304 |
+
"mcp_bioconductor-nebulosa",
|
| 305 |
+
"mcp_bioconductor-nnsvg",
|
| 306 |
+
"mcp_bioconductor-noiseq",
|
| 307 |
+
"mcp_bioconductor-orfhunter",
|
| 308 |
+
"mcp_bioconductor-org.ce.eg.db",
|
| 309 |
+
"mcp_bioconductor-org.hs.eg.db",
|
| 310 |
+
"mcp_bioconductor-org.mm.eg.db",
|
| 311 |
+
"mcp_bioconductor-partcnv",
|
| 312 |
+
"mcp_bioconductor-phemd",
|
| 313 |
+
"mcp_bioconductor-phyloseq",
|
| 314 |
+
"mcp_bioconductor-pipecomp",
|
| 315 |
+
"mcp_bioconductor-poem",
|
| 316 |
+
"mcp_bioconductor-preprocesscore",
|
| 317 |
+
"mcp_bioconductor-proteomicsannotationhubdata",
|
| 318 |
+
"mcp_bioconductor-protgenerics",
|
| 319 |
+
"mcp_bioconductor-qvalue",
|
| 320 |
+
"mcp_bioconductor-rbgl",
|
| 321 |
+
"mcp_bioconductor-reactomegsa.data",
|
| 322 |
+
"mcp_bioconductor-regionalst",
|
| 323 |
+
"mcp_bioconductor-rforproteomics",
|
| 324 |
+
"mcp_bioconductor-rgraphviz",
|
| 325 |
+
"mcp_bioconductor-rhdf5",
|
| 326 |
+
"mcp_bioconductor-rhdf5filters",
|
| 327 |
+
"mcp_bioconductor-rhdf5lib",
|
| 328 |
+
"mcp_bioconductor-rhtslib",
|
| 329 |
+
"mcp_bioconductor-rsamtools",
|
| 330 |
+
"mcp_bioconductor-scaledmatrix",
|
| 331 |
+
"mcp_bioconductor-scalign",
|
| 332 |
+
"mcp_bioconductor-scarray.sat",
|
| 333 |
+
"mcp_bioconductor-scbfa",
|
| 334 |
+
"mcp_bioconductor-scbubbletree",
|
| 335 |
+
"mcp_bioconductor-sccb2",
|
| 336 |
+
"mcp_bioconductor-scclassifr",
|
| 337 |
+
"mcp_bioconductor-scdataviz",
|
| 338 |
+
"mcp_bioconductor-scdotplot",
|
| 339 |
+
"mcp_bioconductor-scfeatures",
|
| 340 |
+
"mcp_bioconductor-scider",
|
| 341 |
+
"mcp_bioconductor-scmageck",
|
| 342 |
+
"mcp_bioconductor-scqtltools",
|
| 343 |
+
"mcp_bioconductor-scran",
|
| 344 |
+
"mcp_bioconductor-screpertoire",
|
| 345 |
+
"mcp_bioconductor-scrnaseq",
|
| 346 |
+
"mcp_bioconductor-sctreeviz",
|
| 347 |
+
"mcp_bioconductor-scuttle",
|
| 348 |
+
"mcp_bioconductor-scvir",
|
| 349 |
+
"mcp_bioconductor-seraster",
|
| 350 |
+
"mcp_bioconductor-shortread",
|
| 351 |
+
"mcp_bioconductor-signifinder",
|
| 352 |
+
"mcp_bioconductor-singlecellmultimodal",
|
| 353 |
+
"mcp_bioconductor-smoothclust",
|
| 354 |
+
"mcp_bioconductor-smoppix",
|
| 355 |
+
"mcp_bioconductor-sosta",
|
| 356 |
+
"mcp_bioconductor-spacetrooper",
|
| 357 |
+
"mcp_bioconductor-spaniel",
|
| 358 |
+
"mcp_bioconductor-spari",
|
| 359 |
+
"mcp_bioconductor-sparsematrixstats",
|
| 360 |
+
"mcp_bioconductor-spatialcpie",
|
| 361 |
+
"mcp_bioconductor-spatialdatasets",
|
| 362 |
+
"mcp_bioconductor-spatialdecon",
|
| 363 |
+
"mcp_bioconductor-spatialdmelxsim",
|
| 364 |
+
"mcp_bioconductor-spatialexperiment",
|
| 365 |
+
"mcp_bioconductor-spatialexperimentio",
|
| 366 |
+
"mcp_bioconductor-spatialfda",
|
| 367 |
+
"mcp_bioconductor-spatialfeatureexperiment",
|
| 368 |
+
"mcp_bioconductor-spatialheatmap",
|
| 369 |
+
"mcp_bioconductor-speckle",
|
| 370 |
+
"mcp_bioconductor-spoon",
|
| 371 |
+
"mcp_bioconductor-spotclean",
|
| 372 |
+
"mcp_bioconductor-spotsweeper",
|
| 373 |
+
"mcp_bioconductor-standr",
|
| 374 |
+
"mcp_bioconductor-statial",
|
| 375 |
+
"mcp_bioconductor-stjoincount",
|
| 376 |
+
"mcp_bioconductor-summarizedexperiment",
|
| 377 |
+
"mcp_bioconductor-svp",
|
| 378 |
+
"mcp_bioconductor-tenxxeniumdata",
|
| 379 |
+
"mcp_bioconductor-tidyomics",
|
| 380 |
+
"mcp_bioconductor-tidyspatialexperiment",
|
| 381 |
+
"mcp_bioconductor-treesummarizedexperiment",
|
| 382 |
+
"mcp_bioconductor-vectrapolarisdata",
|
| 383 |
+
"mcp_bioconductor-visiumio",
|
| 384 |
+
"mcp_bioconductor-weberdivechalcdata",
|
| 385 |
+
"mcp_bioconductor-xcms",
|
| 386 |
+
"mcp_bioconductor-xenlite",
|
| 387 |
+
"mcp_bioconductor-xvector",
|
| 388 |
+
"mcp_bioconductor-zlibbioc",
|
| 389 |
+
"mcp_biopython",
|
| 390 |
+
"mcp_bowtie",
|
| 391 |
+
"mcp_bowtie2",
|
| 392 |
+
"mcp_bpipe",
|
| 393 |
+
"mcp_brooklyn_plot",
|
| 394 |
+
"mcp_busco",
|
| 395 |
+
"mcp_bx-python",
|
| 396 |
+
"mcp_c-ares",
|
| 397 |
+
"mcp_cascade-reg",
|
| 398 |
+
"mcp_cd-hit",
|
| 399 |
+
"mcp_cdbtools",
|
| 400 |
+
"mcp_cell2cell",
|
| 401 |
+
"mcp_cellitac",
|
| 402 |
+
"mcp_cellqc",
|
| 403 |
+
"mcp_cellrank",
|
| 404 |
+
"mcp_cellsnake",
|
| 405 |
+
"mcp_celltypist-so",
|
| 406 |
+
"mcp_checkatlas",
|
| 407 |
+
"mcp_checkm-genome"
|
| 408 |
+
]
|
| 409 |
+
},
|
| 410 |
+
"1500": {
|
| 411 |
+
"config": "/225040511/project/Biomni/experiments/bioagent_bench/configs/mcp_scale_1500.yaml",
|
| 412 |
+
"server_count": 378,
|
| 413 |
+
"tool_count": 1527,
|
| 414 |
+
"servers": [
|
| 415 |
+
"mcp_kallisto",
|
| 416 |
+
"mcp_kraken2",
|
| 417 |
+
"mcp_csvtk",
|
| 418 |
+
"mcp_megahit",
|
| 419 |
+
"mcp_kaiju",
|
| 420 |
+
"mcp_fastp",
|
| 421 |
+
"mcp_spades",
|
| 422 |
+
"mcp_scanpy",
|
| 423 |
+
"mcp_scanpy-cli",
|
| 424 |
+
"mcp_scanpy-scripts",
|
| 425 |
+
"mcp_seurat-scripts",
|
| 426 |
+
"mcp_bioconductor-deseq2",
|
| 427 |
+
"mcp_bioconductor-edger",
|
| 428 |
+
"mcp_bioconductor-limma",
|
| 429 |
+
"mcp_bioconductor-clusterprofiler",
|
| 430 |
+
"mcp_star",
|
| 431 |
+
"mcp_hisat2",
|
| 432 |
+
"mcp_subread",
|
| 433 |
+
"mcp_bcftools",
|
| 434 |
+
"mcp_bwa",
|
| 435 |
+
"mcp_gatk4",
|
| 436 |
+
"mcp_gatk",
|
| 437 |
+
"mcp_snpeff",
|
| 438 |
+
"mcp_freebayes",
|
| 439 |
+
"mcp_orthofinder",
|
| 440 |
+
"mcp_blast",
|
| 441 |
+
"mcp_blast-legacy",
|
| 442 |
+
"mcp_diamond",
|
| 443 |
+
"mcp_gffread",
|
| 444 |
+
"mcp_seqkit",
|
| 445 |
+
"mcp_multiqc",
|
| 446 |
+
"mcp_fastqc",
|
| 447 |
+
"mcp_trimmomatic",
|
| 448 |
+
"mcp_bioconductor-keggrest",
|
| 449 |
+
"mcp_snpsift",
|
| 450 |
+
"mcp_bioconductor-rsubread",
|
| 451 |
+
"mcp_bioconductor-tximport",
|
| 452 |
+
"mcp_bioconductor-variantannotation",
|
| 453 |
+
"mcp_celltypist",
|
| 454 |
+
"mcp_abnumber",
|
| 455 |
+
"mcp_abricate",
|
| 456 |
+
"mcp_abundancebin",
|
| 457 |
+
"mcp_abyss",
|
| 458 |
+
"mcp_adapterremoval",
|
| 459 |
+
"mcp_alfred",
|
| 460 |
+
"mcp_anansescanpy",
|
| 461 |
+
"mcp_anarci",
|
| 462 |
+
"mcp_aragorn",
|
| 463 |
+
"mcp_aria2",
|
| 464 |
+
"mcp_ariba",
|
| 465 |
+
"mcp_arriba",
|
| 466 |
+
"mcp_art",
|
| 467 |
+
"mcp_arvados-cwl-runner",
|
| 468 |
+
"mcp_arvados-python-client",
|
| 469 |
+
"mcp_atropos",
|
| 470 |
+
"mcp_augustus",
|
| 471 |
+
"mcp_auspice",
|
| 472 |
+
"mcp_bactopia",
|
| 473 |
+
"mcp_bamtools",
|
| 474 |
+
"mcp_barrnap",
|
| 475 |
+
"mcp_bbmap",
|
| 476 |
+
"mcp_bcbio-gff",
|
| 477 |
+
"mcp_bcbio-nextgen",
|
| 478 |
+
"mcp_bedops",
|
| 479 |
+
"mcp_bedtools",
|
| 480 |
+
"mcp_bin2cell",
|
| 481 |
+
"mcp_bioawk",
|
| 482 |
+
"mcp_biobambam",
|
| 483 |
+
"mcp_bioconductor-affy",
|
| 484 |
+
"mcp_bioconductor-affyio",
|
| 485 |
+
"mcp_bioconductor-alabaster",
|
| 486 |
+
"mcp_bioconductor-alabaster.sfe",
|
| 487 |
+
"mcp_bioconductor-alabaster.spatial",
|
| 488 |
+
"mcp_bioconductor-annotate",
|
| 489 |
+
"mcp_bioconductor-annotationdbi",
|
| 490 |
+
"mcp_bioconductor-annotationfilter",
|
| 491 |
+
"mcp_bioconductor-apeglm",
|
| 492 |
+
"mcp_bioconductor-apl",
|
| 493 |
+
"mcp_bioconductor-awaggregator",
|
| 494 |
+
"mcp_bioconductor-banksy",
|
| 495 |
+
"mcp_bioconductor-beachmat",
|
| 496 |
+
"mcp_bioconductor-benchdamic",
|
| 497 |
+
"mcp_bioconductor-biobase",
|
| 498 |
+
"mcp_bioconductor-biocbaseutils",
|
| 499 |
+
"mcp_bioconductor-biocfilecache",
|
| 500 |
+
"mcp_bioconductor-biocgenerics",
|
| 501 |
+
"mcp_bioconductor-biocio",
|
| 502 |
+
"mcp_bioconductor-biocneighbors",
|
| 503 |
+
"mcp_bioconductor-biocparallel",
|
| 504 |
+
"mcp_bioconductor-biocsingular",
|
| 505 |
+
"mcp_bioconductor-biomart",
|
| 506 |
+
"mcp_bioconductor-biomformat",
|
| 507 |
+
"mcp_bioconductor-biostrings",
|
| 508 |
+
"mcp_bioconductor-biovizbase",
|
| 509 |
+
"mcp_bioconductor-blase",
|
| 510 |
+
"mcp_bioconductor-bluster",
|
| 511 |
+
"mcp_bioconductor-bulksignalr",
|
| 512 |
+
"mcp_bioconductor-cardspa",
|
| 513 |
+
"mcp_bioconductor-catscradle",
|
| 514 |
+
"mcp_bioconductor-cdi",
|
| 515 |
+
"mcp_bioconductor-cellhashr",
|
| 516 |
+
"mcp_bioconductor-cellid",
|
| 517 |
+
"mcp_bioconductor-cellmigration",
|
| 518 |
+
"mcp_bioconductor-clusterfoldsimilarity",
|
| 519 |
+
"mcp_bioconductor-clustifyr",
|
| 520 |
+
"mcp_bioconductor-clustsignal",
|
| 521 |
+
"mcp_bioconductor-complexheatmap",
|
| 522 |
+
"mcp_bioconductor-concordexr",
|
| 523 |
+
"mcp_bioconductor-cotan",
|
| 524 |
+
"mcp_bioconductor-csoa",
|
| 525 |
+
"mcp_bioconductor-ctsv",
|
| 526 |
+
"mcp_bioconductor-curatedatlasqueryr",
|
| 527 |
+
"mcp_bioconductor-cytomapper",
|
| 528 |
+
"mcp_bioconductor-dada2",
|
| 529 |
+
"mcp_bioconductor-data-packages",
|
| 530 |
+
"mcp_bioconductor-decipher",
|
| 531 |
+
"mcp_bioconductor-decontam",
|
| 532 |
+
"mcp_bioconductor-decontx",
|
| 533 |
+
"mcp_bioconductor-deconvobuddies",
|
| 534 |
+
"mcp_bioconductor-delayedmatrixstats",
|
| 535 |
+
"mcp_bioconductor-despace",
|
| 536 |
+
"mcp_bioconductor-diffbind",
|
| 537 |
+
"mcp_bioconductor-dino",
|
| 538 |
+
"mcp_bioconductor-dirichletmultinomial",
|
| 539 |
+
"mcp_bioconductor-dnacopy",
|
| 540 |
+
"mcp_bioconductor-dose",
|
| 541 |
+
"mcp_bioconductor-ensembldb",
|
| 542 |
+
"mcp_bioconductor-erma",
|
| 543 |
+
"mcp_bioconductor-escher",
|
| 544 |
+
"mcp_bioconductor-experimentsubset",
|
| 545 |
+
"mcp_bioconductor-fgsea",
|
| 546 |
+
"mcp_bioconductor-genefilter",
|
| 547 |
+
"mcp_bioconductor-geneplotter",
|
| 548 |
+
"mcp_bioconductor-genomeinfodb",
|
| 549 |
+
"mcp_bioconductor-genomeinfodbdata",
|
| 550 |
+
"mcp_bioconductor-genomicalignments",
|
| 551 |
+
"mcp_bioconductor-genomicfeatures",
|
| 552 |
+
"mcp_bioconductor-genomicranges",
|
| 553 |
+
"mcp_bioconductor-geomxtools",
|
| 554 |
+
"mcp_bioconductor-ggsc",
|
| 555 |
+
"mcp_bioconductor-ggspavis",
|
| 556 |
+
"mcp_bioconductor-ggtree",
|
| 557 |
+
"mcp_bioconductor-glmgampoi",
|
| 558 |
+
"mcp_bioconductor-go.db",
|
| 559 |
+
"mcp_bioconductor-gosemsim",
|
| 560 |
+
"mcp_bioconductor-graph",
|
| 561 |
+
"mcp_bioconductor-gsva",
|
| 562 |
+
"mcp_bioconductor-hcatonsildata",
|
| 563 |
+
"mcp_bioconductor-hdf5array",
|
| 564 |
+
"mcp_bioconductor-hoodscanr",
|
| 565 |
+
"mcp_bioconductor-humanhippocampus2024",
|
| 566 |
+
"mcp_bioconductor-imcdatasets",
|
| 567 |
+
"mcp_bioconductor-imcrtools",
|
| 568 |
+
"mcp_bioconductor-impute",
|
| 569 |
+
"mcp_bioconductor-infercnv",
|
| 570 |
+
"mcp_bioconductor-interactivedisplaybase",
|
| 571 |
+
"mcp_bioconductor-iranges",
|
| 572 |
+
"mcp_bioconductor-irisfgm",
|
| 573 |
+
"mcp_bioconductor-jazzpanda",
|
| 574 |
+
"mcp_bioconductor-lisaclust",
|
| 575 |
+
"mcp_bioconductor-mastr",
|
| 576 |
+
"mcp_bioconductor-matrixgenerics",
|
| 577 |
+
"mcp_bioconductor-merfishdata",
|
| 578 |
+
"mcp_bioconductor-metabolomicsworkbenchr",
|
| 579 |
+
"mcp_bioconductor-metapod",
|
| 580 |
+
"mcp_bioconductor-mia",
|
| 581 |
+
"mcp_bioconductor-moleculeexperiment",
|
| 582 |
+
"mcp_bioconductor-mosim",
|
| 583 |
+
"mcp_bioconductor-mousegastrulationdata",
|
| 584 |
+
"mcp_bioconductor-mspurity",
|
| 585 |
+
"mcp_bioconductor-multiassayexperiment",
|
| 586 |
+
"mcp_bioconductor-multtest",
|
| 587 |
+
"mcp_bioconductor-nebulosa",
|
| 588 |
+
"mcp_bioconductor-nnsvg",
|
| 589 |
+
"mcp_bioconductor-noiseq",
|
| 590 |
+
"mcp_bioconductor-orfhunter",
|
| 591 |
+
"mcp_bioconductor-org.ce.eg.db",
|
| 592 |
+
"mcp_bioconductor-org.hs.eg.db",
|
| 593 |
+
"mcp_bioconductor-org.mm.eg.db",
|
| 594 |
+
"mcp_bioconductor-partcnv",
|
| 595 |
+
"mcp_bioconductor-phemd",
|
| 596 |
+
"mcp_bioconductor-phyloseq",
|
| 597 |
+
"mcp_bioconductor-pipecomp",
|
| 598 |
+
"mcp_bioconductor-poem",
|
| 599 |
+
"mcp_bioconductor-preprocesscore",
|
| 600 |
+
"mcp_bioconductor-proteomicsannotationhubdata",
|
| 601 |
+
"mcp_bioconductor-protgenerics",
|
| 602 |
+
"mcp_bioconductor-qvalue",
|
| 603 |
+
"mcp_bioconductor-rbgl",
|
| 604 |
+
"mcp_bioconductor-reactomegsa.data",
|
| 605 |
+
"mcp_bioconductor-regionalst",
|
| 606 |
+
"mcp_bioconductor-rforproteomics",
|
| 607 |
+
"mcp_bioconductor-rgraphviz",
|
| 608 |
+
"mcp_bioconductor-rhdf5",
|
| 609 |
+
"mcp_bioconductor-rhdf5filters",
|
| 610 |
+
"mcp_bioconductor-rhdf5lib",
|
| 611 |
+
"mcp_bioconductor-rhtslib",
|
| 612 |
+
"mcp_bioconductor-rsamtools",
|
| 613 |
+
"mcp_bioconductor-scaledmatrix",
|
| 614 |
+
"mcp_bioconductor-scalign",
|
| 615 |
+
"mcp_bioconductor-scarray.sat",
|
| 616 |
+
"mcp_bioconductor-scbfa",
|
| 617 |
+
"mcp_bioconductor-scbubbletree",
|
| 618 |
+
"mcp_bioconductor-sccb2",
|
| 619 |
+
"mcp_bioconductor-scclassifr",
|
| 620 |
+
"mcp_bioconductor-scdataviz",
|
| 621 |
+
"mcp_bioconductor-scdotplot",
|
| 622 |
+
"mcp_bioconductor-scfeatures",
|
| 623 |
+
"mcp_bioconductor-scider",
|
| 624 |
+
"mcp_bioconductor-scmageck",
|
| 625 |
+
"mcp_bioconductor-scqtltools",
|
| 626 |
+
"mcp_bioconductor-scran",
|
| 627 |
+
"mcp_bioconductor-screpertoire",
|
| 628 |
+
"mcp_bioconductor-scrnaseq",
|
| 629 |
+
"mcp_bioconductor-sctreeviz",
|
| 630 |
+
"mcp_bioconductor-scuttle",
|
| 631 |
+
"mcp_bioconductor-scvir",
|
| 632 |
+
"mcp_bioconductor-seraster",
|
| 633 |
+
"mcp_bioconductor-shortread",
|
| 634 |
+
"mcp_bioconductor-signifinder",
|
| 635 |
+
"mcp_bioconductor-singlecellmultimodal",
|
| 636 |
+
"mcp_bioconductor-smoothclust",
|
| 637 |
+
"mcp_bioconductor-smoppix",
|
| 638 |
+
"mcp_bioconductor-sosta",
|
| 639 |
+
"mcp_bioconductor-spacetrooper",
|
| 640 |
+
"mcp_bioconductor-spaniel",
|
| 641 |
+
"mcp_bioconductor-spari",
|
| 642 |
+
"mcp_bioconductor-sparsematrixstats",
|
| 643 |
+
"mcp_bioconductor-spatialcpie",
|
| 644 |
+
"mcp_bioconductor-spatialdatasets",
|
| 645 |
+
"mcp_bioconductor-spatialdecon",
|
| 646 |
+
"mcp_bioconductor-spatialdmelxsim",
|
| 647 |
+
"mcp_bioconductor-spatialexperiment",
|
| 648 |
+
"mcp_bioconductor-spatialexperimentio",
|
| 649 |
+
"mcp_bioconductor-spatialfda",
|
| 650 |
+
"mcp_bioconductor-spatialfeatureexperiment",
|
| 651 |
+
"mcp_bioconductor-spatialheatmap",
|
| 652 |
+
"mcp_bioconductor-speckle",
|
| 653 |
+
"mcp_bioconductor-spoon",
|
| 654 |
+
"mcp_bioconductor-spotclean",
|
| 655 |
+
"mcp_bioconductor-spotsweeper",
|
| 656 |
+
"mcp_bioconductor-standr",
|
| 657 |
+
"mcp_bioconductor-statial",
|
| 658 |
+
"mcp_bioconductor-stjoincount",
|
| 659 |
+
"mcp_bioconductor-summarizedexperiment",
|
| 660 |
+
"mcp_bioconductor-svp",
|
| 661 |
+
"mcp_bioconductor-tenxxeniumdata",
|
| 662 |
+
"mcp_bioconductor-tidyomics",
|
| 663 |
+
"mcp_bioconductor-tidyspatialexperiment",
|
| 664 |
+
"mcp_bioconductor-treesummarizedexperiment",
|
| 665 |
+
"mcp_bioconductor-vectrapolarisdata",
|
| 666 |
+
"mcp_bioconductor-visiumio",
|
| 667 |
+
"mcp_bioconductor-weberdivechalcdata",
|
| 668 |
+
"mcp_bioconductor-xcms",
|
| 669 |
+
"mcp_bioconductor-xenlite",
|
| 670 |
+
"mcp_bioconductor-xvector",
|
| 671 |
+
"mcp_bioconductor-zlibbioc",
|
| 672 |
+
"mcp_biopython",
|
| 673 |
+
"mcp_bowtie",
|
| 674 |
+
"mcp_bowtie2",
|
| 675 |
+
"mcp_bpipe",
|
| 676 |
+
"mcp_brooklyn_plot",
|
| 677 |
+
"mcp_busco",
|
| 678 |
+
"mcp_bx-python",
|
| 679 |
+
"mcp_c-ares",
|
| 680 |
+
"mcp_cascade-reg",
|
| 681 |
+
"mcp_cd-hit",
|
| 682 |
+
"mcp_cdbtools",
|
| 683 |
+
"mcp_cell2cell",
|
| 684 |
+
"mcp_cellitac",
|
| 685 |
+
"mcp_cellqc",
|
| 686 |
+
"mcp_cellrank",
|
| 687 |
+
"mcp_cellsnake",
|
| 688 |
+
"mcp_celltypist-so",
|
| 689 |
+
"mcp_checkatlas",
|
| 690 |
+
"mcp_checkm-genome",
|
| 691 |
+
"mcp_circexplorer2",
|
| 692 |
+
"mcp_circos",
|
| 693 |
+
"mcp_clustalw",
|
| 694 |
+
"mcp_cmappy",
|
| 695 |
+
"mcp_cnmf",
|
| 696 |
+
"mcp_cnvkit",
|
| 697 |
+
"mcp_comebin",
|
| 698 |
+
"mcp_comet-ms",
|
| 699 |
+
"mcp_constellations",
|
| 700 |
+
"mcp_cooler",
|
| 701 |
+
"mcp_cooltools",
|
| 702 |
+
"mcp_coreutils",
|
| 703 |
+
"mcp_cosg",
|
| 704 |
+
"mcp_cospar",
|
| 705 |
+
"mcp_crispresso2",
|
| 706 |
+
"mcp_crispritz",
|
| 707 |
+
"mcp_crisprme",
|
| 708 |
+
"mcp_cromwell",
|
| 709 |
+
"mcp_cutadapt",
|
| 710 |
+
"mcp_cwltool",
|
| 711 |
+
"mcp_cytoscape",
|
| 712 |
+
"mcp_cytotrace2-python",
|
| 713 |
+
"mcp_cyvcf2",
|
| 714 |
+
"mcp_ddocent",
|
| 715 |
+
"mcp_deblur",
|
| 716 |
+
"mcp_decoupler",
|
| 717 |
+
"mcp_deeptools",
|
| 718 |
+
"mcp_deeptoolsintervals",
|
| 719 |
+
"mcp_delly",
|
| 720 |
+
"mcp_dendropy",
|
| 721 |
+
"mcp_dnaio",
|
| 722 |
+
"mcp_doubletdetection",
|
| 723 |
+
"mcp_dropkick",
|
| 724 |
+
"mcp_dsh-bio",
|
| 725 |
+
"mcp_dxpy",
|
| 726 |
+
"mcp_easy_vitessce",
|
| 727 |
+
"mcp_echidna",
|
| 728 |
+
"mcp_emboss",
|
| 729 |
+
"mcp_ena-webin-cli",
|
| 730 |
+
"mcp_ensembl-vep",
|
| 731 |
+
"mcp_entrez-direct",
|
| 732 |
+
"mcp_epic",
|
| 733 |
+
"mcp_episcanpy",
|
| 734 |
+
"mcp_eva-sub-cli",
|
| 735 |
+
"mcp_f5c",
|
| 736 |
+
"mcp_famsa",
|
| 737 |
+
"mcp_fast5",
|
| 738 |
+
"mcp_fastani",
|
| 739 |
+
"mcp_fastdtw",
|
| 740 |
+
"mcp_fasttree",
|
| 741 |
+
"mcp_fermi2",
|
| 742 |
+
"mcp_fgbio",
|
| 743 |
+
"mcp_filechunkio",
|
| 744 |
+
"mcp_flagx",
|
| 745 |
+
"mcp_flye",
|
| 746 |
+
"mcp_ftputil",
|
| 747 |
+
"mcp_fwdpy11",
|
| 748 |
+
"mcp_galaxy-lib",
|
| 749 |
+
"mcp_gatk4-spark",
|
| 750 |
+
"mcp_gdk-pixbuf",
|
| 751 |
+
"mcp_gecode",
|
| 752 |
+
"mcp_gene-trajectory-python",
|
| 753 |
+
"mcp_genecircuitry",
|
| 754 |
+
"mcp_geneimpacts",
|
| 755 |
+
"mcp_genoboo",
|
| 756 |
+
"mcp_genomepy",
|
| 757 |
+
"mcp_genometools-genometools",
|
| 758 |
+
"mcp_gffutils",
|
| 759 |
+
"mcp_ghostscript",
|
| 760 |
+
"mcp_gimmemotifs",
|
| 761 |
+
"mcp_glimmerhmm",
|
| 762 |
+
"mcp_gmap",
|
| 763 |
+
"mcp_gneiss",
|
| 764 |
+
"mcp_gnuplot",
|
| 765 |
+
"mcp_gofasta",
|
| 766 |
+
"mcp_goleft",
|
| 767 |
+
"mcp_gromacs",
|
| 768 |
+
"mcp_gseapy",
|
| 769 |
+
"mcp_gsmap",
|
| 770 |
+
"mcp_gtdbtk",
|
| 771 |
+
"mcp_harpy",
|
| 772 |
+
"mcp_hicexplorer",
|
| 773 |
+
"mcp_hifiasm",
|
| 774 |
+
"mcp_hmmer",
|
| 775 |
+
"mcp_htseq",
|
| 776 |
+
"mcp_humann2",
|
| 777 |
+
"mcp_igv",
|
| 778 |
+
"mcp_igv-reports",
|
| 779 |
+
"mcp_illumina-interop",
|
| 780 |
+
"mcp_infernal",
|
| 781 |
+
"mcp_intarna",
|
| 782 |
+
"mcp_involucro",
|
| 783 |
+
"mcp_iow",
|
| 784 |
+
"mcp_ipyrad",
|
| 785 |
+
"mcp_ipython-cluster-helper",
|
| 786 |
+
"mcp_iqtree",
|
| 787 |
+
"mcp_itsxpress",
|
| 788 |
+
"mcp_ivar",
|
| 789 |
+
"mcp_jalview",
|
| 790 |
+
"mcp_java-jdk",
|
| 791 |
+
"mcp_jbrowse2",
|
| 792 |
+
"mcp_jcvi"
|
| 793 |
+
]
|
| 794 |
+
},
|
| 795 |
+
"2000": {
|
| 796 |
+
"config": "/225040511/project/Biomni/experiments/bioagent_bench/configs/mcp_scale_2000.yaml",
|
| 797 |
+
"server_count": 526,
|
| 798 |
+
"tool_count": 2000,
|
| 799 |
+
"servers": [
|
| 800 |
+
"mcp_kallisto",
|
| 801 |
+
"mcp_kraken2",
|
| 802 |
+
"mcp_csvtk",
|
| 803 |
+
"mcp_megahit",
|
| 804 |
+
"mcp_kaiju",
|
| 805 |
+
"mcp_fastp",
|
| 806 |
+
"mcp_spades",
|
| 807 |
+
"mcp_scanpy",
|
| 808 |
+
"mcp_scanpy-cli",
|
| 809 |
+
"mcp_scanpy-scripts",
|
| 810 |
+
"mcp_seurat-scripts",
|
| 811 |
+
"mcp_bioconductor-deseq2",
|
| 812 |
+
"mcp_bioconductor-edger",
|
| 813 |
+
"mcp_bioconductor-limma",
|
| 814 |
+
"mcp_bioconductor-clusterprofiler",
|
| 815 |
+
"mcp_star",
|
| 816 |
+
"mcp_hisat2",
|
| 817 |
+
"mcp_subread",
|
| 818 |
+
"mcp_bcftools",
|
| 819 |
+
"mcp_bwa",
|
| 820 |
+
"mcp_gatk4",
|
| 821 |
+
"mcp_gatk",
|
| 822 |
+
"mcp_snpeff",
|
| 823 |
+
"mcp_freebayes",
|
| 824 |
+
"mcp_orthofinder",
|
| 825 |
+
"mcp_blast",
|
| 826 |
+
"mcp_blast-legacy",
|
| 827 |
+
"mcp_diamond",
|
| 828 |
+
"mcp_gffread",
|
| 829 |
+
"mcp_seqkit",
|
| 830 |
+
"mcp_multiqc",
|
| 831 |
+
"mcp_fastqc",
|
| 832 |
+
"mcp_trimmomatic",
|
| 833 |
+
"mcp_bioconductor-keggrest",
|
| 834 |
+
"mcp_snpsift",
|
| 835 |
+
"mcp_bioconductor-rsubread",
|
| 836 |
+
"mcp_bioconductor-tximport",
|
| 837 |
+
"mcp_bioconductor-variantannotation",
|
| 838 |
+
"mcp_celltypist",
|
| 839 |
+
"mcp_abnumber",
|
| 840 |
+
"mcp_abricate",
|
| 841 |
+
"mcp_abundancebin",
|
| 842 |
+
"mcp_abyss",
|
| 843 |
+
"mcp_adapterremoval",
|
| 844 |
+
"mcp_alfred",
|
| 845 |
+
"mcp_anansescanpy",
|
| 846 |
+
"mcp_anarci",
|
| 847 |
+
"mcp_aragorn",
|
| 848 |
+
"mcp_aria2",
|
| 849 |
+
"mcp_ariba",
|
| 850 |
+
"mcp_arriba",
|
| 851 |
+
"mcp_art",
|
| 852 |
+
"mcp_arvados-cwl-runner",
|
| 853 |
+
"mcp_arvados-python-client",
|
| 854 |
+
"mcp_atropos",
|
| 855 |
+
"mcp_augustus",
|
| 856 |
+
"mcp_auspice",
|
| 857 |
+
"mcp_bactopia",
|
| 858 |
+
"mcp_bamtools",
|
| 859 |
+
"mcp_barrnap",
|
| 860 |
+
"mcp_bbmap",
|
| 861 |
+
"mcp_bcbio-gff",
|
| 862 |
+
"mcp_bcbio-nextgen",
|
| 863 |
+
"mcp_bedops",
|
| 864 |
+
"mcp_bedtools",
|
| 865 |
+
"mcp_bin2cell",
|
| 866 |
+
"mcp_bioawk",
|
| 867 |
+
"mcp_biobambam",
|
| 868 |
+
"mcp_bioconductor-affy",
|
| 869 |
+
"mcp_bioconductor-affyio",
|
| 870 |
+
"mcp_bioconductor-alabaster",
|
| 871 |
+
"mcp_bioconductor-alabaster.sfe",
|
| 872 |
+
"mcp_bioconductor-alabaster.spatial",
|
| 873 |
+
"mcp_bioconductor-annotate",
|
| 874 |
+
"mcp_bioconductor-annotationdbi",
|
| 875 |
+
"mcp_bioconductor-annotationfilter",
|
| 876 |
+
"mcp_bioconductor-apeglm",
|
| 877 |
+
"mcp_bioconductor-apl",
|
| 878 |
+
"mcp_bioconductor-awaggregator",
|
| 879 |
+
"mcp_bioconductor-banksy",
|
| 880 |
+
"mcp_bioconductor-beachmat",
|
| 881 |
+
"mcp_bioconductor-benchdamic",
|
| 882 |
+
"mcp_bioconductor-biobase",
|
| 883 |
+
"mcp_bioconductor-biocbaseutils",
|
| 884 |
+
"mcp_bioconductor-biocfilecache",
|
| 885 |
+
"mcp_bioconductor-biocgenerics",
|
| 886 |
+
"mcp_bioconductor-biocio",
|
| 887 |
+
"mcp_bioconductor-biocneighbors",
|
| 888 |
+
"mcp_bioconductor-biocparallel",
|
| 889 |
+
"mcp_bioconductor-biocsingular",
|
| 890 |
+
"mcp_bioconductor-biomart",
|
| 891 |
+
"mcp_bioconductor-biomformat",
|
| 892 |
+
"mcp_bioconductor-biostrings",
|
| 893 |
+
"mcp_bioconductor-biovizbase",
|
| 894 |
+
"mcp_bioconductor-blase",
|
| 895 |
+
"mcp_bioconductor-bluster",
|
| 896 |
+
"mcp_bioconductor-bulksignalr",
|
| 897 |
+
"mcp_bioconductor-cardspa",
|
| 898 |
+
"mcp_bioconductor-catscradle",
|
| 899 |
+
"mcp_bioconductor-cdi",
|
| 900 |
+
"mcp_bioconductor-cellhashr",
|
| 901 |
+
"mcp_bioconductor-cellid",
|
| 902 |
+
"mcp_bioconductor-cellmigration",
|
| 903 |
+
"mcp_bioconductor-clusterfoldsimilarity",
|
| 904 |
+
"mcp_bioconductor-clustifyr",
|
| 905 |
+
"mcp_bioconductor-clustsignal",
|
| 906 |
+
"mcp_bioconductor-complexheatmap",
|
| 907 |
+
"mcp_bioconductor-concordexr",
|
| 908 |
+
"mcp_bioconductor-cotan",
|
| 909 |
+
"mcp_bioconductor-csoa",
|
| 910 |
+
"mcp_bioconductor-ctsv",
|
| 911 |
+
"mcp_bioconductor-curatedatlasqueryr",
|
| 912 |
+
"mcp_bioconductor-cytomapper",
|
| 913 |
+
"mcp_bioconductor-dada2",
|
| 914 |
+
"mcp_bioconductor-data-packages",
|
| 915 |
+
"mcp_bioconductor-decipher",
|
| 916 |
+
"mcp_bioconductor-decontam",
|
| 917 |
+
"mcp_bioconductor-decontx",
|
| 918 |
+
"mcp_bioconductor-deconvobuddies",
|
| 919 |
+
"mcp_bioconductor-delayedmatrixstats",
|
| 920 |
+
"mcp_bioconductor-despace",
|
| 921 |
+
"mcp_bioconductor-diffbind",
|
| 922 |
+
"mcp_bioconductor-dino",
|
| 923 |
+
"mcp_bioconductor-dirichletmultinomial",
|
| 924 |
+
"mcp_bioconductor-dnacopy",
|
| 925 |
+
"mcp_bioconductor-dose",
|
| 926 |
+
"mcp_bioconductor-ensembldb",
|
| 927 |
+
"mcp_bioconductor-erma",
|
| 928 |
+
"mcp_bioconductor-escher",
|
| 929 |
+
"mcp_bioconductor-experimentsubset",
|
| 930 |
+
"mcp_bioconductor-fgsea",
|
| 931 |
+
"mcp_bioconductor-genefilter",
|
| 932 |
+
"mcp_bioconductor-geneplotter",
|
| 933 |
+
"mcp_bioconductor-genomeinfodb",
|
| 934 |
+
"mcp_bioconductor-genomeinfodbdata",
|
| 935 |
+
"mcp_bioconductor-genomicalignments",
|
| 936 |
+
"mcp_bioconductor-genomicfeatures",
|
| 937 |
+
"mcp_bioconductor-genomicranges",
|
| 938 |
+
"mcp_bioconductor-geomxtools",
|
| 939 |
+
"mcp_bioconductor-ggsc",
|
| 940 |
+
"mcp_bioconductor-ggspavis",
|
| 941 |
+
"mcp_bioconductor-ggtree",
|
| 942 |
+
"mcp_bioconductor-glmgampoi",
|
| 943 |
+
"mcp_bioconductor-go.db",
|
| 944 |
+
"mcp_bioconductor-gosemsim",
|
| 945 |
+
"mcp_bioconductor-graph",
|
| 946 |
+
"mcp_bioconductor-gsva",
|
| 947 |
+
"mcp_bioconductor-hcatonsildata",
|
| 948 |
+
"mcp_bioconductor-hdf5array",
|
| 949 |
+
"mcp_bioconductor-hoodscanr",
|
| 950 |
+
"mcp_bioconductor-humanhippocampus2024",
|
| 951 |
+
"mcp_bioconductor-imcdatasets",
|
| 952 |
+
"mcp_bioconductor-imcrtools",
|
| 953 |
+
"mcp_bioconductor-impute",
|
| 954 |
+
"mcp_bioconductor-infercnv",
|
| 955 |
+
"mcp_bioconductor-interactivedisplaybase",
|
| 956 |
+
"mcp_bioconductor-iranges",
|
| 957 |
+
"mcp_bioconductor-irisfgm",
|
| 958 |
+
"mcp_bioconductor-jazzpanda",
|
| 959 |
+
"mcp_bioconductor-lisaclust",
|
| 960 |
+
"mcp_bioconductor-mastr",
|
| 961 |
+
"mcp_bioconductor-matrixgenerics",
|
| 962 |
+
"mcp_bioconductor-merfishdata",
|
| 963 |
+
"mcp_bioconductor-metabolomicsworkbenchr",
|
| 964 |
+
"mcp_bioconductor-metapod",
|
| 965 |
+
"mcp_bioconductor-mia",
|
| 966 |
+
"mcp_bioconductor-moleculeexperiment",
|
| 967 |
+
"mcp_bioconductor-mosim",
|
| 968 |
+
"mcp_bioconductor-mousegastrulationdata",
|
| 969 |
+
"mcp_bioconductor-mspurity",
|
| 970 |
+
"mcp_bioconductor-multiassayexperiment",
|
| 971 |
+
"mcp_bioconductor-multtest",
|
| 972 |
+
"mcp_bioconductor-nebulosa",
|
| 973 |
+
"mcp_bioconductor-nnsvg",
|
| 974 |
+
"mcp_bioconductor-noiseq",
|
| 975 |
+
"mcp_bioconductor-orfhunter",
|
| 976 |
+
"mcp_bioconductor-org.ce.eg.db",
|
| 977 |
+
"mcp_bioconductor-org.hs.eg.db",
|
| 978 |
+
"mcp_bioconductor-org.mm.eg.db",
|
| 979 |
+
"mcp_bioconductor-partcnv",
|
| 980 |
+
"mcp_bioconductor-phemd",
|
| 981 |
+
"mcp_bioconductor-phyloseq",
|
| 982 |
+
"mcp_bioconductor-pipecomp",
|
| 983 |
+
"mcp_bioconductor-poem",
|
| 984 |
+
"mcp_bioconductor-preprocesscore",
|
| 985 |
+
"mcp_bioconductor-proteomicsannotationhubdata",
|
| 986 |
+
"mcp_bioconductor-protgenerics",
|
| 987 |
+
"mcp_bioconductor-qvalue",
|
| 988 |
+
"mcp_bioconductor-rbgl",
|
| 989 |
+
"mcp_bioconductor-reactomegsa.data",
|
| 990 |
+
"mcp_bioconductor-regionalst",
|
| 991 |
+
"mcp_bioconductor-rforproteomics",
|
| 992 |
+
"mcp_bioconductor-rgraphviz",
|
| 993 |
+
"mcp_bioconductor-rhdf5",
|
| 994 |
+
"mcp_bioconductor-rhdf5filters",
|
| 995 |
+
"mcp_bioconductor-rhdf5lib",
|
| 996 |
+
"mcp_bioconductor-rhtslib",
|
| 997 |
+
"mcp_bioconductor-rsamtools",
|
| 998 |
+
"mcp_bioconductor-scaledmatrix",
|
| 999 |
+
"mcp_bioconductor-scalign",
|
| 1000 |
+
"mcp_bioconductor-scarray.sat",
|
| 1001 |
+
"mcp_bioconductor-scbfa",
|
| 1002 |
+
"mcp_bioconductor-scbubbletree",
|
| 1003 |
+
"mcp_bioconductor-sccb2",
|
| 1004 |
+
"mcp_bioconductor-scclassifr",
|
| 1005 |
+
"mcp_bioconductor-scdataviz",
|
| 1006 |
+
"mcp_bioconductor-scdotplot",
|
| 1007 |
+
"mcp_bioconductor-scfeatures",
|
| 1008 |
+
"mcp_bioconductor-scider",
|
| 1009 |
+
"mcp_bioconductor-scmageck",
|
| 1010 |
+
"mcp_bioconductor-scqtltools",
|
| 1011 |
+
"mcp_bioconductor-scran",
|
| 1012 |
+
"mcp_bioconductor-screpertoire",
|
| 1013 |
+
"mcp_bioconductor-scrnaseq",
|
| 1014 |
+
"mcp_bioconductor-sctreeviz",
|
| 1015 |
+
"mcp_bioconductor-scuttle",
|
| 1016 |
+
"mcp_bioconductor-scvir",
|
| 1017 |
+
"mcp_bioconductor-seraster",
|
| 1018 |
+
"mcp_bioconductor-shortread",
|
| 1019 |
+
"mcp_bioconductor-signifinder",
|
| 1020 |
+
"mcp_bioconductor-singlecellmultimodal",
|
| 1021 |
+
"mcp_bioconductor-smoothclust",
|
| 1022 |
+
"mcp_bioconductor-smoppix",
|
| 1023 |
+
"mcp_bioconductor-sosta",
|
| 1024 |
+
"mcp_bioconductor-spacetrooper",
|
| 1025 |
+
"mcp_bioconductor-spaniel",
|
| 1026 |
+
"mcp_bioconductor-spari",
|
| 1027 |
+
"mcp_bioconductor-sparsematrixstats",
|
| 1028 |
+
"mcp_bioconductor-spatialcpie",
|
| 1029 |
+
"mcp_bioconductor-spatialdatasets",
|
| 1030 |
+
"mcp_bioconductor-spatialdecon",
|
| 1031 |
+
"mcp_bioconductor-spatialdmelxsim",
|
| 1032 |
+
"mcp_bioconductor-spatialexperiment",
|
| 1033 |
+
"mcp_bioconductor-spatialexperimentio",
|
| 1034 |
+
"mcp_bioconductor-spatialfda",
|
| 1035 |
+
"mcp_bioconductor-spatialfeatureexperiment",
|
| 1036 |
+
"mcp_bioconductor-spatialheatmap",
|
| 1037 |
+
"mcp_bioconductor-speckle",
|
| 1038 |
+
"mcp_bioconductor-spoon",
|
| 1039 |
+
"mcp_bioconductor-spotclean",
|
| 1040 |
+
"mcp_bioconductor-spotsweeper",
|
| 1041 |
+
"mcp_bioconductor-standr",
|
| 1042 |
+
"mcp_bioconductor-statial",
|
| 1043 |
+
"mcp_bioconductor-stjoincount",
|
| 1044 |
+
"mcp_bioconductor-summarizedexperiment",
|
| 1045 |
+
"mcp_bioconductor-svp",
|
| 1046 |
+
"mcp_bioconductor-tenxxeniumdata",
|
| 1047 |
+
"mcp_bioconductor-tidyomics",
|
| 1048 |
+
"mcp_bioconductor-tidyspatialexperiment",
|
| 1049 |
+
"mcp_bioconductor-treesummarizedexperiment",
|
| 1050 |
+
"mcp_bioconductor-vectrapolarisdata",
|
| 1051 |
+
"mcp_bioconductor-visiumio",
|
| 1052 |
+
"mcp_bioconductor-weberdivechalcdata",
|
| 1053 |
+
"mcp_bioconductor-xcms",
|
| 1054 |
+
"mcp_bioconductor-xenlite",
|
| 1055 |
+
"mcp_bioconductor-xvector",
|
| 1056 |
+
"mcp_bioconductor-zlibbioc",
|
| 1057 |
+
"mcp_biopython",
|
| 1058 |
+
"mcp_bowtie",
|
| 1059 |
+
"mcp_bowtie2",
|
| 1060 |
+
"mcp_bpipe",
|
| 1061 |
+
"mcp_brooklyn_plot",
|
| 1062 |
+
"mcp_busco",
|
| 1063 |
+
"mcp_bx-python",
|
| 1064 |
+
"mcp_c-ares",
|
| 1065 |
+
"mcp_cascade-reg",
|
| 1066 |
+
"mcp_cd-hit",
|
| 1067 |
+
"mcp_cdbtools",
|
| 1068 |
+
"mcp_cell2cell",
|
| 1069 |
+
"mcp_cellitac",
|
| 1070 |
+
"mcp_cellqc",
|
| 1071 |
+
"mcp_cellrank",
|
| 1072 |
+
"mcp_cellsnake",
|
| 1073 |
+
"mcp_celltypist-so",
|
| 1074 |
+
"mcp_checkatlas",
|
| 1075 |
+
"mcp_checkm-genome",
|
| 1076 |
+
"mcp_circexplorer2",
|
| 1077 |
+
"mcp_circos",
|
| 1078 |
+
"mcp_clustalw",
|
| 1079 |
+
"mcp_cmappy",
|
| 1080 |
+
"mcp_cnmf",
|
| 1081 |
+
"mcp_cnvkit",
|
| 1082 |
+
"mcp_comebin",
|
| 1083 |
+
"mcp_comet-ms",
|
| 1084 |
+
"mcp_constellations",
|
| 1085 |
+
"mcp_cooler",
|
| 1086 |
+
"mcp_cooltools",
|
| 1087 |
+
"mcp_coreutils",
|
| 1088 |
+
"mcp_cosg",
|
| 1089 |
+
"mcp_cospar",
|
| 1090 |
+
"mcp_crispresso2",
|
| 1091 |
+
"mcp_crispritz",
|
| 1092 |
+
"mcp_crisprme",
|
| 1093 |
+
"mcp_cromwell",
|
| 1094 |
+
"mcp_cutadapt",
|
| 1095 |
+
"mcp_cwltool",
|
| 1096 |
+
"mcp_cytoscape",
|
| 1097 |
+
"mcp_cytotrace2-python",
|
| 1098 |
+
"mcp_cyvcf2",
|
| 1099 |
+
"mcp_ddocent",
|
| 1100 |
+
"mcp_deblur",
|
| 1101 |
+
"mcp_decoupler",
|
| 1102 |
+
"mcp_deeptools",
|
| 1103 |
+
"mcp_deeptoolsintervals",
|
| 1104 |
+
"mcp_delly",
|
| 1105 |
+
"mcp_dendropy",
|
| 1106 |
+
"mcp_dnaio",
|
| 1107 |
+
"mcp_doubletdetection",
|
| 1108 |
+
"mcp_dropkick",
|
| 1109 |
+
"mcp_dsh-bio",
|
| 1110 |
+
"mcp_dxpy",
|
| 1111 |
+
"mcp_easy_vitessce",
|
| 1112 |
+
"mcp_echidna",
|
| 1113 |
+
"mcp_emboss",
|
| 1114 |
+
"mcp_ena-webin-cli",
|
| 1115 |
+
"mcp_ensembl-vep",
|
| 1116 |
+
"mcp_entrez-direct",
|
| 1117 |
+
"mcp_epic",
|
| 1118 |
+
"mcp_episcanpy",
|
| 1119 |
+
"mcp_eva-sub-cli",
|
| 1120 |
+
"mcp_f5c",
|
| 1121 |
+
"mcp_famsa",
|
| 1122 |
+
"mcp_fast5",
|
| 1123 |
+
"mcp_fastani",
|
| 1124 |
+
"mcp_fastdtw",
|
| 1125 |
+
"mcp_fasttree",
|
| 1126 |
+
"mcp_fermi2",
|
| 1127 |
+
"mcp_fgbio",
|
| 1128 |
+
"mcp_filechunkio",
|
| 1129 |
+
"mcp_flagx",
|
| 1130 |
+
"mcp_flye",
|
| 1131 |
+
"mcp_ftputil",
|
| 1132 |
+
"mcp_fwdpy11",
|
| 1133 |
+
"mcp_galaxy-lib",
|
| 1134 |
+
"mcp_gatk4-spark",
|
| 1135 |
+
"mcp_gdk-pixbuf",
|
| 1136 |
+
"mcp_gecode",
|
| 1137 |
+
"mcp_gene-trajectory-python",
|
| 1138 |
+
"mcp_genecircuitry",
|
| 1139 |
+
"mcp_geneimpacts",
|
| 1140 |
+
"mcp_genoboo",
|
| 1141 |
+
"mcp_genomepy",
|
| 1142 |
+
"mcp_genometools-genometools",
|
| 1143 |
+
"mcp_gffutils",
|
| 1144 |
+
"mcp_ghostscript",
|
| 1145 |
+
"mcp_gimmemotifs",
|
| 1146 |
+
"mcp_glimmerhmm",
|
| 1147 |
+
"mcp_gmap",
|
| 1148 |
+
"mcp_gneiss",
|
| 1149 |
+
"mcp_gnuplot",
|
| 1150 |
+
"mcp_gofasta",
|
| 1151 |
+
"mcp_goleft",
|
| 1152 |
+
"mcp_gromacs",
|
| 1153 |
+
"mcp_gseapy",
|
| 1154 |
+
"mcp_gsmap",
|
| 1155 |
+
"mcp_gtdbtk",
|
| 1156 |
+
"mcp_harpy",
|
| 1157 |
+
"mcp_hicexplorer",
|
| 1158 |
+
"mcp_hifiasm",
|
| 1159 |
+
"mcp_hmmer",
|
| 1160 |
+
"mcp_htseq",
|
| 1161 |
+
"mcp_humann2",
|
| 1162 |
+
"mcp_igv",
|
| 1163 |
+
"mcp_igv-reports",
|
| 1164 |
+
"mcp_illumina-interop",
|
| 1165 |
+
"mcp_infernal",
|
| 1166 |
+
"mcp_intarna",
|
| 1167 |
+
"mcp_involucro",
|
| 1168 |
+
"mcp_iow",
|
| 1169 |
+
"mcp_ipyrad",
|
| 1170 |
+
"mcp_ipython-cluster-helper",
|
| 1171 |
+
"mcp_iqtree",
|
| 1172 |
+
"mcp_itsxpress",
|
| 1173 |
+
"mcp_ivar",
|
| 1174 |
+
"mcp_jalview",
|
| 1175 |
+
"mcp_java-jdk",
|
| 1176 |
+
"mcp_jbrowse2",
|
| 1177 |
+
"mcp_jcvi",
|
| 1178 |
+
"mcp_jellyfish",
|
| 1179 |
+
"mcp_jq",
|
| 1180 |
+
"mcp_k8",
|
| 1181 |
+
"mcp_kalign2",
|
| 1182 |
+
"mcp_kb-python",
|
| 1183 |
+
"mcp_khipu-metabolomics",
|
| 1184 |
+
"mcp_kmer-jellyfish",
|
| 1185 |
+
"mcp_krona",
|
| 1186 |
+
"mcp_last",
|
| 1187 |
+
"mcp_lastz",
|
| 1188 |
+
"mcp_libcifpp",
|
| 1189 |
+
"mcp_libdeflate",
|
| 1190 |
+
"mcp_libsequence",
|
| 1191 |
+
"mcp_locarna",
|
| 1192 |
+
"mcp_macs2",
|
| 1193 |
+
"mcp_mafft",
|
| 1194 |
+
"mcp_mageck",
|
| 1195 |
+
"mcp_mameshiba",
|
| 1196 |
+
"mcp_mappy",
|
| 1197 |
+
"mcp_markerrepo",
|
| 1198 |
+
"mcp_mash",
|
| 1199 |
+
"mcp_maxquant",
|
| 1200 |
+
"mcp_medaka",
|
| 1201 |
+
"mcp_meme",
|
| 1202 |
+
"mcp_mentalist",
|
| 1203 |
+
"mcp_metagenome-atlas",
|
| 1204 |
+
"mcp_metaphlan",
|
| 1205 |
+
"mcp_metaphlan2",
|
| 1206 |
+
"mcp_mikado",
|
| 1207 |
+
"mcp_minced",
|
| 1208 |
+
"mcp_miniasm",
|
| 1209 |
+
"mcp_minimap2",
|
| 1210 |
+
"mcp_minvar",
|
| 1211 |
+
"mcp_mitos",
|
| 1212 |
+
"mcp_mmtf-python",
|
| 1213 |
+
"mcp_mobivision-m",
|
| 1214 |
+
"mcp_moments",
|
| 1215 |
+
"mcp_mosdepth",
|
| 1216 |
+
"mcp_mothur",
|
| 1217 |
+
"mcp_msisensor-pro",
|
| 1218 |
+
"mcp_msproteomicstools",
|
| 1219 |
+
"mcp_msstitch",
|
| 1220 |
+
"mcp_multiqc-bcbio",
|
| 1221 |
+
"mcp_multiqc-xenium-extra",
|
| 1222 |
+
"mcp_multivelo",
|
| 1223 |
+
"mcp_mummer",
|
| 1224 |
+
"mcp_munkres",
|
| 1225 |
+
"mcp_muscle",
|
| 1226 |
+
"mcp_mysql-connector-c",
|
| 1227 |
+
"mcp_mztosqlite",
|
| 1228 |
+
"mcp_nanocomp",
|
| 1229 |
+
"mcp_nanoget",
|
| 1230 |
+
"mcp_nanomath",
|
| 1231 |
+
"mcp_nanopolish",
|
| 1232 |
+
"mcp_ncbi-datasets-pylib",
|
| 1233 |
+
"mcp_ncbi-genome-download",
|
| 1234 |
+
"mcp_ncbi-ngs-sdk",
|
| 1235 |
+
"mcp_ncbi-vdb",
|
| 1236 |
+
"mcp_ncls",
|
| 1237 |
+
"mcp_nextalign",
|
| 1238 |
+
"mcp_nextclade",
|
| 1239 |
+
"mcp_nextflow",
|
| 1240 |
+
"mcp_nf-core",
|
| 1241 |
+
"mcp_nglview",
|
| 1242 |
+
"mcp_ngmlr",
|
| 1243 |
+
"mcp_ngs-smap",
|
| 1244 |
+
"mcp_ngs-tools",
|
| 1245 |
+
"mcp_novae",
|
| 1246 |
+
"mcp_novoalign",
|
| 1247 |
+
"mcp_ont-fast5-api",
|
| 1248 |
+
"mcp_openms-thirdparty",
|
| 1249 |
+
"mcp_pairix",
|
| 1250 |
+
"mcp_palantir",
|
| 1251 |
+
"mcp_paml",
|
| 1252 |
+
"mcp_pango-designation",
|
| 1253 |
+
"mcp_pangolearn",
|
| 1254 |
+
"mcp_parasail-python",
|
| 1255 |
+
"mcp_pasta",
|
| 1256 |
+
"mcp_paste-bio",
|
| 1257 |
+
"mcp_pcdl",
|
| 1258 |
+
"mcp_peakqc",
|
| 1259 |
+
"mcp_peptide-shaker",
|
| 1260 |
+
"mcp_perl-aceperl",
|
| 1261 |
+
"mcp_perl-algorithm-diff",
|
| 1262 |
+
"mcp_perl-alien-libxml2",
|
| 1263 |
+
"mcp_perl-app-cpanminus",
|
| 1264 |
+
"mcp_perl-appconfig",
|
| 1265 |
+
"mcp_perl-archive-tar",
|
| 1266 |
+
"mcp_perl-autoloader",
|
| 1267 |
+
"mcp_perl-base",
|
| 1268 |
+
"mcp_perl-bio-asn1-entrezgene",
|
| 1269 |
+
"mcp_perl-bio-coordinate",
|
| 1270 |
+
"mcp_perl-bio-featureio",
|
| 1271 |
+
"mcp_perl-bio-samtools",
|
| 1272 |
+
"mcp_perl-bio-searchio-hmmer",
|
| 1273 |
+
"mcp_perl-bio-tools-phylo-paml",
|
| 1274 |
+
"mcp_perl-bio-tools-run-alignment-clustalw",
|
| 1275 |
+
"mcp_perl-bioperl-core",
|
| 1276 |
+
"mcp_perl-business-isbn",
|
| 1277 |
+
"mcp_perl-business-isbn-data",
|
| 1278 |
+
"mcp_perl-capture-tiny",
|
| 1279 |
+
"mcp_perl-cgi",
|
| 1280 |
+
"mcp_perl-class-inspector",
|
| 1281 |
+
"mcp_perl-common-sense",
|
| 1282 |
+
"mcp_perl-compress-raw-zlib",
|
| 1283 |
+
"mcp_perl-config-general",
|
| 1284 |
+
"mcp_perl-constant",
|
| 1285 |
+
"mcp_perl-convert-binary-c",
|
| 1286 |
+
"mcp_perl-convert-binhex",
|
| 1287 |
+
"mcp_perl-cpan-meta-requirements",
|
| 1288 |
+
"mcp_perl-crypt-rc4",
|
| 1289 |
+
"mcp_perl-data-dumper",
|
| 1290 |
+
"mcp_perl-data-optlist",
|
| 1291 |
+
"mcp_perl-date-format",
|
| 1292 |
+
"mcp_perl-dbi",
|
| 1293 |
+
"mcp_perl-devel-overloadinfo",
|
| 1294 |
+
"mcp_perl-devel-stacktrace",
|
| 1295 |
+
"mcp_perl-digest-md5",
|
| 1296 |
+
"mcp_perl-digest-perl-md5",
|
| 1297 |
+
"mcp_perl-digest-sha1",
|
| 1298 |
+
"mcp_perl-encode-locale",
|
| 1299 |
+
"mcp_perl-exception-class",
|
| 1300 |
+
"mcp_perl-exporter",
|
| 1301 |
+
"mcp_perl-extutils-cbuilder",
|
| 1302 |
+
"mcp_perl-extutils-makemaker",
|
| 1303 |
+
"mcp_perl-ffi-checklib",
|
| 1304 |
+
"mcp_perl-file-sort",
|
| 1305 |
+
"mcp_perl-file-spec",
|
| 1306 |
+
"mcp_perl-file-which",
|
| 1307 |
+
"mcp_perl-font-afm",
|
| 1308 |
+
"mcp_perl-getopt-long",
|
| 1309 |
+
"mcp_perl-graph",
|
| 1310 |
+
"mcp_perl-graphviz",
|
| 1311 |
+
"mcp_perl-html-element-extended",
|
| 1312 |
+
"mcp_perl-html-formatter",
|
| 1313 |
+
"mcp_perl-html-parser",
|
| 1314 |
+
"mcp_perl-html-tableextract",
|
| 1315 |
+
"mcp_perl-html-tagset",
|
| 1316 |
+
"mcp_perl-html-tree",
|
| 1317 |
+
"mcp_perl-html-treebuilder-xpath",
|
| 1318 |
+
"mcp_perl-http-cookies",
|
| 1319 |
+
"mcp_perl-http-daemon",
|
| 1320 |
+
"mcp_perl-http-date",
|
| 1321 |
+
"mcp_perl-http-negotiate",
|
| 1322 |
+
"mcp_perl-image-info",
|
| 1323 |
+
"mcp_perl-image-size",
|
| 1324 |
+
"mcp_perl-importer",
|
| 1325 |
+
"mcp_perl-io-compress"
|
| 1326 |
+
]
|
| 1327 |
+
}
|
| 1328 |
+
}
|
| 1329 |
+
}
|
experiments/bioagent_bench/evaluate_bioagent_bench.py
ADDED
|
@@ -0,0 +1,1198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Evaluate Hypo_Bio_OS BioAgent Bench runs.
|
| 3 |
+
|
| 4 |
+
This evaluator follows the BioAgent Bench paper's grader design:
|
| 5 |
+
evaluate each trial as a pipeline execution, prioritize demonstrable
|
| 6 |
+
pipeline completion over exact numeric agreement, and return an
|
| 7 |
+
EvaluationResults-style JSON object.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
import argparse
|
| 13 |
+
import csv
|
| 14 |
+
import gzip
|
| 15 |
+
import json
|
| 16 |
+
import os
|
| 17 |
+
import re
|
| 18 |
+
from bisect import bisect_right
|
| 19 |
+
from datetime import timezone, datetime
|
| 20 |
+
from difflib import SequenceMatcher
|
| 21 |
+
from pathlib import Path
|
| 22 |
+
from statistics import mean
|
| 23 |
+
from typing import Any
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
DATASET_ROOT = Path("/225040511/project/bioagent-bench/dataset")
|
| 27 |
+
METADATA_PATH = Path("/225040511/project/bioagent-bench/src/task_metadata.json")
|
| 28 |
+
DEFAULT_RUNS_ROOT = Path("/225040511/project/Hypo_Bio_OS/bioagent-bench-runs")
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
FLEXIBLE_TABLE_MATCH_CONFIGS: dict[tuple[str, str], dict[str, Any]] = {
|
| 32 |
+
(
|
| 33 |
+
"alzheimer-mouse",
|
| 34 |
+
"pathway_comparison.csv",
|
| 35 |
+
): {
|
| 36 |
+
"soft_key_columns": ["Pathway"],
|
| 37 |
+
"threshold": 0.62,
|
| 38 |
+
"text_weight": 0.9,
|
| 39 |
+
"numeric_weight": 0.1,
|
| 40 |
+
},
|
| 41 |
+
(
|
| 42 |
+
"comparative-genomics",
|
| 43 |
+
"cluster_annotation_mapping.csv",
|
| 44 |
+
): {
|
| 45 |
+
"soft_key_columns": ["consensus_annotation"],
|
| 46 |
+
"threshold": 0.68,
|
| 47 |
+
},
|
| 48 |
+
(
|
| 49 |
+
"metagenomics",
|
| 50 |
+
"phylum_relative_abundances.csv",
|
| 51 |
+
): {
|
| 52 |
+
"soft_key_columns": ["Phylum"],
|
| 53 |
+
"threshold": 0.9,
|
| 54 |
+
"text_weight": 1.0,
|
| 55 |
+
"numeric_weight": 0.0,
|
| 56 |
+
},
|
| 57 |
+
(
|
| 58 |
+
"single-cell",
|
| 59 |
+
"all_clusters_de_genes.csv",
|
| 60 |
+
): {
|
| 61 |
+
"soft_key_columns": ["gene_name", "cluster_id"],
|
| 62 |
+
"threshold": 0.78,
|
| 63 |
+
"text_weight": 0.85,
|
| 64 |
+
"numeric_weight": 0.15,
|
| 65 |
+
},
|
| 66 |
+
(
|
| 67 |
+
"viral-metagenomics",
|
| 68 |
+
"taxonomy.csv",
|
| 69 |
+
): {
|
| 70 |
+
"soft_key_columns": ["domain", "species"],
|
| 71 |
+
"threshold": 0.72,
|
| 72 |
+
"text_weight": 0.85,
|
| 73 |
+
"numeric_weight": 0.15,
|
| 74 |
+
},
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
TASK_CONFIGS: dict[str, dict[str, Any]] = {
|
| 79 |
+
"alzheimer-mouse": {
|
| 80 |
+
"truth_files": ["pathway_comparison.csv"],
|
| 81 |
+
"result_files": ["pathway_comparison.csv"],
|
| 82 |
+
"key_columns": ["Pathway"],
|
| 83 |
+
"numeric_columns": ["5xFAD_pvalue", "3xTG_AD_pvalue", "PS3O1S_pvalue"],
|
| 84 |
+
"pipeline_steps": [
|
| 85 |
+
"inspect mouse count/DEA inputs",
|
| 86 |
+
"prepare metadata and count matrices for 5xFAD and 3xTG-AD",
|
| 87 |
+
"perform differential expression for 5xFAD",
|
| 88 |
+
"perform differential expression for 3xTG-AD",
|
| 89 |
+
"use provided PS3O1S differential expression results",
|
| 90 |
+
"run pathway enrichment per model",
|
| 91 |
+
"merge shared/comparative pathway p-values into final CSV",
|
| 92 |
+
],
|
| 93 |
+
"results_match_guidance": (
|
| 94 |
+
"Treat mouse mmu/Mus musculus pathway labels as semantically compatible with hsa/Homo sapiens labels "
|
| 95 |
+
"when the pathway identity is the same. Do not require exact p-value equality if the pipeline is plausible."
|
| 96 |
+
),
|
| 97 |
+
},
|
| 98 |
+
"comparative-genomics": {
|
| 99 |
+
"truth_files": ["cluster_annotation_mapping.csv"],
|
| 100 |
+
"result_files": ["cluster_annotation_mapping.csv"],
|
| 101 |
+
"key_columns": ["cluster_number", "consensus_annotation"],
|
| 102 |
+
"numeric_columns": [],
|
| 103 |
+
"pipeline_steps": [
|
| 104 |
+
"inspect Micrococcus FASTA/GFF/reference inputs",
|
| 105 |
+
"predict or extract protein-coding genes",
|
| 106 |
+
"identify orthologous/co-evolving clusters across genomes",
|
| 107 |
+
"filter clusters present across intended genomes and coding-only",
|
| 108 |
+
"assign high-confidence consensus annotations",
|
| 109 |
+
"write cluster_number,consensus_annotation CSV",
|
| 110 |
+
],
|
| 111 |
+
"results_match_guidance": (
|
| 112 |
+
"Prioritize whether the output represents conserved annotated coding clusters. Exact cluster numbering "
|
| 113 |
+
"can vary by method, but placeholder annotations or unrelated organisms should not match."
|
| 114 |
+
),
|
| 115 |
+
},
|
| 116 |
+
"cystic-fibrosis": {
|
| 117 |
+
"truth_files": ["cf_variants.csv"],
|
| 118 |
+
"result_files": ["cf_variants.csv"],
|
| 119 |
+
"key_columns": ["chromosome", "position", "reference", "alternate"],
|
| 120 |
+
"numeric_columns": [],
|
| 121 |
+
"verifiable": True,
|
| 122 |
+
"pipeline_steps": [
|
| 123 |
+
"inspect family description and variant VCF",
|
| 124 |
+
"filter variants by recessive inheritance in affected siblings",
|
| 125 |
+
"exclude variants inconsistent with unaffected relatives/parents",
|
| 126 |
+
"annotate candidate variant with ClinVar/reference metadata",
|
| 127 |
+
"write the requested causal-variant CSV schema",
|
| 128 |
+
],
|
| 129 |
+
"results_match_guidance": (
|
| 130 |
+
"The result should identify the CFTR pathogenic recessive variant. Exact textual disease lists may differ, "
|
| 131 |
+
"but chromosome, position, ref/alt, gene, and clinical interpretation must be consistent."
|
| 132 |
+
),
|
| 133 |
+
},
|
| 134 |
+
"deseq": {
|
| 135 |
+
"truth_files": ["up_regulated_genes.csv"],
|
| 136 |
+
"result_files": ["up_regulated_genes.csv"],
|
| 137 |
+
"key_columns": ["gene_id"],
|
| 138 |
+
"numeric_columns": ["log2FoldChange", "pvalue", "padj"],
|
| 139 |
+
"pipeline_steps": [
|
| 140 |
+
"inspect RNA-seq reads and Candida reference files",
|
| 141 |
+
"prepare genome annotation/index",
|
| 142 |
+
"align reads or otherwise quantify genes",
|
| 143 |
+
"count reads per gene",
|
| 144 |
+
"construct biofilm/planktonic sample metadata",
|
| 145 |
+
"run differential expression",
|
| 146 |
+
"filter up-regulated significant genes and write final CSV",
|
| 147 |
+
],
|
| 148 |
+
"results_match_guidance": (
|
| 149 |
+
"Prioritize evidence of a complete RNA-seq DE pipeline and a plausible up-regulated gene table. "
|
| 150 |
+
"Do not require exact equality for all p-values/log fold changes."
|
| 151 |
+
),
|
| 152 |
+
},
|
| 153 |
+
"evolution": {
|
| 154 |
+
"truth_files": ["variants_shared.csv", "gene_annotations.csv"],
|
| 155 |
+
"result_files": ["variants_shared.csv", "gene_annotations.csv"],
|
| 156 |
+
"key_columns": {
|
| 157 |
+
"variants_shared.csv": ["CHROM", "POS", "REF", "ALT"],
|
| 158 |
+
"gene_annotations.csv": ["Gene_Name"],
|
| 159 |
+
},
|
| 160 |
+
"numeric_columns": {"variants_shared.csv": [], "gene_annotations.csv": []},
|
| 161 |
+
"pipeline_steps": [
|
| 162 |
+
"inspect ancestor/evolved-line reads",
|
| 163 |
+
"prepare or identify valid E. coli reference/assembly",
|
| 164 |
+
"align ancestor and evolved reads",
|
| 165 |
+
"call variants for all samples",
|
| 166 |
+
"identify variants shared by evolved lines and absent from ancestor",
|
| 167 |
+
"annotate variant/gene effects",
|
| 168 |
+
"write variants_shared.csv and gene_annotations.csv",
|
| 169 |
+
],
|
| 170 |
+
"results_match_guidance": (
|
| 171 |
+
"Reward a biologically coherent shared-variant workflow. Penalize using forbidden external/sibling references "
|
| 172 |
+
"or hallucinated annotations even if the final schema exists."
|
| 173 |
+
),
|
| 174 |
+
},
|
| 175 |
+
"giab": {
|
| 176 |
+
"truth_files": ["HG001_GRCh38_1_22_v4.2.1_benchmark.vcf.gz"],
|
| 177 |
+
"result_files": ["predicted.vcf.gz"],
|
| 178 |
+
"vcf": True,
|
| 179 |
+
"verifiable": True,
|
| 180 |
+
"pipeline_steps": [
|
| 181 |
+
"inspect paired reads, BED targets, and GRCh38 reference",
|
| 182 |
+
"align reads to reference",
|
| 183 |
+
"sort/index BAM",
|
| 184 |
+
"mark duplicates or prepare analysis-ready BAM",
|
| 185 |
+
"call variants",
|
| 186 |
+
"compress/index final VCF if needed",
|
| 187 |
+
"write predicted.vcf.gz",
|
| 188 |
+
],
|
| 189 |
+
"results_match_guidance": (
|
| 190 |
+
"Use GIAB variant concordance as the correctness signal. The f1_score field should reflect variant-level "
|
| 191 |
+
"overlap where available."
|
| 192 |
+
),
|
| 193 |
+
},
|
| 194 |
+
"metagenomics": {
|
| 195 |
+
"truth_files": ["phylum_relative_abundances.csv"],
|
| 196 |
+
"result_files": ["phylum_relative_abundances.csv"],
|
| 197 |
+
"key_columns": ["OTU"],
|
| 198 |
+
"numeric_columns": ["JP4D", "JC1A"],
|
| 199 |
+
"pipeline_steps": [
|
| 200 |
+
"inspect paired metagenomic reads and reference database",
|
| 201 |
+
"classify reads taxonomically",
|
| 202 |
+
"aggregate classifications at bacterial phylum level",
|
| 203 |
+
"normalize relative abundances for JP4D and JC1A",
|
| 204 |
+
"write OTU,Kingdom,Phylum,JP4D,JC1A CSV",
|
| 205 |
+
],
|
| 206 |
+
"results_match_guidance": (
|
| 207 |
+
"Prioritize correct use of the bacterial/metagenomic reference and plausible phylum-level abundance table. "
|
| 208 |
+
"Small abundance differences are acceptable."
|
| 209 |
+
),
|
| 210 |
+
},
|
| 211 |
+
"single-cell": {
|
| 212 |
+
"truth_files": ["all_clusters_de_genes.csv"],
|
| 213 |
+
"result_files": ["all_clusters_de_genes.csv"],
|
| 214 |
+
"key_columns": ["cluster_id", "gene_name"],
|
| 215 |
+
"numeric_columns": ["logfoldchanges", "pvals", "pvals_adj", "abs_logfc"],
|
| 216 |
+
"pipeline_steps": [
|
| 217 |
+
"load 10X matrices and metadata",
|
| 218 |
+
"perform QC/normalization",
|
| 219 |
+
"cluster cells and preserve cluster IDs",
|
| 220 |
+
"annotate cell types using marker evidence",
|
| 221 |
+
"compare pre/post exercise within cell types or clusters",
|
| 222 |
+
"write all significant DE genes in requested schema",
|
| 223 |
+
],
|
| 224 |
+
"results_match_guidance": (
|
| 225 |
+
"Cell-type labels and cluster identities may vary, so prioritize marker-supported annotation, DE evidence, "
|
| 226 |
+
"and schema correctness over exact row equality."
|
| 227 |
+
),
|
| 228 |
+
},
|
| 229 |
+
"transcript-quant": {
|
| 230 |
+
"truth_files": ["truth.tsv"],
|
| 231 |
+
"result_files": ["truth.tsv"],
|
| 232 |
+
"tsv_no_header": True,
|
| 233 |
+
"key_columns": ["transcript_id"],
|
| 234 |
+
"numeric_columns": ["count"],
|
| 235 |
+
"verifiable": True,
|
| 236 |
+
"pipeline_steps": [
|
| 237 |
+
"inspect paired RNA-seq reads and transcriptome reference",
|
| 238 |
+
"build transcriptome index",
|
| 239 |
+
"quantify transcript abundance/counts",
|
| 240 |
+
"extract transcript_id and count columns",
|
| 241 |
+
"write no-header two-column TSV",
|
| 242 |
+
],
|
| 243 |
+
"results_match_guidance": (
|
| 244 |
+
"Because the data is simulated, counts should closely reproduce the truth. Headerless two-column TSV format "
|
| 245 |
+
"is required."
|
| 246 |
+
),
|
| 247 |
+
},
|
| 248 |
+
"viral-metagenomics": {
|
| 249 |
+
"truth_files": ["taxonomy.csv"],
|
| 250 |
+
"result_files": ["taxonomy.csv"],
|
| 251 |
+
"key_columns": ["domain", "species"],
|
| 252 |
+
"numeric_columns": ["contig_count"],
|
| 253 |
+
"verifiable": True,
|
| 254 |
+
"pipeline_steps": [
|
| 255 |
+
"inspect dolphin metagenomic reads and viral reference resources",
|
| 256 |
+
"assemble reads into contigs",
|
| 257 |
+
"classify contigs against compatible viral reference resources",
|
| 258 |
+
"aggregate contig counts by domain/species",
|
| 259 |
+
"write contig_count,domain,species CSV",
|
| 260 |
+
],
|
| 261 |
+
"results_match_guidance": (
|
| 262 |
+
"Prioritize using this task's viral reference resources and correctly identifying viral species. "
|
| 263 |
+
"Unclassified contigs are allowed when supported by classification output."
|
| 264 |
+
),
|
| 265 |
+
},
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
def utc_timestamp() -> str:
|
| 270 |
+
return datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
def normalize_text(value: Any) -> str:
|
| 274 |
+
return " ".join(str(value).strip().split()).lower()
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def normalize_for_soft_match(value: Any) -> str:
|
| 278 |
+
text = normalize_text(value)
|
| 279 |
+
text = re.sub(r"\b(homo sapiens|mus musculus|human|mouse)\b", " ", text)
|
| 280 |
+
text = re.sub(r"\b(hsa|mmu)(?=\d)", " ", text)
|
| 281 |
+
text = re.sub(r"[^a-z0-9]+", " ", text)
|
| 282 |
+
return " ".join(text.split())
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
def normalize_chrom(value: Any) -> str:
|
| 286 |
+
chrom = normalize_text(value)
|
| 287 |
+
if chrom.startswith("chr"):
|
| 288 |
+
chrom = chrom[3:]
|
| 289 |
+
return {"m": "mt", "mitochondria": "mt"}.get(chrom, chrom)
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def maybe_float(value: Any) -> float | None:
|
| 293 |
+
try:
|
| 294 |
+
return float(str(value).strip())
|
| 295 |
+
except Exception:
|
| 296 |
+
return None
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def get_row_value(row: dict, column: str, default: str = ""):
|
| 300 |
+
if column in row:
|
| 301 |
+
return row.get(column, default)
|
| 302 |
+
target = normalize_text(column)
|
| 303 |
+
for key, value in row.items():
|
| 304 |
+
if normalize_text(key) == target:
|
| 305 |
+
return value
|
| 306 |
+
return default
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def token_similarity(left: str, right: str) -> float:
|
| 310 |
+
left_tokens = set(left.split())
|
| 311 |
+
right_tokens = set(right.split())
|
| 312 |
+
if not left_tokens and not right_tokens:
|
| 313 |
+
return 1.0
|
| 314 |
+
if not left_tokens or not right_tokens:
|
| 315 |
+
return 0.0
|
| 316 |
+
return len(left_tokens & right_tokens) / len(left_tokens | right_tokens)
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
def text_similarity(left: Any, right: Any) -> float:
|
| 320 |
+
left_norm = normalize_for_soft_match(left)
|
| 321 |
+
right_norm = normalize_for_soft_match(right)
|
| 322 |
+
if left_norm == right_norm:
|
| 323 |
+
return 1.0
|
| 324 |
+
if not left_norm or not right_norm:
|
| 325 |
+
return 0.0
|
| 326 |
+
sequence_score = SequenceMatcher(None, left_norm, right_norm).ratio()
|
| 327 |
+
token_score = token_similarity(left_norm, right_norm)
|
| 328 |
+
return max(sequence_score, token_score)
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def numeric_similarity(left: Any, right: Any, rel_scale: float = 0.1) -> float | None:
|
| 332 |
+
left_value = maybe_float(left)
|
| 333 |
+
right_value = maybe_float(right)
|
| 334 |
+
if left_value is None or right_value is None:
|
| 335 |
+
return None
|
| 336 |
+
if left_value == right_value:
|
| 337 |
+
return 1.0
|
| 338 |
+
scale = max(abs(left_value), abs(right_value), 1e-12)
|
| 339 |
+
relative_error = abs(left_value - right_value) / scale
|
| 340 |
+
return max(0.0, 1.0 - (relative_error / rel_scale))
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
def load_task_metadata(metadata_path: Path) -> dict[str, dict]:
|
| 344 |
+
if not metadata_path.exists():
|
| 345 |
+
return {}
|
| 346 |
+
payload = json.loads(metadata_path.read_text(encoding="utf-8"))
|
| 347 |
+
return {item["task_id"]: item for item in payload}
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def load_run_metadata(run_dir: Path) -> dict:
|
| 351 |
+
path = run_dir / "run_metadata.json"
|
| 352 |
+
if not path.exists():
|
| 353 |
+
return {}
|
| 354 |
+
try:
|
| 355 |
+
return json.loads(path.read_text(encoding="utf-8"))
|
| 356 |
+
except json.JSONDecodeError:
|
| 357 |
+
return {}
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def load_csv_rows(path: Path) -> list[dict]:
|
| 361 |
+
with path.open("r", encoding="utf-8", errors="ignore", newline="") as handle:
|
| 362 |
+
return list(csv.DictReader(handle))
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def load_tsv_no_header(path: Path) -> list[dict]:
|
| 366 |
+
rows = []
|
| 367 |
+
with path.open("r", encoding="utf-8", errors="ignore") as handle:
|
| 368 |
+
for line in handle:
|
| 369 |
+
line = line.rstrip("\n")
|
| 370 |
+
if not line:
|
| 371 |
+
continue
|
| 372 |
+
parts = line.split("\t")
|
| 373 |
+
if len(parts) >= 2:
|
| 374 |
+
rows.append({"transcript_id": parts[0], "count": parts[1]})
|
| 375 |
+
return rows
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def read_text_preview(path: Path, max_lines: int = 80, max_chars: int = 24000) -> str:
|
| 379 |
+
opener = gzip.open if path.suffix == ".gz" else open
|
| 380 |
+
preview_lines = []
|
| 381 |
+
total_chars = 0
|
| 382 |
+
with opener(path, "rt", encoding="utf-8", errors="ignore") as handle:
|
| 383 |
+
for i, line in enumerate(handle):
|
| 384 |
+
if i >= max_lines:
|
| 385 |
+
preview_lines.append("... [truncated]")
|
| 386 |
+
break
|
| 387 |
+
preview_lines.append(line.rstrip("\n"))
|
| 388 |
+
total_chars += len(line)
|
| 389 |
+
if total_chars >= max_chars:
|
| 390 |
+
preview_lines.append("... [truncated]")
|
| 391 |
+
break
|
| 392 |
+
return "\n".join(preview_lines)
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
def load_prediction_rows(task_id: str, file_name: str, prediction_path: Path) -> list[dict]:
|
| 396 |
+
if TASK_CONFIGS[task_id].get("tsv_no_header"):
|
| 397 |
+
return load_tsv_no_header(prediction_path)
|
| 398 |
+
return load_csv_rows(prediction_path)
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def open_text_auto(path: Path):
|
| 402 |
+
return gzip.open(path, "rt", encoding="utf-8", errors="ignore") if path.suffix == ".gz" else path.open(
|
| 403 |
+
"r", encoding="utf-8", errors="ignore"
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
def parse_bed_intervals(path: Path) -> dict[str, list[tuple[int, int]]]:
|
| 408 |
+
intervals: dict[str, list[tuple[int, int]]] = {}
|
| 409 |
+
if not path.exists():
|
| 410 |
+
return intervals
|
| 411 |
+
with open_text_auto(path) as handle:
|
| 412 |
+
for line in handle:
|
| 413 |
+
if not line.strip() or line.startswith(("#", "track", "browser")):
|
| 414 |
+
continue
|
| 415 |
+
fields = line.rstrip("\n").split("\t")
|
| 416 |
+
if len(fields) < 3:
|
| 417 |
+
continue
|
| 418 |
+
try:
|
| 419 |
+
start = int(fields[1])
|
| 420 |
+
end = int(fields[2])
|
| 421 |
+
except ValueError:
|
| 422 |
+
continue
|
| 423 |
+
chrom = normalize_chrom(fields[0])
|
| 424 |
+
intervals.setdefault(chrom, []).append((start, end))
|
| 425 |
+
for chrom in intervals:
|
| 426 |
+
intervals[chrom].sort()
|
| 427 |
+
return intervals
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
def position_in_intervals(chrom: str, pos: str, intervals: dict[str, list[tuple[int, int]]] | None) -> bool:
|
| 431 |
+
if not intervals:
|
| 432 |
+
return True
|
| 433 |
+
try:
|
| 434 |
+
pos_1_based = int(pos)
|
| 435 |
+
except ValueError:
|
| 436 |
+
return False
|
| 437 |
+
pos_0_based = pos_1_based - 1
|
| 438 |
+
chrom_intervals = intervals.get(normalize_chrom(chrom), [])
|
| 439 |
+
interval_index = bisect_right(chrom_intervals, (pos_0_based, 10**18)) - 1
|
| 440 |
+
if interval_index < 0:
|
| 441 |
+
return False
|
| 442 |
+
start, end = chrom_intervals[interval_index]
|
| 443 |
+
return start <= pos_0_based < end
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
def load_vcf_keys(path: Path, intervals: dict[str, list[tuple[int, int]]] | None = None) -> set[tuple[str, str, str, str]]:
|
| 447 |
+
keys = set()
|
| 448 |
+
with open_text_auto(path) as handle:
|
| 449 |
+
for line in handle:
|
| 450 |
+
if not line or line.startswith("#"):
|
| 451 |
+
continue
|
| 452 |
+
fields = line.rstrip("\n").split("\t")
|
| 453 |
+
if len(fields) < 5:
|
| 454 |
+
continue
|
| 455 |
+
chrom, pos, _vid, ref, alt = fields[:5]
|
| 456 |
+
if not position_in_intervals(chrom, pos, intervals):
|
| 457 |
+
continue
|
| 458 |
+
for alt_item in alt.split(","):
|
| 459 |
+
keys.add((normalize_chrom(chrom), normalize_text(pos), normalize_text(ref), normalize_text(alt_item)))
|
| 460 |
+
return keys
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
def f1_from_counts(tp: int, pred_count: int, truth_count: int) -> dict[str, float]:
|
| 464 |
+
precision = tp / pred_count if pred_count else 0.0
|
| 465 |
+
recall = tp / truth_count if truth_count else 0.0
|
| 466 |
+
f1 = 0.0 if precision + recall == 0 else (2 * precision * recall) / (precision + recall)
|
| 467 |
+
return {"precision": precision, "recall": recall, "f1": f1}
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
def compare_variant_sets(pred_keys: set[tuple[str, str, str, str]], truth_keys: set[tuple[str, str, str, str]]) -> dict:
|
| 471 |
+
tp = len(pred_keys & truth_keys)
|
| 472 |
+
metrics = f1_from_counts(tp, len(pred_keys), len(truth_keys))
|
| 473 |
+
return {
|
| 474 |
+
"pred_variant_count": len(pred_keys),
|
| 475 |
+
"truth_variant_count": len(truth_keys),
|
| 476 |
+
"shared_variant_count": tp,
|
| 477 |
+
"precision": metrics["precision"],
|
| 478 |
+
"recall": metrics["recall"],
|
| 479 |
+
"f1": metrics["f1"],
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def compare_vcf(prediction_path: Path, truth_path: Path, task_id: str, dataset_root: Path) -> dict:
|
| 484 |
+
bed_candidates = [
|
| 485 |
+
dataset_root / task_id / "data" / "Agilent_v7.chr.bed",
|
| 486 |
+
dataset_root / task_id / "results" / "HG001_GRCh38_1_22_v4.2.1_benchmark.bed",
|
| 487 |
+
]
|
| 488 |
+
bed_path = next((path for path in bed_candidates if path.exists()), None)
|
| 489 |
+
if not bed_path:
|
| 490 |
+
unfiltered_pred_keys = load_vcf_keys(prediction_path)
|
| 491 |
+
unfiltered_truth_keys = load_vcf_keys(truth_path)
|
| 492 |
+
unfiltered = compare_variant_sets(unfiltered_pred_keys, unfiltered_truth_keys)
|
| 493 |
+
return {
|
| 494 |
+
**unfiltered,
|
| 495 |
+
"truth_scope": "all_truth_variants",
|
| 496 |
+
"unfiltered_f1": unfiltered["f1"],
|
| 497 |
+
"note": "Approximate VCF comparison by normalized CHROM,POS,REF,ALT; no BED scope was available.",
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
intervals = parse_bed_intervals(bed_path)
|
| 501 |
+
scoped_pred_keys = load_vcf_keys(prediction_path, intervals=intervals)
|
| 502 |
+
scoped_truth_keys = load_vcf_keys(truth_path, intervals=intervals)
|
| 503 |
+
scoped = compare_variant_sets(scoped_pred_keys, scoped_truth_keys)
|
| 504 |
+
return {
|
| 505 |
+
**scoped,
|
| 506 |
+
"truth_scope": "target_bed",
|
| 507 |
+
"target_bed": str(bed_path),
|
| 508 |
+
"unfiltered_f1": None,
|
| 509 |
+
"note": "Primary F1 is restricted to the task target BED and uses normalized CHROM,POS,REF,ALT.",
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
def row_similarity(
|
| 514 |
+
pred: dict,
|
| 515 |
+
truth: dict,
|
| 516 |
+
soft_key_columns: list[str],
|
| 517 |
+
numeric_columns: list[str],
|
| 518 |
+
text_weight: float,
|
| 519 |
+
numeric_weight: float,
|
| 520 |
+
) -> float:
|
| 521 |
+
text_scores = [
|
| 522 |
+
text_similarity(get_row_value(pred, column, ""), get_row_value(truth, column, ""))
|
| 523 |
+
for column in soft_key_columns
|
| 524 |
+
]
|
| 525 |
+
text_score = sum(text_scores) / len(text_scores) if text_scores else 0.0
|
| 526 |
+
|
| 527 |
+
numeric_scores = [
|
| 528 |
+
score
|
| 529 |
+
for column in numeric_columns
|
| 530 |
+
if (score := numeric_similarity(get_row_value(pred, column, ""), get_row_value(truth, column, ""))) is not None
|
| 531 |
+
]
|
| 532 |
+
if not numeric_scores or numeric_weight <= 0:
|
| 533 |
+
return text_score
|
| 534 |
+
numeric_score = sum(numeric_scores) / len(numeric_scores)
|
| 535 |
+
total_weight = text_weight + numeric_weight
|
| 536 |
+
return ((text_score * text_weight) + (numeric_score * numeric_weight)) / total_weight
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
def greedy_soft_row_match(
|
| 540 |
+
pred_rows: list[dict],
|
| 541 |
+
truth_rows: list[dict],
|
| 542 |
+
soft_key_columns: list[str],
|
| 543 |
+
numeric_columns: list[str],
|
| 544 |
+
threshold: float,
|
| 545 |
+
text_weight: float,
|
| 546 |
+
numeric_weight: float,
|
| 547 |
+
) -> tuple[list[tuple[int, int, float]], list[int], list[int]]:
|
| 548 |
+
candidates = []
|
| 549 |
+
first_soft_column = soft_key_columns[0] if soft_key_columns else None
|
| 550 |
+
truth_buckets: dict[str, list[tuple[int, dict]]] = {}
|
| 551 |
+
use_bucketed_candidates = first_soft_column is not None and len(pred_rows) * len(truth_rows) > 200_000
|
| 552 |
+
if use_bucketed_candidates:
|
| 553 |
+
for truth_index, truth in enumerate(truth_rows):
|
| 554 |
+
bucket_key = normalize_for_soft_match(get_row_value(truth, first_soft_column, ""))
|
| 555 |
+
truth_buckets.setdefault(bucket_key, []).append((truth_index, truth))
|
| 556 |
+
|
| 557 |
+
for pred_index, pred in enumerate(pred_rows):
|
| 558 |
+
if use_bucketed_candidates:
|
| 559 |
+
bucket_key = normalize_for_soft_match(get_row_value(pred, first_soft_column, ""))
|
| 560 |
+
candidate_truth_rows = truth_buckets.get(bucket_key, [])
|
| 561 |
+
else:
|
| 562 |
+
candidate_truth_rows = list(enumerate(truth_rows))
|
| 563 |
+
for truth_index, truth in candidate_truth_rows:
|
| 564 |
+
score = row_similarity(pred, truth, soft_key_columns, numeric_columns, text_weight, numeric_weight)
|
| 565 |
+
if score >= threshold:
|
| 566 |
+
candidates.append((score, pred_index, truth_index))
|
| 567 |
+
|
| 568 |
+
candidates.sort(reverse=True)
|
| 569 |
+
used_pred = set()
|
| 570 |
+
used_truth = set()
|
| 571 |
+
matches = []
|
| 572 |
+
for score, pred_index, truth_index in candidates:
|
| 573 |
+
if pred_index in used_pred or truth_index in used_truth:
|
| 574 |
+
continue
|
| 575 |
+
used_pred.add(pred_index)
|
| 576 |
+
used_truth.add(truth_index)
|
| 577 |
+
matches.append((pred_index, truth_index, score))
|
| 578 |
+
unmatched_pred = [index for index in range(len(pred_rows)) if index not in used_pred]
|
| 579 |
+
unmatched_truth = [index for index in range(len(truth_rows)) if index not in used_truth]
|
| 580 |
+
return matches, unmatched_pred, unmatched_truth
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
def compare_table_rows(task_id: str, file_name: str, pred_rows: list[dict], truth_rows: list[dict]) -> dict:
|
| 584 |
+
config = TASK_CONFIGS[task_id]
|
| 585 |
+
key_columns = config["key_columns"][file_name] if isinstance(config["key_columns"], dict) else config["key_columns"]
|
| 586 |
+
numeric_columns = (
|
| 587 |
+
config["numeric_columns"][file_name] if isinstance(config["numeric_columns"], dict) else config["numeric_columns"]
|
| 588 |
+
)
|
| 589 |
+
|
| 590 |
+
pred_map = {
|
| 591 |
+
tuple(normalize_text(get_row_value(row, col, "")) for col in key_columns): row
|
| 592 |
+
for row in pred_rows
|
| 593 |
+
}
|
| 594 |
+
truth_map = {
|
| 595 |
+
tuple(normalize_text(get_row_value(row, col, "")) for col in key_columns): row
|
| 596 |
+
for row in truth_rows
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
pred_keys = set(pred_map)
|
| 600 |
+
truth_keys = set(truth_map)
|
| 601 |
+
shared_keys = pred_keys & truth_keys
|
| 602 |
+
metrics = f1_from_counts(len(shared_keys), len(pred_keys), len(truth_keys))
|
| 603 |
+
|
| 604 |
+
numeric_diffs = {col: [] for col in numeric_columns}
|
| 605 |
+
for key in shared_keys:
|
| 606 |
+
pred = pred_map[key]
|
| 607 |
+
truth = truth_map[key]
|
| 608 |
+
for col in numeric_columns:
|
| 609 |
+
pred_value = maybe_float(get_row_value(pred, col, ""))
|
| 610 |
+
truth_value = maybe_float(get_row_value(truth, col, ""))
|
| 611 |
+
if pred_value is not None and truth_value is not None:
|
| 612 |
+
numeric_diffs[col].append(abs(pred_value - truth_value))
|
| 613 |
+
|
| 614 |
+
summary = {
|
| 615 |
+
"pred_row_count": len(pred_rows),
|
| 616 |
+
"truth_row_count": len(truth_rows),
|
| 617 |
+
"shared_key_count": len(shared_keys),
|
| 618 |
+
"exact_key_precision": metrics["precision"],
|
| 619 |
+
"exact_key_recall": metrics["recall"],
|
| 620 |
+
"exact_key_f1": metrics["f1"],
|
| 621 |
+
"key_precision": metrics["precision"],
|
| 622 |
+
"key_recall": metrics["recall"],
|
| 623 |
+
"key_f1": metrics["f1"],
|
| 624 |
+
"match_strategy": "exact_key",
|
| 625 |
+
"match_precision": metrics["precision"],
|
| 626 |
+
"match_recall": metrics["recall"],
|
| 627 |
+
"match_f1": metrics["f1"],
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
flexible_config = FLEXIBLE_TABLE_MATCH_CONFIGS.get((task_id, file_name))
|
| 631 |
+
if flexible_config:
|
| 632 |
+
soft_key_columns = flexible_config.get("soft_key_columns", key_columns)
|
| 633 |
+
threshold = flexible_config.get("threshold", 0.75)
|
| 634 |
+
text_weight = flexible_config.get("text_weight", 1.0)
|
| 635 |
+
numeric_weight = flexible_config.get("numeric_weight", 0.0)
|
| 636 |
+
matches, unmatched_pred, unmatched_truth = greedy_soft_row_match(
|
| 637 |
+
pred_rows,
|
| 638 |
+
truth_rows,
|
| 639 |
+
soft_key_columns,
|
| 640 |
+
numeric_columns,
|
| 641 |
+
threshold,
|
| 642 |
+
text_weight,
|
| 643 |
+
numeric_weight,
|
| 644 |
+
)
|
| 645 |
+
soft_metrics = f1_from_counts(len(matches), len(pred_rows), len(truth_rows))
|
| 646 |
+
summary.update(
|
| 647 |
+
{
|
| 648 |
+
"match_strategy": "soft_row_similarity",
|
| 649 |
+
"soft_key_columns": soft_key_columns,
|
| 650 |
+
"match_threshold": threshold,
|
| 651 |
+
"soft_match_count": len(matches),
|
| 652 |
+
"match_precision": soft_metrics["precision"],
|
| 653 |
+
"match_recall": soft_metrics["recall"],
|
| 654 |
+
"match_f1": soft_metrics["f1"],
|
| 655 |
+
"mean_match_score": mean([score for *_unused, score in matches]) if matches else 0.0,
|
| 656 |
+
"unmatched_prediction_examples": [
|
| 657 |
+
{column: get_row_value(pred_rows[index], column, "") for column in soft_key_columns}
|
| 658 |
+
for index in unmatched_pred[:5]
|
| 659 |
+
],
|
| 660 |
+
"unmatched_truth_examples": [
|
| 661 |
+
{column: get_row_value(truth_rows[index], column, "") for column in soft_key_columns}
|
| 662 |
+
for index in unmatched_truth[:5]
|
| 663 |
+
],
|
| 664 |
+
}
|
| 665 |
+
)
|
| 666 |
+
if numeric_columns:
|
| 667 |
+
summary["numeric_mae"] = {
|
| 668 |
+
col: (sum(values) / len(values) if values else None) for col, values in numeric_diffs.items()
|
| 669 |
+
}
|
| 670 |
+
return summary
|
| 671 |
+
|
| 672 |
+
|
| 673 |
+
def summarize_file_for_judge(task_id: str, path: Path) -> dict:
|
| 674 |
+
config = TASK_CONFIGS[task_id]
|
| 675 |
+
if not path.exists():
|
| 676 |
+
return {"path": str(path), "exists": False}
|
| 677 |
+
if config.get("vcf"):
|
| 678 |
+
return {
|
| 679 |
+
"path": str(path),
|
| 680 |
+
"exists": True,
|
| 681 |
+
"type": "vcf.gz",
|
| 682 |
+
"size_bytes": path.stat().st_size,
|
| 683 |
+
"preview": read_text_preview(path, max_lines=60, max_chars=20000),
|
| 684 |
+
}
|
| 685 |
+
if path.suffix.lower() == ".tsv" and config.get("tsv_no_header"):
|
| 686 |
+
rows = load_tsv_no_header(path)
|
| 687 |
+
return {
|
| 688 |
+
"path": str(path),
|
| 689 |
+
"exists": True,
|
| 690 |
+
"type": "tsv",
|
| 691 |
+
"row_count": len(rows),
|
| 692 |
+
"columns": ["transcript_id", "count"],
|
| 693 |
+
"preview": read_text_preview(path, max_lines=80, max_chars=22000),
|
| 694 |
+
}
|
| 695 |
+
rows = load_csv_rows(path)
|
| 696 |
+
return {
|
| 697 |
+
"path": str(path),
|
| 698 |
+
"exists": True,
|
| 699 |
+
"type": path.suffix.lower().lstrip(".") or "text",
|
| 700 |
+
"row_count": len(rows),
|
| 701 |
+
"columns": list(rows[0].keys()) if rows else [],
|
| 702 |
+
"preview": read_text_preview(path, max_lines=80, max_chars=22000),
|
| 703 |
+
}
|
| 704 |
+
|
| 705 |
+
|
| 706 |
+
def locate_prediction_file(task_id: str, run_dir: Path, file_name: str) -> Path | None:
|
| 707 |
+
candidate = run_dir / file_name
|
| 708 |
+
if candidate.exists():
|
| 709 |
+
return candidate
|
| 710 |
+
matches = sorted(run_dir.rglob(file_name))
|
| 711 |
+
return matches[0] if matches else None
|
| 712 |
+
|
| 713 |
+
|
| 714 |
+
def infer_latest_run_dir(task_id: str, runs_root: Path) -> Path | None:
|
| 715 |
+
candidates = sorted(path for path in runs_root.glob(f"{task_id}_*") if path.is_dir())
|
| 716 |
+
return candidates[-1] if candidates else None
|
| 717 |
+
|
| 718 |
+
|
| 719 |
+
def build_processing_tree(run_dir: Path, max_entries: int = 600) -> list[str]:
|
| 720 |
+
entries = []
|
| 721 |
+
for path in sorted(run_dir.rglob("*")):
|
| 722 |
+
rel = path.relative_to(run_dir)
|
| 723 |
+
if len(entries) >= max_entries:
|
| 724 |
+
entries.append("... [truncated]")
|
| 725 |
+
break
|
| 726 |
+
if path.is_dir():
|
| 727 |
+
entries.append(f"{rel}/")
|
| 728 |
+
else:
|
| 729 |
+
entries.append(f"{rel}\t{path.stat().st_size} bytes")
|
| 730 |
+
return entries
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
def collect_trace_path_evidence(run_dir: Path) -> dict:
|
| 734 |
+
"""Collect only folders/file paths from the run, matching the paper's trace input."""
|
| 735 |
+
execution_log = run_dir / "execution_log.txt"
|
| 736 |
+
path_mentions = []
|
| 737 |
+
if execution_log.exists():
|
| 738 |
+
text = execution_log.read_text(encoding="utf-8", errors="ignore")
|
| 739 |
+
for token in text.replace('"', " ").replace("'", " ").split():
|
| 740 |
+
if token.startswith("/") and ("/bioagent-bench/" in token or "/bioagent-bench-runs/" in token):
|
| 741 |
+
cleaned = token.rstrip("),.;:<>")
|
| 742 |
+
if cleaned not in path_mentions:
|
| 743 |
+
path_mentions.append(cleaned)
|
| 744 |
+
if len(path_mentions) >= 250:
|
| 745 |
+
break
|
| 746 |
+
return {
|
| 747 |
+
"processing_tree": build_processing_tree(run_dir),
|
| 748 |
+
"path_mentions_from_trace": path_mentions,
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
|
| 752 |
+
def build_artifact_metrics(task_id: str, run_dir: Path, dataset_root: Path) -> list[dict]:
|
| 753 |
+
config = TASK_CONFIGS[task_id]
|
| 754 |
+
truth_dir = dataset_root / task_id / "results"
|
| 755 |
+
artifacts = []
|
| 756 |
+
for truth_name, result_name in zip(config["truth_files"], config["result_files"], strict=False):
|
| 757 |
+
truth_path = truth_dir / truth_name
|
| 758 |
+
prediction_path = locate_prediction_file(task_id, run_dir, result_name)
|
| 759 |
+
entry = {
|
| 760 |
+
"truth_file": str(truth_path),
|
| 761 |
+
"prediction_file": str(prediction_path) if prediction_path else None,
|
| 762 |
+
"prediction_exists": bool(prediction_path and prediction_path.exists()),
|
| 763 |
+
}
|
| 764 |
+
if not prediction_path or not prediction_path.exists() or not truth_path.exists():
|
| 765 |
+
entry["metrics"] = {"error": "missing prediction or truth file"}
|
| 766 |
+
artifacts.append(entry)
|
| 767 |
+
continue
|
| 768 |
+
if config.get("vcf"):
|
| 769 |
+
entry["metrics"] = compare_vcf(prediction_path, truth_path, task_id, dataset_root)
|
| 770 |
+
else:
|
| 771 |
+
pred_rows = load_prediction_rows(task_id, truth_name, prediction_path)
|
| 772 |
+
truth_rows = load_prediction_rows(task_id, truth_name, truth_path)
|
| 773 |
+
entry["metrics"] = compare_table_rows(task_id, truth_name, pred_rows, truth_rows)
|
| 774 |
+
artifacts.append(entry)
|
| 775 |
+
return artifacts
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
def result_artifact_summaries(task_id: str, run_dir: Path, dataset_root: Path) -> tuple[list[dict], list[dict]]:
|
| 779 |
+
config = TASK_CONFIGS[task_id]
|
| 780 |
+
truth_dir = dataset_root / task_id / "results"
|
| 781 |
+
result_summaries = []
|
| 782 |
+
truth_summaries = []
|
| 783 |
+
for truth_name, result_name in zip(config["truth_files"], config["result_files"], strict=False):
|
| 784 |
+
prediction_path = locate_prediction_file(task_id, run_dir, result_name)
|
| 785 |
+
truth_path = truth_dir / truth_name
|
| 786 |
+
result_summaries.append(
|
| 787 |
+
summarize_file_for_judge(task_id, prediction_path) if prediction_path else {"path": result_name, "exists": False}
|
| 788 |
+
)
|
| 789 |
+
truth_summaries.append(summarize_file_for_judge(task_id, truth_path))
|
| 790 |
+
return result_summaries, truth_summaries
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
def infer_rule_steps(task_id: str, artifacts: list[dict], trace_evidence: dict) -> tuple[int, int, list[str]]:
|
| 794 |
+
config = TASK_CONFIGS[task_id]
|
| 795 |
+
expected_steps = config["pipeline_steps"]
|
| 796 |
+
total = len(expected_steps)
|
| 797 |
+
haystack = "\n".join(trace_evidence["processing_tree"] + trace_evidence["path_mentions_from_trace"]).lower()
|
| 798 |
+
completed = 0
|
| 799 |
+
evidence = []
|
| 800 |
+
keyword_sets = {
|
| 801 |
+
"inspect": ["task_query", "run_metadata", "data"],
|
| 802 |
+
"metadata": ["metadata", "counts"],
|
| 803 |
+
"differential": ["deseq", "de_results", "differential", "log2fold"],
|
| 804 |
+
"enrichment": ["kegg", "enrichment", "pathway"],
|
| 805 |
+
"protein": ["prodigal", ".faa", "protein"],
|
| 806 |
+
"ortholog": ["orthofinder", "mmseqs", "diamond", "cluster"],
|
| 807 |
+
"variant": [".vcf", "bcftools", "variant"],
|
| 808 |
+
"align": [".bam", ".sam", "bwa", "star", "aligned"],
|
| 809 |
+
"count": ["feature_counts", "counts", "abundance"],
|
| 810 |
+
"classify": ["kraken", "kaiju", "classification", "report"],
|
| 811 |
+
"assemble": ["spades", "megahit", "contigs", "assembly"],
|
| 812 |
+
"single-cell": ["matrix", "scanpy", "cluster", "umap", "markers"],
|
| 813 |
+
"index": ["index", ".idx", "star_index"],
|
| 814 |
+
"final": [artifact["prediction_file"] or "" for artifact in artifacts],
|
| 815 |
+
}
|
| 816 |
+
for step in expected_steps:
|
| 817 |
+
step_l = step.lower()
|
| 818 |
+
if (
|
| 819 |
+
("write" in step_l or "final" in step_l)
|
| 820 |
+
and any(name in step_l for name in ["csv", "tsv", "vcf", "predicted"])
|
| 821 |
+
and not any(artifact.get("prediction_exists") for artifact in artifacts)
|
| 822 |
+
):
|
| 823 |
+
continue
|
| 824 |
+
if (
|
| 825 |
+
("write" in step_l or "final" in step_l)
|
| 826 |
+
and any(name in step_l for name in ["csv", "tsv", "vcf", "predicted"])
|
| 827 |
+
and all(artifact.get("prediction_exists") for artifact in artifacts)
|
| 828 |
+
):
|
| 829 |
+
completed += 1
|
| 830 |
+
evidence.append(step)
|
| 831 |
+
continue
|
| 832 |
+
keys = []
|
| 833 |
+
for concept, words in keyword_sets.items():
|
| 834 |
+
if concept in step_l or any(word in step_l for word in words[:2]):
|
| 835 |
+
keys.extend(words)
|
| 836 |
+
if not keys:
|
| 837 |
+
generic = {"and", "or", "the", "to", "a", "an", "write", "final", "requested"}
|
| 838 |
+
keys = [token for token in step_l.replace(",", " ").split() if len(token) > 3 and token not in generic][:4]
|
| 839 |
+
if any(key and key.lower() in haystack for key in keys):
|
| 840 |
+
completed += 1
|
| 841 |
+
evidence.append(step)
|
| 842 |
+
|
| 843 |
+
# Final artifacts are the strongest evidence for final-result step.
|
| 844 |
+
final_files = [artifact for artifact in artifacts if artifact.get("prediction_exists")]
|
| 845 |
+
if final_files and completed < total:
|
| 846 |
+
completed = max(completed, total - 1)
|
| 847 |
+
evidence.append("final artifact(s) exist; inferred most upstream steps completed")
|
| 848 |
+
return min(completed, total), total, evidence
|
| 849 |
+
|
| 850 |
+
|
| 851 |
+
def rule_trial_judge(task_id: str, artifacts: list[dict], trace_evidence: dict) -> dict:
|
| 852 |
+
steps_completed, steps_to_completion, evidence = infer_rule_steps(task_id, artifacts, trace_evidence)
|
| 853 |
+
final_result_reached = all(artifact.get("prediction_exists") for artifact in artifacts)
|
| 854 |
+
metric_scores = []
|
| 855 |
+
giab_f1 = None
|
| 856 |
+
for artifact in artifacts:
|
| 857 |
+
metrics = artifact.get("metrics", {})
|
| 858 |
+
if "match_f1" in metrics:
|
| 859 |
+
metric_scores.append(metrics["match_f1"])
|
| 860 |
+
elif "f1" in metrics:
|
| 861 |
+
metric_scores.append(metrics["f1"])
|
| 862 |
+
if task_id == "giab":
|
| 863 |
+
giab_f1 = metrics["f1"]
|
| 864 |
+
elif "key_f1" in metrics:
|
| 865 |
+
metric_scores.append(metrics["key_f1"])
|
| 866 |
+
mean_metric_score = mean(metric_scores) if metric_scores else 0.0
|
| 867 |
+
|
| 868 |
+
# Paper-style correctness flag is task/rubric specific. In rule mode, use a conservative helper.
|
| 869 |
+
if TASK_CONFIGS[task_id].get("verifiable"):
|
| 870 |
+
threshold = 0.5 if task_id == "giab" else 0.8
|
| 871 |
+
results_match = final_result_reached and mean_metric_score >= threshold
|
| 872 |
+
else:
|
| 873 |
+
results_match = final_result_reached and steps_completed == steps_to_completion
|
| 874 |
+
|
| 875 |
+
notes = (
|
| 876 |
+
"Rule-mode approximation of the paper's LLM grader. "
|
| 877 |
+
f"Evidence-backed steps: {', '.join(evidence[:8]) if evidence else 'none detected'}."
|
| 878 |
+
)
|
| 879 |
+
return {
|
| 880 |
+
"steps_completed": steps_completed,
|
| 881 |
+
"steps_to_completion": steps_to_completion,
|
| 882 |
+
"final_result_reached": final_result_reached,
|
| 883 |
+
"notes": notes,
|
| 884 |
+
"results_match": results_match,
|
| 885 |
+
"f1_score": giab_f1,
|
| 886 |
+
}
|
| 887 |
+
|
| 888 |
+
|
| 889 |
+
def resolve_judge_client(provider: str, model: str | None, base_url: str | None, api_key: str | None):
|
| 890 |
+
try:
|
| 891 |
+
from openai import OpenAI
|
| 892 |
+
except ImportError as exc:
|
| 893 |
+
raise ImportError("openai package is required for LLM judging.") from exc
|
| 894 |
+
|
| 895 |
+
provider = provider.lower().strip()
|
| 896 |
+
if provider == "deepseek":
|
| 897 |
+
resolved_model = model or os.getenv("DEEPSEEK_MODEL_NAME", "deepseek-chat")
|
| 898 |
+
resolved_base_url = base_url or os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1")
|
| 899 |
+
resolved_api_key = api_key or os.getenv("DEEPSEEK_API_KEY")
|
| 900 |
+
if not resolved_api_key:
|
| 901 |
+
raise RuntimeError("Missing DEEPSEEK_API_KEY for DeepSeek LLM judge.")
|
| 902 |
+
return OpenAI(api_key=resolved_api_key, base_url=resolved_base_url), resolved_model, provider
|
| 903 |
+
if provider == "openai":
|
| 904 |
+
resolved_model = model or os.getenv("OPENAI_MODEL_NAME", "gpt-5.1")
|
| 905 |
+
resolved_api_key = api_key or os.getenv("OPENAI_API_KEY")
|
| 906 |
+
if not resolved_api_key:
|
| 907 |
+
raise RuntimeError("Missing OPENAI_API_KEY for OpenAI LLM judge.")
|
| 908 |
+
return OpenAI(api_key=resolved_api_key), resolved_model, provider
|
| 909 |
+
raise ValueError(f"Unsupported LLM judge provider: {provider}")
|
| 910 |
+
|
| 911 |
+
|
| 912 |
+
def extract_json_object(text: str) -> dict:
|
| 913 |
+
text = text.strip()
|
| 914 |
+
if not text:
|
| 915 |
+
raise ValueError("Empty LLM judge response.")
|
| 916 |
+
try:
|
| 917 |
+
return json.loads(text)
|
| 918 |
+
except json.JSONDecodeError:
|
| 919 |
+
start = text.find("{")
|
| 920 |
+
end = text.rfind("}")
|
| 921 |
+
if start != -1 and end != -1 and end > start:
|
| 922 |
+
return json.loads(text[start : end + 1])
|
| 923 |
+
raise
|
| 924 |
+
|
| 925 |
+
|
| 926 |
+
def llm_trial_judge(
|
| 927 |
+
task_id: str,
|
| 928 |
+
task_prompt: str,
|
| 929 |
+
input_data_path: Path,
|
| 930 |
+
reference_data_path: Path,
|
| 931 |
+
result_summaries: list[dict],
|
| 932 |
+
truth_summaries: list[dict],
|
| 933 |
+
trace_evidence: dict,
|
| 934 |
+
artifacts: list[dict],
|
| 935 |
+
provider: str,
|
| 936 |
+
model: str | None,
|
| 937 |
+
base_url: str | None,
|
| 938 |
+
api_key: str | None,
|
| 939 |
+
) -> dict:
|
| 940 |
+
client, resolved_model, resolved_provider = resolve_judge_client(provider, model, base_url, api_key)
|
| 941 |
+
config = TASK_CONFIGS[task_id]
|
| 942 |
+
compact_tree = "\n".join(trace_evidence["processing_tree"][:450])
|
| 943 |
+
path_mentions = "\n".join(trace_evidence["path_mentions_from_trace"][:180])
|
| 944 |
+
|
| 945 |
+
user_prompt = f"""
|
| 946 |
+
You are a strict, impartial Bioinformatics Pipeline Judge. Evaluate an LLM agent's work for executing a bioinformatics pipeline instructed by the prompt.
|
| 947 |
+
|
| 948 |
+
Inputs:
|
| 949 |
+
1. Input data: {input_data_path}
|
| 950 |
+
2. Reference data: {reference_data_path if reference_data_path.exists() else "<none>"}
|
| 951 |
+
3. Processing tree:
|
| 952 |
+
{compact_tree}
|
| 953 |
+
|
| 954 |
+
Trace path mentions:
|
| 955 |
+
{path_mentions or "<none>"}
|
| 956 |
+
|
| 957 |
+
4. Results:
|
| 958 |
+
{json.dumps(result_summaries, ensure_ascii=False, indent=2)}
|
| 959 |
+
|
| 960 |
+
5. Truth:
|
| 961 |
+
{json.dumps(truth_summaries, ensure_ascii=False, indent=2)}
|
| 962 |
+
|
| 963 |
+
6. Prompt:
|
| 964 |
+
{task_prompt}
|
| 965 |
+
|
| 966 |
+
Task expected pipeline steps:
|
| 967 |
+
{json.dumps(config["pipeline_steps"], ensure_ascii=False, indent=2)}
|
| 968 |
+
|
| 969 |
+
Auxiliary rule-based artifact metrics:
|
| 970 |
+
{json.dumps(artifacts, ensure_ascii=False, indent=2)}
|
| 971 |
+
|
| 972 |
+
Evaluation rules:
|
| 973 |
+
- Prioritize evaluation of pipeline completion over exact numerical correctness.
|
| 974 |
+
- If gene names are of different naming conventions, the result can still be valid.
|
| 975 |
+
- Estimate steps_to_completion from bioinformatics-relevant steps required for this task.
|
| 976 |
+
- Count upstream steps only if their expected artifacts are present.
|
| 977 |
+
- Do not count placeholders or mock completion as completed steps.
|
| 978 |
+
- For p-values, log-fold values, abundances, or other statistics, check that plausible prior steps generated them.
|
| 979 |
+
- Task-specific results-match guidance: {config.get("results_match_guidance", "")}
|
| 980 |
+
|
| 981 |
+
Return JSON only with exactly these fields:
|
| 982 |
+
- steps_completed: integer
|
| 983 |
+
- steps_to_completion: integer
|
| 984 |
+
- final_result_reached: boolean
|
| 985 |
+
- notes: string
|
| 986 |
+
- results_match: boolean
|
| 987 |
+
- f1_score: number or null; only use a real F1 for GIAB/variant concordance, otherwise null
|
| 988 |
+
""".strip()
|
| 989 |
+
|
| 990 |
+
response = client.chat.completions.create(
|
| 991 |
+
model=resolved_model,
|
| 992 |
+
temperature=0,
|
| 993 |
+
response_format={"type": "json_object"},
|
| 994 |
+
messages=[
|
| 995 |
+
{"role": "system", "content": "Return strict JSON only. Follow the BioAgent Bench EvaluationResults schema."},
|
| 996 |
+
{"role": "user", "content": user_prompt},
|
| 997 |
+
],
|
| 998 |
+
)
|
| 999 |
+
parsed = extract_json_object(response.choices[0].message.content or "{}")
|
| 1000 |
+
parsed["provider"] = resolved_provider
|
| 1001 |
+
parsed["model"] = resolved_model
|
| 1002 |
+
return parsed
|
| 1003 |
+
|
| 1004 |
+
|
| 1005 |
+
def normalize_evaluation_results(raw: dict) -> dict:
|
| 1006 |
+
steps_completed = int(raw.get("steps_completed", 0) or 0)
|
| 1007 |
+
steps_to_completion = int(raw.get("steps_to_completion", 0) or 0)
|
| 1008 |
+
final_result_reached = bool(raw.get("final_result_reached", False))
|
| 1009 |
+
results_match = bool(raw.get("results_match", False))
|
| 1010 |
+
f1_score = raw.get("f1_score")
|
| 1011 |
+
if f1_score is not None:
|
| 1012 |
+
try:
|
| 1013 |
+
f1_score = float(f1_score)
|
| 1014 |
+
except Exception:
|
| 1015 |
+
f1_score = None
|
| 1016 |
+
completion_rate = steps_completed / steps_to_completion if steps_to_completion else 0.0
|
| 1017 |
+
return {
|
| 1018 |
+
"steps_completed": steps_completed,
|
| 1019 |
+
"steps_to_completion": steps_to_completion,
|
| 1020 |
+
"completion_rate": completion_rate,
|
| 1021 |
+
"final_result_reached": final_result_reached,
|
| 1022 |
+
"results_match": results_match,
|
| 1023 |
+
"f1_score": f1_score,
|
| 1024 |
+
"notes": str(raw.get("notes", "")),
|
| 1025 |
+
}
|
| 1026 |
+
|
| 1027 |
+
|
| 1028 |
+
def evaluate_task(
|
| 1029 |
+
task_id: str,
|
| 1030 |
+
run_dir: Path,
|
| 1031 |
+
dataset_root: Path,
|
| 1032 |
+
task_metadata: dict[str, dict],
|
| 1033 |
+
judge_mode: str = "rule",
|
| 1034 |
+
llm_provider: str = "deepseek",
|
| 1035 |
+
llm_model: str | None = None,
|
| 1036 |
+
llm_base_url: str | None = None,
|
| 1037 |
+
llm_api_key: str | None = None,
|
| 1038 |
+
) -> dict:
|
| 1039 |
+
task_dir = dataset_root / task_id
|
| 1040 |
+
run_metadata = load_run_metadata(run_dir)
|
| 1041 |
+
task_prompt = (
|
| 1042 |
+
run_metadata.get("benchmark_task_context", {}).get("task_prompt")
|
| 1043 |
+
or task_metadata.get(task_id, {}).get("task_prompt")
|
| 1044 |
+
or run_metadata.get("query")
|
| 1045 |
+
or ""
|
| 1046 |
+
)
|
| 1047 |
+
trace_evidence = collect_trace_path_evidence(run_dir)
|
| 1048 |
+
artifacts = build_artifact_metrics(task_id, run_dir, dataset_root)
|
| 1049 |
+
result_summaries, truth_summaries = result_artifact_summaries(task_id, run_dir, dataset_root)
|
| 1050 |
+
|
| 1051 |
+
rule_raw = rule_trial_judge(task_id, artifacts, trace_evidence)
|
| 1052 |
+
rule_result = normalize_evaluation_results(rule_raw)
|
| 1053 |
+
|
| 1054 |
+
selected_raw = rule_raw
|
| 1055 |
+
llm_result = None
|
| 1056 |
+
if judge_mode in {"llm", "both"}:
|
| 1057 |
+
selected_raw = llm_trial_judge(
|
| 1058 |
+
task_id=task_id,
|
| 1059 |
+
task_prompt=task_prompt,
|
| 1060 |
+
input_data_path=task_dir / "data",
|
| 1061 |
+
reference_data_path=task_dir / "reference",
|
| 1062 |
+
result_summaries=result_summaries,
|
| 1063 |
+
truth_summaries=truth_summaries,
|
| 1064 |
+
trace_evidence=trace_evidence,
|
| 1065 |
+
artifacts=artifacts,
|
| 1066 |
+
provider=llm_provider,
|
| 1067 |
+
model=llm_model,
|
| 1068 |
+
base_url=llm_base_url,
|
| 1069 |
+
api_key=llm_api_key,
|
| 1070 |
+
)
|
| 1071 |
+
llm_result = normalize_evaluation_results(selected_raw)
|
| 1072 |
+
|
| 1073 |
+
selected = rule_result if judge_mode == "rule" else llm_result
|
| 1074 |
+
assert selected is not None
|
| 1075 |
+
return {
|
| 1076 |
+
"task_id": task_id,
|
| 1077 |
+
"run_dir": str(run_dir),
|
| 1078 |
+
"evaluated_at_utc": utc_timestamp(),
|
| 1079 |
+
"judge_mode": judge_mode,
|
| 1080 |
+
"evaluation_results": selected,
|
| 1081 |
+
"overall_score": selected["completion_rate"],
|
| 1082 |
+
"score_definition": (
|
| 1083 |
+
"BioAgent Bench-style completion rate: steps_completed / steps_to_completion. "
|
| 1084 |
+
"results_match and f1_score are reported separately."
|
| 1085 |
+
),
|
| 1086 |
+
"rule_evaluation_results": rule_result,
|
| 1087 |
+
"llm_evaluation_results": llm_result,
|
| 1088 |
+
"artifacts": artifacts,
|
| 1089 |
+
"result_summaries": result_summaries,
|
| 1090 |
+
"truth_summaries": truth_summaries,
|
| 1091 |
+
"trace_evidence": trace_evidence,
|
| 1092 |
+
"paper_alignment": {
|
| 1093 |
+
"grader_inputs": [
|
| 1094 |
+
"input data path",
|
| 1095 |
+
"reference data path",
|
| 1096 |
+
"expected outcome/truth as text summary",
|
| 1097 |
+
"agent outcome as text summary",
|
| 1098 |
+
"agent trace represented as folders/file paths",
|
| 1099 |
+
"task prompt and grading logic",
|
| 1100 |
+
],
|
| 1101 |
+
"grader_outputs": [
|
| 1102 |
+
"steps_completed",
|
| 1103 |
+
"steps_to_completion",
|
| 1104 |
+
"final_result_reached",
|
| 1105 |
+
"notes",
|
| 1106 |
+
"results_match",
|
| 1107 |
+
"f1_score",
|
| 1108 |
+
],
|
| 1109 |
+
},
|
| 1110 |
+
}
|
| 1111 |
+
|
| 1112 |
+
|
| 1113 |
+
def parse_args() -> argparse.Namespace:
|
| 1114 |
+
parser = argparse.ArgumentParser(description="Evaluate Hypo_Bio_OS outputs on bioagent-bench.")
|
| 1115 |
+
parser.add_argument("--task", action="append", help="Task ID to evaluate. Can be provided multiple times.")
|
| 1116 |
+
parser.add_argument("--all", action="store_true", help="Evaluate all tasks that have run directories.")
|
| 1117 |
+
parser.add_argument("--run-dir", help="Specific run directory for single-task evaluation.")
|
| 1118 |
+
parser.add_argument("--runs-root", default=str(DEFAULT_RUNS_ROOT))
|
| 1119 |
+
parser.add_argument("--dataset-root", default=str(DATASET_ROOT))
|
| 1120 |
+
parser.add_argument("--metadata", default=str(METADATA_PATH))
|
| 1121 |
+
parser.add_argument("--output", default=None, help="Where to save the evaluation JSON.")
|
| 1122 |
+
parser.add_argument(
|
| 1123 |
+
"--judge-mode",
|
| 1124 |
+
choices=["rule", "llm", "both"],
|
| 1125 |
+
default="rule",
|
| 1126 |
+
help="Use local paper-shaped heuristic grading, LLM grading, or both.",
|
| 1127 |
+
)
|
| 1128 |
+
parser.add_argument(
|
| 1129 |
+
"--llm-provider",
|
| 1130 |
+
choices=["openai", "deepseek"],
|
| 1131 |
+
default=os.getenv("BIOAGENT_BENCH_JUDGE_PROVIDER", "deepseek"),
|
| 1132 |
+
help="OpenAI-compatible backend provider for the LLM judge.",
|
| 1133 |
+
)
|
| 1134 |
+
parser.add_argument("--llm-model", default=None, help="Override model name for the LLM judge.")
|
| 1135 |
+
parser.add_argument("--llm-base-url", default=None, help="Override base URL for the LLM judge.")
|
| 1136 |
+
parser.add_argument("--llm-api-key", default=None, help="Override API key for the LLM judge.")
|
| 1137 |
+
return parser.parse_args()
|
| 1138 |
+
|
| 1139 |
+
|
| 1140 |
+
def main() -> int:
|
| 1141 |
+
args = parse_args()
|
| 1142 |
+
dataset_root = Path(args.dataset_root)
|
| 1143 |
+
runs_root = Path(args.runs_root)
|
| 1144 |
+
task_metadata = load_task_metadata(Path(args.metadata))
|
| 1145 |
+
|
| 1146 |
+
if args.all:
|
| 1147 |
+
task_ids = list(TASK_CONFIGS)
|
| 1148 |
+
else:
|
| 1149 |
+
task_ids = args.task or []
|
| 1150 |
+
if not task_ids:
|
| 1151 |
+
raise SystemExit("Provide --task <task_id> or use --all.")
|
| 1152 |
+
|
| 1153 |
+
results = []
|
| 1154 |
+
for task_id in task_ids:
|
| 1155 |
+
if task_id not in TASK_CONFIGS:
|
| 1156 |
+
raise SystemExit(f"Unsupported task ID: {task_id}")
|
| 1157 |
+
if args.run_dir and len(task_ids) == 1:
|
| 1158 |
+
run_dir = Path(args.run_dir)
|
| 1159 |
+
else:
|
| 1160 |
+
run_dir = infer_latest_run_dir(task_id, runs_root)
|
| 1161 |
+
if run_dir is None:
|
| 1162 |
+
print(f"Skipping {task_id}: no run directory found under {runs_root}")
|
| 1163 |
+
continue
|
| 1164 |
+
result = evaluate_task(
|
| 1165 |
+
task_id=task_id,
|
| 1166 |
+
run_dir=run_dir,
|
| 1167 |
+
dataset_root=dataset_root,
|
| 1168 |
+
task_metadata=task_metadata,
|
| 1169 |
+
judge_mode=args.judge_mode,
|
| 1170 |
+
llm_provider=args.llm_provider,
|
| 1171 |
+
llm_model=args.llm_model,
|
| 1172 |
+
llm_base_url=args.llm_base_url,
|
| 1173 |
+
llm_api_key=args.llm_api_key,
|
| 1174 |
+
)
|
| 1175 |
+
results.append(result)
|
| 1176 |
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
| 1177 |
+
|
| 1178 |
+
completion_rates = [item["evaluation_results"]["completion_rate"] for item in results]
|
| 1179 |
+
payload = {
|
| 1180 |
+
"evaluated_at_utc": utc_timestamp(),
|
| 1181 |
+
"judge_mode": args.judge_mode,
|
| 1182 |
+
"primary_metric": "completion_rate",
|
| 1183 |
+
"mean_completion_rate": mean(completion_rates) if completion_rates else 0.0,
|
| 1184 |
+
"results": results,
|
| 1185 |
+
}
|
| 1186 |
+
if args.output:
|
| 1187 |
+
output_path = Path(args.output)
|
| 1188 |
+
else:
|
| 1189 |
+
task_eval_dir = runs_root / "evaluation_results"
|
| 1190 |
+
task_eval_dir.mkdir(parents=True, exist_ok=True)
|
| 1191 |
+
output_path = task_eval_dir / f"evaluation_{task_id}.json"
|
| 1192 |
+
output_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
| 1193 |
+
print(f"Saved evaluation summary to: {output_path}")
|
| 1194 |
+
return 0
|
| 1195 |
+
|
| 1196 |
+
|
| 1197 |
+
if __name__ == "__main__":
|
| 1198 |
+
raise SystemExit(main())
|
experiments/bioagent_bench/gold_tools.json
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"alzheimer-mouse": {
|
| 3 |
+
"task_name": "Alzheimer Mouse Models: Comparative Pathway Analysis",
|
| 4 |
+
"gold_tools": [
|
| 5 |
+
"DESeq2",
|
| 6 |
+
"edgeR",
|
| 7 |
+
"limma",
|
| 8 |
+
"clusterProfiler",
|
| 9 |
+
"KEGGREST"
|
| 10 |
+
],
|
| 11 |
+
"gold_servers": [
|
| 12 |
+
"mcp_bioconductor-deseq2",
|
| 13 |
+
"mcp_bioconductor-edger",
|
| 14 |
+
"mcp_bioconductor-limma",
|
| 15 |
+
"mcp_bioconductor-clusterprofiler",
|
| 16 |
+
"mcp_bioconductor-keggrest"
|
| 17 |
+
],
|
| 18 |
+
"workflow": [
|
| 19 |
+
{
|
| 20 |
+
"name": "load_mouse_count_tables",
|
| 21 |
+
"tool_patterns": [
|
| 22 |
+
"read",
|
| 23 |
+
"csv",
|
| 24 |
+
"count",
|
| 25 |
+
"pandas",
|
| 26 |
+
"data.table"
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"name": "normalize_and_model_expression",
|
| 31 |
+
"tool_patterns": [
|
| 32 |
+
"deseq",
|
| 33 |
+
"edger",
|
| 34 |
+
"limma",
|
| 35 |
+
"normalize"
|
| 36 |
+
]
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"name": "run_kegg_pathway_enrichment",
|
| 40 |
+
"tool_patterns": [
|
| 41 |
+
"clusterprofiler",
|
| 42 |
+
"kegg",
|
| 43 |
+
"enrich",
|
| 44 |
+
"pathway"
|
| 45 |
+
]
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"name": "compare_shared_pathways",
|
| 49 |
+
"tool_patterns": [
|
| 50 |
+
"merge",
|
| 51 |
+
"join",
|
| 52 |
+
"shared",
|
| 53 |
+
"csv",
|
| 54 |
+
"pandas"
|
| 55 |
+
]
|
| 56 |
+
}
|
| 57 |
+
]
|
| 58 |
+
},
|
| 59 |
+
"comparative-genomics": {
|
| 60 |
+
"task_name": "Comparative Genomics: Co-evolving Gene Clusters",
|
| 61 |
+
"gold_tools": [
|
| 62 |
+
"OrthoFinder",
|
| 63 |
+
"BLAST",
|
| 64 |
+
"DIAMOND",
|
| 65 |
+
"gffread",
|
| 66 |
+
"seqkit"
|
| 67 |
+
],
|
| 68 |
+
"gold_servers": [
|
| 69 |
+
"mcp_orthofinder",
|
| 70 |
+
"mcp_blast",
|
| 71 |
+
"mcp_diamond",
|
| 72 |
+
"mcp_gffread",
|
| 73 |
+
"mcp_seqkit"
|
| 74 |
+
],
|
| 75 |
+
"workflow": [
|
| 76 |
+
{
|
| 77 |
+
"name": "extract_or_prepare_proteomes",
|
| 78 |
+
"tool_patterns": [
|
| 79 |
+
"gffread",
|
| 80 |
+
"seqkit",
|
| 81 |
+
"fasta",
|
| 82 |
+
"faa"
|
| 83 |
+
]
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"name": "infer_orthogroups",
|
| 87 |
+
"tool_patterns": [
|
| 88 |
+
"orthofinder",
|
| 89 |
+
"orthogroup",
|
| 90 |
+
"blast",
|
| 91 |
+
"diamond"
|
| 92 |
+
]
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"name": "filter_core_coding_clusters",
|
| 96 |
+
"tool_patterns": [
|
| 97 |
+
"core",
|
| 98 |
+
"present",
|
| 99 |
+
"coding",
|
| 100 |
+
"orthogroup",
|
| 101 |
+
"pandas"
|
| 102 |
+
]
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"name": "assign_consensus_annotations",
|
| 106 |
+
"tool_patterns": [
|
| 107 |
+
"blast",
|
| 108 |
+
"diamond",
|
| 109 |
+
"kegg",
|
| 110 |
+
"annotation",
|
| 111 |
+
"consensus"
|
| 112 |
+
]
|
| 113 |
+
}
|
| 114 |
+
]
|
| 115 |
+
},
|
| 116 |
+
"cystic-fibrosis": {
|
| 117 |
+
"task_name": "Cystic Fibrosis Mendelian Variant Identification",
|
| 118 |
+
"gold_tools": [
|
| 119 |
+
"BWA",
|
| 120 |
+
"samtools",
|
| 121 |
+
"bcftools",
|
| 122 |
+
"GATK",
|
| 123 |
+
"SnpEff",
|
| 124 |
+
"SnpSift"
|
| 125 |
+
],
|
| 126 |
+
"gold_servers": [
|
| 127 |
+
"mcp_bwa",
|
| 128 |
+
"mcp_samtools",
|
| 129 |
+
"mcp_bcftools",
|
| 130 |
+
"mcp_gatk4",
|
| 131 |
+
"mcp_snpeff",
|
| 132 |
+
"mcp_snpsift"
|
| 133 |
+
],
|
| 134 |
+
"workflow": [
|
| 135 |
+
{
|
| 136 |
+
"name": "prepare_family_variant_data",
|
| 137 |
+
"tool_patterns": [
|
| 138 |
+
"bwa",
|
| 139 |
+
"samtools",
|
| 140 |
+
"bam",
|
| 141 |
+
"vcf"
|
| 142 |
+
]
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"name": "call_or_filter_variants",
|
| 146 |
+
"tool_patterns": [
|
| 147 |
+
"bcftools",
|
| 148 |
+
"gatk",
|
| 149 |
+
"variant",
|
| 150 |
+
"filter"
|
| 151 |
+
]
|
| 152 |
+
},
|
| 153 |
+
{
|
| 154 |
+
"name": "annotate_with_effect_and_clinvar",
|
| 155 |
+
"tool_patterns": [
|
| 156 |
+
"snpeff",
|
| 157 |
+
"snpsift",
|
| 158 |
+
"clinvar",
|
| 159 |
+
"annotation"
|
| 160 |
+
]
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"name": "apply_recessive_family_filter",
|
| 164 |
+
"tool_patterns": [
|
| 165 |
+
"recessive",
|
| 166 |
+
"affected",
|
| 167 |
+
"family",
|
| 168 |
+
"genotype",
|
| 169 |
+
"pandas"
|
| 170 |
+
]
|
| 171 |
+
}
|
| 172 |
+
]
|
| 173 |
+
},
|
| 174 |
+
"deseq": {
|
| 175 |
+
"task_name": "RNA-Seq Differential Expression (DESeq2)",
|
| 176 |
+
"gold_tools": [
|
| 177 |
+
"STAR",
|
| 178 |
+
"HISAT2",
|
| 179 |
+
"featureCounts",
|
| 180 |
+
"Subread",
|
| 181 |
+
"DESeq2",
|
| 182 |
+
"tximport"
|
| 183 |
+
],
|
| 184 |
+
"gold_servers": [
|
| 185 |
+
"mcp_star",
|
| 186 |
+
"mcp_hisat2",
|
| 187 |
+
"mcp_subread",
|
| 188 |
+
"mcp_bioconductor-rsubread",
|
| 189 |
+
"mcp_bioconductor-deseq2",
|
| 190 |
+
"mcp_bioconductor-tximport"
|
| 191 |
+
],
|
| 192 |
+
"workflow": [
|
| 193 |
+
{
|
| 194 |
+
"name": "align_rnaseq_reads",
|
| 195 |
+
"tool_patterns": [
|
| 196 |
+
"star",
|
| 197 |
+
"hisat2",
|
| 198 |
+
"align",
|
| 199 |
+
"bam"
|
| 200 |
+
]
|
| 201 |
+
},
|
| 202 |
+
{
|
| 203 |
+
"name": "count_gene_features",
|
| 204 |
+
"tool_patterns": [
|
| 205 |
+
"featurecounts",
|
| 206 |
+
"subread",
|
| 207 |
+
"count"
|
| 208 |
+
]
|
| 209 |
+
},
|
| 210 |
+
{
|
| 211 |
+
"name": "differential_expression",
|
| 212 |
+
"tool_patterns": [
|
| 213 |
+
"deseq",
|
| 214 |
+
"edger",
|
| 215 |
+
"limma",
|
| 216 |
+
"log2foldchange"
|
| 217 |
+
]
|
| 218 |
+
},
|
| 219 |
+
{
|
| 220 |
+
"name": "write_de_results_csv",
|
| 221 |
+
"tool_patterns": [
|
| 222 |
+
"csv",
|
| 223 |
+
"gene_id",
|
| 224 |
+
"padj",
|
| 225 |
+
"pvalue"
|
| 226 |
+
]
|
| 227 |
+
}
|
| 228 |
+
]
|
| 229 |
+
},
|
| 230 |
+
"evolution": {
|
| 231 |
+
"task_name": "Experimental Evolution Variant Calling (E. coli)",
|
| 232 |
+
"gold_tools": [
|
| 233 |
+
"BWA",
|
| 234 |
+
"samtools",
|
| 235 |
+
"bcftools",
|
| 236 |
+
"FreeBayes",
|
| 237 |
+
"SnpEff",
|
| 238 |
+
"SnpSift"
|
| 239 |
+
],
|
| 240 |
+
"gold_servers": [
|
| 241 |
+
"mcp_bwa",
|
| 242 |
+
"mcp_samtools",
|
| 243 |
+
"mcp_bcftools",
|
| 244 |
+
"mcp_freebayes",
|
| 245 |
+
"mcp_snpeff",
|
| 246 |
+
"mcp_snpsift"
|
| 247 |
+
],
|
| 248 |
+
"workflow": [
|
| 249 |
+
{
|
| 250 |
+
"name": "align_evolved_and_ancestor_reads",
|
| 251 |
+
"tool_patterns": [
|
| 252 |
+
"bwa",
|
| 253 |
+
"samtools",
|
| 254 |
+
"align",
|
| 255 |
+
"bam"
|
| 256 |
+
]
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"name": "call_variants_per_line",
|
| 260 |
+
"tool_patterns": [
|
| 261 |
+
"bcftools",
|
| 262 |
+
"freebayes",
|
| 263 |
+
"variant",
|
| 264 |
+
"vcf"
|
| 265 |
+
]
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"name": "annotate_variant_effects",
|
| 269 |
+
"tool_patterns": [
|
| 270 |
+
"snpeff",
|
| 271 |
+
"effect",
|
| 272 |
+
"impact",
|
| 273 |
+
"moderate"
|
| 274 |
+
]
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
"name": "intersect_shared_variants",
|
| 278 |
+
"tool_patterns": [
|
| 279 |
+
"shared",
|
| 280 |
+
"intersect",
|
| 281 |
+
"evolved",
|
| 282 |
+
"pandas"
|
| 283 |
+
]
|
| 284 |
+
}
|
| 285 |
+
]
|
| 286 |
+
},
|
| 287 |
+
"giab": {
|
| 288 |
+
"task_name": "GIAB Variant Calling",
|
| 289 |
+
"gold_tools": [
|
| 290 |
+
"BWA",
|
| 291 |
+
"samtools",
|
| 292 |
+
"bcftools",
|
| 293 |
+
"GATK",
|
| 294 |
+
"FreeBayes",
|
| 295 |
+
"VariantAnnotation"
|
| 296 |
+
],
|
| 297 |
+
"gold_servers": [
|
| 298 |
+
"mcp_bwa",
|
| 299 |
+
"mcp_samtools",
|
| 300 |
+
"mcp_bcftools",
|
| 301 |
+
"mcp_gatk4",
|
| 302 |
+
"mcp_freebayes",
|
| 303 |
+
"mcp_bioconductor-variantannotation"
|
| 304 |
+
],
|
| 305 |
+
"workflow": [
|
| 306 |
+
{
|
| 307 |
+
"name": "align_exome_reads",
|
| 308 |
+
"tool_patterns": [
|
| 309 |
+
"bwa",
|
| 310 |
+
"samtools",
|
| 311 |
+
"align",
|
| 312 |
+
"bam"
|
| 313 |
+
]
|
| 314 |
+
},
|
| 315 |
+
{
|
| 316 |
+
"name": "sort_index_and_qc_bam",
|
| 317 |
+
"tool_patterns": [
|
| 318 |
+
"samtools",
|
| 319 |
+
"sort",
|
| 320 |
+
"index",
|
| 321 |
+
"flagstat"
|
| 322 |
+
]
|
| 323 |
+
},
|
| 324 |
+
{
|
| 325 |
+
"name": "call_germline_variants",
|
| 326 |
+
"tool_patterns": [
|
| 327 |
+
"gatk",
|
| 328 |
+
"bcftools",
|
| 329 |
+
"freebayes",
|
| 330 |
+
"haplotypecaller"
|
| 331 |
+
]
|
| 332 |
+
},
|
| 333 |
+
{
|
| 334 |
+
"name": "compress_and_index_vcf",
|
| 335 |
+
"tool_patterns": [
|
| 336 |
+
"vcf",
|
| 337 |
+
"bgzip",
|
| 338 |
+
"tabix",
|
| 339 |
+
"index"
|
| 340 |
+
]
|
| 341 |
+
}
|
| 342 |
+
]
|
| 343 |
+
},
|
| 344 |
+
"metagenomics": {
|
| 345 |
+
"task_name": "Metagenomics: Community Comparison (Cuatro Cienegas)",
|
| 346 |
+
"gold_tools": [
|
| 347 |
+
"Kraken2",
|
| 348 |
+
"csvtk",
|
| 349 |
+
"pandas"
|
| 350 |
+
],
|
| 351 |
+
"gold_servers": [
|
| 352 |
+
"mcp_kraken2",
|
| 353 |
+
"mcp_csvtk"
|
| 354 |
+
],
|
| 355 |
+
"workflow": [
|
| 356 |
+
{
|
| 357 |
+
"name": "classify_paired_reads",
|
| 358 |
+
"tool_patterns": [
|
| 359 |
+
"kraken2",
|
| 360 |
+
"classify",
|
| 361 |
+
"paired",
|
| 362 |
+
"fastq"
|
| 363 |
+
]
|
| 364 |
+
},
|
| 365 |
+
{
|
| 366 |
+
"name": "parse_kraken_reports",
|
| 367 |
+
"tool_patterns": [
|
| 368 |
+
"report",
|
| 369 |
+
"taxonomy",
|
| 370 |
+
"taxid",
|
| 371 |
+
"phylum"
|
| 372 |
+
]
|
| 373 |
+
},
|
| 374 |
+
{
|
| 375 |
+
"name": "compute_bacterial_phylum_relative_abundance",
|
| 376 |
+
"tool_patterns": [
|
| 377 |
+
"bacteria",
|
| 378 |
+
"phylum",
|
| 379 |
+
"relative",
|
| 380 |
+
"abundance"
|
| 381 |
+
]
|
| 382 |
+
},
|
| 383 |
+
{
|
| 384 |
+
"name": "write_phylum_abundance_csv",
|
| 385 |
+
"tool_patterns": [
|
| 386 |
+
"csv",
|
| 387 |
+
"OTU",
|
| 388 |
+
"Kingdom",
|
| 389 |
+
"Phylum",
|
| 390 |
+
"JP4D",
|
| 391 |
+
"JC1A"
|
| 392 |
+
]
|
| 393 |
+
}
|
| 394 |
+
]
|
| 395 |
+
},
|
| 396 |
+
"single-cell": {
|
| 397 |
+
"task_name": "Single-cell RNA-seq: Skeletal Muscle Exercise Response",
|
| 398 |
+
"gold_tools": [
|
| 399 |
+
"Scanpy",
|
| 400 |
+
"Seurat",
|
| 401 |
+
"CellTypist"
|
| 402 |
+
],
|
| 403 |
+
"gold_servers": [
|
| 404 |
+
"mcp_scanpy",
|
| 405 |
+
"mcp_scanpy-cli",
|
| 406 |
+
"mcp_scanpy-scripts",
|
| 407 |
+
"mcp_seurat-scripts",
|
| 408 |
+
"mcp_celltypist"
|
| 409 |
+
],
|
| 410 |
+
"workflow": [
|
| 411 |
+
{
|
| 412 |
+
"name": "load_single_cell_matrix",
|
| 413 |
+
"tool_patterns": [
|
| 414 |
+
"scanpy",
|
| 415 |
+
"read_h5ad",
|
| 416 |
+
"h5ad",
|
| 417 |
+
"matrix"
|
| 418 |
+
]
|
| 419 |
+
},
|
| 420 |
+
{
|
| 421 |
+
"name": "normalize_cluster_and_embed",
|
| 422 |
+
"tool_patterns": [
|
| 423 |
+
"normalize",
|
| 424 |
+
"neighbors",
|
| 425 |
+
"leiden",
|
| 426 |
+
"umap",
|
| 427 |
+
"scanpy"
|
| 428 |
+
]
|
| 429 |
+
},
|
| 430 |
+
{
|
| 431 |
+
"name": "annotate_cell_types",
|
| 432 |
+
"tool_patterns": [
|
| 433 |
+
"celltypist",
|
| 434 |
+
"marker",
|
| 435 |
+
"annotation",
|
| 436 |
+
"cell_type"
|
| 437 |
+
]
|
| 438 |
+
},
|
| 439 |
+
{
|
| 440 |
+
"name": "differential_expression_by_condition",
|
| 441 |
+
"tool_patterns": [
|
| 442 |
+
"rank_genes",
|
| 443 |
+
"differential",
|
| 444 |
+
"exercise",
|
| 445 |
+
"wilcoxon"
|
| 446 |
+
]
|
| 447 |
+
}
|
| 448 |
+
]
|
| 449 |
+
},
|
| 450 |
+
"transcript-quant": {
|
| 451 |
+
"task_name": "Transcript Quantification (Simulated RNA-Seq)",
|
| 452 |
+
"gold_tools": [
|
| 453 |
+
"Kallisto",
|
| 454 |
+
"Salmon"
|
| 455 |
+
],
|
| 456 |
+
"gold_servers": [
|
| 457 |
+
"mcp_kallisto",
|
| 458 |
+
"mcp_salmon"
|
| 459 |
+
],
|
| 460 |
+
"workflow": [
|
| 461 |
+
{
|
| 462 |
+
"name": "build_transcriptome_index",
|
| 463 |
+
"tool_patterns": [
|
| 464 |
+
"kallisto_index",
|
| 465 |
+
"salmon",
|
| 466 |
+
"index",
|
| 467 |
+
"transcriptome"
|
| 468 |
+
]
|
| 469 |
+
},
|
| 470 |
+
{
|
| 471 |
+
"name": "quantify_paired_end_reads",
|
| 472 |
+
"tool_patterns": [
|
| 473 |
+
"kallisto_quant",
|
| 474 |
+
"salmon",
|
| 475 |
+
"quant",
|
| 476 |
+
"paired"
|
| 477 |
+
]
|
| 478 |
+
},
|
| 479 |
+
{
|
| 480 |
+
"name": "extract_transcript_counts",
|
| 481 |
+
"tool_patterns": [
|
| 482 |
+
"abundance",
|
| 483 |
+
"est_counts",
|
| 484 |
+
"count",
|
| 485 |
+
"transcript_id"
|
| 486 |
+
]
|
| 487 |
+
},
|
| 488 |
+
{
|
| 489 |
+
"name": "write_no_header_tsv",
|
| 490 |
+
"tool_patterns": [
|
| 491 |
+
"tsv",
|
| 492 |
+
"transcript_id",
|
| 493 |
+
"count"
|
| 494 |
+
]
|
| 495 |
+
}
|
| 496 |
+
]
|
| 497 |
+
},
|
| 498 |
+
"viral-metagenomics": {
|
| 499 |
+
"task_name": "Viral Metagenomics: Species Identification (Dolphin)",
|
| 500 |
+
"gold_tools": [
|
| 501 |
+
"MEGAHIT",
|
| 502 |
+
"SPAdes",
|
| 503 |
+
"Kaiju",
|
| 504 |
+
"fastp",
|
| 505 |
+
"BLAST"
|
| 506 |
+
],
|
| 507 |
+
"gold_servers": [
|
| 508 |
+
"mcp_megahit",
|
| 509 |
+
"mcp_spades",
|
| 510 |
+
"mcp_kaiju",
|
| 511 |
+
"mcp_fastp",
|
| 512 |
+
"mcp_blast"
|
| 513 |
+
],
|
| 514 |
+
"workflow": [
|
| 515 |
+
{
|
| 516 |
+
"name": "assemble_viral_metagenomic_contigs",
|
| 517 |
+
"tool_patterns": [
|
| 518 |
+
"megahit",
|
| 519 |
+
"spades",
|
| 520 |
+
"assembly",
|
| 521 |
+
"contig"
|
| 522 |
+
]
|
| 523 |
+
},
|
| 524 |
+
{
|
| 525 |
+
"name": "classify_contigs_taxonomically",
|
| 526 |
+
"tool_patterns": [
|
| 527 |
+
"kaiju",
|
| 528 |
+
"blast",
|
| 529 |
+
"taxonomy",
|
| 530 |
+
"virus"
|
| 531 |
+
]
|
| 532 |
+
},
|
| 533 |
+
{
|
| 534 |
+
"name": "resolve_taxon_names",
|
| 535 |
+
"tool_patterns": [
|
| 536 |
+
"names",
|
| 537 |
+
"nodes",
|
| 538 |
+
"taxon",
|
| 539 |
+
"species"
|
| 540 |
+
]
|
| 541 |
+
},
|
| 542 |
+
{
|
| 543 |
+
"name": "summarize_contig_counts_by_species",
|
| 544 |
+
"tool_patterns": [
|
| 545 |
+
"species",
|
| 546 |
+
"contig_count",
|
| 547 |
+
"domain",
|
| 548 |
+
"csv"
|
| 549 |
+
]
|
| 550 |
+
}
|
| 551 |
+
]
|
| 552 |
+
}
|
| 553 |
+
}
|
experiments/lab_bench/README_RUN.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# LAB-Bench Biomni 实验新手运行指南
|
| 2 |
+
|
| 3 |
+
这份说明用于运行 Biomni 在 LAB-Bench 上的实验,并且数据集设置要和
|
| 4 |
+
`/225040511/project/Hypo_Bio_OS/experiments/lab_bench/scripts` 里的 HypoBioOS
|
| 5 |
+
实验保持一致。
|
| 6 |
+
|
| 7 |
+
```bash
|
| 8 |
+
/225040511/project/Biomni/experiments/lab_bench
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
LAB-Bench 数据和代码默认位于:
|
| 12 |
+
|
| 13 |
+
```bash
|
| 14 |
+
/225040511/project/LAB-Bench
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
## 1. 进入 Biomni 项目
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
cd /225040511/project/Biomni
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## 2. 本实验跑什么数据
|
| 24 |
+
|
| 25 |
+
不要跑 LAB-Bench 全量。为了和 HypoBioOS 对照实验一致,本实验只跑:
|
| 26 |
+
|
| 27 |
+
- `DbQA`
|
| 28 |
+
- `SeqQA`
|
| 29 |
+
|
| 30 |
+
默认数据设置:
|
| 31 |
+
|
| 32 |
+
- split: `test`
|
| 33 |
+
- dev offset: `45`
|
| 34 |
+
- test size: `315`
|
| 35 |
+
- seed: `20260514`
|
| 36 |
+
- shard count: `10`
|
| 37 |
+
- runner threads: `1`
|
| 38 |
+
|
| 39 |
+
对应 HypoBioOS 脚本里的默认环境变量:
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
LAB_BENCH_SPLITS=test
|
| 43 |
+
LAB_BENCH_DEV_SIZE=45
|
| 44 |
+
LAB_BENCH_TEST_SIZE=315
|
| 45 |
+
LAB_BENCH_SUBSET_SEED=20260514
|
| 46 |
+
LAB_BENCH_TEST_SHARD_COUNT=10
|
| 47 |
+
LAB_BENCH_N_THREADS=1
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Biomni 这边的 `run_all_labbench_with_biomni.sh` 已经改成只代理 DbQA + SeqQA 子集,不会启动 LAB-Bench 全量 eval。
|
| 51 |
+
|
| 52 |
+
## 3. 挂 DeepSeek 后端
|
| 53 |
+
|
| 54 |
+
推荐把 DeepSeek key 写到 Biomni 项目根目录的 `.env`:
|
| 55 |
+
|
| 56 |
+
```bash
|
| 57 |
+
cat > /225040511/project/Biomni/.env <<'EOF'
|
| 58 |
+
DEEPSEEK_API_KEY=你的 DeepSeek API Key
|
| 59 |
+
DEEPSEEK_MODEL_NAME=deepseek-chat
|
| 60 |
+
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
|
| 61 |
+
EOF
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
也可以只在当前 shell 里临时设置:
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
export DEEPSEEK_API_KEY="你的 DeepSeek API Key"
|
| 68 |
+
export DEEPSEEK_MODEL_NAME="deepseek-chat"
|
| 69 |
+
export DEEPSEEK_BASE_URL="https://api.deepseek.com/v1"
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
实验脚本会自动把 DeepSeek 变量转换成 Biomni 需要的 Custom provider 配置:
|
| 73 |
+
|
| 74 |
+
```bash
|
| 75 |
+
BIOMNI_SOURCE=Custom
|
| 76 |
+
BIOMNI_LLM=$DEEPSEEK_MODEL_NAME
|
| 77 |
+
BIOMNI_CUSTOM_BASE_URL=$DEEPSEEK_BASE_URL
|
| 78 |
+
BIOMNI_CUSTOM_API_KEY=$DEEPSEEK_API_KEY
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
可以用下面命令确认 key 是否已设置,不会打印 key 内容:
|
| 82 |
+
|
| 83 |
+
```bash
|
| 84 |
+
for k in DEEPSEEK_API_KEY BIOMNI_CUSTOM_API_KEY BIOMNI_SOURCE BIOMNI_LLM BIOMNI_CUSTOM_BASE_URL; do
|
| 85 |
+
if [ -n "${!k:-}" ]; then echo "$k=set"; else echo "$k=unset"; fi
|
| 86 |
+
done
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
如果你把 key 写进 `.env`,上面的检查命令在手动 `source .env` 之前可能显示 unset,这是正常的;运行脚本时会自动读取 `/225040511/project/Biomni/.env` 和 `/225040511/project/LAB-Bench/.env`。
|
| 90 |
+
|
| 91 |
+
## 4. Python 环境
|
| 92 |
+
|
| 93 |
+
默认使用:
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
/225040511/miniconda3/envs/biomni_e1/bin/python
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
如果要换 Python,可以设置:
|
| 100 |
+
|
| 101 |
+
```bash
|
| 102 |
+
export LAB_BENCH_RUNNER_PYTHON=/path/to/python
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
## 5. 快速跑一个小样本
|
| 106 |
+
|
| 107 |
+
先用 debug 模式验证 DeepSeek、Biomni 和 LAB-Bench 都能跑通:
|
| 108 |
+
|
| 109 |
+
```bash
|
| 110 |
+
/225040511/miniconda3/envs/biomni_e1/bin/python \
|
| 111 |
+
experiments/lab_bench/run_labbench_with_biomni.py \
|
| 112 |
+
--eval DbQA \
|
| 113 |
+
--split test \
|
| 114 |
+
--debug \
|
| 115 |
+
--output experiments/lab_bench/results/debug_dbqa.json
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
成功后会生成:
|
| 119 |
+
|
| 120 |
+
```bash
|
| 121 |
+
experiments/lab_bench/results/debug_dbqa.json
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
## 6. 正式运行 DbQA 和 SeqQA
|
| 125 |
+
|
| 126 |
+
推荐使用分片脚本。它们默认后台运行、默认断点续跑,并把结果追加到 JSONL。
|
| 127 |
+
|
| 128 |
+
同时跑 DbQA + SeqQA:
|
| 129 |
+
|
| 130 |
+
```bash
|
| 131 |
+
export LAB_BENCH_SPLITS=test
|
| 132 |
+
export LAB_BENCH_DEV_SIZE=45
|
| 133 |
+
export LAB_BENCH_TEST_SIZE=315
|
| 134 |
+
export LAB_BENCH_SUBSET_SEED=20260514
|
| 135 |
+
export LAB_BENCH_TEST_SHARD_COUNT=10
|
| 136 |
+
experiments/lab_bench/run_dbqa_seqqa_biomni_experiment.sh --background --resume
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
只跑 DbQA:
|
| 140 |
+
|
| 141 |
+
```bash
|
| 142 |
+
experiments/lab_bench/run_dbqa_biomni_experiment.sh --background --resume
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
只跑 SeqQA:
|
| 146 |
+
|
| 147 |
+
```bash
|
| 148 |
+
experiments/lab_bench/run_seqqa_biomni_experiment.sh --background --resume
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
默认每个 eval 分成 10 个 shard 并行跑。可以调整 shard 数:
|
| 152 |
+
|
| 153 |
+
```bash
|
| 154 |
+
export LAB_BENCH_TEST_SHARD_COUNT=5
|
| 155 |
+
experiments/lab_bench/run_dbqa_seqqa_biomni_experiment.sh --background --resume
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
## 7. 前台调试
|
| 159 |
+
|
| 160 |
+
如果想直接在终端看到错误,使用前台模式:
|
| 161 |
+
|
| 162 |
+
```bash
|
| 163 |
+
experiments/lab_bench/run_dbqa_biomni_experiment.sh --foreground --resume
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
或者:
|
| 167 |
+
|
| 168 |
+
```bash
|
| 169 |
+
experiments/lab_bench/run_seqqa_biomni_experiment.sh --foreground --resume
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
前台模式会占住当前终端,适合调试;长时间正式实验建议用后台模式。
|
| 173 |
+
|
| 174 |
+
## 8. 结果位置
|
| 175 |
+
|
| 176 |
+
DbQA 分片结果:
|
| 177 |
+
|
| 178 |
+
```bash
|
| 179 |
+
experiments/lab_bench/results/dbqa_batch/dbqa_results.jsonl
|
| 180 |
+
experiments/lab_bench/results/dbqa_batch/dbqa_reasoning.log
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
SeqQA 分片结果:
|
| 184 |
+
|
| 185 |
+
```bash
|
| 186 |
+
experiments/lab_bench/results/seqqa_batch/seqqa_results.jsonl
|
| 187 |
+
experiments/lab_bench/results/seqqa_batch/seqqa_reasoning.log
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
`*_results.jsonl` 每行是一道题的紧凑结果,包含:
|
| 191 |
+
|
| 192 |
+
- `question`
|
| 193 |
+
- `answer`
|
| 194 |
+
- `agent_answer`
|
| 195 |
+
|
| 196 |
+
`*_reasoning.log` 保存每题的 prompt、raw output 和 Biomni reasoning log,方便排查错误。
|
| 197 |
+
|
| 198 |
+
## 9. 断点续跑和重新开始
|
| 199 |
+
|
| 200 |
+
默认推荐断点续跑:
|
| 201 |
+
|
| 202 |
+
```bash
|
| 203 |
+
experiments/lab_bench/run_dbqa_seqqa_biomni_experiment.sh --background --resume
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
脚本会跳过已经存在于 `*_results.jsonl` 里的问题。
|
| 207 |
+
|
| 208 |
+
如果���清空旧结果重新跑:
|
| 209 |
+
|
| 210 |
+
```bash
|
| 211 |
+
experiments/lab_bench/run_dbqa_seqqa_biomni_experiment.sh --background --fresh
|
| 212 |
+
```
|
| 213 |
+
|
| 214 |
+
## 10. 常用参数
|
| 215 |
+
|
| 216 |
+
切换 split。为了和 HypoBioOS 正式实验一致,默认用 `test`:
|
| 217 |
+
|
| 218 |
+
```bash
|
| 219 |
+
export LAB_BENCH_SPLITS=test
|
| 220 |
+
```
|
| 221 |
+
|
| 222 |
+
Biomni 分片脚本一次只支持一个 split。如果设置成 `dev test`,脚本会退出,避免和 HypoBioOS 默认测试集设置混淆。
|
| 223 |
+
|
| 224 |
+
调整 dev/test 数量:
|
| 225 |
+
|
| 226 |
+
```bash
|
| 227 |
+
export LAB_BENCH_DEV_SIZE=45
|
| 228 |
+
export LAB_BENCH_TEST_SIZE=315
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
固定抽样种子:
|
| 232 |
+
|
| 233 |
+
```bash
|
| 234 |
+
export LAB_BENCH_SUBSET_SEED=20260514
|
| 235 |
+
```
|
| 236 |
+
|
| 237 |
+
Biomni A1 runner 是有状态的,脚本默认:
|
| 238 |
+
|
| 239 |
+
```bash
|
| 240 |
+
LAB_BENCH_N_THREADS=1
|
| 241 |
+
```
|
| 242 |
+
|
| 243 |
+
不建议改成更大的值。
|
| 244 |
+
|
| 245 |
+
## 11. 检查是否在跑
|
| 246 |
+
|
| 247 |
+
查看后台进程:
|
| 248 |
+
|
| 249 |
+
```bash
|
| 250 |
+
ps -ef | grep lab_bench | grep -v grep
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
查看结果是否持续增长:
|
| 254 |
+
|
| 255 |
+
```bash
|
| 256 |
+
wc -l experiments/lab_bench/results/dbqa_batch/dbqa_results.jsonl
|
| 257 |
+
wc -l experiments/lab_bench/results/seqqa_batch/seqqa_results.jsonl
|
| 258 |
+
```
|
| 259 |
+
|
| 260 |
+
查看最近 reasoning log:
|
| 261 |
+
|
| 262 |
+
```bash
|
| 263 |
+
tail -80 experiments/lab_bench/results/dbqa_batch/dbqa_reasoning.log
|
| 264 |
+
tail -80 experiments/lab_bench/results/seqqa_batch/seqqa_reasoning.log
|
| 265 |
+
```
|
| 266 |
+
|
| 267 |
+
## 12. 常见报错
|
| 268 |
+
|
| 269 |
+
如果看到:
|
| 270 |
+
|
| 271 |
+
```text
|
| 272 |
+
No LLM API key found.
|
| 273 |
+
```
|
| 274 |
+
|
| 275 |
+
说明脚本没有读到 DeepSeek key。检查:
|
| 276 |
+
|
| 277 |
+
```bash
|
| 278 |
+
ls -lh /225040511/project/Biomni/.env
|
| 279 |
+
```
|
| 280 |
+
|
| 281 |
+
并确认 `.env` 里至少有:
|
| 282 |
+
|
| 283 |
+
```bash
|
| 284 |
+
DEEPSEEK_API_KEY=你的 DeepSeek API Key
|
| 285 |
+
DEEPSEEK_MODEL_NAME=deepseek-chat
|
| 286 |
+
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
|
| 287 |
+
```
|
| 288 |
+
|
| 289 |
+
如果使用临时环境变量,确认你是在同一个 shell 里先 `export`,再启动脚本。
|
experiments/lab_bench/__pycache__/summarize_labbench_repeats.cpython-310.pyc
ADDED
|
Binary file (2.36 kB). View file
|
|
|
experiments/lab_bench/__pycache__/summarize_labbench_repeats.cpython-313.pyc
ADDED
|
Binary file (3.58 kB). View file
|
|
|
experiments/lab_bench/run_all_labbench_with_biomni.sh
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 5 |
+
|
| 6 |
+
echo "This experiment is aligned with Hypo_Bio_OS: DbQA + SeqQA only, default split=test."
|
| 7 |
+
echo "Delegating to run_dbqa_seqqa_biomni_experiment.sh; no full LAB-Bench run will be launched."
|
| 8 |
+
|
| 9 |
+
"${SCRIPT_DIR}/run_dbqa_seqqa_biomni_experiment.sh" "$@"
|
experiments/lab_bench/run_dbqa_biomni_experiment.sh
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
BIOMNI_ROOT="/225040511/project/Biomni"
|
| 5 |
+
LAB_BENCH_ROOT="/225040511/project/LAB-Bench"
|
| 6 |
+
RUNNER="${BIOMNI_ROOT}/experiments/lab_bench/run_labbench_with_biomni.py"
|
| 7 |
+
RUNNER_PYTHON="${LAB_BENCH_RUNNER_PYTHON:-${BIOMNI_EXECUTION_PYTHON:-/225040511/miniconda3/envs/biomni_e1/bin/python}}"
|
| 8 |
+
|
| 9 |
+
OUTPUT_ROOT="${BIOMNI_ROOT}/experiments/lab_bench/results/dbqa_batch"
|
| 10 |
+
RESULT_FILE="${OUTPUT_ROOT}/dbqa_results.jsonl"
|
| 11 |
+
REASONING_LOG="${OUTPUT_ROOT}/dbqa_reasoning.log"
|
| 12 |
+
EVAL_NAME="DbQA"
|
| 13 |
+
|
| 14 |
+
DEV_SIZE="${LAB_BENCH_DEV_SIZE:-45}"
|
| 15 |
+
TEST_SIZE="${LAB_BENCH_TEST_SIZE:-315}"
|
| 16 |
+
SEED="${LAB_BENCH_SUBSET_SEED:-20260514}"
|
| 17 |
+
N_THREADS="${LAB_BENCH_N_THREADS:-1}"
|
| 18 |
+
TEST_SHARD_COUNT="${LAB_BENCH_TEST_SHARD_COUNT:-10}"
|
| 19 |
+
SPLITS_TEXT="${LAB_BENCH_SPLITS:-${LAB_BENCH_SPLIT:-test}}"
|
| 20 |
+
|
| 21 |
+
BACKGROUND=1
|
| 22 |
+
RESUME=1
|
| 23 |
+
while [[ $# -gt 0 ]]; do
|
| 24 |
+
case "$1" in
|
| 25 |
+
--foreground)
|
| 26 |
+
BACKGROUND=0
|
| 27 |
+
shift
|
| 28 |
+
;;
|
| 29 |
+
--background)
|
| 30 |
+
BACKGROUND=1
|
| 31 |
+
shift
|
| 32 |
+
;;
|
| 33 |
+
--fresh)
|
| 34 |
+
RESUME=0
|
| 35 |
+
shift
|
| 36 |
+
;;
|
| 37 |
+
--resume)
|
| 38 |
+
RESUME=1
|
| 39 |
+
shift
|
| 40 |
+
;;
|
| 41 |
+
*)
|
| 42 |
+
exit 2
|
| 43 |
+
;;
|
| 44 |
+
esac
|
| 45 |
+
done
|
| 46 |
+
|
| 47 |
+
if [[ "${BACKGROUND}" -eq 1 ]]; then
|
| 48 |
+
SCRIPT_ABS_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
| 49 |
+
FORWARD_ARGS=(--foreground)
|
| 50 |
+
if [[ "${RESUME}" -eq 0 ]]; then
|
| 51 |
+
FORWARD_ARGS+=(--fresh)
|
| 52 |
+
else
|
| 53 |
+
FORWARD_ARGS+=(--resume)
|
| 54 |
+
fi
|
| 55 |
+
nohup "${SCRIPT_ABS_PATH}" "${FORWARD_ARGS[@]}" > /dev/null 2>&1 < /dev/null &
|
| 56 |
+
exit 0
|
| 57 |
+
fi
|
| 58 |
+
|
| 59 |
+
mkdir -p "${OUTPUT_ROOT}"
|
| 60 |
+
if [[ "${RESUME}" -eq 0 ]]; then
|
| 61 |
+
: > "${RESULT_FILE}"
|
| 62 |
+
: > "${REASONING_LOG}"
|
| 63 |
+
else
|
| 64 |
+
touch "${RESULT_FILE}" "${REASONING_LOG}"
|
| 65 |
+
fi
|
| 66 |
+
|
| 67 |
+
for ENV_FILE in "${BIOMNI_ROOT}/.env" "${LAB_BENCH_ROOT}/.env"; do
|
| 68 |
+
if [[ -f "${ENV_FILE}" ]]; then
|
| 69 |
+
set -a
|
| 70 |
+
# shellcheck disable=SC1090
|
| 71 |
+
source "${ENV_FILE}"
|
| 72 |
+
set +a
|
| 73 |
+
fi
|
| 74 |
+
done
|
| 75 |
+
|
| 76 |
+
if [[ -n "${DEEPSEEK_API_KEY:-}" && -z "${BIOMNI_CUSTOM_API_KEY:-}" ]]; then
|
| 77 |
+
export BIOMNI_SOURCE="${BIOMNI_SOURCE:-Custom}"
|
| 78 |
+
export BIOMNI_LLM="${BIOMNI_LLM:-${DEEPSEEK_MODEL_NAME:-deepseek-chat}}"
|
| 79 |
+
export BIOMNI_CUSTOM_BASE_URL="${BIOMNI_CUSTOM_BASE_URL:-${DEEPSEEK_BASE_URL:-https://api.deepseek.com/v1}}"
|
| 80 |
+
export BIOMNI_CUSTOM_API_KEY="${DEEPSEEK_API_KEY}"
|
| 81 |
+
fi
|
| 82 |
+
|
| 83 |
+
if [[ -z "${ANTHROPIC_API_KEY:-}" && -z "${OPENAI_API_KEY:-}" && -z "${BIOMNI_CUSTOM_API_KEY:-}" ]]; then
|
| 84 |
+
printf "[launcher] missing LLM API key environment variables; batch did not start\n" >> "${REASONING_LOG}"
|
| 85 |
+
exit 2
|
| 86 |
+
fi
|
| 87 |
+
|
| 88 |
+
read -r -a SPLIT_ARRAY <<< "${SPLITS_TEXT}"
|
| 89 |
+
if [[ "${#SPLIT_ARRAY[@]}" -ne 1 ]]; then
|
| 90 |
+
printf "[launcher] Biomni LAB-Bench runner supports one split per batch; got LAB_BENCH_SPLITS='%s'\n" "${SPLITS_TEXT}" >> "${REASONING_LOG}"
|
| 91 |
+
exit 2
|
| 92 |
+
fi
|
| 93 |
+
SPLIT="${SPLIT_ARRAY[0]}"
|
| 94 |
+
|
| 95 |
+
declare -a CHILD_PIDS=()
|
| 96 |
+
|
| 97 |
+
launch_shard() {
|
| 98 |
+
local shard_index="$1"
|
| 99 |
+
local run_label="dbqa_shard$(printf '%02d' "$((shard_index + 1))")of$(printf '%02d' "${TEST_SHARD_COUNT}")"
|
| 100 |
+
local -a cmd=(
|
| 101 |
+
"${RUNNER_PYTHON}" "${RUNNER}"
|
| 102 |
+
"--eval" "${EVAL_NAME}"
|
| 103 |
+
"--split" "${SPLIT}"
|
| 104 |
+
"--dev-size" "${DEV_SIZE}"
|
| 105 |
+
"--test-size" "${TEST_SIZE}"
|
| 106 |
+
"--seed" "${SEED}"
|
| 107 |
+
"--repeat" "1"
|
| 108 |
+
"--n_threads" "${N_THREADS}"
|
| 109 |
+
"--output-root" "${OUTPUT_ROOT}"
|
| 110 |
+
"--agent-root" "${BIOMNI_ROOT}/experiments/lab_bench/agent_runtime"
|
| 111 |
+
"--run-label" "${run_label}"
|
| 112 |
+
"--shard-index" "${shard_index}"
|
| 113 |
+
"--shard-count" "${TEST_SHARD_COUNT}"
|
| 114 |
+
"--reasoning-log-path" "${REASONING_LOG}"
|
| 115 |
+
"--compact-results-path" "${RESULT_FILE}"
|
| 116 |
+
"--compact-output-only"
|
| 117 |
+
"--skip-existing-results"
|
| 118 |
+
)
|
| 119 |
+
"${cmd[@]}" > /dev/null 2>&1 &
|
| 120 |
+
CHILD_PIDS+=("$!")
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
for (( shard_index=0; shard_index<TEST_SHARD_COUNT; shard_index++ )); do
|
| 124 |
+
launch_shard "${shard_index}"
|
| 125 |
+
done
|
| 126 |
+
|
| 127 |
+
for pid in "${CHILD_PIDS[@]}"; do
|
| 128 |
+
wait "${pid}"
|
| 129 |
+
done
|
experiments/lab_bench/run_dbqa_seqqa_biomni_experiment.sh
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
BIOMNI_ROOT="/225040511/project/Biomni"
|
| 5 |
+
DBQA_SCRIPT="${BIOMNI_ROOT}/experiments/lab_bench/run_dbqa_biomni_experiment.sh"
|
| 6 |
+
SEQQA_SCRIPT="${BIOMNI_ROOT}/experiments/lab_bench/run_seqqa_biomni_experiment.sh"
|
| 7 |
+
|
| 8 |
+
BACKGROUND=1
|
| 9 |
+
EXTRA_ARGS=()
|
| 10 |
+
while [[ $# -gt 0 ]]; do
|
| 11 |
+
case "$1" in
|
| 12 |
+
--foreground)
|
| 13 |
+
BACKGROUND=0
|
| 14 |
+
EXTRA_ARGS+=("$1")
|
| 15 |
+
shift
|
| 16 |
+
;;
|
| 17 |
+
--background)
|
| 18 |
+
shift
|
| 19 |
+
;;
|
| 20 |
+
--fresh|--resume)
|
| 21 |
+
EXTRA_ARGS+=("$1")
|
| 22 |
+
shift
|
| 23 |
+
;;
|
| 24 |
+
*)
|
| 25 |
+
exit 2
|
| 26 |
+
;;
|
| 27 |
+
esac
|
| 28 |
+
done
|
| 29 |
+
|
| 30 |
+
if [[ "${BACKGROUND}" -eq 1 ]]; then
|
| 31 |
+
SCRIPT_ABS_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
| 32 |
+
nohup "${SCRIPT_ABS_PATH}" --foreground "${EXTRA_ARGS[@]}" > /dev/null 2>&1 < /dev/null &
|
| 33 |
+
exit 0
|
| 34 |
+
fi
|
| 35 |
+
|
| 36 |
+
"${DBQA_SCRIPT}" "${EXTRA_ARGS[@]}"
|
| 37 |
+
"${SEQQA_SCRIPT}" "${EXTRA_ARGS[@]}"
|
experiments/lab_bench/run_seqqa_biomni_experiment.sh
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
BIOMNI_ROOT="/225040511/project/Biomni"
|
| 5 |
+
LAB_BENCH_ROOT="/225040511/project/LAB-Bench"
|
| 6 |
+
RUNNER="${BIOMNI_ROOT}/experiments/lab_bench/run_labbench_with_biomni.py"
|
| 7 |
+
RUNNER_PYTHON="${LAB_BENCH_RUNNER_PYTHON:-${BIOMNI_EXECUTION_PYTHON:-/225040511/miniconda3/envs/biomni_e1/bin/python}}"
|
| 8 |
+
|
| 9 |
+
OUTPUT_ROOT="${BIOMNI_ROOT}/experiments/lab_bench/results/seqqa_batch"
|
| 10 |
+
RESULT_FILE="${OUTPUT_ROOT}/seqqa_results.jsonl"
|
| 11 |
+
REASONING_LOG="${OUTPUT_ROOT}/seqqa_reasoning.log"
|
| 12 |
+
EVAL_NAME="SeqQA"
|
| 13 |
+
|
| 14 |
+
DEV_SIZE="${LAB_BENCH_DEV_SIZE:-45}"
|
| 15 |
+
TEST_SIZE="${LAB_BENCH_TEST_SIZE:-315}"
|
| 16 |
+
SEED="${LAB_BENCH_SUBSET_SEED:-20260514}"
|
| 17 |
+
N_THREADS="${LAB_BENCH_N_THREADS:-1}"
|
| 18 |
+
TEST_SHARD_COUNT="${LAB_BENCH_TEST_SHARD_COUNT:-10}"
|
| 19 |
+
SPLITS_TEXT="${LAB_BENCH_SPLITS:-${LAB_BENCH_SPLIT:-test}}"
|
| 20 |
+
|
| 21 |
+
BACKGROUND=1
|
| 22 |
+
RESUME=1
|
| 23 |
+
while [[ $# -gt 0 ]]; do
|
| 24 |
+
case "$1" in
|
| 25 |
+
--foreground)
|
| 26 |
+
BACKGROUND=0
|
| 27 |
+
shift
|
| 28 |
+
;;
|
| 29 |
+
--background)
|
| 30 |
+
BACKGROUND=1
|
| 31 |
+
shift
|
| 32 |
+
;;
|
| 33 |
+
--fresh)
|
| 34 |
+
RESUME=0
|
| 35 |
+
shift
|
| 36 |
+
;;
|
| 37 |
+
--resume)
|
| 38 |
+
RESUME=1
|
| 39 |
+
shift
|
| 40 |
+
;;
|
| 41 |
+
*)
|
| 42 |
+
exit 2
|
| 43 |
+
;;
|
| 44 |
+
esac
|
| 45 |
+
done
|
| 46 |
+
|
| 47 |
+
if [[ "${BACKGROUND}" -eq 1 ]]; then
|
| 48 |
+
SCRIPT_ABS_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
| 49 |
+
FORWARD_ARGS=(--foreground)
|
| 50 |
+
if [[ "${RESUME}" -eq 0 ]]; then
|
| 51 |
+
FORWARD_ARGS+=(--fresh)
|
| 52 |
+
else
|
| 53 |
+
FORWARD_ARGS+=(--resume)
|
| 54 |
+
fi
|
| 55 |
+
nohup "${SCRIPT_ABS_PATH}" "${FORWARD_ARGS[@]}" > /dev/null 2>&1 < /dev/null &
|
| 56 |
+
exit 0
|
| 57 |
+
fi
|
| 58 |
+
|
| 59 |
+
mkdir -p "${OUTPUT_ROOT}"
|
| 60 |
+
if [[ "${RESUME}" -eq 0 ]]; then
|
| 61 |
+
: > "${RESULT_FILE}"
|
| 62 |
+
: > "${REASONING_LOG}"
|
| 63 |
+
else
|
| 64 |
+
touch "${RESULT_FILE}" "${REASONING_LOG}"
|
| 65 |
+
fi
|
| 66 |
+
|
| 67 |
+
for ENV_FILE in "${BIOMNI_ROOT}/.env" "${LAB_BENCH_ROOT}/.env"; do
|
| 68 |
+
if [[ -f "${ENV_FILE}" ]]; then
|
| 69 |
+
set -a
|
| 70 |
+
# shellcheck disable=SC1090
|
| 71 |
+
source "${ENV_FILE}"
|
| 72 |
+
set +a
|
| 73 |
+
fi
|
| 74 |
+
done
|
| 75 |
+
|
| 76 |
+
if [[ -n "${DEEPSEEK_API_KEY:-}" && -z "${BIOMNI_CUSTOM_API_KEY:-}" ]]; then
|
| 77 |
+
export BIOMNI_SOURCE="${BIOMNI_SOURCE:-Custom}"
|
| 78 |
+
export BIOMNI_LLM="${BIOMNI_LLM:-${DEEPSEEK_MODEL_NAME:-deepseek-chat}}"
|
| 79 |
+
export BIOMNI_CUSTOM_BASE_URL="${BIOMNI_CUSTOM_BASE_URL:-${DEEPSEEK_BASE_URL:-https://api.deepseek.com/v1}}"
|
| 80 |
+
export BIOMNI_CUSTOM_API_KEY="${DEEPSEEK_API_KEY}"
|
| 81 |
+
fi
|
| 82 |
+
|
| 83 |
+
if [[ -z "${ANTHROPIC_API_KEY:-}" && -z "${OPENAI_API_KEY:-}" && -z "${BIOMNI_CUSTOM_API_KEY:-}" ]]; then
|
| 84 |
+
printf "[launcher] missing LLM API key environment variables; batch did not start\n" >> "${REASONING_LOG}"
|
| 85 |
+
exit 2
|
| 86 |
+
fi
|
| 87 |
+
|
| 88 |
+
read -r -a SPLIT_ARRAY <<< "${SPLITS_TEXT}"
|
| 89 |
+
if [[ "${#SPLIT_ARRAY[@]}" -ne 1 ]]; then
|
| 90 |
+
printf "[launcher] Biomni LAB-Bench runner supports one split per batch; got LAB_BENCH_SPLITS='%s'\n" "${SPLITS_TEXT}" >> "${REASONING_LOG}"
|
| 91 |
+
exit 2
|
| 92 |
+
fi
|
| 93 |
+
SPLIT="${SPLIT_ARRAY[0]}"
|
| 94 |
+
|
| 95 |
+
declare -a CHILD_PIDS=()
|
| 96 |
+
|
| 97 |
+
launch_shard() {
|
| 98 |
+
local shard_index="$1"
|
| 99 |
+
local run_label="seqqa_shard$(printf '%02d' "$((shard_index + 1))")of$(printf '%02d' "${TEST_SHARD_COUNT}")"
|
| 100 |
+
local -a cmd=(
|
| 101 |
+
"${RUNNER_PYTHON}" "${RUNNER}"
|
| 102 |
+
"--eval" "${EVAL_NAME}"
|
| 103 |
+
"--split" "${SPLIT}"
|
| 104 |
+
"--dev-size" "${DEV_SIZE}"
|
| 105 |
+
"--test-size" "${TEST_SIZE}"
|
| 106 |
+
"--seed" "${SEED}"
|
| 107 |
+
"--repeat" "1"
|
| 108 |
+
"--n_threads" "${N_THREADS}"
|
| 109 |
+
"--output-root" "${OUTPUT_ROOT}"
|
| 110 |
+
"--agent-root" "${BIOMNI_ROOT}/experiments/lab_bench/agent_runtime"
|
| 111 |
+
"--run-label" "${run_label}"
|
| 112 |
+
"--shard-index" "${shard_index}"
|
| 113 |
+
"--shard-count" "${TEST_SHARD_COUNT}"
|
| 114 |
+
"--reasoning-log-path" "${REASONING_LOG}"
|
| 115 |
+
"--compact-results-path" "${RESULT_FILE}"
|
| 116 |
+
"--compact-output-only"
|
| 117 |
+
"--skip-existing-results"
|
| 118 |
+
)
|
| 119 |
+
"${cmd[@]}" > /dev/null 2>&1 &
|
| 120 |
+
CHILD_PIDS+=("$!")
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
for (( shard_index=0; shard_index<TEST_SHARD_COUNT; shard_index++ )); do
|
| 124 |
+
launch_shard "${shard_index}"
|
| 125 |
+
done
|
| 126 |
+
|
| 127 |
+
for pid in "${CHILD_PIDS[@]}"; do
|
| 128 |
+
wait "${pid}"
|
| 129 |
+
done
|
figs/biomni_logo.png
ADDED
|
license_info.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Biomni Data Source License Information
|
| 2 |
+
|
| 3 |
+
This document provides an overview of the data sources used by Biomni, their licenses, and suitability for internal hosting and commercial use.
|
| 4 |
+
|
| 5 |
+
A significant portion of the data used in Biomni requires a commercial license for any commercial application. Several datasets are explicitly licensed for non-commercial use only, which would prohibit their use in a commercial product without a separate agreement. Before proceeding with any commercial use, a thorough legal review of the licenses for each dataset you intend to use is strongly recommended.
|
| 6 |
+
|
| 7 |
+
## Data License and Internal Hosting Analysis
|
| 8 |
+
|
| 9 |
+
| Data Source Category | Example Files | License | Internal Hosting | Source |
|
| 10 |
+
| :--- | :--- | :--- | :--- | :--- |
|
| 11 |
+
| **COSMIC** | `Cosmic_*.csv`, `Cosmic_*.parquet` | Requires commercial license for commercial use. | Yes, with a valid commercial license. | [Sanger Institute](https://cancer.sanger.ac.uk/cosmic) |
|
| 12 |
+
| **BindingDB** | `BindingDB_All_202409.tsv` | Custom, non-commercial use granted. Commercial use requires a license. | Yes, with a commercial license. | [BindingDB](https://www.bindingdb.org) |
|
| 13 |
+
| **Broad Repurposing Hub** | `broad_repurposing_hub_*.parquet` | CC BY 4.0 | Yes | [Broad Institute](https://www.broadinstitute.org/drug-repurposing-hub) |
|
| 14 |
+
| **DDInter** | `ddinter_*.csv` | CC BY-NC-SA 4.0 | No, non-commercial use only. | [DDInter](http://ddinter.scbdd.com/) |
|
| 15 |
+
| **DisGeNET** | `DisGeNET.parquet` | CC BY-NC-SA 4.0 | No, non-commercial use only. | [DisGeNET](https://www.disgenet.org/) |
|
| 16 |
+
| **Enamine** | `enamine_cloud_library_smiles.pkl` | Proprietary. Requires license for screening. | Yes, with a valid license. | [Enamine](https://enamine.net/) |
|
| 17 |
+
| **EveBio** | `evebio_*.csv` | Appears to be proprietary data from EveBio. | Requires permission from EveBio. | EveBio |
|
| 18 |
+
| **Gene Ontology (GO)** | `go-plus.json` | CC BY 4.0 | Yes | [Gene Ontology Consortium](http://geneontology.org/) |
|
| 19 |
+
| **GTEx** | `gtex_tissue_gene_tpm.parquet` | dbGaP controlled access. | Yes, with authorization. | [GTEx Portal](https://gtexportal.org/) |
|
| 20 |
+
| **Human Protein Atlas** | `proteinatlas.tsv` | CC BY-SA 3.0 | Yes | [Human Protein Atlas](https://www.proteinatlas.org/) |
|
| 21 |
+
| **MSigDB** | `msigdb_human_*.parquet` | Custom, requires license for commercial use. | Yes, with a license. | [Broad Institute](https://www.gsea-msigdb.org/gsea/msigdb) |
|
| 22 |
+
| **OMIM** | `omim.parquet` | Custom, requires license for commercial use. | Yes, with a license. | [OMIM](https://omim.org/) |
|
| 23 |
+
| **BioGRID** | `affinity_capture-ms.parquet`, etc. | OSL 3.0 | Yes | [BioGRID](https://thebiogrid.org/) |
|
| 24 |
+
| **CZI Cell Census** | `czi_census_datasets_v4.parquet` | CC BY 4.0 | Yes | [Chan Zuckerberg Initiative](https://cellxgene.cziscience.com/census) |
|
| 25 |
+
| **DepMap** | `DepMap_*.csv` | CC BY 4.0 | Yes | [Broad Institute DepMap](https://depmap.org/) |
|
| 26 |
+
| **Genebass** | `genebass_*.pkl` | ODC-By v1.0 | Yes | [Genebass](https://genebass.org/) |
|
| 27 |
+
| **GWAS Catalog** | `gwas_catalog.pkl` | Apache 2.0 | Yes | [EBI GWAS Catalog](https://www.ebi.ac.uk/gwas/) |
|
| 28 |
+
| **HPO** | `hp.obo` | Custom, free for all uses. | Yes | [Human Phenotype Ontology](https://hpo.jax.org/) |
|
| 29 |
+
| **McPAS-TCR** | `McPAS-TCR.parquet` | CC BY-NC-SA 4.0 | No, non-commercial use only. | [McPAS-TCR](http://friedmanlab.weizmann.ac.il/McPAS-TCR/) |
|
| 30 |
+
| **miRDB** | `miRDB_v6.0_results.parquet` | Custom, free for non-commercial use. | No, non-commercial use only. | [miRDB](http://mirdb.org/) |
|
| 31 |
+
| **miRTarBase** | `miRTarBase_*.parquet` | CC BY-NC 4.0 | No, non-commercial use only. | [miRTarBase](https://mirtarbase.cuhk.edu.cn/) |
|
| 32 |
+
| **MouseMine** | `mousemine_*.parquet` | CC BY 4.0 | Yes | [MouseMine](http://www.mousemine.org/) |
|
| 33 |
+
| **P-HIPSTER** | `Virus-Host_PPI_P-HIPSTER_2020.parquet` | CC BY 4.0 | Yes | [P-HIPSTER](http://phipster.org) |
|
| 34 |
+
| **TXGNN** | `txgnn_*.pkl` | MIT License | Yes | - |
|
| 35 |
+
|
| 36 |
+
## Configuration for Commercial Use
|
| 37 |
+
|
| 38 |
+
To manage which datasets are used based on licensing, Biomni provides a configuration option. You can set the `commercial_mode` flag to `True` in your configuration to automatically exclude datasets that are not licensed for commercial use.
|
| 39 |
+
|
| 40 |
+
### Usage
|
| 41 |
+
|
| 42 |
+
```python
|
| 43 |
+
from biomni.agent import A1
|
| 44 |
+
|
| 45 |
+
# For commercial use (excludes non-commercial datasets)
|
| 46 |
+
agent = A1(commercial_mode=True)
|
| 47 |
+
|
| 48 |
+
# For academic/research use (includes all datasets)
|
| 49 |
+
agent = A1(commercial_mode=False) # default
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
This configuration automatically selects the appropriate data environment description file and ensures compliance with licensing requirements.
|
pyproject.toml
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=61.0", "wheel"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "biomni"
|
| 7 |
+
dynamic = ["version"]
|
| 8 |
+
description = "Biomni"
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
license = "Apache-2.0"
|
| 11 |
+
license-files = ["LICENSE"]
|
| 12 |
+
authors = [
|
| 13 |
+
{name = "Biomni Team", email = "kexinh@cs.stanford.edu"}
|
| 14 |
+
]
|
| 15 |
+
requires-python = ">=3.11"
|
| 16 |
+
dependencies = ["pydantic", "langchain", "python-dotenv"]
|
| 17 |
+
|
| 18 |
+
[project.urls]
|
| 19 |
+
Homepage = "https://github.com/snap-stanford/biomni"
|
| 20 |
+
Repository = "https://github.com/snap-stanford/biomni"
|
| 21 |
+
|
| 22 |
+
[project.optional-dependencies]
|
| 23 |
+
gradio = ["gradio>=5.0,<6.0"]
|
| 24 |
+
|
| 25 |
+
[tool.setuptools]
|
| 26 |
+
include-package-data = true
|
| 27 |
+
|
| 28 |
+
[tool.setuptools.dynamic]
|
| 29 |
+
version = {attr = "biomni.version.__version__"}
|
| 30 |
+
|
| 31 |
+
[tool.setuptools.packages.find]
|
| 32 |
+
exclude = ["test*", "tutorials*"]
|
| 33 |
+
|
| 34 |
+
[tool.ruff]
|
| 35 |
+
src = ["src"]
|
| 36 |
+
line-length = 120
|
| 37 |
+
|
| 38 |
+
[tool.ruff.format]
|
| 39 |
+
docstring-code-format = true
|
| 40 |
+
|
| 41 |
+
[tool.ruff.lint]
|
| 42 |
+
select = [
|
| 43 |
+
"F", # Errors detected by Pyflakes
|
| 44 |
+
"E", # Error detected by Pycodestyle
|
| 45 |
+
"W", # Warning detected by Pycodestyle
|
| 46 |
+
"I", # isort
|
| 47 |
+
#"D", # pydocstyle
|
| 48 |
+
"B", # flake8-bugbear
|
| 49 |
+
"TID", # flake8-tidy-imports
|
| 50 |
+
"C4", # flake8-comprehensions
|
| 51 |
+
"BLE", # flake8-blind-except
|
| 52 |
+
"UP", # pyupgrade
|
| 53 |
+
"RUF100", # Report unused noqa directives
|
| 54 |
+
"TCH", # Typing imports
|
| 55 |
+
#"NPY", # Numpy specific rules
|
| 56 |
+
#"PTH", # Use pathlib
|
| 57 |
+
#"PL" # pylint
|
| 58 |
+
]
|
| 59 |
+
ignore = [
|
| 60 |
+
# line too long -> we accept long comment lines; black gets rid of long code lines
|
| 61 |
+
"E501",
|
| 62 |
+
# Do not assign a lambda expression, use a def -> lambda expression assignments are convenient
|
| 63 |
+
"E731",
|
| 64 |
+
# allow I, O, l as variable names -> I is the identity matrix
|
| 65 |
+
"E741",
|
| 66 |
+
# Missing docstring in public package
|
| 67 |
+
"D104",
|
| 68 |
+
# Missing docstring in public module
|
| 69 |
+
"D100",
|
| 70 |
+
# Missing docstring in __init__
|
| 71 |
+
"D107",
|
| 72 |
+
# Errors from function calls in argument defaults. These are fine when the result is immutable.
|
| 73 |
+
"B008",
|
| 74 |
+
# __magic__ methods are are often self-explanatory, allow missing docstrings
|
| 75 |
+
"D105",
|
| 76 |
+
# first line should end with a period [Bug: doesn't work with single-line docstrings]
|
| 77 |
+
"D400",
|
| 78 |
+
# First line should be in imperative mood; try rephrasing
|
| 79 |
+
"D401",
|
| 80 |
+
## Disable one in each pair of mutually incompatible rules
|
| 81 |
+
# We don’t want a blank line before a class docstring
|
| 82 |
+
"D203",
|
| 83 |
+
# We want docstrings to start immediately after the opening triple quote
|
| 84 |
+
"D213",
|
| 85 |
+
# camcelcase imported as lowercase
|
| 86 |
+
"N813",
|
| 87 |
+
# module import not at top level of file
|
| 88 |
+
"E402",
|
| 89 |
+
# Too many arguments in function definition
|
| 90 |
+
"PLR0913",
|
| 91 |
+
# Too many branches
|
| 92 |
+
"PLR0912",
|
| 93 |
+
# magic value used in comparison
|
| 94 |
+
"PLR2004",
|
| 95 |
+
# Too many statements
|
| 96 |
+
"PLR0915",
|
| 97 |
+
# Extracting value from dictionary without calling `.items()` - clashes with sim
|
| 98 |
+
"PLC0206",
|
| 99 |
+
# import should be at top of file
|
| 100 |
+
"PLC0415",
|
| 101 |
+
# do not catch blind exception
|
| 102 |
+
"BLE001",
|
| 103 |
+
# strip with multi characters
|
| 104 |
+
"B005"
|
| 105 |
+
]
|
tutorials/biomni_101.ipynb
ADDED
|
@@ -0,0 +1,1070 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"## Biomni 101"
|
| 8 |
+
]
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"cell_type": "markdown",
|
| 12 |
+
"metadata": {},
|
| 13 |
+
"source": [
|
| 14 |
+
"Welcome to Biomni! Here is a simple tutorial on the basics of Biomni package. After you installed the environment, you can then simply try this to initialize the agent, which will automatically download the raw data lake files:"
|
| 15 |
+
]
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"cell_type": "code",
|
| 19 |
+
"execution_count": 1,
|
| 20 |
+
"metadata": {},
|
| 21 |
+
"outputs": [
|
| 22 |
+
{
|
| 23 |
+
"name": "stdout",
|
| 24 |
+
"output_type": "stream",
|
| 25 |
+
"text": [
|
| 26 |
+
"🎓 Academic mode: Using all datasets (including non-commercial)\n",
|
| 27 |
+
"\n",
|
| 28 |
+
"==================================================\n",
|
| 29 |
+
"🔧 BIOMNI CONFIGURATION\n",
|
| 30 |
+
"==================================================\n",
|
| 31 |
+
"📋 DEFAULT CONFIG (Including Database LLM):\n",
|
| 32 |
+
" Path: ./data\n",
|
| 33 |
+
" Timeout Seconds: 600\n",
|
| 34 |
+
" Llm: claude-sonnet-4-5\n",
|
| 35 |
+
" Temperature: 0.7\n",
|
| 36 |
+
" Use Tool Retriever: True\n",
|
| 37 |
+
" Commercial Mode: Academic (all datasets)\n",
|
| 38 |
+
"\n",
|
| 39 |
+
"🤖 AGENT LLM (Constructor Override):\n",
|
| 40 |
+
" LLM Model: claude-sonnet-4-5-20250929\n",
|
| 41 |
+
"==================================================\n",
|
| 42 |
+
"\n",
|
| 43 |
+
"Created directory: ./biomni_data\n",
|
| 44 |
+
"Checking and downloading missing data lake files...\n",
|
| 45 |
+
"Downloading affinity_capture-ms.parquet from data_lake...\n"
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"name": "stderr",
|
| 50 |
+
"output_type": "stream",
|
| 51 |
+
"text": [
|
| 52 |
+
"affinity_capture-ms.parquet: 100%|█████████| 5.59M/5.59M [00:00<00:00, 11.8MB/s]\n"
|
| 53 |
+
]
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"name": "stdout",
|
| 57 |
+
"output_type": "stream",
|
| 58 |
+
"text": [
|
| 59 |
+
"✓ Successfully downloaded: affinity_capture-ms.parquet\n",
|
| 60 |
+
"Downloading affinity_capture-rna.parquet from data_lake...\n"
|
| 61 |
+
]
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"name": "stderr",
|
| 65 |
+
"output_type": "stream",
|
| 66 |
+
"text": [
|
| 67 |
+
"affinity_capture-rna.parquet: 100%|████████| 43.0k/43.0k [00:00<00:00, 1.21MB/s]\n"
|
| 68 |
+
]
|
| 69 |
+
},
|
| 70 |
+
{
|
| 71 |
+
"name": "stdout",
|
| 72 |
+
"output_type": "stream",
|
| 73 |
+
"text": [
|
| 74 |
+
"✓ Successfully downloaded: affinity_capture-rna.parquet\n",
|
| 75 |
+
"Downloading BindingDB_All_202409.tsv from data_lake...\n"
|
| 76 |
+
]
|
| 77 |
+
},
|
| 78 |
+
{
|
| 79 |
+
"name": "stderr",
|
| 80 |
+
"output_type": "stream",
|
| 81 |
+
"text": [
|
| 82 |
+
"BindingDB_All_202409.tsv: 2%|▎ | 154M/6.25G [00:43<28:35, 3.55MB/s]\n"
|
| 83 |
+
]
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"ename": "KeyboardInterrupt",
|
| 87 |
+
"evalue": "",
|
| 88 |
+
"output_type": "error",
|
| 89 |
+
"traceback": [
|
| 90 |
+
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
| 91 |
+
"\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)",
|
| 92 |
+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m 3\u001b[39m sys.path.append(\u001b[33m\"\u001b[39m\u001b[33m../\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 5\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mbiomni\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01magent\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m A1\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m agent = \u001b[43mA1\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpath\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m./biomni_data\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mllm\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mclaude-sonnet-4-5-20250929\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
|
| 93 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/Biomni/tutorials/../biomni/agent/a1.py:167\u001b[39m, in \u001b[36mA1.__init__\u001b[39m\u001b[34m(self, path, llm, source, use_tool_retriever, timeout_seconds, base_url, api_key, commercial_mode, expected_data_lake_files)\u001b[39m\n\u001b[32m 165\u001b[39m \u001b[38;5;66;03m# Check and download missing data lake files\u001b[39;00m\n\u001b[32m 166\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mChecking and downloading missing data lake files...\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m167\u001b[39m \u001b[43mcheck_and_download_s3_files\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 168\u001b[39m \u001b[43m \u001b[49m\u001b[43ms3_bucket_url\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mhttps://biomni-release.s3.amazonaws.com\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 169\u001b[39m \u001b[43m \u001b[49m\u001b[43mlocal_data_lake_path\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdata_lake_dir\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 170\u001b[39m \u001b[43m \u001b[49m\u001b[43mexpected_files\u001b[49m\u001b[43m=\u001b[49m\u001b[43mexpected_data_lake_files\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 171\u001b[39m \u001b[43m \u001b[49m\u001b[43mfolder\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mdata_lake\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 172\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 174\u001b[39m \u001b[38;5;66;03m# Check if benchmark directory structure is complete\u001b[39;00m\n\u001b[32m 175\u001b[39m benchmark_ok = \u001b[38;5;28;01mFalse\u001b[39;00m\n",
|
| 94 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/Biomni/tutorials/../biomni/utils.py:1011\u001b[39m, in \u001b[36mcheck_and_download_s3_files\u001b[39m\u001b[34m(s3_bucket_url, local_data_lake_path, expected_files, folder)\u001b[39m\n\u001b[32m 1008\u001b[39m s3_file_url = urljoin(s3_bucket_url + \u001b[33m\"\u001b[39m\u001b[33m/\u001b[39m\u001b[33m\"\u001b[39m + folder + \u001b[33m\"\u001b[39m\u001b[33m/\u001b[39m\u001b[33m\"\u001b[39m, filename)\n\u001b[32m 1009\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mDownloading \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfilename\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m from \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfolder\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m...\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m1011\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mdownload_with_progress\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms3_file_url\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlocal_file_path\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[32m 1012\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33m✓ Successfully downloaded: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfilename\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 1013\u001b[39m download_results[filename] = \u001b[38;5;28;01mTrue\u001b[39;00m\n",
|
| 95 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/Biomni/tutorials/../biomni/utils.py:949\u001b[39m, in \u001b[36mcheck_and_download_s3_files.<locals>.download_with_progress\u001b[39m\u001b[34m(url, file_path, desc)\u001b[39m\n\u001b[32m 947\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m total_size > \u001b[32m0\u001b[39m:\n\u001b[32m 948\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m tqdm.tqdm(total=total_size, unit=\u001b[33m\"\u001b[39m\u001b[33mB\u001b[39m\u001b[33m\"\u001b[39m, unit_scale=\u001b[38;5;28;01mTrue\u001b[39;00m, desc=desc, ncols=\u001b[32m80\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m pbar:\n\u001b[32m--> \u001b[39m\u001b[32m949\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mresponse\u001b[49m\u001b[43m.\u001b[49m\u001b[43miter_content\u001b[49m\u001b[43m(\u001b[49m\u001b[43mchunk_size\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m8192\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 950\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 951\u001b[39m \u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m.\u001b[49m\u001b[43mwrite\u001b[49m\u001b[43m(\u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m)\u001b[49m\n",
|
| 96 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/site-packages/requests/models.py:820\u001b[39m, in \u001b[36mResponse.iter_content.<locals>.generate\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m 818\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m.raw, \u001b[33m\"\u001b[39m\u001b[33mstream\u001b[39m\u001b[33m\"\u001b[39m):\n\u001b[32m 819\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m820\u001b[39m \u001b[38;5;28;01myield from\u001b[39;00m \u001b[38;5;28mself\u001b[39m.raw.stream(chunk_size, decode_content=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m 821\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m ProtocolError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 822\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m ChunkedEncodingError(e)\n",
|
| 97 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/site-packages/urllib3/response.py:1091\u001b[39m, in \u001b[36mHTTPResponse.stream\u001b[39m\u001b[34m(self, amt, decode_content)\u001b[39m\n\u001b[32m 1089\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 1090\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m is_fp_closed(\u001b[38;5;28mself\u001b[39m._fp) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m._decoded_buffer) > \u001b[32m0\u001b[39m:\n\u001b[32m-> \u001b[39m\u001b[32m1091\u001b[39m data = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m=\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1093\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m data:\n\u001b[32m 1094\u001b[39m \u001b[38;5;28;01myield\u001b[39;00m data\n",
|
| 98 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/site-packages/urllib3/response.py:980\u001b[39m, in \u001b[36mHTTPResponse.read\u001b[39m\u001b[34m(self, amt, decode_content, cache_content)\u001b[39m\n\u001b[32m 977\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m._decoded_buffer) >= amt:\n\u001b[32m 978\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._decoded_buffer.get(amt)\n\u001b[32m--> \u001b[39m\u001b[32m980\u001b[39m data = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_raw_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 982\u001b[39m flush_decoder = amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m (amt != \u001b[32m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data)\n\u001b[32m 984\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m._decoded_buffer) == \u001b[32m0\u001b[39m:\n",
|
| 99 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/site-packages/urllib3/response.py:904\u001b[39m, in \u001b[36mHTTPResponse._raw_read\u001b[39m\u001b[34m(self, amt, read1)\u001b[39m\n\u001b[32m 901\u001b[39m fp_closed = \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m._fp, \u001b[33m\"\u001b[39m\u001b[33mclosed\u001b[39m\u001b[33m\"\u001b[39m, \u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[32m 903\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._error_catcher():\n\u001b[32m--> \u001b[39m\u001b[32m904\u001b[39m data = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_fp_read\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mread1\u001b[49m\u001b[43m=\u001b[49m\u001b[43mread1\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m fp_closed \u001b[38;5;28;01melse\u001b[39;00m \u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 905\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m amt != \u001b[32m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data:\n\u001b[32m 906\u001b[39m \u001b[38;5;66;03m# Platform-specific: Buggy versions of Python.\u001b[39;00m\n\u001b[32m 907\u001b[39m \u001b[38;5;66;03m# Close the connection when no data is returned\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 912\u001b[39m \u001b[38;5;66;03m# not properly close the connection in all cases. There is\u001b[39;00m\n\u001b[32m 913\u001b[39m \u001b[38;5;66;03m# no harm in redundantly calling close.\u001b[39;00m\n\u001b[32m 914\u001b[39m \u001b[38;5;28mself\u001b[39m._fp.close()\n",
|
| 100 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/site-packages/urllib3/response.py:887\u001b[39m, in \u001b[36mHTTPResponse._fp_read\u001b[39m\u001b[34m(self, amt, read1)\u001b[39m\n\u001b[32m 884\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._fp.read1(amt) \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mself\u001b[39m._fp.read1()\n\u001b[32m 885\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 886\u001b[39m \u001b[38;5;66;03m# StringIO doesn't like amt=None\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m887\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_fp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mamt\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mif\u001b[39;00m amt \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mself\u001b[39m._fp.read()\n",
|
| 101 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/http/client.py:473\u001b[39m, in \u001b[36mHTTPResponse.read\u001b[39m\u001b[34m(self, amt)\u001b[39m\n\u001b[32m 470\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.length \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m amt > \u001b[38;5;28mself\u001b[39m.length:\n\u001b[32m 471\u001b[39m \u001b[38;5;66;03m# clip the read to the \"end of response\"\u001b[39;00m\n\u001b[32m 472\u001b[39m amt = \u001b[38;5;28mself\u001b[39m.length\n\u001b[32m--> \u001b[39m\u001b[32m473\u001b[39m s = \u001b[38;5;28mself\u001b[39m.fp.read(amt)\n\u001b[32m 474\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m s \u001b[38;5;129;01mand\u001b[39;00m amt:\n\u001b[32m 475\u001b[39m \u001b[38;5;66;03m# Ideally, we would raise IncompleteRead if the content-length\u001b[39;00m\n\u001b[32m 476\u001b[39m \u001b[38;5;66;03m# wasn't satisfied, but it might break compatibility.\u001b[39;00m\n\u001b[32m 477\u001b[39m \u001b[38;5;28mself\u001b[39m._close_conn()\n",
|
| 102 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/socket.py:718\u001b[39m, in \u001b[36mSocketIO.readinto\u001b[39m\u001b[34m(self, b)\u001b[39m\n\u001b[32m 716\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m 717\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m718\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sock\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrecv_into\u001b[49m\u001b[43m(\u001b[49m\u001b[43mb\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 719\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m timeout:\n\u001b[32m 720\u001b[39m \u001b[38;5;28mself\u001b[39m._timeout_occurred = \u001b[38;5;28;01mTrue\u001b[39;00m\n",
|
| 103 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/ssl.py:1314\u001b[39m, in \u001b[36mSSLSocket.recv_into\u001b[39m\u001b[34m(self, buffer, nbytes, flags)\u001b[39m\n\u001b[32m 1310\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m flags != \u001b[32m0\u001b[39m:\n\u001b[32m 1311\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 1312\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mnon-zero flags not allowed in calls to recv_into() on \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[33m\"\u001b[39m %\n\u001b[32m 1313\u001b[39m \u001b[38;5;28mself\u001b[39m.\u001b[34m__class__\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m1314\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnbytes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1315\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 1316\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msuper\u001b[39m().recv_into(buffer, nbytes, flags)\n",
|
| 104 |
+
"\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/biomni_fixed/lib/python3.11/ssl.py:1166\u001b[39m, in \u001b[36mSSLSocket.read\u001b[39m\u001b[34m(self, len, buffer)\u001b[39m\n\u001b[32m 1164\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 1165\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m buffer \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1166\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sslobj\u001b[49m\u001b[43m.\u001b[49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbuffer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1167\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 1168\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._sslobj.read(\u001b[38;5;28mlen\u001b[39m)\n",
|
| 105 |
+
"\u001b[31mKeyboardInterrupt\u001b[39m: "
|
| 106 |
+
]
|
| 107 |
+
}
|
| 108 |
+
],
|
| 109 |
+
"source": [
|
| 110 |
+
"import sys\n",
|
| 111 |
+
"\n",
|
| 112 |
+
"sys.path.append(\"../\")\n",
|
| 113 |
+
"\n",
|
| 114 |
+
"from biomni.agent import A1\n",
|
| 115 |
+
"\n",
|
| 116 |
+
"agent = A1(path=\"./biomni_data\", llm=\"claude-sonnet-4-5-20250929\")"
|
| 117 |
+
]
|
| 118 |
+
},
|
| 119 |
+
{
|
| 120 |
+
"cell_type": "markdown",
|
| 121 |
+
"metadata": {},
|
| 122 |
+
"source": [
|
| 123 |
+
"Then, you can simply start prompting the agent with the desired biomedical research task!"
|
| 124 |
+
]
|
| 125 |
+
},
|
| 126 |
+
{
|
| 127 |
+
"cell_type": "code",
|
| 128 |
+
"execution_count": 2,
|
| 129 |
+
"metadata": {},
|
| 130 |
+
"outputs": [
|
| 131 |
+
{
|
| 132 |
+
"name": "stdout",
|
| 133 |
+
"output_type": "stream",
|
| 134 |
+
"text": [
|
| 135 |
+
"Using prompt-based retrieval with the agent's LLM\n",
|
| 136 |
+
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
| 137 |
+
"\n",
|
| 138 |
+
"Plan a CRISPR screen to identify genes that regulate T cell exhaustion, \n",
|
| 139 |
+
" measured by the change in T cell receptor (TCR) signaling between acute \n",
|
| 140 |
+
" (interleukin-2 [IL-2] only) and chronic (anti-CD3 and IL-2) stimulation conditions. \n",
|
| 141 |
+
" Generate 32 genes that maximize the perturbation effect.\n",
|
| 142 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 143 |
+
"\n",
|
| 144 |
+
"I'll help you plan a CRISPR screen to identify genes that regulate T cell exhaustion. Let me break this down into a systematic approach.\n",
|
| 145 |
+
"\n",
|
| 146 |
+
"## Plan:\n",
|
| 147 |
+
"\n",
|
| 148 |
+
"1. [ ] Research T cell exhaustion biology and identify key pathways\n",
|
| 149 |
+
"2. [ ] Query databases for genes involved in T cell exhaustion, TCR signaling, and chronic stimulation\n",
|
| 150 |
+
"3. [ ] Identify genes from exhaustion-related pathways (checkpoint inhibitors, metabolic regulators, transcription factors)\n",
|
| 151 |
+
"4. [ ] Select genes that are likely to show differential effects between acute vs chronic stimulation\n",
|
| 152 |
+
"5. [ ] Design sgRNAs for the selected 32 genes\n",
|
| 153 |
+
"6. [ ] Validate the gene list and provide experimental design recommendations\n",
|
| 154 |
+
"\n",
|
| 155 |
+
"Let me start by researching the biological basis of T cell exhaustion and identifying relevant genes.\n",
|
| 156 |
+
"\n",
|
| 157 |
+
"<execute>\n",
|
| 158 |
+
"# First, let's import necessary libraries and query databases for T cell exhaustion-related genes\n",
|
| 159 |
+
"from biomni.tool.database import query_uniprot, query_kegg, query_reactome, query_ensembl\n",
|
| 160 |
+
"from biomni.tool.genomics import gene_set_enrichment_analysis, get_gene_set_enrichment_analysis_supported_database_list\n",
|
| 161 |
+
"from biomni.tool.molecular_biology import design_knockout_sgrna\n",
|
| 162 |
+
"import pandas as pd\n",
|
| 163 |
+
"import numpy as np\n",
|
| 164 |
+
"\n",
|
| 165 |
+
"print(\"=== Step 1: Researching T cell exhaustion biology ===\")\n",
|
| 166 |
+
"\n",
|
| 167 |
+
"# Query for T cell exhaustion pathways\n",
|
| 168 |
+
"exhaustion_pathways = query_reactome(\n",
|
| 169 |
+
" prompt=\"Find pathways related to T cell exhaustion, TCR signaling, and chronic T cell stimulation\"\n",
|
| 170 |
+
")\n",
|
| 171 |
+
"print(\"Reactome pathways for T cell exhaustion:\")\n",
|
| 172 |
+
"print(exhaustion_pathways)\n",
|
| 173 |
+
"</execute>\n",
|
| 174 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 175 |
+
"\n",
|
| 176 |
+
"<observation>The output is too long to be added to context. Here are the first 10K characters...\n",
|
| 177 |
+
"=== Step 1: Researching T cell exhaustion biology ===\n",
|
| 178 |
+
"Reactome pathways for T cell exhaustion:\n",
|
| 179 |
+
"{'success': True, 'query_info': {'endpoint': 'https://reactome.org/ContentService/search/query', 'method': 'GET', 'description': 'Search Reactome for pathways related to T cell exhaustion, T cell receptor signaling, and chronic T cell stimulation'}, 'result': {'results': [{'entries': [{'dbId': '202403', 'stId': 'R-HSA-202403', 'id': 'R-HSA-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Homo sapiens'], 'summation': 'The <span class=\"highlighting\" >TCR</span> is a multisubunit complex that consists of clonotypic alpha/beta chains noncovalently associated with the invariant CD3 delta/epsilon/gamma and <span class=\"highlighting\" >TCR</span> zeta chains. T <span class=\"highlighting\" >cell</span> activation by antigen presenting cells (APCs) results in the activation of protein tyrosine kinases (PTKs) that associate with CD3 and <span class=\"highlighting\" >TCR</span> zeta subunits and the co-receptor CD4. Members of the Src kinases (Lck), Syk kinases (ZAP-70), Tec (Itk) and Csk families of nonreceptor PTKs play a crucial role in T <span class=\"highlighting\" >cell</span> activation. Activation of PTKs following <span class=\"highlighting\" >TCR</span> engagement results in the recruitment and tyrosine phosphorylation of enzymes such as phospholipase C gamma1 and Vav as well as critical adaptor proteins such as LAT, SLP-76 and Gads. These proximal activation leads to reorganization of the cytoskeleton as well as transcription activation of multiple genes leading to T lymphocyte proliferation, differentiation and/or effector function. ', 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10069478', 'stId': 'R-MMU-202403', 'id': 'R-MMU-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Mus musculus'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10185057', 'stId': 'R-RNO-202403', 'id': 'R-RNO-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Rattus norvegicus'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10293315', 'stId': 'R-CFA-202403', 'id': 'R-CFA-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Canis familiaris'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10407569', 'stId': 'R-BTA-202403', 'id': 'R-BTA-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Bos taurus'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10523735', 'stId': 'R-SSC-202403', 'id': 'R-SSC-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Sus scrofa'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10585787', 'stId': 'R-DRE-202403', 'id': 'R-DRE-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Danio rerio'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10661801', 'stId': 'R-XTR-202403', 'id': 'R-XTR-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Xenopus tropicalis'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10759741', 'stId': 'R-GGA-202403', 'id': 'R-GGA-202403', 'name': '<span class=\"highlighting\" >TCR</span> <span class=\"highlighting\" >signaling</span>', 'exactType': 'Pathway', 'species': ['Gallus gallus'], 'summation': \"This event has been computationally inferred from an event that has been demonstrated in another species.<p>The inference is based on the homology mapping from PANTHER. Briefly, reactions for which all involved PhysicalEntities (in input, output and catalyst) have a mapped orthologue/paralogue (for complexes at least 75% of components must have a mapping) are inferred to the other species. High level events are also inferred for these events to allow for easier navigation.<p><a href='/electronic_inference_compara.html' target = 'NEW'>More details and caveats of the event inference in Reactome.</a> For details on PANTHER see also: <a href='http://www.pantherdb.org/about.jsp' target='NEW'>http://www.pantherdb.org/about.jsp</a>\", 'isDisease': False, 'disease': False, 'icon': False}, {'dbId': '10848043', 'stId': 'R-D</observation>\n",
|
| 180 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 181 |
+
"\n",
|
| 182 |
+
"<execute>\n",
|
| 183 |
+
"# Let's also query for immune checkpoint and T cell activation pathways\n",
|
| 184 |
+
"print(\"\\n=== Querying for immune checkpoint pathways ===\")\n",
|
| 185 |
+
"checkpoint_pathways = query_reactome(\n",
|
| 186 |
+
" prompt=\"Find pathways related to immune checkpoint inhibition, PD-1, CTLA-4, and T cell activation\"\n",
|
| 187 |
+
")\n",
|
| 188 |
+
"print(\"Checkpoint pathways found:\", len(checkpoint_pathways.get('result', {}).get('results', [])))\n",
|
| 189 |
+
"</execute>\n",
|
| 190 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 191 |
+
"\n",
|
| 192 |
+
"<observation>\n",
|
| 193 |
+
"=== Querying for immune checkpoint pathways ===\n",
|
| 194 |
+
"Checkpoint pathways found: 10\n",
|
| 195 |
+
"</observation>\n",
|
| 196 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 197 |
+
"\n",
|
| 198 |
+
"<execute>\n",
|
| 199 |
+
"# Now let's identify key genes involved in T cell exhaustion from literature and databases\n",
|
| 200 |
+
"print(\"=== Step 2: Identifying key T cell exhaustion genes ===\")\n",
|
| 201 |
+
"\n",
|
| 202 |
+
"# Key categories of genes involved in T cell exhaustion:\n",
|
| 203 |
+
"# 1. Immune checkpoint receptors and ligands\n",
|
| 204 |
+
"checkpoint_genes = [\n",
|
| 205 |
+
" 'PDCD1', # PD-1\n",
|
| 206 |
+
" 'CD274', # PD-L1\n",
|
| 207 |
+
" 'PDCD1LG2', # PD-L2\n",
|
| 208 |
+
" 'CTLA4', # CTLA-4\n",
|
| 209 |
+
" 'HAVCR2', # TIM-3\n",
|
| 210 |
+
" 'LAG3', # LAG-3\n",
|
| 211 |
+
" 'TIGIT', # TIGIT\n",
|
| 212 |
+
" 'CD96', # CD96\n",
|
| 213 |
+
" 'BTLA', # BTLA\n",
|
| 214 |
+
" 'CD160' # CD160\n",
|
| 215 |
+
"]\n",
|
| 216 |
+
"\n",
|
| 217 |
+
"# 2. Transcription factors regulating exhaustion\n",
|
| 218 |
+
"transcription_factors = [\n",
|
| 219 |
+
" 'TOX', # TOX - master regulator of exhaustion\n",
|
| 220 |
+
" 'TOX2', # TOX2\n",
|
| 221 |
+
" 'EOMES', # Eomesodermin\n",
|
| 222 |
+
" 'TBX21', # T-bet\n",
|
| 223 |
+
" 'BATF', # BATF\n",
|
| 224 |
+
" 'IRF4', # IRF4\n",
|
| 225 |
+
" 'NFAT5', # NFAT5\n",
|
| 226 |
+
" 'NFATC1', # NFATC1\n",
|
| 227 |
+
" 'RUNX3', # RUNX3\n",
|
| 228 |
+
" 'STAT3' # STAT3\n",
|
| 229 |
+
"]\n",
|
| 230 |
+
"\n",
|
| 231 |
+
"# 3. Metabolic regulators\n",
|
| 232 |
+
"metabolic_genes = [\n",
|
| 233 |
+
" 'PPAR', # PPAR family\n",
|
| 234 |
+
" 'HIF1A', # HIF-1α\n",
|
| 235 |
+
" 'MYC', # c-Myc\n",
|
| 236 |
+
" 'MTOR', # mTOR\n",
|
| 237 |
+
" 'AMPK', # AMPK (PRKAA1)\n",
|
| 238 |
+
" 'LDHA', # Lactate dehydrogenase A\n",
|
| 239 |
+
" 'PKM', # Pyruvate kinase M\n",
|
| 240 |
+
" 'GLUT1' # Glucose transporter 1 (SLC2A1)\n",
|
| 241 |
+
"]\n",
|
| 242 |
+
"\n",
|
| 243 |
+
"# Let's refine the metabolic genes with proper gene symbols\n",
|
| 244 |
+
"metabolic_genes_refined = [\n",
|
| 245 |
+
" 'PPARA', # PPAR alpha\n",
|
| 246 |
+
" 'HIF1A', # HIF-1α\n",
|
| 247 |
+
" 'MYC', # c-Myc\n",
|
| 248 |
+
" 'MTOR', # mTOR\n",
|
| 249 |
+
" 'PRKAA1', # AMPK alpha 1\n",
|
| 250 |
+
" 'LDHA', # Lactate dehydrogenase A\n",
|
| 251 |
+
" 'PKM', # Pyruvate kinase M\n",
|
| 252 |
+
" 'SLC2A1' # GLUT1\n",
|
| 253 |
+
"]\n",
|
| 254 |
+
"\n",
|
| 255 |
+
"# 4. TCR signaling components\n",
|
| 256 |
+
"tcr_signaling_genes = [\n",
|
| 257 |
+
" 'LCK', # Lck\n",
|
| 258 |
+
" 'ZAP70', # ZAP-70\n",
|
| 259 |
+
" 'LAT', # LAT\n",
|
| 260 |
+
" 'SLP76', # SLP-76 (LCP2)\n",
|
| 261 |
+
" 'PLCG1', # PLCγ1\n",
|
| 262 |
+
" 'VAV1', # Vav1\n",
|
| 263 |
+
" 'ITK', # Itk\n",
|
| 264 |
+
" 'CD3E', # CD3ε\n",
|
| 265 |
+
" 'CD3G', # CD3γ\n",
|
| 266 |
+
" 'CD3D' # CD3δ\n",
|
| 267 |
+
"]\n",
|
| 268 |
+
"\n",
|
| 269 |
+
"# Refine TCR signaling genes\n",
|
| 270 |
+
"tcr_signaling_genes_refined = [\n",
|
| 271 |
+
" 'LCK',\n",
|
| 272 |
+
" 'ZAP70',\n",
|
| 273 |
+
" 'LAT',\n",
|
| 274 |
+
" 'LCP2', # SLP-76\n",
|
| 275 |
+
" 'PLCG1',\n",
|
| 276 |
+
" 'VAV1',\n",
|
| 277 |
+
" 'ITK',\n",
|
| 278 |
+
" 'CD3E',\n",
|
| 279 |
+
" 'CD3G',\n",
|
| 280 |
+
" 'CD3D'\n",
|
| 281 |
+
"]\n",
|
| 282 |
+
"\n",
|
| 283 |
+
"print(\"Checkpoint genes:\", len(checkpoint_genes))\n",
|
| 284 |
+
"print(\"Transcription factors:\", len(transcription_factors))\n",
|
| 285 |
+
"print(\"Metabolic genes:\", len(metabolic_genes_refined))\n",
|
| 286 |
+
"print(\"TCR signaling genes:\", len(tcr_signaling_genes_refined))\n",
|
| 287 |
+
"\n",
|
| 288 |
+
"# Combine all categories\n",
|
| 289 |
+
"all_candidate_genes = checkpoint_genes + transcription_factors + metabolic_genes_refined + tcr_signaling_genes_refined\n",
|
| 290 |
+
"print(f\"\\nTotal candidate genes: {len(all_candidate_genes)}\")\n",
|
| 291 |
+
"print(\"All candidates:\", all_candidate_genes)\n",
|
| 292 |
+
"</execute>\n",
|
| 293 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 294 |
+
"\n",
|
| 295 |
+
"<observation>=== Step 2: Identifying key T cell exhaustion genes ===\n",
|
| 296 |
+
"Checkpoint genes: 10\n",
|
| 297 |
+
"Transcription factors: 10\n",
|
| 298 |
+
"Metabolic genes: 8\n",
|
| 299 |
+
"TCR signaling genes: 10\n",
|
| 300 |
+
"\n",
|
| 301 |
+
"Total candidate genes: 38\n",
|
| 302 |
+
"All candidates: ['PDCD1', 'CD274', 'PDCD1LG2', 'CTLA4', 'HAVCR2', 'LAG3', 'TIGIT', 'CD96', 'BTLA', 'CD160', 'TOX', 'TOX2', 'EOMES', 'TBX21', 'BATF', 'IRF4', 'NFAT5', 'NFATC1', 'RUNX3', 'STAT3', 'PPARA', 'HIF1A', 'MYC', 'MTOR', 'PRKAA1', 'LDHA', 'PKM', 'SLC2A1', 'LCK', 'ZAP70', 'LAT', 'LCP2', 'PLCG1', 'VAV1', 'ITK', 'CD3E', 'CD3G', 'CD3D']\n",
|
| 303 |
+
"</observation>\n",
|
| 304 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 305 |
+
"\n",
|
| 306 |
+
"<execute>\n",
|
| 307 |
+
"# Now let's prioritize and select the most impactful 32 genes\n",
|
| 308 |
+
"print(\"=== Step 3: Prioritizing genes for maximum perturbation effect ===\")\n",
|
| 309 |
+
"\n",
|
| 310 |
+
"# Priority scoring based on:\n",
|
| 311 |
+
"# 1. Known role in T cell exhaustion (literature evidence)\n",
|
| 312 |
+
"# 2. Potential for differential effect between acute vs chronic stimulation\n",
|
| 313 |
+
"# 3. Druggability/therapeutic relevance\n",
|
| 314 |
+
"\n",
|
| 315 |
+
"high_priority_genes = {\n",
|
| 316 |
+
" # Master regulators of exhaustion (highest priority)\n",
|
| 317 |
+
" 'TOX': {'priority': 10, 'category': 'Master TF', 'rationale': 'Master regulator of T cell exhaustion'},\n",
|
| 318 |
+
" 'PDCD1': {'priority': 10, 'category': 'Checkpoint', 'rationale': 'PD-1, primary exhaustion checkpoint'},\n",
|
| 319 |
+
" 'EOMES': {'priority': 9, 'category': 'TF', 'rationale': 'Key exhaustion transcription factor'},\n",
|
| 320 |
+
" 'HAVCR2': {'priority': 9, 'category': 'Checkpoint', 'rationale': 'TIM-3, critical exhaustion marker'},\n",
|
| 321 |
+
" \n",
|
| 322 |
+
" # High impact checkpoint receptors\n",
|
| 323 |
+
" 'LAG3': {'priority': 9, 'category': 'Checkpoint', 'rationale': 'LAG-3, major exhaustion checkpoint'},\n",
|
| 324 |
+
" 'TIGIT': {'priority': 8, 'category': 'Checkpoint', 'rationale': 'TIGIT, emerging exhaustion target'},\n",
|
| 325 |
+
" 'CTLA4': {'priority': 8, 'category': 'Checkpoint', 'rationale': 'CTLA-4, early checkpoint inhibitor'},\n",
|
| 326 |
+
" \n",
|
| 327 |
+
" # Transcription factors\n",
|
| 328 |
+
" 'BATF': {'priority': 8, 'category': 'TF', 'rationale': 'BATF, exhaustion-promoting TF'},\n",
|
| 329 |
+
" 'TBX21': {'priority': 7, 'category': 'TF', 'rationale': 'T-bet, effector vs exhaustion balance'},\n",
|
| 330 |
+
" 'IRF4': {'priority': 7, 'category': 'TF', 'rationale': 'IRF4, T cell differentiation'},\n",
|
| 331 |
+
" 'NFATC1': {'priority': 7, 'category': 'TF', 'rationale': 'NFAT, chronic stimulation response'},\n",
|
| 332 |
+
" 'TOX2': {'priority': 7, 'category': 'TF', 'rationale': 'TOX2, TOX family member'},\n",
|
| 333 |
+
" \n",
|
| 334 |
+
" # Metabolic regulators (key for chronic vs acute differences)\n",
|
| 335 |
+
" 'HIF1A': {'priority': 8, 'category': 'Metabolism', 'rationale': 'HIF-1α, hypoxia and exhaustion'},\n",
|
| 336 |
+
" 'MYC': {'priority': 8, 'category': 'Metabolism', 'rationale': 'c-Myc, metabolic reprogramming'},\n",
|
| 337 |
+
" 'MTOR': {'priority': 7, 'category': 'Metabolism', 'rationale': 'mTOR, metabolic sensing'},\n",
|
| 338 |
+
" 'PRKAA1': {'priority': 7, 'category': 'Metabolism', 'rationale': 'AMPK, energy sensing'},\n",
|
| 339 |
+
" 'LDHA': {'priority': 6, 'category': 'Metabolism', 'rationale': 'Lactate metabolism in exhaustion'},\n",
|
| 340 |
+
" \n",
|
| 341 |
+
" # TCR signaling (differential between acute/chronic)\n",
|
| 342 |
+
" 'LCK': {'priority': 8, 'category': 'TCR', 'rationale': 'Lck, proximal TCR signaling'},\n",
|
| 343 |
+
" 'ZAP70': {'priority': 8, 'category': 'TCR', 'rationale': 'ZAP-70, TCR signal strength'},\n",
|
| 344 |
+
" 'LAT': {'priority': 7, 'category': 'TCR', 'rationale': 'LAT, TCR signaling hub'},\n",
|
| 345 |
+
" 'PLCG1': {'priority': 7, 'category': 'TCR', 'rationale': 'PLCγ1, downstream TCR signaling'},\n",
|
| 346 |
+
" 'LCP2': {'priority': 6, 'category': 'TCR', 'rationale': 'SLP-76, TCR signaling adaptor'},\n",
|
| 347 |
+
" \n",
|
| 348 |
+
" # Additional checkpoint molecules\n",
|
| 349 |
+
" 'CD274': {'priority': 6, 'category': 'Checkpoint', 'rationale': 'PD-L1, PD-1 ligand'},\n",
|
| 350 |
+
" 'BTLA': {'priority': 6, 'category': 'Checkpoint', 'rationale': 'BTLA, inhibitory receptor'},\n",
|
| 351 |
+
" 'CD96': {'priority': 6, 'category': 'Checkpoint', 'rationale': 'CD96, TIGIT pathway'},\n",
|
| 352 |
+
" \n",
|
| 353 |
+
" # Additional metabolic/signaling\n",
|
| 354 |
+
" 'STAT3': {'priority': 7, 'category': 'Signaling', 'rationale': 'STAT3, cytokine signaling'},\n",
|
| 355 |
+
" 'VAV1': {'priority': 6, 'category': 'TCR', 'rationale': 'Vav1, TCR signaling'},\n",
|
| 356 |
+
" 'ITK': {'priority': 6, 'category': 'TCR', 'rationale': 'Itk, TCR signaling kinase'},\n",
|
| 357 |
+
" 'RUNX3': {'priority': 6, 'category': 'TF', 'rationale': 'RUNX3, T cell differentiation'},\n",
|
| 358 |
+
" 'PKM': {'priority': 5, 'category': 'Metabolism', 'rationale': 'Pyruvate kinase, glycolysis'},\n",
|
| 359 |
+
" 'SLC2A1': {'priority': 5, 'category': 'Metabolism', 'rationale': 'GLUT1, glucose uptake'},\n",
|
| 360 |
+
" 'PPARA': {'priority': 5, 'category': 'Metabolism', 'rationale': 'PPARα, fatty acid oxidation'},\n",
|
| 361 |
+
" 'CD160': {'priority': 5, 'category': 'Checkpoint', 'rationale': 'CD160, inhibitory receptor'}\n",
|
| 362 |
+
"}\n",
|
| 363 |
+
"\n",
|
| 364 |
+
"# Sort by priority and select top 32\n",
|
| 365 |
+
"sorted_genes = sorted(high_priority_genes.items(), key=lambda x: x[1]['priority'], reverse=True)\n",
|
| 366 |
+
"selected_32_genes = [gene for gene, info in sorted_genes[:32]]\n",
|
| 367 |
+
"\n",
|
| 368 |
+
"print(f\"Selected 32 genes for CRISPR screen:\")\n",
|
| 369 |
+
"for i, (gene, info) in enumerate(sorted_genes[:32], 1):\n",
|
| 370 |
+
" print(f\"{i:2d}. {gene:<8} (Priority: {info['priority']}, {info['category']}) - {info['rationale']}\")\n",
|
| 371 |
+
"\n",
|
| 372 |
+
"print(f\"\\nFinal gene list: {selected_32_genes}\")\n",
|
| 373 |
+
"</execute>\n",
|
| 374 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 375 |
+
"\n",
|
| 376 |
+
"<observation>=== Step 3: Prioritizing genes for maximum perturbation effect ===\n",
|
| 377 |
+
"Selected 32 genes for CRISPR screen:\n",
|
| 378 |
+
" 1. TOX (Priority: 10, Master TF) - Master regulator of T cell exhaustion\n",
|
| 379 |
+
" 2. PDCD1 (Priority: 10, Checkpoint) - PD-1, primary exhaustion checkpoint\n",
|
| 380 |
+
" 3. EOMES (Priority: 9, TF) - Key exhaustion transcription factor\n",
|
| 381 |
+
" 4. HAVCR2 (Priority: 9, Checkpoint) - TIM-3, critical exhaustion marker\n",
|
| 382 |
+
" 5. LAG3 (Priority: 9, Checkpoint) - LAG-3, major exhaustion checkpoint\n",
|
| 383 |
+
" 6. TIGIT (Priority: 8, Checkpoint) - TIGIT, emerging exhaustion target\n",
|
| 384 |
+
" 7. CTLA4 (Priority: 8, Checkpoint) - CTLA-4, early checkpoint inhibitor\n",
|
| 385 |
+
" 8. BATF (Priority: 8, TF) - BATF, exhaustion-promoting TF\n",
|
| 386 |
+
" 9. HIF1A (Priority: 8, Metabolism) - HIF-1α, hypoxia and exhaustion\n",
|
| 387 |
+
"10. MYC (Priority: 8, Metabolism) - c-Myc, metabolic reprogramming\n",
|
| 388 |
+
"11. LCK (Priority: 8, TCR) - Lck, proximal TCR signaling\n",
|
| 389 |
+
"12. ZAP70 (Priority: 8, TCR) - ZAP-70, TCR signal strength\n",
|
| 390 |
+
"13. TBX21 (Priority: 7, TF) - T-bet, effector vs exhaustion balance\n",
|
| 391 |
+
"14. IRF4 (Priority: 7, TF) - IRF4, T cell differentiation\n",
|
| 392 |
+
"15. NFATC1 (Priority: 7, TF) - NFAT, chronic stimulation response\n",
|
| 393 |
+
"16. TOX2 (Priority: 7, TF) - TOX2, TOX family member\n",
|
| 394 |
+
"17. MTOR (Priority: 7, Metabolism) - mTOR, metabolic sensing\n",
|
| 395 |
+
"18. PRKAA1 (Priority: 7, Metabolism) - AMPK, energy sensing\n",
|
| 396 |
+
"19. LAT (Priority: 7, TCR) - LAT, TCR signaling hub\n",
|
| 397 |
+
"20. PLCG1 (Priority: 7, TCR) - PLCγ1, downstream TCR signaling\n",
|
| 398 |
+
"21. STAT3 (Priority: 7, Signaling) - STAT3, cytokine signaling\n",
|
| 399 |
+
"22. LDHA (Priority: 6, Metabolism) - Lactate metabolism in exhaustion\n",
|
| 400 |
+
"23. LCP2 (Priority: 6, TCR) - SLP-76, TCR signaling adaptor\n",
|
| 401 |
+
"24. CD274 (Priority: 6, Checkpoint) - PD-L1, PD-1 ligand\n",
|
| 402 |
+
"25. BTLA (Priority: 6, Checkpoint) - BTLA, inhibitory receptor\n",
|
| 403 |
+
"26. CD96 (Priority: 6, Checkpoint) - CD96, TIGIT pathway\n",
|
| 404 |
+
"27. VAV1 (Priority: 6, TCR) - Vav1, TCR signaling\n",
|
| 405 |
+
"28. ITK (Priority: 6, TCR) - Itk, TCR signaling kinase\n",
|
| 406 |
+
"29. RUNX3 (Priority: 6, TF) - RUNX3, T cell differentiation\n",
|
| 407 |
+
"30. PKM (Priority: 5, Metabolism) - Pyruvate kinase, glycolysis\n",
|
| 408 |
+
"31. SLC2A1 (Priority: 5, Metabolism) - GLUT1, glucose uptake\n",
|
| 409 |
+
"32. PPARA (Priority: 5, Metabolism) - PPARα, fatty acid oxidation\n",
|
| 410 |
+
"\n",
|
| 411 |
+
"Final gene list: ['TOX', 'PDCD1', 'EOMES', 'HAVCR2', 'LAG3', 'TIGIT', 'CTLA4', 'BATF', 'HIF1A', 'MYC', 'LCK', 'ZAP70', 'TBX21', 'IRF4', 'NFATC1', 'TOX2', 'MTOR', 'PRKAA1', 'LAT', 'PLCG1', 'STAT3', 'LDHA', 'LCP2', 'CD274', 'BTLA', 'CD96', 'VAV1', 'ITK', 'RUNX3', 'PKM', 'SLC2A1', 'PPARA']\n",
|
| 412 |
+
"</observation>\n",
|
| 413 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 414 |
+
"\n",
|
| 415 |
+
"1. [✓] Research T cell exhaustion biology and identify key pathways (completed)\n",
|
| 416 |
+
"2. [✓] Query databases for genes involved in T cell exhaustion, TCR signaling, and chronic stimulation (completed)\n",
|
| 417 |
+
"3. [✓] Identify genes from exhaustion-related pathways (checkpoint inhibitors, metabolic regulators, transcription factors) (completed)\n",
|
| 418 |
+
"4. [✓] Select genes that are likely to show differential effects between acute vs chronic stimulation (completed)\n",
|
| 419 |
+
"5. [ ] Design sgRNAs for the selected 32 genes\n",
|
| 420 |
+
"6. [ ] Validate the gene list and provide experimental design recommendations\n",
|
| 421 |
+
"\n",
|
| 422 |
+
"<execute>\n",
|
| 423 |
+
"# Now let's design sgRNAs for our selected genes\n",
|
| 424 |
+
"print(\"=== Step 4: Designing sgRNAs for selected genes ===\")\n",
|
| 425 |
+
"\n",
|
| 426 |
+
"data_lake_path = \"/dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake\"\n",
|
| 427 |
+
"\n",
|
| 428 |
+
"# We'll design sgRNAs for each of our 32 selected genes\n",
|
| 429 |
+
"selected_genes = ['TOX', 'PDCD1', 'EOMES', 'HAVCR2', 'LAG3', 'TIGIT', 'CTLA4', 'BATF', 'HIF1A', 'MYC', \n",
|
| 430 |
+
" 'LCK', 'ZAP70', 'TBX21', 'IRF4', 'NFATC1', 'TOX2', 'MTOR', 'PRKAA1', 'LAT', 'PLCG1', \n",
|
| 431 |
+
" 'STAT3', 'LDHA', 'LCP2', 'CD274', 'BTLA', 'CD96', 'VAV1', 'ITK', 'RUNX3', 'PKM', \n",
|
| 432 |
+
" 'SLC2A1', 'PPARA']\n",
|
| 433 |
+
"\n",
|
| 434 |
+
"print(f\"Designing sgRNAs for {len(selected_genes)} genes...\")\n",
|
| 435 |
+
"\n",
|
| 436 |
+
"# Let's try to design sgRNAs for a few key genes as examples\n",
|
| 437 |
+
"key_genes_for_sgrna = ['TOX', 'PDCD1', 'EOMES', 'HAVCR2', 'LAG3']\n",
|
| 438 |
+
"\n",
|
| 439 |
+
"sgrna_results = {}\n",
|
| 440 |
+
"for gene in key_genes_for_sgrna:\n",
|
| 441 |
+
" try:\n",
|
| 442 |
+
" print(f\"\\nDesigning sgRNAs for {gene}...\")\n",
|
| 443 |
+
" result = design_knockout_sgrna(\n",
|
| 444 |
+
" gene_name=gene,\n",
|
| 445 |
+
" data_lake_path=data_lake_path,\n",
|
| 446 |
+
" species=\"human\",\n",
|
| 447 |
+
" num_guides=3 # Get 3 guides per gene for redundancy\n",
|
| 448 |
+
" )\n",
|
| 449 |
+
" sgrna_results[gene] = result\n",
|
| 450 |
+
" print(f\"Successfully designed sgRNAs for {gene}\")\n",
|
| 451 |
+
" if result:\n",
|
| 452 |
+
" print(f\"Result type: {type(result)}\")\n",
|
| 453 |
+
" except Exception as e:\n",
|
| 454 |
+
" print(f\"Error designing sgRNAs for {gene}: {str(e)}\")\n",
|
| 455 |
+
" sgrna_results[gene] = None\n",
|
| 456 |
+
"\n",
|
| 457 |
+
"print(f\"\\nsgRNA design completed for {len(sgrna_results)} genes\")\n",
|
| 458 |
+
"</execute>\n",
|
| 459 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 460 |
+
"\n",
|
| 461 |
+
"<observation>=== Step 4: Designing sgRNAs for selected genes ===\n",
|
| 462 |
+
"Designing sgRNAs for 32 genes...\n",
|
| 463 |
+
"\n",
|
| 464 |
+
"Designing sgRNAs for TOX...\n",
|
| 465 |
+
"Error designing sgRNAs for TOX: Library file for human not found at path: /dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake/sgRNA/KO_SP_human.txt\n",
|
| 466 |
+
"\n",
|
| 467 |
+
"Designing sgRNAs for PDCD1...\n",
|
| 468 |
+
"Error designing sgRNAs for PDCD1: Library file for human not found at path: /dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake/sgRNA/KO_SP_human.txt\n",
|
| 469 |
+
"\n",
|
| 470 |
+
"Designing sgRNAs for EOMES...\n",
|
| 471 |
+
"Error designing sgRNAs for EOMES: Library file for human not found at path: /dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake/sgRNA/KO_SP_human.txt\n",
|
| 472 |
+
"\n",
|
| 473 |
+
"Designing sgRNAs for HAVCR2...\n",
|
| 474 |
+
"Error designing sgRNAs for HAVCR2: Library file for human not found at path: /dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake/sgRNA/KO_SP_human.txt\n",
|
| 475 |
+
"\n",
|
| 476 |
+
"Designing sgRNAs for LAG3...\n",
|
| 477 |
+
"Error designing sgRNAs for LAG3: Library file for human not found at path: /dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake/sgRNA/KO_SP_human.txt\n",
|
| 478 |
+
"\n",
|
| 479 |
+
"sgRNA design completed for 5 genes\n",
|
| 480 |
+
"</observation>\n",
|
| 481 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 482 |
+
"\n",
|
| 483 |
+
"<execute>\n",
|
| 484 |
+
"# Let's check what's available in the data lake for sgRNA design\n",
|
| 485 |
+
"import os\n",
|
| 486 |
+
"\n",
|
| 487 |
+
"print(\"=== Checking data lake contents for sgRNA libraries ===\")\n",
|
| 488 |
+
"data_lake_path = \"/dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake\"\n",
|
| 489 |
+
"\n",
|
| 490 |
+
"# Check if the directory exists\n",
|
| 491 |
+
"if os.path.exists(data_lake_path):\n",
|
| 492 |
+
" print(f\"Data lake path exists: {data_lake_path}\")\n",
|
| 493 |
+
" \n",
|
| 494 |
+
" # List contents\n",
|
| 495 |
+
" contents = os.listdir(data_lake_path)\n",
|
| 496 |
+
" print(f\"Contents of data lake: {contents}\")\n",
|
| 497 |
+
" \n",
|
| 498 |
+
" # Check for sgRNA directory\n",
|
| 499 |
+
" sgrna_path = os.path.join(data_lake_path, \"sgRNA\")\n",
|
| 500 |
+
" if os.path.exists(sgrna_path):\n",
|
| 501 |
+
" print(f\"sgRNA directory exists\")\n",
|
| 502 |
+
" sgrna_contents = os.listdir(sgrna_path)\n",
|
| 503 |
+
" print(f\"sgRNA directory contents: {sgrna_contents}\")\n",
|
| 504 |
+
" else:\n",
|
| 505 |
+
" print(\"sgRNA directory does not exist\")\n",
|
| 506 |
+
" \n",
|
| 507 |
+
" # Check for any files that might contain sgRNA data\n",
|
| 508 |
+
" for item in contents:\n",
|
| 509 |
+
" item_path = os.path.join(data_lake_path, item)\n",
|
| 510 |
+
" if os.path.isfile(item_path) and ('sgrna' in item.lower() or 'guide' in item.lower() or 'crispr' in item.lower()):\n",
|
| 511 |
+
" print(f\"Found potential sgRNA file: {item}\")\n",
|
| 512 |
+
"else:\n",
|
| 513 |
+
" print(f\"Data lake path does not exist: {data_lake_path}\")\n",
|
| 514 |
+
"</execute>\n",
|
| 515 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 516 |
+
"\n",
|
| 517 |
+
"<observation>=== Checking data lake contents for sgRNA libraries ===\n",
|
| 518 |
+
"Data lake path exists: /dfs/project/bioagentos/biomni_data_test/biomni_data/data_lake\n",
|
| 519 |
+
"Contents of data lake: ['co-fractionation.csv', 'Cosmic_CompleteCNA_v101_GRCh38.tsv.gz', 'Cosmic_Breakpoints_v101_GRCh38.csv', 'two-hybrid.csv', 'Cosmic_CompleteGeneExpression_v101_GRCh38.tsv.gz', 'broad_repurposing_hub_phase_moa_target_info.csv', 'hp.obo', 'BindingDB_All_202409.tsv', 'genebass_synonymous_filtered.pkl', 'omim.csv', 'miRTarBase_microRNA_target_interaction.csv', 'gtex_tissue_gene_tpm.csv', 'Cosmic_CompleteDifferentialMethylation_v101_GRCh38.tsv.gz', 'synthetic_lethality.csv', 'proximity_label-ms.csv', 'synthetic_growth_defect.csv', 'broad_repurposing_hub_molecule_with_smiles.csv', 'czi_census_datasets_v4.csv', 'mousemine_mh_hallmark_geneset.csv', 'mousemine_m1_positional_geneset.csv', 'genebass_pLoF_filtered.pkl', 'Virus-Host_PPI_P-HIPSTER_2020.csv', 'msigdb_human_c8_celltype_signature_geneset.csv', 'msigdb_human_c6_oncogenic_signature_geneset.csv', 'Cosmic_CancerGeneCensus_v101_GRCh38.csv', 'gwas_catalog.pkl', 'miRTarBase_microRNA_target_interaction_pubmed_abtract.txt', 'Cosmic_CancerGeneCensusHallmarksOfCancer_v101_GRCh38.csv', 'mousemine_m5_ontology_geneset.csv', 'go-plus.json', 'gene_info.csv', 'msigdb_human_h_hallmark_geneset.csv', 'msigdb_human_c5_ontology_geneset.csv', 'variant_table.csv', 'enamine_cloud_library_smiles.pkl', 'Cosmic_MutantCensus_v101_GRCh38.csv', 'reconstituted_complex.csv', 'DisGeNET.csv', 'msigdb_human_c3_regulatory_target_geneset.csv', 'genebass_missense_LC_filtered.pkl', 'Cosmic_Genes_v101_GRCh38.csv', 'mousemine_m3_regulatory_target_geneset.csv', 'msigdb_human_c1_positional_geneset.csv', 'msigdb_human_c4_computational_geneset.csv', 'miRDB_v6.0_results.csv', 'Cosmic_ResistanceMutations_v101_GRCh38.csv', 'msigdb_human_c2_curated_geneset.csv', 'Cosmic_GenomeScreensMutant_v101_GRCh38.tsv.gz', 'mousemine_m2_curated_geneset.csv', 'affinity_capture-ms.csv', 'msigdb_human_c3_subset_transcription_factor_targets_from_GTRD.csv', 'miRTarBase_MicroRNA_Target_Sites.csv', 'msigdb_human_c7_immunologic_signature_geneset.csv', 'McPAS-TCR.csv', 'dosage_growth_defect.csv', 'Cosmic_Classification_v101_GRCh38.csv', 'affinity_capture-rna.csv', 'proteinatlas.tsv', 'genetic_interaction.csv', 'synthetic_rescue.csv', 'Cosmic_Fusion_v101_GRCh38.csv', 'Cosmic_ClassificationPaper_v101_GRCh38.csv', 'mousemine_m8_celltype_signature_geneset.csv', 'marker_celltype.csv']\n",
|
| 520 |
+
"sgRNA directory does not exist\n",
|
| 521 |
+
"</observation>\n",
|
| 522 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 523 |
+
"\n",
|
| 524 |
+
"5. [✗] Design sgRNAs for the selected 32 genes (failed because sgRNA library not available in data lake)\n",
|
| 525 |
+
"5. [ ] Provide theoretical sgRNA design recommendations and validate gene selection\n",
|
| 526 |
+
"6. [ ] Provide comprehensive experimental design recommendations\n",
|
| 527 |
+
"\n",
|
| 528 |
+
"<execute>\n",
|
| 529 |
+
"# Since we don't have access to the sgRNA library, let's provide theoretical sgRNA design recommendations\n",
|
| 530 |
+
"# and create a comprehensive experimental plan\n",
|
| 531 |
+
"\n",
|
| 532 |
+
"print(\"=== Step 5: Theoretical sgRNA Design and Validation ===\")\n",
|
| 533 |
+
"\n",
|
| 534 |
+
"selected_genes = ['TOX', 'PDCD1', 'EOMES', 'HAVCR2', 'LAG3', 'TIGIT', 'CTLA4', 'BATF', 'HIF1A', 'MYC', \n",
|
| 535 |
+
" 'LCK', 'ZAP70', 'TBX21', 'IRF4', 'NFATC1', 'TOX2', 'MTOR', 'PRKAA1', 'LAT', 'PLCG1', \n",
|
| 536 |
+
" 'STAT3', 'LDHA', 'LCP2', 'CD274', 'BTLA', 'CD96', 'VAV1', 'ITK', 'RUNX3', 'PKM', \n",
|
| 537 |
+
" 'SLC2A1', 'PPARA']\n",
|
| 538 |
+
"\n",
|
| 539 |
+
"# Categorize genes by functional category for analysis\n",
|
| 540 |
+
"gene_categories = {\n",
|
| 541 |
+
" 'Immune Checkpoints': ['PDCD1', 'HAVCR2', 'LAG3', 'TIGIT', 'CTLA4', 'CD274', 'BTLA', 'CD96'],\n",
|
| 542 |
+
" 'Transcription Factors': ['TOX', 'EOMES', 'BATF', 'TBX21', 'IRF4', 'NFATC1', 'TOX2', 'RUNX3'],\n",
|
| 543 |
+
" 'Metabolic Regulators': ['HIF1A', 'MYC', 'MTOR', 'PRKAA1', 'LDHA', 'PKM', 'SLC2A1', 'PPARA'],\n",
|
| 544 |
+
" 'TCR Signaling': ['LCK', 'ZAP70', 'LAT', 'PLCG1', 'LCP2', 'VAV1', 'ITK'],\n",
|
| 545 |
+
" 'Cytokine Signaling': ['STAT3']\n",
|
| 546 |
+
"}\n",
|
| 547 |
+
"\n",
|
| 548 |
+
"print(\"Gene categorization:\")\n",
|
| 549 |
+
"for category, genes in gene_categories.items():\n",
|
| 550 |
+
" print(f\"{category}: {len(genes)} genes\")\n",
|
| 551 |
+
" print(f\" {', '.join(genes)}\")\n",
|
| 552 |
+
" print()\n",
|
| 553 |
+
"\n",
|
| 554 |
+
"# Expected effects on T cell exhaustion\n",
|
| 555 |
+
"expected_effects = {\n",
|
| 556 |
+
" 'TOX': 'KO should reduce exhaustion (master exhaustion TF)',\n",
|
| 557 |
+
" 'PDCD1': 'KO should reduce exhaustion (remove PD-1 checkpoint)',\n",
|
| 558 |
+
" 'EOMES': 'KO should reduce exhaustion (exhaustion-promoting TF)',\n",
|
| 559 |
+
" 'HAVCR2': 'KO should reduce exhaustion (remove TIM-3 checkpoint)',\n",
|
| 560 |
+
" 'LAG3': 'KO should reduce exhaustion (remove LAG-3 checkpoint)',\n",
|
| 561 |
+
" 'TIGIT': 'KO should reduce exhaustion (remove TIGIT checkpoint)',\n",
|
| 562 |
+
" 'CTLA4': 'KO should enhance early activation',\n",
|
| 563 |
+
" 'BATF': 'KO should reduce exhaustion (exhaustion-promoting TF)',\n",
|
| 564 |
+
" 'HIF1A': 'KO may reduce exhaustion (metabolic stress response)',\n",
|
| 565 |
+
" 'MYC': 'KO may increase exhaustion (metabolic reprogramming)',\n",
|
| 566 |
+
" 'LCK': 'KO should reduce TCR signaling strength',\n",
|
| 567 |
+
" 'ZAP70': 'KO should reduce TCR signaling strength',\n",
|
| 568 |
+
" 'TBX21': 'KO should shift toward exhaustion (effector TF)',\n",
|
| 569 |
+
" 'IRF4': 'KO may affect T cell differentiation balance',\n",
|
| 570 |
+
" 'NFATC1': 'KO may reduce chronic stimulation response',\n",
|
| 571 |
+
" 'MTOR': 'KO should affect metabolic reprogramming',\n",
|
| 572 |
+
" 'STAT3': 'KO should affect cytokine responsiveness'\n",
|
| 573 |
+
"}\n",
|
| 574 |
+
"\n",
|
| 575 |
+
"print(\"Expected functional outcomes:\")\n",
|
| 576 |
+
"for gene in selected_genes[:17]: # Show first 17 as examples\n",
|
| 577 |
+
" if gene in expected_effects:\n",
|
| 578 |
+
" print(f\"{gene}: {expected_effects[gene]}\")\n",
|
| 579 |
+
"\n",
|
| 580 |
+
"print(\"\\nsgRNA Design Recommendations:\")\n",
|
| 581 |
+
"print(\"1. Use 3-4 sgRNAs per gene for redundancy\")\n",
|
| 582 |
+
"print(\"2. Target early exons to ensure knockout\")\n",
|
| 583 |
+
"print(\"3. Include non-targeting controls (scrambled guides)\")\n",
|
| 584 |
+
"print(\"4. Include positive controls (known exhaustion regulators)\")\n",
|
| 585 |
+
"print(\"5. Use established sgRNA design tools (e.g., Broad GPP portal, CHOPCHOP)\")\n",
|
| 586 |
+
"</execute>\n",
|
| 587 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 588 |
+
"\n",
|
| 589 |
+
"<observation>=== Step 5: Theoretical sgRNA Design and Validation ===\n",
|
| 590 |
+
"Gene categorization:\n",
|
| 591 |
+
"Immune Checkpoints: 8 genes\n",
|
| 592 |
+
" PDCD1, HAVCR2, LAG3, TIGIT, CTLA4, CD274, BTLA, CD96\n",
|
| 593 |
+
"\n",
|
| 594 |
+
"Transcription Factors: 8 genes\n",
|
| 595 |
+
" TOX, EOMES, BATF, TBX21, IRF4, NFATC1, TOX2, RUNX3\n",
|
| 596 |
+
"\n",
|
| 597 |
+
"Metabolic Regulators: 8 genes\n",
|
| 598 |
+
" HIF1A, MYC, MTOR, PRKAA1, LDHA, PKM, SLC2A1, PPARA\n",
|
| 599 |
+
"\n",
|
| 600 |
+
"TCR Signaling: 7 genes\n",
|
| 601 |
+
" LCK, ZAP70, LAT, PLCG1, LCP2, VAV1, ITK\n",
|
| 602 |
+
"\n",
|
| 603 |
+
"Cytokine Signaling: 1 genes\n",
|
| 604 |
+
" STAT3\n",
|
| 605 |
+
"\n",
|
| 606 |
+
"Expected functional outcomes:\n",
|
| 607 |
+
"TOX: KO should reduce exhaustion (master exhaustion TF)\n",
|
| 608 |
+
"PDCD1: KO should reduce exhaustion (remove PD-1 checkpoint)\n",
|
| 609 |
+
"EOMES: KO should reduce exhaustion (exhaustion-promoting TF)\n",
|
| 610 |
+
"HAVCR2: KO should reduce exhaustion (remove TIM-3 checkpoint)\n",
|
| 611 |
+
"LAG3: KO should reduce exhaustion (remove LAG-3 checkpoint)\n",
|
| 612 |
+
"TIGIT: KO should reduce exhaustion (remove TIGIT checkpoint)\n",
|
| 613 |
+
"CTLA4: KO should enhance early activation\n",
|
| 614 |
+
"BATF: KO should reduce exhaustion (exhaustion-promoting TF)\n",
|
| 615 |
+
"HIF1A: KO may reduce exhaustion (metabolic stress response)\n",
|
| 616 |
+
"MYC: KO may increase exhaustion (metabolic reprogramming)\n",
|
| 617 |
+
"LCK: KO should reduce TCR signaling strength\n",
|
| 618 |
+
"ZAP70: KO should reduce TCR signaling strength\n",
|
| 619 |
+
"TBX21: KO should shift toward exhaustion (effector TF)\n",
|
| 620 |
+
"IRF4: KO may affect T cell differentiation balance\n",
|
| 621 |
+
"NFATC1: KO may reduce chronic stimulation response\n",
|
| 622 |
+
"MTOR: KO should affect metabolic reprogramming\n",
|
| 623 |
+
"\n",
|
| 624 |
+
"sgRNA Design Recommendations:\n",
|
| 625 |
+
"1. Use 3-4 sgRNAs per gene for redundancy\n",
|
| 626 |
+
"2. Target early exons to ensure knockout\n",
|
| 627 |
+
"3. Include non-targeting controls (scrambled guides)\n",
|
| 628 |
+
"4. Include positive controls (known exhaustion regulators)\n",
|
| 629 |
+
"5. Use established sgRNA design tools (e.g., Broad GPP portal, CHOPCHOP)\n",
|
| 630 |
+
"</observation>\n",
|
| 631 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 632 |
+
"\n",
|
| 633 |
+
"<execute>\n",
|
| 634 |
+
"# Now let's create a comprehensive experimental design\n",
|
| 635 |
+
"print(\"=== Step 6: Comprehensive Experimental Design for CRISPR Screen ===\")\n",
|
| 636 |
+
"\n",
|
| 637 |
+
"experimental_design = {\n",
|
| 638 |
+
" 'objective': 'Identify genes regulating T cell exhaustion through differential TCR signaling between acute and chronic stimulation',\n",
|
| 639 |
+
" 'readout': 'TCR signaling strength measured by phospho-flow cytometry, cytokine production, and transcriptional markers',\n",
|
| 640 |
+
" 'conditions': {\n",
|
| 641 |
+
" 'acute': 'IL-2 only (maintains T cell survival without chronic stimulation)',\n",
|
| 642 |
+
" 'chronic': 'Anti-CD3 + IL-2 (chronic TCR stimulation leading to exhaustion)'\n",
|
| 643 |
+
" }\n",
|
| 644 |
+
"}\n",
|
| 645 |
+
"\n",
|
| 646 |
+
"print(\"EXPERIMENTAL DESIGN OVERVIEW\")\n",
|
| 647 |
+
"print(\"=\"*50)\n",
|
| 648 |
+
"print(f\"Objective: {experimental_design['objective']}\")\n",
|
| 649 |
+
"print(f\"Primary Readout: {experimental_design['readout']}\")\n",
|
| 650 |
+
"print()\n",
|
| 651 |
+
"\n",
|
| 652 |
+
"print(\"STIMULATION CONDITIONS:\")\n",
|
| 653 |
+
"print(f\"1. Acute condition: {experimental_design['conditions']['acute']}\")\n",
|
| 654 |
+
"print(f\"2. Chronic condition: {experimental_design['conditions']['chronic']}\")\n",
|
| 655 |
+
"print()\n",
|
| 656 |
+
"\n",
|
| 657 |
+
"print(\"SELECTED 32 GENES FOR CRISPR SCREEN:\")\n",
|
| 658 |
+
"print(\"=\"*50)\n",
|
| 659 |
+
"\n",
|
| 660 |
+
"# Create a detailed gene table\n",
|
| 661 |
+
"gene_info = []\n",
|
| 662 |
+
"for i, gene in enumerate(selected_genes, 1):\n",
|
| 663 |
+
" category = None\n",
|
| 664 |
+
" for cat, genes in gene_categories.items():\n",
|
| 665 |
+
" if gene in genes:\n",
|
| 666 |
+
" category = cat\n",
|
| 667 |
+
" break\n",
|
| 668 |
+
" \n",
|
| 669 |
+
" gene_info.append({\n",
|
| 670 |
+
" 'rank': i,\n",
|
| 671 |
+
" 'gene': gene,\n",
|
| 672 |
+
" 'category': category,\n",
|
| 673 |
+
" 'expected_effect': expected_effects.get(gene, 'To be determined')\n",
|
| 674 |
+
" })\n",
|
| 675 |
+
"\n",
|
| 676 |
+
"# Print gene table\n",
|
| 677 |
+
"print(f\"{'Rank':<4} {'Gene':<8} {'Category':<20} {'Expected Effect'}\")\n",
|
| 678 |
+
"print(\"-\" * 80)\n",
|
| 679 |
+
"for info in gene_info:\n",
|
| 680 |
+
" print(f\"{info['rank']:<4} {info['gene']:<8} {info['category']:<20} {info['expected_effect'][:50]}\")\n",
|
| 681 |
+
"\n",
|
| 682 |
+
"print()\n",
|
| 683 |
+
"print(\"EXPERIMENTAL PROTOCOL:\")\n",
|
| 684 |
+
"print(\"=\"*50)\n",
|
| 685 |
+
"\n",
|
| 686 |
+
"protocol_steps = [\n",
|
| 687 |
+
" \"1. T Cell Isolation and Activation\",\n",
|
| 688 |
+
" \" - Isolate primary CD8+ T cells from healthy donors\",\n",
|
| 689 |
+
" \" - Activate with anti-CD3/CD28 for 2-3 days\",\n",
|
| 690 |
+
" \"\",\n",
|
| 691 |
+
" \"2. CRISPR-Cas9 Delivery\",\n",
|
| 692 |
+
" \" - Electroporate Cas9 RNP complexes with sgRNA library\",\n",
|
| 693 |
+
" \" - 3-4 sgRNAs per gene + non-targeting controls\",\n",
|
| 694 |
+
" \" - Allow 3-5 days for gene knockout\",\n",
|
| 695 |
+
" \"\",\n",
|
| 696 |
+
" \"3. Stimulation Conditions (7-14 days)\",\n",
|
| 697 |
+
" \" - Acute: IL-2 (100 U/ml) only\",\n",
|
| 698 |
+
" \" - Chronic: Anti-CD3 (1 μg/ml) + IL-2 (100 U/ml)\",\n",
|
| 699 |
+
" \" - Refresh media every 2-3 days\",\n",
|
| 700 |
+
" \"\",\n",
|
| 701 |
+
" \"4. Readout Measurements\",\n",
|
| 702 |
+
" \" - TCR signaling: Phospho-flow for pZAP70, pERK, pS6\",\n",
|
| 703 |
+
" \" - Exhaustion markers: PD-1, TIM-3, LAG-3 expression\",\n",
|
| 704 |
+
" \" - Function: IFN-γ, TNF-α, IL-2 production\",\n",
|
| 705 |
+
" \" - Transcription: RNA-seq for exhaustion signatures\",\n",
|
| 706 |
+
" \"\",\n",
|
| 707 |
+
" \"5. Data Analysis\",\n",
|
| 708 |
+
" \" - Calculate perturbation scores for each gene\",\n",
|
| 709 |
+
" \" - Identify genes with differential effects between conditions\",\n",
|
| 710 |
+
" \" - Validate top hits with individual knockouts\"\n",
|
| 711 |
+
"]\n",
|
| 712 |
+
"\n",
|
| 713 |
+
"for step in protocol_steps:\n",
|
| 714 |
+
" print(step)\n",
|
| 715 |
+
"\n",
|
| 716 |
+
"print()\n",
|
| 717 |
+
"print(\"KEY CONTROLS:\")\n",
|
| 718 |
+
"print(\"=\"*20)\n",
|
| 719 |
+
"controls = [\n",
|
| 720 |
+
" \"- Non-targeting sgRNAs (negative control)\",\n",
|
| 721 |
+
" \"- Known exhaustion genes (positive control): TOX, PDCD1\",\n",
|
| 722 |
+
" \"- Essential genes (toxicity control): RPL23, RPS19\",\n",
|
| 723 |
+
" \"- Unstimulated cells (baseline)\",\n",
|
| 724 |
+
" \"- Wild-type cells (no CRISPR)\"\n",
|
| 725 |
+
"]\n",
|
| 726 |
+
"\n",
|
| 727 |
+
"for control in controls:\n",
|
| 728 |
+
" print(control)\n",
|
| 729 |
+
"\n",
|
| 730 |
+
"print()\n",
|
| 731 |
+
"print(\"EXPECTED OUTCOMES:\")\n",
|
| 732 |
+
"print(\"=\"*20)\n",
|
| 733 |
+
"outcomes = [\n",
|
| 734 |
+
" \"1. Genes that reduce exhaustion when knocked out:\",\n",
|
| 735 |
+
" \" - Should show enhanced TCR signaling in chronic condition\",\n",
|
| 736 |
+
" \" - Examples: TOX, PDCD1, HAVCR2, LAG3, EOMES\",\n",
|
| 737 |
+
" \"\",\n",
|
| 738 |
+
" \"2. Genes that promote effector function:\",\n",
|
| 739 |
+
" \" - Should show reduced signaling when knocked out\",\n",
|
| 740 |
+
" \" - Examples: TBX21, MYC (context-dependent)\",\n",
|
| 741 |
+
" \"\",\n",
|
| 742 |
+
" \"3. Metabolic regulators:\",\n",
|
| 743 |
+
" \" - Should show differential effects on sustained signaling\",\n",
|
| 744 |
+
" \" - Examples: MTOR, HIF1A, PRKAA1\",\n",
|
| 745 |
+
" \"\",\n",
|
| 746 |
+
" \"4. TCR signaling components:\",\n",
|
| 747 |
+
" \" - Should show proportional effects on signaling strength\",\n",
|
| 748 |
+
" \" - Examples: LCK, ZAP70, LAT\"\n",
|
| 749 |
+
"]\n",
|
| 750 |
+
"\n",
|
| 751 |
+
"for outcome in outcomes:\n",
|
| 752 |
+
" print(outcome)\n",
|
| 753 |
+
"</execute>\n",
|
| 754 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 755 |
+
"\n",
|
| 756 |
+
"<observation>=== Step 6: Comprehensive Experimental Design for CRISPR Screen ===\n",
|
| 757 |
+
"EXPERIMENTAL DESIGN OVERVIEW\n",
|
| 758 |
+
"==================================================\n",
|
| 759 |
+
"Objective: Identify genes regulating T cell exhaustion through differential TCR signaling between acute and chronic stimulation\n",
|
| 760 |
+
"Primary Readout: TCR signaling strength measured by phospho-flow cytometry, cytokine production, and transcriptional markers\n",
|
| 761 |
+
"\n",
|
| 762 |
+
"STIMULATION CONDITIONS:\n",
|
| 763 |
+
"1. Acute condition: IL-2 only (maintains T cell survival without chronic stimulation)\n",
|
| 764 |
+
"2. Chronic condition: Anti-CD3 + IL-2 (chronic TCR stimulation leading to exhaustion)\n",
|
| 765 |
+
"\n",
|
| 766 |
+
"SELECTED 32 GENES FOR CRISPR SCREEN:\n",
|
| 767 |
+
"==================================================\n",
|
| 768 |
+
"Rank Gene Category Expected Effect\n",
|
| 769 |
+
"--------------------------------------------------------------------------------\n",
|
| 770 |
+
"1 TOX Transcription Factors KO should reduce exhaustion (master exhaustion TF)\n",
|
| 771 |
+
"2 PDCD1 Immune Checkpoints KO should reduce exhaustion (remove PD-1 checkpoin\n",
|
| 772 |
+
"3 EOMES Transcription Factors KO should reduce exhaustion (exhaustion-promoting \n",
|
| 773 |
+
"4 HAVCR2 Immune Checkpoints KO should reduce exhaustion (remove TIM-3 checkpoi\n",
|
| 774 |
+
"5 LAG3 Immune Checkpoints KO should reduce exhaustion (remove LAG-3 checkpoi\n",
|
| 775 |
+
"6 TIGIT Immune Checkpoints KO should reduce exhaustion (remove TIGIT checkpoi\n",
|
| 776 |
+
"7 CTLA4 Immune Checkpoints KO should enhance early activation\n",
|
| 777 |
+
"8 BATF Transcription Factors KO should reduce exhaustion (exhaustion-promoting \n",
|
| 778 |
+
"9 HIF1A Metabolic Regulators KO may reduce exhaustion (metabolic stress respons\n",
|
| 779 |
+
"10 MYC Metabolic Regulators KO may increase exhaustion (metabolic reprogrammin\n",
|
| 780 |
+
"11 LCK TCR Signaling KO should reduce TCR signaling strength\n",
|
| 781 |
+
"12 ZAP70 TCR Signaling KO should reduce TCR signaling strength\n",
|
| 782 |
+
"13 TBX21 Transcription Factors KO should shift toward exhaustion (effector TF)\n",
|
| 783 |
+
"14 IRF4 Transcription Factors KO may affect T cell differentiation balance\n",
|
| 784 |
+
"15 NFATC1 Transcription Factors KO may reduce chronic stimulation response\n",
|
| 785 |
+
"16 TOX2 Transcription Factors To be determined\n",
|
| 786 |
+
"17 MTOR Metabolic Regulators KO should affect metabolic reprogramming\n",
|
| 787 |
+
"18 PRKAA1 Metabolic Regulators To be determined\n",
|
| 788 |
+
"19 LAT TCR Signaling To be determined\n",
|
| 789 |
+
"20 PLCG1 TCR Signaling To be determined\n",
|
| 790 |
+
"21 STAT3 Cytokine Signaling KO should affect cytokine responsiveness\n",
|
| 791 |
+
"22 LDHA Metabolic Regulators To be determined\n",
|
| 792 |
+
"23 LCP2 TCR Signaling To be determined\n",
|
| 793 |
+
"24 CD274 Immune Checkpoints To be determined\n",
|
| 794 |
+
"25 BTLA Immune Checkpoints To be determined\n",
|
| 795 |
+
"26 CD96 Immune Checkpoints To be determined\n",
|
| 796 |
+
"27 VAV1 TCR Signaling To be determined\n",
|
| 797 |
+
"28 ITK TCR Signaling To be determined\n",
|
| 798 |
+
"29 RUNX3 Transcription Factors To be determined\n",
|
| 799 |
+
"30 PKM Metabolic Regulators To be determined\n",
|
| 800 |
+
"31 SLC2A1 Metabolic Regulators To be determined\n",
|
| 801 |
+
"32 PPARA Metabolic Regulators To be determined\n",
|
| 802 |
+
"\n",
|
| 803 |
+
"EXPERIMENTAL PROTOCOL:\n",
|
| 804 |
+
"==================================================\n",
|
| 805 |
+
"1. T Cell Isolation and Activation\n",
|
| 806 |
+
" - Isolate primary CD8+ T cells from healthy donors\n",
|
| 807 |
+
" - Activate with anti-CD3/CD28 for 2-3 days\n",
|
| 808 |
+
"\n",
|
| 809 |
+
"2. CRISPR-Cas9 Delivery\n",
|
| 810 |
+
" - Electroporate Cas9 RNP complexes with sgRNA library\n",
|
| 811 |
+
" - 3-4 sgRNAs per gene + non-targeting controls\n",
|
| 812 |
+
" - Allow 3-5 days for gene knockout\n",
|
| 813 |
+
"\n",
|
| 814 |
+
"3. Stimulation Conditions (7-14 days)\n",
|
| 815 |
+
" - Acute: IL-2 (100 U/ml) only\n",
|
| 816 |
+
" - Chronic: Anti-CD3 (1 μg/ml) + IL-2 (100 U/ml)\n",
|
| 817 |
+
" - Refresh media every 2-3 days\n",
|
| 818 |
+
"\n",
|
| 819 |
+
"4. Readout Measurements\n",
|
| 820 |
+
" - TCR signaling: Phospho-flow for pZAP70, pERK, pS6\n",
|
| 821 |
+
" - Exhaustion markers: PD-1, TIM-3, LAG-3 expression\n",
|
| 822 |
+
" - Function: IFN-γ, TNF-α, IL-2 production\n",
|
| 823 |
+
" - Transcription: RNA-seq for exhaustion signatures\n",
|
| 824 |
+
"\n",
|
| 825 |
+
"5. Data Analysis\n",
|
| 826 |
+
" - Calculate perturbation scores for each gene\n",
|
| 827 |
+
" - Identify genes with differential effects between conditions\n",
|
| 828 |
+
" - Validate top hits with individual knockouts\n",
|
| 829 |
+
"\n",
|
| 830 |
+
"KEY CONTROLS:\n",
|
| 831 |
+
"====================\n",
|
| 832 |
+
"- Non-targeting sgRNAs (negative control)\n",
|
| 833 |
+
"- Known exhaustion genes (positive control): TOX, PDCD1\n",
|
| 834 |
+
"- Essential genes (toxicity control): RPL23, RPS19\n",
|
| 835 |
+
"- Unstimulated cells (baseline)\n",
|
| 836 |
+
"- Wild-type cells (no CRISPR)\n",
|
| 837 |
+
"\n",
|
| 838 |
+
"EXPECTED OUTCOMES:\n",
|
| 839 |
+
"====================\n",
|
| 840 |
+
"1. Genes that reduce exhaustion when knocked out:\n",
|
| 841 |
+
" - Should show enhanced TCR signaling in chronic condition\n",
|
| 842 |
+
" - Examples: TOX, PDCD1, HAVCR2, LAG3, EOMES\n",
|
| 843 |
+
"\n",
|
| 844 |
+
"2. Genes that promote effector function:\n",
|
| 845 |
+
" - Should show reduced signaling when knocked out\n",
|
| 846 |
+
" - Examples: TBX21, MYC (context-dependent)\n",
|
| 847 |
+
"\n",
|
| 848 |
+
"3. Metabolic regulators:\n",
|
| 849 |
+
" - Should show differential effects on sustained signaling\n",
|
| 850 |
+
" - Examples: MTOR, HIF1A, PRKAA1\n",
|
| 851 |
+
"\n",
|
| 852 |
+
"4. TCR signaling components:\n",
|
| 853 |
+
" - Should show proportional effects on signaling strength\n",
|
| 854 |
+
" - Examples: LCK, ZAP70, LAT\n",
|
| 855 |
+
"</observation>\n",
|
| 856 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 857 |
+
"\n",
|
| 858 |
+
"5. [✓] Provide theoretical sgRNA design recommendations and validate gene selection (completed)\n",
|
| 859 |
+
"6. [✓] Provide comprehensive experimental design recommendations (completed)\n",
|
| 860 |
+
"\n",
|
| 861 |
+
"<solution>\n",
|
| 862 |
+
"# CRISPR Screen to Identify T Cell Exhaustion Regulators\n",
|
| 863 |
+
"\n",
|
| 864 |
+
"## Selected 32 Genes for Maximum Perturbation Effect\n",
|
| 865 |
+
"\n",
|
| 866 |
+
"Based on comprehensive analysis of T cell exhaustion biology, I have identified 32 genes that will maximize perturbation effects in your CRISPR screen measuring differential TCR signaling between acute (IL-2 only) and chronic (anti-CD3 + IL-2) stimulation conditions:\n",
|
| 867 |
+
"\n",
|
| 868 |
+
"### Gene List (Ranked by Priority):\n",
|
| 869 |
+
"\n",
|
| 870 |
+
"**Immune Checkpoint Molecules (8 genes):**\n",
|
| 871 |
+
"1. **PDCD1** (PD-1) - Primary exhaustion checkpoint receptor\n",
|
| 872 |
+
"2. **HAVCR2** (TIM-3) - Critical exhaustion marker and checkpoint\n",
|
| 873 |
+
"3. **LAG3** - Major exhaustion checkpoint receptor\n",
|
| 874 |
+
"4. **TIGIT** - Emerging exhaustion checkpoint target\n",
|
| 875 |
+
"5. **CTLA4** - Early checkpoint inhibitor\n",
|
| 876 |
+
"6. **CD274** (PD-L1) - PD-1 ligand\n",
|
| 877 |
+
"7. **BTLA** - Inhibitory receptor\n",
|
| 878 |
+
"8. **CD96** - TIGIT pathway component\n",
|
| 879 |
+
"\n",
|
| 880 |
+
"**Transcription Factors (8 genes):**\n",
|
| 881 |
+
"9. **TOX** - Master regulator of T cell exhaustion\n",
|
| 882 |
+
"10. **EOMES** - Key exhaustion-promoting transcription factor\n",
|
| 883 |
+
"11. **BATF** - Exhaustion-promoting transcription factor\n",
|
| 884 |
+
"12. **TBX21** (T-bet) - Effector vs exhaustion balance\n",
|
| 885 |
+
"13. **IRF4** - T cell differentiation regulator\n",
|
| 886 |
+
"14. **NFATC1** - Chronic stimulation response factor\n",
|
| 887 |
+
"15. **TOX2** - TOX family member\n",
|
| 888 |
+
"16. **RUNX3** - T cell differentiation factor\n",
|
| 889 |
+
"\n",
|
| 890 |
+
"**Metabolic Regulators (8 genes):**\n",
|
| 891 |
+
"17. **HIF1A** - Hypoxia and exhaustion regulator\n",
|
| 892 |
+
"18. **MYC** - Metabolic reprogramming master regulator\n",
|
| 893 |
+
"19. **MTOR** - Metabolic sensing and signaling\n",
|
| 894 |
+
"20. **PRKAA1** (AMPK) - Energy sensing kinase\n",
|
| 895 |
+
"21. **LDHA** - Lactate metabolism in exhaustion\n",
|
| 896 |
+
"22. **PKM** - Pyruvate kinase, glycolysis regulator\n",
|
| 897 |
+
"23. **SLC2A1** (GLUT1) - Glucose uptake transporter\n",
|
| 898 |
+
"24. **PPARA** - Fatty acid oxidation regulator\n",
|
| 899 |
+
"\n",
|
| 900 |
+
"**TCR Signaling Components (7 genes):**\n",
|
| 901 |
+
"25. **LCK** - Proximal TCR signaling kinase\n",
|
| 902 |
+
"26. **ZAP70** - TCR signal strength regulator\n",
|
| 903 |
+
"27. **LAT** - TCR signaling hub adaptor\n",
|
| 904 |
+
"28. **PLCG1** (PLCγ1) - Downstream TCR signaling\n",
|
| 905 |
+
"29. **LCP2** (SLP-76) - TCR signaling adaptor\n",
|
| 906 |
+
"30. **VAV1** - TCR signaling regulator\n",
|
| 907 |
+
"31. **ITK** - TCR signaling kinase\n",
|
| 908 |
+
"\n",
|
| 909 |
+
"**Cytokine Signaling (1 gene):**\n",
|
| 910 |
+
"32. **STAT3** - Cytokine signaling transcription factor\n",
|
| 911 |
+
"\n",
|
| 912 |
+
"## Experimental Design Overview\n",
|
| 913 |
+
"\n",
|
| 914 |
+
"### Objective:\n",
|
| 915 |
+
"Identify genes that regulate T cell exhaustion by measuring differential TCR signaling responses between acute and chronic stimulation conditions.\n",
|
| 916 |
+
"\n",
|
| 917 |
+
"### Key Features:\n",
|
| 918 |
+
"- **Balanced gene selection** across 4 major functional categories\n",
|
| 919 |
+
"- **High-priority targets** based on literature evidence and therapeutic relevance\n",
|
| 920 |
+
"- **Expected differential effects** between acute vs chronic conditions\n",
|
| 921 |
+
"- **Multiple readouts** for comprehensive phenotyping\n",
|
| 922 |
+
"\n",
|
| 923 |
+
"### Stimulation Conditions:\n",
|
| 924 |
+
"- **Acute**: IL-2 only (maintains survival without exhaustion)\n",
|
| 925 |
+
"- **Chronic**: Anti-CD3 + IL-2 (induces exhaustion through chronic TCR stimulation)\n",
|
| 926 |
+
"\n",
|
| 927 |
+
"### Primary Readouts:\n",
|
| 928 |
+
"1. **TCR signaling strength**: Phospho-flow cytometry (pZAP70, pERK, pS6)\n",
|
| 929 |
+
"2. **Exhaustion markers**: PD-1, TIM-3, LAG-3 surface expression\n",
|
| 930 |
+
"3. **Functional capacity**: Cytokine production (IFN-γ, TNF-α, IL-2)\n",
|
| 931 |
+
"4. **Transcriptional state**: RNA-seq for exhaustion gene signatures\n",
|
| 932 |
+
"\n",
|
| 933 |
+
"### Expected Outcomes:\n",
|
| 934 |
+
"- **Checkpoint inhibitors** (PDCD1, HAVCR2, LAG3): KO should enhance signaling in chronic condition\n",
|
| 935 |
+
"- **Exhaustion TFs** (TOX, EOMES, BATF): KO should reduce exhaustion phenotype\n",
|
| 936 |
+
"- **Metabolic regulators** (MTOR, HIF1A, MYC): Context-dependent effects on sustained signaling\n",
|
| 937 |
+
"- **TCR components** (LCK, ZAP70, LAT): Proportional effects on signaling strength\n",
|
| 938 |
+
"\n",
|
| 939 |
+
"### sgRNA Design Recommendations:\n",
|
| 940 |
+
"- Use 3-4 sgRNAs per gene for redundancy\n",
|
| 941 |
+
"- Include non-targeting controls and positive controls (TOX, PDCD1)\n",
|
| 942 |
+
"- Target early exons for effective knockouts\n",
|
| 943 |
+
"- Use established design tools (Broad GPP portal, CHOPCHOP)\n",
|
| 944 |
+
"\n",
|
| 945 |
+
"This gene set maximizes the potential to identify novel regulators of T cell exhaustion while covering all major pathways involved in the acute-to-chronic transition of T cell responses.\n",
|
| 946 |
+
"</solution>\n",
|
| 947 |
+
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
| 948 |
+
"\n",
|
| 949 |
+
"5. [✓] Provide theoretical sgRNA design recommendations and validate gene selection (completed)\n",
|
| 950 |
+
"6. [✓] Provide comprehensive experimental design recommendations (completed)\n",
|
| 951 |
+
"\n",
|
| 952 |
+
"<solution>\n",
|
| 953 |
+
"# CRISPR Screen to Identify T Cell Exhaustion Regulators\n",
|
| 954 |
+
"\n",
|
| 955 |
+
"## Selected 32 Genes for Maximum Perturbation Effect\n",
|
| 956 |
+
"\n",
|
| 957 |
+
"Based on comprehensive analysis of T cell exhaustion biology, I have identified 32 genes that will maximize perturbation effects in your CRISPR screen measuring differential TCR signaling between acute (IL-2 only) and chronic (anti-CD3 + IL-2) stimulation conditions:\n",
|
| 958 |
+
"\n",
|
| 959 |
+
"### Gene List (Ranked by Priority):\n",
|
| 960 |
+
"\n",
|
| 961 |
+
"**Immune Checkpoint Molecules (8 genes):**\n",
|
| 962 |
+
"1. **PDCD1** (PD-1) - Primary exhaustion checkpoint receptor\n",
|
| 963 |
+
"2. **HAVCR2** (TIM-3) - Critical exhaustion marker and checkpoint\n",
|
| 964 |
+
"3. **LAG3** - Major exhaustion checkpoint receptor\n",
|
| 965 |
+
"4. **TIGIT** - Emerging exhaustion checkpoint target\n",
|
| 966 |
+
"5. **CTLA4** - Early checkpoint inhibitor\n",
|
| 967 |
+
"6. **CD274** (PD-L1) - PD-1 ligand\n",
|
| 968 |
+
"7. **BTLA** - Inhibitory receptor\n",
|
| 969 |
+
"8. **CD96** - TIGIT pathway component\n",
|
| 970 |
+
"\n",
|
| 971 |
+
"**Transcription Factors (8 genes):**\n",
|
| 972 |
+
"9. **TOX** - Master regulator of T cell exhaustion\n",
|
| 973 |
+
"10. **EOMES** - Key exhaustion-promoting transcription factor\n",
|
| 974 |
+
"11. **BATF** - Exhaustion-promoting transcription factor\n",
|
| 975 |
+
"12. **TBX21** (T-bet) - Effector vs exhaustion balance\n",
|
| 976 |
+
"13. **IRF4** - T cell differentiation regulator\n",
|
| 977 |
+
"14. **NFATC1** - Chronic stimulation response factor\n",
|
| 978 |
+
"15. **TOX2** - TOX family member\n",
|
| 979 |
+
"16. **RUNX3** - T cell differentiation factor\n",
|
| 980 |
+
"\n",
|
| 981 |
+
"**Metabolic Regulators (8 genes):**\n",
|
| 982 |
+
"17. **HIF1A** - Hypoxia and exhaustion regulator\n",
|
| 983 |
+
"18. **MYC** - Metabolic reprogramming master regulator\n",
|
| 984 |
+
"19. **MTOR** - Metabolic sensing and signaling\n",
|
| 985 |
+
"20. **PRKAA1** (AMPK) - Energy sensing kinase\n",
|
| 986 |
+
"21. **LDHA** - Lactate metabolism in exhaustion\n",
|
| 987 |
+
"22. **PKM** - Pyruvate kinase, glycolysis regulator\n",
|
| 988 |
+
"23. **SLC2A1** (GLUT1) - Glucose uptake transporter\n",
|
| 989 |
+
"24. **PPARA** - Fatty acid oxidation regulator\n",
|
| 990 |
+
"\n",
|
| 991 |
+
"**TCR Signaling Components (7 genes):**\n",
|
| 992 |
+
"25. **LCK** - Proximal TCR signaling kinase\n",
|
| 993 |
+
"26. **ZAP70** - TCR signal strength regulator\n",
|
| 994 |
+
"27. **LAT** - TCR signaling hub adaptor\n",
|
| 995 |
+
"28. **PLCG1** (PLCγ1) - Downstream TCR signaling\n",
|
| 996 |
+
"29. **LCP2** (SLP-76) - TCR signaling adaptor\n",
|
| 997 |
+
"30. **VAV1** - TCR signaling regulator\n",
|
| 998 |
+
"31. **ITK** - TCR signaling kinase\n",
|
| 999 |
+
"\n",
|
| 1000 |
+
"**Cytokine Signaling (1 gene):**\n",
|
| 1001 |
+
"32. **STAT3** - Cytokine signaling transcription factor\n",
|
| 1002 |
+
"\n",
|
| 1003 |
+
"## Experimental Design Overview\n",
|
| 1004 |
+
"\n",
|
| 1005 |
+
"### Objective:\n",
|
| 1006 |
+
"Identify genes that regulate T cell exhaustion by measuring differential TCR signaling responses between acute and chronic stimulation conditions.\n",
|
| 1007 |
+
"\n",
|
| 1008 |
+
"### Key Features:\n",
|
| 1009 |
+
"- **Balanced gene selection** across 4 major functional categories\n",
|
| 1010 |
+
"- **High-priority targets** based on literature evidence and therapeutic relevance\n",
|
| 1011 |
+
"- **Expected differential effects** between acute vs chronic conditions\n",
|
| 1012 |
+
"- **Multiple readouts** for comprehensive phenotyping\n",
|
| 1013 |
+
"\n",
|
| 1014 |
+
"### Stimulation Conditions:\n",
|
| 1015 |
+
"- **Acute**: IL-2 only (maintains survival without exhaustion)\n",
|
| 1016 |
+
"- **Chronic**: Anti-CD3 + IL-2 (induces exhaustion through chronic TCR stimulation)\n",
|
| 1017 |
+
"\n",
|
| 1018 |
+
"### Primary Readouts:\n",
|
| 1019 |
+
"1. **TCR signaling strength**: Phospho-flow cytometry (pZAP70, pERK, pS6)\n",
|
| 1020 |
+
"2. **Exhaustion markers**: PD-1, TIM-3, LAG-3 surface expression\n",
|
| 1021 |
+
"3. **Functional capacity**: Cytokine production (IFN-γ, TNF-α, IL-2)\n",
|
| 1022 |
+
"4. **Transcriptional state**: RNA-seq for exhaustion gene signatures\n",
|
| 1023 |
+
"\n",
|
| 1024 |
+
"### Expected Outcomes:\n",
|
| 1025 |
+
"- **Checkpoint inhibitors** (PDCD1, HAVCR2, LAG3): KO should enhance signaling in chronic condition\n",
|
| 1026 |
+
"- **Exhaustion TFs** (TOX, EOMES, BATF): KO should reduce exhaustion phenotype\n",
|
| 1027 |
+
"- **Metabolic regulators** (MTOR, HIF1A, MYC): Context-dependent effects on sustained signaling\n",
|
| 1028 |
+
"- **TCR components** (LCK, ZAP70, LAT): Proportional effects on signaling strength\n",
|
| 1029 |
+
"\n",
|
| 1030 |
+
"### sgRNA Design Recommendations:\n",
|
| 1031 |
+
"- Use 3-4 sgRNAs per gene for redundancy\n",
|
| 1032 |
+
"- Include non-targeting controls and positive controls (TOX, PDCD1)\n",
|
| 1033 |
+
"- Target early exons for effective knockouts\n",
|
| 1034 |
+
"- Use established design tools (Broad GPP portal, CHOPCHOP)\n",
|
| 1035 |
+
"\n",
|
| 1036 |
+
"This gene set maximizes the potential to identify novel regulators of T cell exhaustion while covering all major pathways involved in the acute-to-chronic transition of T cell responses.\n",
|
| 1037 |
+
"</solution>\n"
|
| 1038 |
+
]
|
| 1039 |
+
}
|
| 1040 |
+
],
|
| 1041 |
+
"source": [
|
| 1042 |
+
"log = agent.go(\"\"\"Plan a CRISPR screen to identify genes that regulate T cell exhaustion,\n",
|
| 1043 |
+
" measured by the change in T cell receptor (TCR) signaling between acute\n",
|
| 1044 |
+
" (interleukin-2 [IL-2] only) and chronic (anti-CD3 and IL-2) stimulation conditions.\n",
|
| 1045 |
+
" Generate 32 genes that maximize the perturbation effect.\"\"\")"
|
| 1046 |
+
]
|
| 1047 |
+
}
|
| 1048 |
+
],
|
| 1049 |
+
"metadata": {
|
| 1050 |
+
"kernelspec": {
|
| 1051 |
+
"display_name": "biomni_fixed",
|
| 1052 |
+
"language": "python",
|
| 1053 |
+
"name": "python3"
|
| 1054 |
+
},
|
| 1055 |
+
"language_info": {
|
| 1056 |
+
"codemirror_mode": {
|
| 1057 |
+
"name": "ipython",
|
| 1058 |
+
"version": 3
|
| 1059 |
+
},
|
| 1060 |
+
"file_extension": ".py",
|
| 1061 |
+
"mimetype": "text/x-python",
|
| 1062 |
+
"name": "python",
|
| 1063 |
+
"nbconvert_exporter": "python",
|
| 1064 |
+
"pygments_lexer": "ipython3",
|
| 1065 |
+
"version": "3.11.13"
|
| 1066 |
+
}
|
| 1067 |
+
},
|
| 1068 |
+
"nbformat": 4,
|
| 1069 |
+
"nbformat_minor": 2
|
| 1070 |
+
}
|