czty commited on
Commit
d1ce356
·
verified ·
1 Parent(s): 96e6518

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Biomanus/.env +1 -0
  2. Biomanus/.env.example +22 -0
  3. Biomanus/.gitattributes +2 -0
  4. Biomanus/.gitignore +236 -0
  5. Biomanus/.pre-commit-config.yaml +41 -0
  6. Biomanus/3xTG_AD_DE_results.csv +0 -0
  7. Biomanus/3xTG_AD_counts.csv +0 -0
  8. Biomanus/3xTG_AD_metadata.csv +7 -0
  9. Biomanus/5xFAD_DE_results.csv +0 -0
  10. Biomanus/5xFAD_counts.csv +0 -0
  11. Biomanus/5xFAD_counts_int.csv +0 -0
  12. Biomanus/5xFAD_metadata.csv +21 -0
  13. Biomanus/ALZHEIMER_BENCHMARK_GUIDE.md +261 -0
  14. Biomanus/BENCHMARK_EXECUTION_SUMMARY.md +183 -0
  15. Biomanus/LICENSE +201 -0
  16. Biomanus/LMX1B.meme +19 -0
  17. Biomanus/MANIFEST.in +27 -0
  18. Biomanus/README.md +383 -0
  19. Biomanus/article.xml +1 -0
  20. Biomanus/biomni.md +558 -0
  21. Biomanus/biomni/__init__.py +3 -0
  22. Biomanus/biomni/__pycache__/__init__.cpython-310.pyc +0 -0
  23. Biomanus/biomni/__pycache__/__init__.cpython-311.pyc +0 -0
  24. Biomanus/biomni/__pycache__/__init__.cpython-313.pyc +0 -0
  25. Biomanus/biomni/__pycache__/config.cpython-310.pyc +0 -0
  26. Biomanus/biomni/__pycache__/config.cpython-311.pyc +0 -0
  27. Biomanus/biomni/__pycache__/config.cpython-313.pyc +0 -0
  28. Biomanus/biomni/__pycache__/env_desc.cpython-310.pyc +0 -0
  29. Biomanus/biomni/__pycache__/env_desc.cpython-311.pyc +0 -0
  30. Biomanus/biomni/__pycache__/llm.cpython-310.pyc +0 -0
  31. Biomanus/biomni/__pycache__/llm.cpython-311.pyc +0 -0
  32. Biomanus/biomni/__pycache__/llm.cpython-313.pyc +0 -0
  33. Biomanus/biomni/__pycache__/utils.cpython-310.pyc +0 -0
  34. Biomanus/biomni/__pycache__/utils.cpython-313.pyc +0 -0
  35. Biomanus/biomni/__pycache__/version.cpython-310.pyc +0 -0
  36. Biomanus/biomni/__pycache__/version.cpython-311.pyc +0 -0
  37. Biomanus/biomni/__pycache__/version.cpython-313.pyc +0 -0
  38. Biomanus/biomni/agent/__init__.py +1 -0
  39. Biomanus/biomni/agent/__pycache__/__init__.cpython-310.pyc +0 -0
  40. Biomanus/biomni/agent/__pycache__/__init__.cpython-311.pyc +0 -0
  41. Biomanus/biomni/agent/__pycache__/__init__.cpython-313.pyc +0 -0
  42. Biomanus/biomni/agent/__pycache__/a1.cpython-310.pyc +0 -0
  43. Biomanus/biomni/agent/a1.py +0 -0
  44. Biomanus/biomni/agent/env_collection.py +313 -0
  45. Biomanus/biomni/agent/function_generator.py +119 -0
  46. Biomanus/biomni/agent/qa_llm.py +50 -0
  47. Biomanus/biomni/agent/react.py +465 -0
  48. Biomanus/biomni/biorxiv_scripts/extract_biorxiv_tasks.py +404 -0
  49. Biomanus/biomni/biorxiv_scripts/generate_function.py +72 -0
  50. Biomanus/biomni/biorxiv_scripts/process_all_subjects.py +315 -0
Biomanus/.env ADDED
@@ -0,0 +1 @@
 
 
1
+ export DEEPSEEK_API_KEY=sk-06e6154722b84e89b081b1c9571838ef
Biomanus/.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
Biomanus/.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.bam filter=lfs diff=lfs merge=lfs -text
2
+ *.h5ad filter=lfs diff=lfs merge=lfs -text
Biomanus/.gitignore ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/
235
+ /paper_reproduction_runs/
236
+ /bioagent-bench-runs/
Biomanus/.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"]
Biomanus/3xTG_AD_DE_results.csv ADDED
The diff for this file is too large to render. See raw diff
 
Biomanus/3xTG_AD_counts.csv ADDED
The diff for this file is too large to render. See raw diff
 
Biomanus/3xTG_AD_metadata.csv ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ sample,condition
2
+ G3R1_Cortex_3xTgAD,3xTG_AD
3
+ G3R3_Cortex_3xTgAD,3xTG_AD
4
+ G3R4_Cortex_3xTgAD,3xTG_AD
5
+ G3R10_Cortex_WT,WT
6
+ G3R7_Cortex_WT,WT
7
+ G3R9_Cortex_WT,WT
Biomanus/5xFAD_DE_results.csv ADDED
The diff for this file is too large to render. See raw diff
 
Biomanus/5xFAD_counts.csv ADDED
The diff for this file is too large to render. See raw diff
 
Biomanus/5xFAD_counts_int.csv ADDED
The diff for this file is too large to render. See raw diff
 
Biomanus/5xFAD_metadata.csv ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ sample,condition
2
+ 5xFAD;BL6_cortex_8mon_Female_295,5xFAD
3
+ 5xFAD;BL6_cortex_8mon_Female_312,5xFAD
4
+ 5xFAD;BL6_cortex_8mon_Female_339,5xFAD
5
+ 5xFAD;BL6_cortex_8mon_Female_341,5xFAD
6
+ 5xFAD;BL6_cortex_8mon_Female_342,5xFAD
7
+ 5xFAD;BL6_cortex_8mon_Male_299,5xFAD
8
+ 5xFAD;BL6_cortex_8mon_Male_300,5xFAD
9
+ 5xFAD;BL6_cortex_8mon_Male_307,5xFAD
10
+ 5xFAD;BL6_cortex_8mon_Male_387,5xFAD
11
+ 5xFAD;BL6_cortex_8mon_Male_390,5xFAD
12
+ BL6_cortex_8mon_Female_322,WT
13
+ BL6_cortex_8mon_Female_338,WT
14
+ BL6_cortex_8mon_Female_340,WT
15
+ BL6_cortex_8mon_Female_348,WT
16
+ BL6_cortex_8mon_Female_351,WT
17
+ BL6_cortex_8mon_Male_389,WT
18
+ BL6_cortex_8mon_Male_396,WT
19
+ BL6_cortex_8mon_Male_399,WT
20
+ BL6_cortex_8mon_Male_410,WT
21
+ BL6_cortex_8mon_Male_412,WT
Biomanus/ALZHEIMER_BENCHMARK_GUIDE.md ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Alzheimer's Disease Mouse Models Benchmark 执行指南
2
+
3
+ 本文档详细说明如何使用Hypo_Bio_OS agent系统运行Alzheimer's Disease mouse models benchmark分析。
4
+
5
+ ## 概述
6
+
7
+ 本benchmark要求对三种不同的Alzheimer's Disease小鼠模型(5xFAD、3xTG-AD和PS3O1S)进行比较分析,识别共享的分子通路。分析包括:
8
+ 1. 差异表达分析
9
+ 2. 使用KEGG通路进行通路富集分析
10
+ 3. 生成包含三个模型共有通路及对应p-value的比较表格
11
+
12
+ ## 文件结构
13
+
14
+ ### 数据文件
15
+ ```
16
+ /225040511/project/bioagent-bench/dataset/alzheimer-mouse/data/
17
+ ├── GSE168137_countList.txt # 5xFAD模型数据
18
+ ├── GSE161904_Raw_gene_counts_cortex.txt # 3xTG-AD模型数据
19
+ └── DEA_PS3O1S.csv # PS3O1S模型差异表达结果
20
+ ```
21
+
22
+ ### 生成的脚本
23
+ ```
24
+ /225040511/project/Hypo_Bio_OS/
25
+ ├── run_alzheimer_benchmark.py # 主执行脚本
26
+ ├── test_alzheimer_analysis.py # 测试脚本
27
+ ├── verify_data_access.py # 数据访问验证脚本
28
+ └── ALZHEIMER_BENCHMARK_GUIDE.md # 本指南
29
+ ```
30
+
31
+ ### 结果目录
32
+ ```
33
+ /225040511/project/Hypo_Bio_OS/bioagent-bench-results/
34
+ ├── README.md # 结果说明
35
+ ├── template/ # 目录结构模板
36
+ └── alzheimer_analysis_YYYYMMDD_HHMMSS/ # 每次运行的结果
37
+ ```
38
+
39
+ ## 环境要求
40
+
41
+ ### 1. Python环境
42
+ - Python 3.8+
43
+ - 已安装Hypo_Bio_OS依赖
44
+ - 建议使用conda环境 `biomni_e1`
45
+
46
+ ### 2. 生物信息学工具
47
+ - R语言环境(用于部分分析)
48
+ - 必要的R包:DESeq2, clusterProfiler, org.Mm.eg.db等
49
+ - Python生物信息学库:scanpy, mygene, gseapy等
50
+
51
+ ### 3. API密钥
52
+ - DeepSeek API密钥(或其他支持的LLM API密钥)
53
+ - 设置环境变量:`export DEEPSEEK_API_KEY='your_api_key'`
54
+
55
+ ## 执行步骤
56
+
57
+ ### 步骤1:验证数据访问
58
+ ```bash
59
+ cd /225040511/project/Hypo_Bio_OS
60
+ python verify_data_access.py
61
+ ```
62
+
63
+ ### 步骤2:设置API密钥
64
+ ```bash
65
+ # 方法1:设置环境变量
66
+ export DEEPSEEK_API_KEY='your_actual_api_key_here'
67
+
68
+ # 方法2:在脚本运行时输入
69
+ # 脚本会提示输入API密钥
70
+ ```
71
+
72
+ ### 步骤3:运行完整分析
73
+ ```bash
74
+ cd /225040511/project/Hypo_Bio_OS
75
+ python run_alzheimer_benchmark.py
76
+ ```
77
+
78
+ ## 脚本详细说明
79
+
80
+ ### 1. `run_alzheimer_benchmark.py`
81
+
82
+ 这是主执行脚本,负责:
83
+ - 初始化Hypo_Bio_OS agent
84
+ - 配置MCP工具
85
+ - 执行分析查询
86
+ - 保存结果
87
+
88
+ **关键配置:**
89
+ ```python
90
+ agent_config = {
91
+ "path": str(DATA_PATH), # 数据路径
92
+ "llm": "deepseek-chat", # 使用的LLM
93
+ "source": "Custom", # API源
94
+ "base_url": "https://api.deepseek.com/v1", # API地址
95
+ "api_key": os.getenv("DEEPSEEK_API_KEY", ""), # API密钥
96
+ "rewrite_user_query": True, # 重写查询以优化检索
97
+ "dynamic_mcp_registration": True, # 动态注册MCP工具
98
+ "mcp_server_top_k": 20, # 使用的MCP服务器数量
99
+ "mcp_tool_top_k": 12, # 每个服务器的工具数量
100
+ }
101
+ ```
102
+
103
+ ### 2. 分析查询
104
+
105
+ 脚本使用以下查询执行分析:
106
+ ```
107
+ Perform a comparative analysis of three different Alzheimer's Disease mouse models
108
+ (5xFAD, 3xTG-AD, and PS3O1S) to identify shared molecular pathways by performing
109
+ differential expression analysis, conducting pathway enrichment analysis using
110
+ KEGG pathways for each model and generate a final comparison which contains common
111
+ pathways present across all three models, corresponding p-values for each
112
+ pathway-model combination.
113
+
114
+ Data files available:
115
+ 1. GSE168137_countList.txt - 5xFAD mouse model data
116
+ 2. GSE161904_Raw_gene_counts_cortex.txt - 3xTG-AD mouse model data
117
+ 3. DEA_PS3O1S.csv - PS3O1S tau transgenic mouse model differential expression results
118
+
119
+ Requirements:
120
+ 1. Load and preprocess each dataset appropriately
121
+ 2. Perform differential expression analysis for 5xFAD and 3xTG-AD models
122
+ 3. For PS3O1S model, use the provided differential expression results
123
+ 4. Perform KEGG pathway enrichment analysis for each model
124
+ 5. Identify common pathways across all three models
125
+ 6. Generate a comprehensive comparison table with p-values for each pathway-model combination
126
+ 7. Save all intermediate results and final outputs
127
+
128
+ Please provide a detailed summary of the analysis process and findings.
129
+ ```
130
+
131
+ ## 预期输出
132
+
133
+ ### 1. 结果文件结构
134
+ 每次运行会在`/225040511/project/Hypo_Bio_OS/bioagent-bench-results/`下创建时间戳目录:
135
+ ```
136
+ alzheimer_analysis_20260511_143022/
137
+ ├── run_metadata.json # 运行配置和元数据
138
+ ├── execution_log.txt # 详细执行日志
139
+ ├── execution_log.json # JSON格式日志
140
+ ├── final_answer.txt # agent的最终分析报告
141
+ ├── processed_data/ # 处理后的数据文件
142
+ ├── analysis_results/ # 分析结果
143
+ │ ├── differential_expression/
144
+ │ ├── pathway_enrichment/
145
+ │ └── comparative_analysis/
146
+ ├── visualizations/ # 图表文件
147
+ └── reports/ # 生成报告
148
+ ```
149
+
150
+ ### 2. 关键分析结果
151
+ 1. **差异表达分析结果**:每个模型的显著差异表达基因列表
152
+ 2. **通路富集结果**:每个模型的KEGG通路富集分析结果
153
+ 3. **共同通路表格**:三个模型共有的通路及对应p-value
154
+ 4. **可视化图表**:火山图、通路富集图、热图等
155
+ 5. **综合分析报告**:详细的分析过程、方法和发现总结
156
+
157
+ ## 故障排除
158
+
159
+ ### 常见问题1:API密钥错误
160
+ **症状**:脚本提示API密钥无效或未设置
161
+ **解决**:
162
+ ```bash
163
+ # 确认环境变量已设置
164
+ echo $DEEPSEEK_API_KEY
165
+
166
+ # 重新设置
167
+ export DEEPSEEK_API_KEY='your_actual_api_key'
168
+ ```
169
+
170
+ ### 常见问题2:依赖缺失
171
+ **症状**:导入错误或运行时错误
172
+ **解决**:
173
+ ```bash
174
+ # 激活conda环境
175
+ conda activate biomni_e1
176
+
177
+ # 安装缺失包
178
+ pip install pandas numpy scipy statsmodels mygene gseapy
179
+ ```
180
+
181
+ ### 常见问题3:权限问题
182
+ **症状**:无法读取数据文件或写入结果
183
+ **解决**:
184
+ ```bash
185
+ # 检查文件权限
186
+ ls -la /225040511/project/bioagent-bench/dataset/alzheimer-mouse/data/
187
+
188
+ # 检查结果目录权限
189
+ ls -la /225040511/project/Hypo_Bio_OS/bioagent-bench-results/
190
+ ```
191
+
192
+ ### 常见问题4:内存不足
193
+ **症状**:分析过程中程序崩溃
194
+ **解决**:
195
+ - 减少同时处理的数据量
196
+ - 增加系统内存
197
+ - 使用更高效的数据结构
198
+
199
+ ## 扩展和定制
200
+
201
+ ### 1. 使用不同的LLM
202
+ 修改`run_alzheimer_benchmark.py`中的配置:
203
+ ```python
204
+ # 使用OpenAI
205
+ agent_config = {
206
+ "llm": "gpt-4",
207
+ "source": "OpenAI",
208
+ "api_key": os.getenv("OPENAI_API_KEY", ""),
209
+ }
210
+
211
+ # 使用Claude
212
+ agent_config = {
213
+ "llm": "claude-3-5-sonnet-20241022",
214
+ "source": "Anthropic",
215
+ "api_key": os.getenv("ANTHROPIC_API_KEY", ""),
216
+ }
217
+ ```
218
+
219
+ ### 2. 添加自定义分析步骤
220
+ 在查询中添加特定要求:
221
+ ```python
222
+ custom_query = QUERY + """
223
+
224
+ 额外要求:
225
+ 1. 执行蛋白质互作网络分析
226
+ 2. 识别hub基因
227
+ 3. 与人类Alzheimer's Disease数据进行比较
228
+ """
229
+ ```
230
+
231
+ ### 3. 调整输出格式
232
+ 修改`save_results`函数以生成特定格式的输出。
233
+
234
+ ## 性能优化建议
235
+
236
+ 1. **数据预处理**:对大型计数矩阵进行适当的下采样或过滤
237
+ 2. **并行处理**:对独立分析步骤使用多进程
238
+ 3. **缓存中间结果**:避免重复计算
239
+ 4. **内存管理**:及时释放不再需要的大型对象
240
+
241
+ ## 参考资源
242
+
243
+ 1. **原始benchmark**:`/225040511/project/bioagent-bench/README.md`
244
+ 2. **Hypo_Bio_OS文档**:`/225040511/project/Hypo_Bio_OS/README.md`
245
+ 3. **相关论文**:
246
+ - 5xFAD模型:GSE168137
247
+ - 3xTG-AD模型:GSE161904
248
+ - PS3O1S模型:GSE118523
249
+
250
+ ## 支持
251
+
252
+ 如遇问题,请:
253
+ 1. 检查日志文件中的详细错误信息
254
+ 2. 验证数据文件和权限
255
+ 3. 确认API密钥和网络连接
256
+ 4. 参考Hypo_Bio_OS系统文档
257
+
258
+ ---
259
+
260
+ *最后更新:2026年5月11日*
261
+ *作者:Hypo_Bio_OS开发团队*
Biomanus/BENCHMARK_EXECUTION_SUMMARY.md ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Alzheimer's Disease Mouse Models Benchmark 执行总结
2
+
3
+ ## 项目概述
4
+
5
+ 成功创建了使用Hypo_Bio_OS agent系统执行Alzheimer's Disease mouse models benchmark分析的完整解决方案。该方案能够自动执行三个小鼠模型(5xFAD、3xTG-AD、PS3O1S)的比较分析,识别共享分子通路。
6
+
7
+ ## 完成的工作
8
+
9
+ ### 1. 系统分析
10
+ - ✅ 分析了bioagent-bench benchmark的结构
11
+ - ✅ 研究了Alzheimer's mouse数据文件格式
12
+ - ✅ 理解了Hypo_Bio_OS agent系统的架构
13
+ - ✅ 掌握了agent调用接口和配置方法
14
+
15
+ ### 2. 代码开发
16
+ - ✅ 创建了主执行脚本 `run_alzheimer_benchmark.py`
17
+ - ✅ 开发了测试脚本 `test_alzheimer_analysis.py`
18
+ - ✅ 实现了数据访问验证脚本 `verify_data_access.py`
19
+ - ✅ 编写了Shell执行脚本 `run_benchmark.sh`
20
+
21
+ ### 3. 配置设置
22
+ - ✅ 设置了结果保存路径 `/225040511/project/Hypo_Bio_OS/bioagent-bench-results`
23
+ - ✅ 配置了MCP工具集成
24
+ - ✅ 定义了标准化的输出格式
25
+ - ✅ 创建了目录结构模板
26
+
27
+ ### 4. 文档编写
28
+ - ✅ 编写了详细执行指南 `ALZHEIMER_BENCHMARK_GUIDE.md`
29
+ - ✅ 创建了结果目录说明 `README.md`
30
+ - ✅ 提供了故障排除指南
31
+ - ✅ 记录了性能优化建议
32
+
33
+ ## 技术架构
34
+
35
+ ### 核心组件
36
+ 1. **Hypo_Bio_OS Agent**:基于LangGraph的智能体系统
37
+ 2. **MCP工具集成**:通过strata_mcp_config_shim.yaml配置
38
+ 3. **数据分析管道**:差异表达分析 + 通路富集分析
39
+ 4. **结果管理系统**:结构化输出和元数据记录
40
+
41
+ ### 数据流程
42
+ ```
43
+ 原始数据文件 → 数据预处理 → 差异表达分析 →
44
+ 通路富集分析 → 比较分析 → 结果生成 → 报告输出
45
+ ```
46
+
47
+ ### 支持的数据集
48
+ 1. **5xFAD模型**:GSE168137_countList.txt
49
+ 2. **3xTG-AD模型**:GSE161904_Raw_gene_counts_cortex.txt
50
+ 3. **PS3O1S模型**:DEA_PS3O1S.csv(已包含差异表达结果)
51
+
52
+ ## 功能特性
53
+
54
+ ### 1. 自动化分析
55
+ - 自动加载和预处理三个数据集
56
+ - 执行差异表达分析(5xFAD和3xTG-AD)
57
+ - 利用现有差异表达结果(PS3O1S)
58
+ - 进行KEGG通路富集分析
59
+ - 识别共有通路并生成比较表格
60
+
61
+ ### 2. 智能配置
62
+ - 支持多种LLM(DeepSeek、OpenAI、Claude等)
63
+ - 动态MCP工具注册和选择
64
+ - 可配置的分析参数
65
+ - 灵活的输出格式
66
+
67
+ ### 3. 结果管理
68
+ - 时间戳目录自动创建
69
+ - 完整的元数据记录
70
+ - 结构化文件存储
71
+ - 可视化图表生成
72
+
73
+ ### 4. 错误处理
74
+ - 数据文件验证
75
+ - API密钥检查
76
+ - 依赖缺失检测
77
+ - 详细错误日志
78
+
79
+ ## 使用方法
80
+
81
+ ### 快速开始
82
+ ```bash
83
+ cd /225040511/project/Hypo_Bio_OS
84
+
85
+ # 设置API密钥
86
+ export DEEPSEEK_API_KEY='your_api_key'
87
+
88
+ # 运行分析
89
+ ./run_benchmark.sh
90
+ ```
91
+
92
+ ### 分步执行
93
+ ```bash
94
+ # 1. 验证数据访问
95
+ python verify_data_access.py
96
+
97
+ # 2. 运行完整分析
98
+ python run_alzheimer_benchmark.py
99
+
100
+ # 3. 查看结果
101
+ ls -la /225040511/project/Hypo_Bio_OS/bioagent-bench-results/
102
+ ```
103
+
104
+ ## 预期输出
105
+
106
+ ### 分析结果
107
+ 1. **差异表达基因列表**:每个模型的显著差异表达基因
108
+ 2. **通路富集结果**:KEGG通路富集分析结果
109
+ 3. **共同通路表格**:三个模型共有的通路及p-value
110
+ 4. **可视化图表**:火山图、富集图、热图等
111
+
112
+ ### 报告文件
113
+ 1. **技术报告**:详细的分析方法和过程
114
+ 2. **结果摘要**:关键发现和结论
115
+ 3. **元数据文件**:运行配置和环境信息
116
+ 4. **执行日志**:完整的运行记录
117
+
118
+ ## 扩展能力
119
+
120
+ ### 自定义分析
121
+ - 添加新的分析步骤
122
+ - 修改分析参数
123
+ - 集成额外数据源
124
+ - 生成定制化报告
125
+
126
+ ### 性能优化
127
+ - 并行处理支持
128
+ - 内存管理优化
129
+ - 缓存机制
130
+ - 增量分析
131
+
132
+ ### 集成扩展
133
+ - 与其他生物信息学工具集成
134
+ - 支持更多数据格式
135
+ - 云平台部署
136
+ - 工作流自动化
137
+
138
+ ## 验证状态
139
+
140
+ ### 已验证的功能
141
+ - ✅ 数据文件访问权限
142
+ - ✅ Python环境兼容性
143
+ - ✅ 脚本基本功能
144
+ - ✅ 目录结构创建
145
+
146
+ ### 待验证的功能
147
+ - ⏳ 完整agent执行(需要API密钥)
148
+ - ⏳ 实际分析结果质量
149
+ - ⏳ 大规模数据处理性能
150
+ - ⏳ 错误恢复机制
151
+
152
+ ## 后续建议
153
+
154
+ ### 短期改进
155
+ 1. 添加更多测试用例
156
+ 2. 优化内存使用
157
+ 3. 改进错误消息
158
+ 4. 增加进度指示
159
+
160
+ ### 长期规划
161
+ 1. 支持更多小鼠模型
162
+ 2. 集成单细胞分析
163
+ 3. 添加机器学习组件
164
+ 4. 开发Web界面
165
+
166
+ ## 贡献指南
167
+
168
+ 欢迎通过以下方式贡献:
169
+ 1. 报告问题和建议
170
+ 2. 提交代码改进
171
+ 3. 添加新的分析功能
172
+ 4. 完善文档和示例
173
+
174
+ ## 联系信息
175
+
176
+ - 项目位置:`/225040511/project/Hypo_Bio_OS`
177
+ - 数据源:`/225040511/project/bioagent-bench`
178
+ - 结果目录:`/225040511/project/Hypo_Bio_OS/bioagent-bench-results`
179
+
180
+ ---
181
+
182
+ *执行总结生成时间:2026年5月11日*
183
+ *系统状态:就绪,等待API密钥执行完整分析*
Biomanus/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.
Biomanus/LMX1B.meme ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MEME version 4
2
+
3
+ ALPHABET= ACGT
4
+
5
+ strands: + -
6
+
7
+ Background letter frequencies
8
+ A 0.25 C 0.25 G 0.25 T 0.25
9
+
10
+ MOTIF LMX1B
11
+ letter-probability matrix: alength= 4 w= 8 nsites= 4101 E= 0
12
+ 0.211220 0.235366 0.239512 0.313902
13
+ 0.111735 0.294216 0.034956 0.559093
14
+ 0.791088 0.059028 0.063465 0.086420
15
+ 0.935021 0.013680 0.009804 0.041496
16
+ 0.070230 0.002460 0.010065 0.917244
17
+ 0.214912 0.065318 0.066433 0.653338
18
+ 0.871811 0.019983 0.004252 0.103954
19
+ 0.513171 0.161463 0.197317 0.128049
Biomanus/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
Biomanus/README.md ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HypoBioOS
2
+
3
+ Step-by-step guide for setting up Biomni and running the generic paper reproduction workflow in this repository.
4
+
5
+ This repository is developed based on the open-source Biomni codebase from [snap-stanford/biomni](https://github.com/snap-stanford/biomni). We extend the original Biomni agent framework with MCP server generation, graph-guided tool retrieval, dynamic MCP routing, and benchmark-oriented experiment runners.
6
+
7
+ ## 1. Goal
8
+
9
+ This document explains how to:
10
+
11
+ - set up the Biomni runtime environment,
12
+ - configure the LLM endpoint used by the reproduction script,
13
+ - verify MCP servers are discoverable,
14
+ - run the generic reproduction runner,
15
+ - reproduce the built-in FOXJ1 example,
16
+ - run the same workflow on a different paper.
17
+
18
+ The main script covered here is:
19
+
20
+ ```text
21
+ /225040511/project/Biomni/reproduce_foxj1_paper.py
22
+ ```
23
+
24
+ Although the filename still contains `foxj1`, the script is now a generic runner.
25
+
26
+ ## 2. Prerequisites
27
+
28
+ Before you start, make sure you have:
29
+
30
+ - access to this Biomni repository,
31
+ - Conda installed,
32
+ - a working Python environment compatible with Biomni,
33
+ - an API key for a model endpoint,
34
+ - generated MCP server directories under `biomni_web/backend/data/mcp_generated/`.
35
+
36
+ ## 3. Enter The Repository
37
+
38
+ ```bash
39
+ cd /225040511/project/Biomni
40
+ ```
41
+
42
+ ## 4. Build The Biomni Environment
43
+
44
+ Follow the environment setup instructions in:
45
+
46
+ ```text
47
+ biomni_env/README.md
48
+ ```
49
+
50
+ After the environment is created, activate it:
51
+
52
+ ```bash
53
+ conda activate biomni_e1
54
+ ```
55
+
56
+ If you are running Biomni from source, install the current repository into the environment:
57
+
58
+ ```bash
59
+ pip install -e .
60
+ ```
61
+
62
+ If you prefer the upstream GitHub version instead:
63
+
64
+ ```bash
65
+ pip install git+https://github.com/snap-stanford/Biomni.git@main
66
+ ```
67
+
68
+ ## 5. Configure The Model Endpoint
69
+
70
+ The current `reproduce_foxj1_paper.py` script creates the agent with:
71
+
72
+ ```python
73
+ source="Custom"
74
+ ```
75
+
76
+ This means the script is using an OpenAI-compatible HTTP endpoint through Biomni's custom backend path.
77
+
78
+ The current code reads credentials in this order:
79
+
80
+ 1. `DEEPSEEK_API_KEY`
81
+ 2. `OPENAI_API_KEY`
82
+ 3. fallback to `"EMPTY"`
83
+
84
+ The current code also reads:
85
+
86
+ - `DEEPSEEK_BASE_URL`
87
+ - `DEEPSEEK_MODEL_NAME`
88
+
89
+ So if you want to use OpenAI, you should not only set `OPENAI_API_KEY`, but also point `DEEPSEEK_BASE_URL` to the OpenAI API endpoint and set `DEEPSEEK_MODEL_NAME` to an OpenAI model name.
90
+
91
+ ### Option A. Use DeepSeek
92
+
93
+ Set these environment variables:
94
+
95
+ ```bash
96
+ export DEEPSEEK_API_KEY="your_api_key"
97
+ export DEEPSEEK_BASE_URL="https://api.deepseek.com/v1"
98
+ export DEEPSEEK_MODEL_NAME="deepseek-chat"
99
+ ```
100
+
101
+ ### Option B. Use OpenAI API Key
102
+
103
+ Use the OpenAI key as the authentication source, but still set the endpoint and model through the same runtime variables used by the script:
104
+
105
+ ```bash
106
+ export OPENAI_API_KEY="your_openai_api_key"
107
+ export DEEPSEEK_BASE_URL="https://api.openai.com/v1"
108
+ export DEEPSEEK_MODEL_NAME="gpt-4.1-mini"
109
+ ```
110
+
111
+ You can replace `gpt-4.1-mini` with another OpenAI model that your account can access, for example:
112
+
113
+ ```bash
114
+ export DEEPSEEK_MODEL_NAME="gpt-4.1"
115
+ ```
116
+
117
+ ### Option C. Use Another OpenAI-Compatible Endpoint
118
+
119
+ If you use another OpenAI-compatible endpoint, point the same variables to your own service:
120
+
121
+ ```bash
122
+ export OPENAI_API_KEY="your_custom_endpoint_key"
123
+ export DEEPSEEK_BASE_URL="http://your-endpoint/v1"
124
+ export DEEPSEEK_MODEL_NAME="your-model-name"
125
+ ```
126
+
127
+ ### Quick Check
128
+
129
+ After exporting the variables, you can confirm they are set:
130
+
131
+ ```bash
132
+ echo "$DEEPSEEK_BASE_URL"
133
+ echo "$DEEPSEEK_MODEL_NAME"
134
+ ```
135
+
136
+ If you are using OpenAI:
137
+
138
+ ```bash
139
+ echo "$OPENAI_API_KEY"
140
+ ```
141
+
142
+ You do not need to print the full key in shared logs. It is enough to verify that the variable is non-empty.
143
+
144
+ Optional but recommended:
145
+
146
+ ```bash
147
+ export BIOMNI_MCP_PYTHON="$(which python)"
148
+ ```
149
+
150
+ This ensures the generated MCP config uses the Python interpreter from your active Biomni environment.
151
+
152
+ ## 6. Verify MCP Servers Exist
153
+
154
+ The script discovers MCP servers from:
155
+
156
+ ```text
157
+ biomni_web/backend/data/mcp_generated/
158
+ ```
159
+
160
+ Each server is expected to look like:
161
+
162
+ ```text
163
+ mcp_<server_name>/app/*_shim_server.py
164
+ ```
165
+
166
+ or:
167
+
168
+ ```text
169
+ mcp_<server_name>/app/*_server.py
170
+ ```
171
+
172
+ You can inspect what the script currently finds:
173
+
174
+ ```bash
175
+ python reproduce_foxj1_paper.py --list-servers
176
+ ```
177
+
178
+ If the list is empty or missing expected servers, check whether the corresponding generated directories exist under:
179
+
180
+ ```text
181
+ /225040511/project/Biomni/biomni_web/backend/data/mcp_generated/
182
+ ```
183
+
184
+ ## 7. Understand What The Script Does
185
+
186
+ The script performs these high-level steps:
187
+
188
+ 1. parses your paper title, context, query, input files, and requested MCP servers,
189
+ 2. discovers available MCP server directories,
190
+ 3. builds an MCP config YAML file for the selected servers,
191
+ 4. creates a run directory under `paper_reproduction_runs/`,
192
+ 5. writes prompt, query, and plan files,
193
+ 6. initializes `A1`,
194
+ 7. loads the MCP config,
195
+ 8. asks Biomni to plan the reproduction steps from your query,
196
+ 9. lets Biomni select suitable MCP tools during execution,
197
+ 10. saves logs, final answer, and report files.
198
+
199
+ ## 8. Do A Dry Run First
200
+
201
+ Before running the full workflow, do a preparation-only run:
202
+
203
+ ```bash
204
+ python reproduce_foxj1_paper.py \
205
+ --dataset-profile foxj1 \
206
+ --prepare-only
207
+ ```
208
+
209
+ This does not call the model. It only prepares:
210
+
211
+ - the run directory,
212
+ - the MCP config,
213
+ - the query file,
214
+ - the prompt file,
215
+ - the execution plan file.
216
+
217
+ The output will be written under:
218
+
219
+ ```text
220
+ paper_reproduction_runs/
221
+ ```
222
+
223
+ ## 9. Run The Built-In FOXJ1 Example
224
+
225
+ Use the built-in dataset profile if you want to reproduce the FOXJ1 example workflow.
226
+
227
+ Command:
228
+
229
+ ```bash
230
+ python reproduce_foxj1_paper.py \
231
+ --dataset-profile foxj1 \
232
+ --query "Firstly, whether FOXJ1 and GMNC were up-regulated in LuCaP35CR, then whether ABCB1 was up-regulated in LuCaP70CR, and finally the enrichment analysis of pathways related to cilia/microtubules was performed"
233
+ ```
234
+
235
+ What this does:
236
+
237
+ 1. loads the built-in FOXJ1 profile,
238
+ 2. downloads the associated GEO processed files if needed,
239
+ 3. selects the preferred MCP server set for that profile,
240
+ 4. builds the execution prompt from your natural-language query,
241
+ 5. runs Biomni with MCP enabled,
242
+ 6. writes logs and outputs into the run directory.
243
+
244
+ ## 10. Run A Different Paper
245
+
246
+ If you want to run another paper, provide your own paper metadata and inputs.
247
+
248
+ Minimal example:
249
+
250
+ ```bash
251
+ python reproduce_foxj1_paper.py \
252
+ --paper-title "Your Paper Title" \
253
+ --context-file /path/to/paper_summary.md \
254
+ --input-file /path/to/data1.csv \
255
+ --input-file /path/to/data2.tsv \
256
+ --query "Reproduce the main findings, choose suitable MCP tools, run the feasible analyses, and summarize what is supported by the provided data." \
257
+ --server jq \
258
+ --server bioconductor-clusterprofiler \
259
+ --server gseapy
260
+ ```
261
+
262
+ You can also pass entire directories:
263
+
264
+ ```bash
265
+ python reproduce_foxj1_paper.py \
266
+ --paper-title "Another Paper" \
267
+ --paper-context "Short summary of the paper and the results you want to reproduce." \
268
+ --input-dir /path/to/input_folder \
269
+ --query "Check differential patterns and enrichment results." \
270
+ --server all
271
+ ```
272
+
273
+ ## 11. Important Command Options
274
+
275
+ Common options supported by the script:
276
+
277
+ - `--paper-title`: title of the paper for this run.
278
+ - `--paper-context`: short context text passed directly on the command line.
279
+ - `--context-file`: markdown or text file containing paper context.
280
+ - `--query`: natural-language reproduction request.
281
+ - `--input-file`: input file to include, can be used multiple times.
282
+ - `--input-dir`: input directory to include, can be used multiple times.
283
+ - `--dataset-profile foxj1`: use the built-in FOXJ1 example profile.
284
+ - `--server`: choose MCP servers explicitly.
285
+ - `--server all`: register all discovered MCP servers.
286
+ - `--prepare-only`: generate files without running the model.
287
+ - `--list-servers`: print discovered MCP servers and exit.
288
+
289
+ ## 12. What Files You Should Expect
290
+
291
+ For each run, the script creates a folder like:
292
+
293
+ ```text
294
+ paper_reproduction_runs/<run_name>/
295
+ ```
296
+
297
+ Inside it, you should see files such as:
298
+
299
+ ```text
300
+ mcp_config.yaml
301
+ paper_context.md
302
+ reproduction_query.txt
303
+ execution_plan.md
304
+ reproduction_prompt.txt
305
+ results/biomni_execution_log_*.txt
306
+ results/biomni_execution_log_*.json
307
+ results/biomni_final_answer_*.txt
308
+ results/biomni_run_metadata_*.json
309
+ results/biomni_conversation_*.md
310
+ results/reproduction_report.md
311
+ ```
312
+
313
+ ## 13. Recommended First-Time Workflow
314
+
315
+ If this is your first time running the script, use this exact order:
316
+
317
+ 1. `cd /225040511/project/Biomni`
318
+ 2. `conda activate biomni_e1`
319
+ 3. `pip install -e .`
320
+ 4. export `DEEPSEEK_API_KEY`
321
+ 5. export `DEEPSEEK_BASE_URL`
322
+ 6. export `DEEPSEEK_MODEL_NAME`
323
+ 7. optionally export `BIOMNI_MCP_PYTHON="$(which python)"`
324
+ 8. run `python reproduce_foxj1_paper.py --list-servers`
325
+ 9. run `python reproduce_foxj1_paper.py --dataset-profile foxj1 --prepare-only`
326
+ 10. run the full FOXJ1 example command
327
+
328
+ ## 14. Troubleshooting
329
+
330
+ If the script does not run, check these items one by one.
331
+
332
+ ### Environment Problems
333
+
334
+ - Make sure the Conda environment is activated.
335
+ - Make sure `pip install -e .` completed successfully.
336
+ - Make sure the same Python is used by both Biomni and MCP config generation.
337
+
338
+ ### Model Problems
339
+
340
+ - Make sure `DEEPSEEK_API_KEY` is set.
341
+ - Make sure `DEEPSEEK_BASE_URL` points to a reachable OpenAI-compatible endpoint.
342
+ - Make sure `DEEPSEEK_MODEL_NAME` matches a model served by that endpoint.
343
+
344
+ ### MCP Problems
345
+
346
+ - Run `python reproduce_foxj1_paper.py --list-servers`.
347
+ - Confirm the required `mcp_<name>` directories exist.
348
+ - Confirm each selected MCP server has an `app/` directory with a `*_shim_server.py` or `*_server.py` file.
349
+
350
+ ### Input Problems
351
+
352
+ - Confirm all `--input-file` paths exist.
353
+ - Confirm all `--input-dir` paths exist.
354
+ - If using the `foxj1` profile, let the script finish downloading the GEO processed files.
355
+
356
+ ### Dry Run Problems
357
+
358
+ - If a full run fails, first retry with `--prepare-only`.
359
+ - Check whether the prompt and MCP config are generated correctly before debugging model execution.
360
+
361
+ ## 15. One-Line Reference Commands
362
+
363
+ List MCP servers:
364
+
365
+ ```bash
366
+ python reproduce_foxj1_paper.py --list-servers
367
+ ```
368
+
369
+ Prepare only:
370
+
371
+ ```bash
372
+ python reproduce_foxj1_paper.py --dataset-profile foxj1 --prepare-only
373
+ ```
374
+
375
+ Run FOXJ1:
376
+
377
+ ```bash
378
+ python reproduce_foxj1_paper.py --dataset-profile foxj1 --query "Firstly, whether FOXJ1 and GMNC were up-regulated in LuCaP35CR, then whether ABCB1 was up-regulated in LuCaP70CR, and finally the enrichment analysis of pathways related to cilia/microtubules was performed"
379
+ ```
380
+
381
+ ## 16. Acknowledgements
382
+
383
+ This work builds on the open-source Biomni project by the Stanford SNAP group: [snap-stanford/biomni](https://github.com/snap-stanford/biomni). We gratefully acknowledge the Biomni authors and contributors for releasing their codebase and biomedical agent framework, which provided the foundation for this development.
Biomanus/article.xml ADDED
@@ -0,0 +1 @@
 
 
1
+ <full-text-retrieval-response xmlns="http://www.elsevier.com/xml/svapi/article/dtd" xmlns:bk="http://www.elsevier.com/xml/bk/dtd" xmlns:cals="http://www.elsevier.com/xml/common/cals/dtd" xmlns:ce="http://www.elsevier.com/xml/common/dtd" xmlns:ja="http://www.elsevier.com/xml/ja/dtd" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:sa="http://www.elsevier.com/xml/common/struct-aff/dtd" xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd" xmlns:tb="http://www.elsevier.com/xml/common/table/dtd" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xocs="http://www.elsevier.com/xml/xocs/dtd" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:prism="http://prismstandard.org/namespaces/basic/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><coredata><prism:url>https://api.elsevier.com/content/article/pii/S0092867419308931</prism:url><dc:identifier>doi:10.1016/j.cell.2019.08.005</dc:identifier><eid>1-s2.0-S0092867419308931</eid><prism:doi>10.1016/j.cell.2019.08.005</prism:doi><pii>S0092-8674(19)30893-1</pii><dc:title>A Structure-Informed Atlas of Human-Virus Interactions </dc:title><prism:publicationName>Cell</prism:publicationName><prism:aggregationType>Journal</prism:aggregationType><prism:issn>00928674</prism:issn><prism:coverDate>2019-09-05</prism:coverDate><prism:coverDisplayDate>5 September 2019</prism:coverDisplayDate><openaccess>1</openaccess><openaccessArticle>true</openaccessArticle><openaccessType>Full</openaccessType><openArchiveArticle>true</openArchiveArticle><openaccessSponsorName/><openaccessSponsorType>ElsevierBranded</openaccessSponsorType><openaccessUserLicense>http://www.elsevier.com/open-access/userlicense/1.0/</openaccessUserLicense><link href="https://api.elsevier.com/content/article/pii/S0092867419308931" rel="self"/><link href="https://www.sciencedirect.com/science/article/pii/S0092867419308931" rel="scidir"/></coredata></full-text-retrieval-response>
Biomanus/biomni.md ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ### Run The Generic Paper Reproduction Script
275
+
276
+ This repository also includes a reusable paper reproduction runner at [`reproduce_foxj1_paper.py`](./reproduce_foxj1_paper.py). Despite the filename, the script is now a generic template: you can provide a paper title, paper context, local input files, and a natural-language query, and Biomni will plan the reproduction steps and select suitable MCP tools from the registered server set.
277
+
278
+ #### 1. Base Environment
279
+
280
+ Follow the installation steps above first, then activate the Biomni environment:
281
+
282
+ ```bash
283
+ conda activate biomni_e1
284
+ ```
285
+
286
+ If you are working from source, install Biomni from the current repository or from GitHub:
287
+
288
+ ```bash
289
+ pip install -e .
290
+ # or
291
+ pip install git+https://github.com/snap-stanford/Biomni.git@main
292
+ ```
293
+
294
+ #### 2. LLM Configuration For This Script
295
+
296
+ The current implementation of `reproduce_foxj1_paper.py` builds the agent with `source="Custom"`, so the simplest working setup is to provide a DeepSeek-compatible endpoint:
297
+
298
+ ```bash
299
+ export DEEPSEEK_API_KEY="your_api_key"
300
+ export DEEPSEEK_BASE_URL="https://api.deepseek.com/v1"
301
+ export DEEPSEEK_MODEL_NAME="deepseek-chat"
302
+ ```
303
+
304
+ If you use another OpenAI-compatible endpoint, point the script to that endpoint instead:
305
+
306
+ ```bash
307
+ export DEEPSEEK_API_KEY="your_custom_endpoint_key"
308
+ export DEEPSEEK_BASE_URL="http://your-openai-compatible-endpoint/v1"
309
+ export DEEPSEEK_MODEL_NAME="your-model-name"
310
+ ```
311
+
312
+ Optional:
313
+
314
+ ```bash
315
+ export BIOMNI_MCP_PYTHON="$(which python)"
316
+ ```
317
+
318
+ This helps ensure the generated MCP config uses the same Python interpreter as your active Biomni environment.
319
+
320
+ #### 3. MCP Server Availability
321
+
322
+ The script discovers MCP servers from:
323
+
324
+ ```text
325
+ biomni_web/backend/data/mcp_generated/
326
+ ```
327
+
328
+ For each selected server, it looks for:
329
+
330
+ - `mcp_<server_name>/app/*_shim_server.py`
331
+ - or `mcp_<server_name>/app/*_server.py`
332
+
333
+ For example, the following generated servers should exist if you want to run the bundled FOXJ1 example:
334
+
335
+ - `mcp_jq`
336
+ - `mcp_bioconductor-clusterprofiler`
337
+ - `mcp_bioconductor-fgsea`
338
+ - `mcp_bioconductor-qvalue`
339
+ - `mcp_gseapy`
340
+
341
+ You can inspect what the script currently discovers with:
342
+
343
+ ```bash
344
+ python reproduce_foxj1_paper.py --list-servers
345
+ ```
346
+
347
+ #### 4. Minimal Dry Run
348
+
349
+ To only generate the run directory, prompt, plan, and MCP config without calling the model:
350
+
351
+ ```bash
352
+ python reproduce_foxj1_paper.py \
353
+ --dataset-profile foxj1 \
354
+ --prepare-only
355
+ ```
356
+
357
+ This creates a run folder under:
358
+
359
+ ```text
360
+ paper_reproduction_runs/
361
+ ```
362
+
363
+ #### 5. Run The FOXJ1 Example
364
+
365
+ The built-in `foxj1` profile downloads the GEO processed files used by the original example and registers the preferred MCP server set automatically:
366
+
367
+ ```bash
368
+ python reproduce_foxj1_paper.py \
369
+ --dataset-profile foxj1 \
370
+ --query "Firstly, whether FOXJ1 and GMNC were up-regulated in LuCaP35CR, then whether ABCB1 was up-regulated in LuCaP70CR, and finally the enrichment analysis of pathways related to cilia/microtubules was performed"
371
+ ```
372
+
373
+ #### 6. Run A Different Paper
374
+
375
+ For a different paper, provide your own metadata and inputs:
376
+
377
+ ```bash
378
+ python reproduce_foxj1_paper.py \
379
+ --paper-title "Your Paper Title" \
380
+ --context-file /path/to/paper_summary.md \
381
+ --input-file /path/to/data1.csv \
382
+ --input-file /path/to/data2.tsv \
383
+ --query "Reproduce the main findings, run the feasible analyses, and summarize what is supported by the provided data." \
384
+ --server jq \
385
+ --server bioconductor-clusterprofiler \
386
+ --server gseapy
387
+ ```
388
+
389
+ #### 7. What The Script Produces
390
+
391
+ Each run writes artifacts under `paper_reproduction_runs/<run_name>/`, including:
392
+
393
+ - `mcp_config.yaml`
394
+ - `paper_context.md`
395
+ - `reproduction_query.txt`
396
+ - `execution_plan.md`
397
+ - `reproduction_prompt.txt`
398
+ - `results/biomni_execution_log_*.txt`
399
+ - `results/biomni_final_answer_*.txt`
400
+ - `results/reproduction_report.md`
401
+
402
+ #### 8. Common Checks
403
+
404
+ If the run fails early, check the following first:
405
+
406
+ - Your Biomni environment is activated.
407
+ - `DEEPSEEK_API_KEY` and related endpoint variables are set.
408
+ - The requested MCP servers actually exist under `biomni_web/backend/data/mcp_generated/`.
409
+ - `python reproduce_foxj1_paper.py --list-servers` returns the servers you expect.
410
+ - `--prepare-only` works before attempting a full run.
411
+
412
+
413
+ ## Biomni-R0
414
+
415
+ **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.
416
+
417
+ - 🤗 Model: [biomni/Biomni-R0-32B-Preview](https://huggingface.co/biomni/Biomni-R0-32B-Preview)
418
+ - 📝 Technical Report: [biomni.stanford.edu/blog/biomni-r0-technical-report](https://biomni.stanford.edu/blog/biomni-r0-technical-report)
419
+
420
+ 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.
421
+
422
+ 1) Launch SGLang with Biomni-R0:
423
+
424
+ ```bash
425
+ 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}'
426
+ ```
427
+
428
+ 2) Point the agent to your SGLang endpoint for reasoning:
429
+
430
+ ```python
431
+ from biomni.config import default_config
432
+ from biomni.agent import A1
433
+
434
+ # Database queries (indexes, retrieval, etc.) use default_config
435
+ default_config.llm = "claude-3-5-sonnet-20241022"
436
+ default_config.source = "Anthropic"
437
+
438
+ # Agent reasoning uses Biomni-R0 served via SGLang (OpenAI-compatible API)
439
+ agent = A1(
440
+ llm="biomni/Biomni-R0-32B-Preview",
441
+ source="Custom",
442
+ base_url="http://localhost:30000/v1",
443
+ api_key="EMPTY",
444
+ )
445
+
446
+ agent.go("Plan a CRISPR screen to identify genes regulating T cell exhaustion")
447
+ ```
448
+
449
+ ## Biomni-Eval1
450
+
451
+ **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.
452
+
453
+ **Tasks Included:**
454
+ - GWAS causal gene identification (3 variants)
455
+ - Lab bench Q&A (2 variants)
456
+ - Patient gene detection
457
+ - Screen gene retrieval
458
+ - GWAS variant prioritization
459
+ - Rare disease diagnosis
460
+ - CRISPR delivery method selection
461
+
462
+ **Resources:**
463
+ - 🤗 Dataset: [biomni/Eval1](https://huggingface.co/datasets/biomni/Eval1)
464
+ - 💻 Quick Start:
465
+ ```python
466
+ from biomni.eval import BiomniEval1
467
+
468
+ evaluator = BiomniEval1()
469
+ score = evaluator.evaluate('gwas_causal_gene_opentargets', 0, 'BRCA1')
470
+ ```
471
+
472
+
473
+ ## 📚 Know-How Library
474
+
475
+ 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.
476
+
477
+ **Features:**
478
+ - Automatic retrieval based on query relevance
479
+ - Metadata tracking (authors, affiliations, licensing, commercial use)
480
+ - Compatible with commercial mode (filters non-commercial content)
481
+
482
+ ### 📝 Contributing Know-How Documents
483
+
484
+ We're actively seeking community contributions to expand our Know-How Library! Share your expertise by contributing:
485
+
486
+ - **Lab protocols** (cell culture, flow cytometry, western blotting, etc.)
487
+ - **Analysis best practices** (NGS workflows, microscopy techniques, etc.)
488
+ - **Troubleshooting guides** (common issues and solutions)
489
+ - **Experimental design guidelines** (sample size, controls, validation)
490
+ - **Domain-specific knowledge** (drug formulation, animal models, clinical trials, etc.)
491
+
492
+ Know-how documents should be practical, succinct, and include proper attribution. Use [this know-how](know_how/single_cell_annotation.md) as an example.
493
+
494
+ **To contribute:** Create a markdown file following our template and submit a pull request.
495
+
496
+ ## 🤝 Contributing to Biomni
497
+
498
+ Biomni is an open-science initiative that thrives on community contributions. We welcome:
499
+
500
+ - **🔧 New Tools**: Specialized analysis functions and algorithms
501
+ - **📊 Datasets**: Curated biomedical data and knowledge bases
502
+ - **💻 Software**: Integration of existing biomedical software packages
503
+ - **📋 Benchmarks**: Evaluation datasets and performance metrics
504
+ - **📚 Know-How**: Best practices, protocols, and domain expertise
505
+ - **📚 Misc**: Tutorials, examples, and use cases
506
+ - **🔧 Update existing tools**: many current tools are not optimized - fix and replacements are welcome!
507
+
508
+ Check out this **[Contributing Guide](CONTRIBUTION.md)** on how to contribute to the Biomni ecosystem.
509
+
510
+ 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.
511
+
512
+ ## 🔬 Call for Contributors: Help Build Biomni-E2
513
+
514
+ Biomni-E1 only scratches the surface of what’s possible in the biomedical action space.
515
+
516
+ Now, we’re building **Biomni-E2** — a next-generation environment developed **with and for the community**.
517
+
518
+ We believe that by collaboratively defining and curating a shared library of standard biomedical actions, we can accelerate science for everyone.
519
+
520
+ **Join us in shaping the future of biomedical AI agent.**
521
+
522
+ - **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.
523
+ - **All contributors** will be acknowledged in our publications.
524
+ - More contributor perks...
525
+
526
+ Let’s build it together.
527
+
528
+
529
+ ## Tutorials and Examples
530
+
531
+ **[Biomni 101](./tutorials/biomni_101.ipynb)** - Basic concepts and first steps
532
+
533
+ More to come!
534
+
535
+ ## 🌐 Web Interface
536
+
537
+ Experience Biomni through our no-code web interface at **[biomni.stanford.edu](https://biomni.stanford.edu)**.
538
+
539
+ [![Watch the video](https://img.youtube.com/vi/E0BRvl23hLs/maxresdefault.jpg)](https://youtu.be/E0BRvl23hLs)
540
+
541
+
542
+ ## Important Note
543
+ - 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.
544
+ - This release was frozen as of April 15 2025, so it differs from the current web platform.
545
+ - 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.
546
+
547
+ ## Cite Us
548
+
549
+ ```
550
+ @article{huang2025biomni,
551
+ title={Biomni: A General-Purpose Biomedical AI Agent},
552
+ 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},
553
+ journal={bioRxiv},
554
+ pages={2025--05},
555
+ year={2025},
556
+ publisher={Cold Spring Harbor Laboratory}
557
+ }
558
+ ```
Biomanus/biomni/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .version import __version__
2
+
3
+ __all__ = ["__version__"]
Biomanus/biomni/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (207 Bytes). View file
 
Biomanus/biomni/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (240 Bytes). View file
 
Biomanus/biomni/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (219 Bytes). View file
 
Biomanus/biomni/__pycache__/config.cpython-310.pyc ADDED
Binary file (3.26 kB). View file
 
Biomanus/biomni/__pycache__/config.cpython-311.pyc ADDED
Binary file (6.06 kB). View file
 
Biomanus/biomni/__pycache__/config.cpython-313.pyc ADDED
Binary file (6.22 kB). View file
 
Biomanus/biomni/__pycache__/env_desc.cpython-310.pyc ADDED
Binary file (22.6 kB). View file
 
Biomanus/biomni/__pycache__/env_desc.cpython-311.pyc ADDED
Binary file (24.9 kB). View file
 
Biomanus/biomni/__pycache__/llm.cpython-310.pyc ADDED
Binary file (6.68 kB). View file
 
Biomanus/biomni/__pycache__/llm.cpython-311.pyc ADDED
Binary file (10.4 kB). View file
 
Biomanus/biomni/__pycache__/llm.cpython-313.pyc ADDED
Binary file (9.39 kB). View file
 
Biomanus/biomni/__pycache__/utils.cpython-310.pyc ADDED
Binary file (66.5 kB). View file
 
Biomanus/biomni/__pycache__/utils.cpython-313.pyc ADDED
Binary file (93.1 kB). View file
 
Biomanus/biomni/__pycache__/version.cpython-310.pyc ADDED
Binary file (162 Bytes). View file
 
Biomanus/biomni/__pycache__/version.cpython-311.pyc ADDED
Binary file (177 Bytes). View file
 
Biomanus/biomni/__pycache__/version.cpython-313.pyc ADDED
Binary file (171 Bytes). View file
 
Biomanus/biomni/agent/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from biomni.agent.a1 import A1 # noqa: F401
Biomanus/biomni/agent/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (190 Bytes). View file
 
Biomanus/biomni/agent/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (220 Bytes). View file
 
Biomanus/biomni/agent/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (197 Bytes). View file
 
Biomanus/biomni/agent/__pycache__/a1.cpython-310.pyc ADDED
Binary file (99.6 kB). View file
 
Biomanus/biomni/agent/a1.py ADDED
The diff for this file is too large to render. See raw diff
 
Biomanus/biomni/agent/env_collection.py ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import re
4
+ from typing import Any
5
+
6
+ from langchain_core.prompts import ChatPromptTemplate
7
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
8
+
9
+ from biomni.agent.base_agent import base_agent
10
+
11
+
12
+ class PaperTaskExtractor(base_agent):
13
+ """Agent that extracts generalizable tasks/experiments from academic papers.
14
+ It processes papers in chunks and identifies common research tasks that could be shared across papers.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ llm="claude-3-7-sonnet-20250219",
20
+ cheap_llm=None,
21
+ tools=None,
22
+ chunk_size=4000,
23
+ chunk_overlap=400,
24
+ ):
25
+ """Initialize the PaperTaskExtractor agent.
26
+
27
+ Args:
28
+ llm (str): The LLM model to use
29
+ cheap_llm (str, optional): A cheaper LLM for simpler tasks
30
+ tools (list, optional): Any tools to use (not needed for this agent)
31
+ chunk_size (int): Size of text chunks for processing
32
+ chunk_overlap (int): Overlap between chunks
33
+
34
+ """
35
+ super().__init__(llm, cheap_llm, tools)
36
+ self.chunk_size = chunk_size
37
+ self.chunk_overlap = chunk_overlap
38
+ self.log = []
39
+ self.configure()
40
+
41
+ def configure(self):
42
+ """Configure the agent with appropriate prompts."""
43
+ # Prompt for analyzing paper chunks
44
+ self.chunk_analysis_prompt = """You are a research methodology expert specializing in identifying computational tasks and data analysis procedures in academic papers.
45
+
46
+ Your job is to analyze chunks of academic papers and identify ONLY the most common, generalizable computational tasks that are widely used across biomedical research and can be implemented with Python or Linux code.
47
+
48
+ STRICT GUIDELINES:
49
+ 1. ONLY extract tasks that are extremely common and standard in computational biomedical research
50
+ 2. Each task MUST have clear, well-defined inputs and outputs
51
+ 3. Tasks MUST be generalizable across many different papers and research questions
52
+ 4. Be VERY selective - only include tasks that appear in hundreds of papers
53
+ 5. If a task is specific to this paper, unclear, or not widely used, DO NOT include it
54
+ 6. Focus on computational tasks that can be implemented with Python or Linux code
55
+ 7. Each task should be something that could be implemented as a function with clear inputs/outputs
56
+ 8. Also identify commonly used databases and software packages mentioned in the text
57
+ 9. Tasks MUST be CONCRETE and SPECIFIC - include exact methodological details
58
+ 10. Avoid vague task names like "Statistical Analysis" - instead use specific protocol names like "Two-way ANOVA with Tukey's Post-hoc Test using SciPy"
59
+ 11. DO NOT include wet lab procedures that cannot be implemented computationally
60
+ 12. ONLY include tasks that could be automated with code
61
+
62
+ For the following chunk of text from an academic paper, provide:
63
+ 1. A list of ONLY the most common, generalizable COMPUTATIONAL tasks identified (be extremely selective)
64
+ 2. For each task, clearly define:
65
+ - Task name: A SPECIFIC and CONCRETE name with methodological details (e.g., "RNA-seq Differential Expression Analysis with DESeq2" instead of just "Gene Expression Analysis")
66
+ - Input: What SPECIFIC data or parameters the task requires
67
+ - Output: What SPECIFIC data or results the task produces
68
+ - Code implementation: How this task could be implemented with Python or Linux code, including key libraries/packages
69
+ - Frequency: How common this computational task is in biomedical research
70
+ - Standard methods: The established computational techniques used to perform this task
71
+ - Example: A brief description of how THIS specific paper uses this task (with specific details from the paper)
72
+ 3. A list of commonly used databases mentioned in the text (if any)
73
+ 4. A list of commonly used software packages/tools mentioned in the text (if any)
74
+
75
+ PAPER CHUNK:
76
+ {chunk_text}
77
+
78
+ Remember, it's better to return NO tasks than to include tasks that aren't extremely common, generalizable, and implementable with code. Quality over quantity is essential. Tasks MUST be CONCRETE with SPECIFIC methodological details and MUST be implementable with Python or Linux code.
79
+ """
80
+
81
+ # Prompt for consolidating tasks
82
+ self.consolidation_prompt = """You are a research methodology expert. Your task is to consolidate lists of computational research tasks extracted from different chunks of an academic paper.
83
+
84
+ BE EXTREMELY SELECTIVE. Only include tasks that are:
85
+ 1. Fundamental to computational biomedical research
86
+ 2. Used in hundreds of papers across different subfields
87
+ 3. Have clear, well-defined inputs and outputs
88
+ 4. Represent standard computational approaches
89
+ 5. Could be implemented as a function with specific inputs and outputs
90
+ 6. Are CONCRETE and SPECIFIC with exact methodological details
91
+ 7. Can be implemented with Python or Linux code
92
+ 8. Are computational in nature, not wet lab procedures
93
+
94
+ REMOVE any tasks that:
95
+ - Are specific to a particular paper or dataset
96
+ - Lack clear inputs or outputs
97
+ - Are not widely used across biomedical research
98
+ - Are vague or poorly defined
99
+ - Represent niche or specialized techniques
100
+ - Have generic names without specific methodological details
101
+ - Cannot be implemented with code
102
+ - Require physical lab equipment or manual intervention
103
+
104
+ The output should be a JSON object with three main keys:
105
+ 1. "tasks": A list of task objects, where each task has:
106
+ - "task_name": A SPECIFIC and CONCRETE name with methodological details (e.g., "Single-cell RNA-seq Clustering with Seurat" instead of just "Cell Clustering")
107
+ - "description": A clear description of what the computational task does
108
+ - "inputs": SPECIFIC data types or parameters the task requires
109
+ - "outputs": SPECIFIC data types or results the task produces
110
+ - "code_implementation": How this task could be implemented with Python or Linux code, including key libraries/packages and a brief pseudocode example
111
+ - "frequency": How common this computational task is in biomedical research
112
+ - "standard_methods": The established computational techniques used to perform this task
113
+ - "example": A specific example from THIS paper showing how the task was used (with concrete details)
114
+
115
+ 2. "databases": A list of database objects, where each database has:
116
+ - "name": The name of the database
117
+ - "description": A brief description of what the database contains
118
+ - "url": The URL of the database (if mentioned)
119
+ - "usage": How the database is commonly used in computational biomedical research
120
+ - "example": How this specific paper uses the database
121
+
122
+ 3. "software": A list of software package objects, where each package has:
123
+ - "name": The name of the software package
124
+ - "description": A brief description of what the software does
125
+ - "url": The URL or reference to the software (if mentioned)
126
+ - "usage": How the software is commonly used in computational biomedical research
127
+ - "example": How this specific paper uses the software
128
+
129
+ EXTRACTED INFORMATION FROM PAPER CHUNKS:
130
+ {task_lists}
131
+
132
+ Be ruthless in filtering - it's better to return a few truly common computational tasks than many that aren't universal or implementable with code.
133
+ Respond with only a valid JSON object containing the three lists described above.
134
+ """
135
+
136
+ def process_paper(self, paper_text: str) -> dict[str, list[dict[str, Any]]]:
137
+ """Process a paper and extract generalizable tasks/experiments, databases, and software.
138
+
139
+ Args:
140
+ paper_text (str): The full text of the paper
141
+
142
+ Returns:
143
+ Dict[str, List[Dict[str, Any]]]: A dictionary with tasks, databases, and software
144
+
145
+ """
146
+ # Split the paper into chunks
147
+ text_splitter = RecursiveCharacterTextSplitter(
148
+ chunk_size=self.chunk_size,
149
+ chunk_overlap=self.chunk_overlap,
150
+ length_function=len,
151
+ separators=["\n\n", "\n", ". ", " ", ""],
152
+ )
153
+ chunks = text_splitter.split_text(paper_text)
154
+
155
+ # Process each chunk to extract tasks
156
+ chunk_results = []
157
+ for i, chunk in enumerate(chunks):
158
+ print(f"Processing chunk {i + 1}/{len(chunks)}...")
159
+ chunk_tasks = self._process_chunk(chunk)
160
+ chunk_results.append(chunk_tasks)
161
+
162
+ # Consolidate tasks from all chunks
163
+ consolidated_results = self._consolidate_tasks(chunk_results)
164
+ return consolidated_results
165
+
166
+ def _process_chunk(self, chunk_text: str) -> str:
167
+ """Process a single chunk of the paper to extract tasks.
168
+
169
+ Args:
170
+ chunk_text (str): The chunk text to process
171
+
172
+ Returns:
173
+ str: Extracted tasks from this chunk
174
+
175
+ """
176
+ prompt = self.chunk_analysis_prompt.format(chunk_text=chunk_text)
177
+ message = self.llm.invoke(prompt)
178
+ return message.content
179
+
180
+ def _consolidate_tasks(self, chunk_results: list[str]) -> dict[str, list[dict[str, Any]]]:
181
+ """Consolidate tasks, databases, and software extracted from different chunks into a unified structure.
182
+
183
+ Args:
184
+ chunk_results (List[str]): Results from each chunk
185
+
186
+ Returns:
187
+ Dict[str, List[Dict[str, Any]]]: Consolidated information with tasks, databases, and software
188
+
189
+ """
190
+ # Combine all chunk results
191
+ all_tasks = "\n\n===== CHUNK SEPARATOR =====\n\n".join(chunk_results)
192
+
193
+ # Use the consolidation prompt to merge and organize tasks
194
+ prompt = self.consolidation_prompt.format(task_lists=all_tasks)
195
+ response = self.llm.invoke(prompt)
196
+
197
+ # Extract the JSON from the response
198
+ try:
199
+ # Try to parse the entire response as JSON
200
+ result = json.loads(response.content)
201
+ except json.JSONDecodeError:
202
+ # If that fails, try to extract JSON from the text
203
+ try:
204
+ # Look for JSON-like content between triple backticks
205
+ json_match = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", response.content)
206
+ if json_match:
207
+ result = json.loads(json_match.group(1))
208
+ else:
209
+ # Fallback: just return the text response
210
+ return {
211
+ "tasks": [
212
+ {
213
+ "error": "Could not parse JSON",
214
+ "raw_response": response.content,
215
+ }
216
+ ],
217
+ "databases": [],
218
+ "software": [],
219
+ }
220
+ except Exception:
221
+ return {
222
+ "tasks": [
223
+ {
224
+ "error": "Could not parse JSON",
225
+ "raw_response": response.content,
226
+ }
227
+ ],
228
+ "databases": [],
229
+ "software": [],
230
+ }
231
+
232
+ # Ensure the result has the expected structure
233
+ if not isinstance(result, dict):
234
+ result = {
235
+ "tasks": result if isinstance(result, list) else [],
236
+ "databases": [],
237
+ "software": [],
238
+ }
239
+
240
+ # Ensure all required keys exist
241
+ for key in ["tasks", "databases", "software"]:
242
+ if key not in result:
243
+ result[key] = []
244
+
245
+ return result
246
+
247
+ def go(self, paper_text: str):
248
+ """Process a paper and return the extracted tasks, databases, and software.
249
+
250
+ Args:
251
+ paper_text (str): The full text of the paper
252
+
253
+ Returns:
254
+ tuple: (log, results) where log is a list of processing steps and results is the final result
255
+
256
+ """
257
+ self.log = []
258
+ self.log.append(
259
+ (
260
+ "user",
261
+ "Extract only the most common and generalizable biomedical research tasks, databases, and software from this paper",
262
+ )
263
+ )
264
+
265
+ results = self.process_paper(paper_text)
266
+
267
+ result_str = json.dumps(results, indent=2)
268
+ self.log.append(("assistant", result_str))
269
+
270
+ return self.log, results
271
+
272
+ def save_results(self, results: dict[str, list[dict[str, Any]]], output_path: str):
273
+ """Save the extracted tasks, databases, and software to a JSON file.
274
+
275
+ Args:
276
+ results (Dict[str, List[Dict[str, Any]]]): The extracted tasks, databases, and software
277
+ output_path (str): Path to save the results
278
+
279
+ """
280
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
281
+ with open(output_path, "w") as f:
282
+ json.dump(results, f, indent=2)
283
+ print(f"Results saved to {output_path}")
284
+
285
+ def result_formatting(self, output_class, task_intention):
286
+ """Format the results according to a specific output class.
287
+
288
+ Args:
289
+ output_class: The class to format the output as
290
+ task_intention: Description of the task
291
+
292
+ Returns:
293
+ The formatted result
294
+
295
+ """
296
+ format_check_prompt = ChatPromptTemplate.from_messages(
297
+ [
298
+ (
299
+ "system",
300
+ (
301
+ "You are evaluateGPT, tasked with extract and parse the task output based on the history of an agent. "
302
+ "Review the entire history of messages provided. "
303
+ "Here is the task output requirement: \n"
304
+ f"'{task_intention.replace('{', '{{').replace('}', '}}')}'.\n"
305
+ ),
306
+ ),
307
+ ("placeholder", "{messages}"),
308
+ ]
309
+ )
310
+
311
+ checker_llm = format_check_prompt | self.llm.with_structured_output(output_class)
312
+ result = checker_llm.invoke({"messages": [("user", str(self.log))]}).dict()
313
+ return result
Biomanus/biomni/agent/function_generator.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ from biomni.llm import get_llm
4
+
5
+
6
+ class base_agent:
7
+ def __init__(self, llm="claude-3-haiku-20240307", cheap_llm=None, tools=None, temperature=0.7):
8
+ self.tools = tools
9
+ self.llm = get_llm(llm, temperature)
10
+ if cheap_llm is None:
11
+ self.cheap_llm = llm
12
+ else:
13
+ self.cheap_llm = cheap_llm
14
+
15
+ def configure(self):
16
+ pass
17
+
18
+ def go(self, input):
19
+ pass
20
+
21
+
22
+ class FunctionGenerator(base_agent):
23
+ """Agent that generates executable Python code scripts given a task description."""
24
+
25
+ def __init__(self, llm="claude-3-7-sonnet-20250219", cheap_llm=None, temperature=0.7):
26
+ """Initialize the PaperTaskExtractor agent.
27
+
28
+ Args:
29
+ llm (str): The LLM model to use
30
+ cheap_llm (str, optional): A cheaper LLM for simpler tasks
31
+ """
32
+ super().__init__(llm, cheap_llm, temperature)
33
+ self.log = []
34
+ self.configure()
35
+
36
+ def configure(self):
37
+ """Configure the agent with appropriate prompts."""
38
+ # Prompt for Python code generation
39
+ self.system_prompt = """You are a senior Python engineer. Generate robust, idiomatic Python code that solves the user's task. Requirements:
40
+ 1. Output ONLY Python code, ideally inside a single triple-backtick code block.
41
+ 2. Include minimal inline comments and a small docstring.
42
+ 3. Add a `main()` and an `if __name__ == '__main__':` guard when appropriate.
43
+ 4. Avoid external dependencies unless necessary; if used, show `pip` installs in comments.
44
+ 5. Do not include prose before or after the code.
45
+ 6. When applicable, prioritize the use of codes on public repositories, such as HuggingFace or Github
46
+
47
+ Generate Python codes for the following task:
48
+ {task}
49
+ """
50
+
51
+ def _generate_code(self, task_description: str) -> str:
52
+ """Generate codes given a task description.
53
+ Args:
54
+ task_description (str): task descriptions (possibly generated from previous steps)
55
+
56
+ Returns:
57
+ str: generated code string
58
+
59
+ """
60
+ prompt = self.system_prompt.format(task=task_description)
61
+ message = self.llm.invoke(prompt)
62
+ return message.content
63
+
64
+ def _generate_script_filename(self, task_description: str, max_words: int = 6) -> str:
65
+ """
66
+ Generate a safe, meaningful Python script filename from a task description.
67
+ Poised for update: may ask the agent to suggest meaningful names.
68
+
69
+ Parameters:
70
+ -----------
71
+ task_description (str): task descriptions (possibly generated from previous steps)
72
+
73
+ max_words : int
74
+ Maximum number of words to include in the filename.
75
+
76
+ Returns:
77
+ --------
78
+ str
79
+ A lowercase, hyphen-free, safe filename ending in '.py'.
80
+ """
81
+ # Lowercase and remove non-alphanumeric (allow spaces for splitting)
82
+ cleaned = re.sub(r"[^a-zA-Z0-9\s]", "", task_description.lower())
83
+
84
+ # Tokenize and select top words
85
+ words = cleaned.split()
86
+ selected_words = words[:max_words] if words else ["script"]
87
+
88
+ # Join with underscores
89
+ base_name = "_".join(selected_words)
90
+ return f"{base_name}.py"
91
+
92
+ def go(self, task_description: str):
93
+ """Implement the inherited function to get the tasks done.
94
+
95
+ Args:
96
+ task_description (str): task descriptions (possibly generated from previous steps)
97
+
98
+ Returns:
99
+ tuple: (script_filename, results) where script_filename is a generated name for script file and results is the generated codes
100
+
101
+ """
102
+ self.log = []
103
+ self.log.append(
104
+ (
105
+ "user",
106
+ "Generate Python codes given a task description",
107
+ )
108
+ )
109
+
110
+ script_filename = self._generate_script_filename(task_description)
111
+ results = self._generate_code(task_description)
112
+ return script_filename, self._extract_code_block(results)
113
+
114
+ def _extract_code_block(self, s: str) -> str:
115
+ """
116
+ Extract the first fenced code block (``` or ```python) from s.
117
+ """
118
+ m = re.search(r"```(?:python)?\s*(.+?)\s*```", s, flags=re.DOTALL | re.IGNORECASE)
119
+ return m.group(1).strip() if m else None
Biomanus/biomni/agent/qa_llm.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.prompts import ChatPromptTemplate
2
+
3
+ from biomni.llm import get_llm
4
+
5
+
6
+ class qa_llm:
7
+ def __init__(self, path="./data", llm="claude-3-haiku-20240307", lab_bench_reproduce=False):
8
+ self.path = path
9
+ self.llm = get_llm(llm)
10
+
11
+ if lab_bench_reproduce:
12
+ self.prompt_modifier = """
13
+ The following is a multiple choice question about biology.
14
+ Please answer by responding with the letter of the correct answer.
15
+
16
+ Think step by step. \n
17
+ """
18
+ else:
19
+ self.prompt_modifier = ""
20
+ self.log = []
21
+
22
+ def configure(self):
23
+ pass
24
+
25
+ def go(self, input):
26
+ self.log = []
27
+ self.log.append(("user", input))
28
+ message = self.llm.invoke(self.prompt_modifier + input)
29
+ self.log.append(("assistant", message.content))
30
+ return [message.content], message.content
31
+
32
+ def result_formatting(self, output_class, task_intention):
33
+ self.format_check_prompt = ChatPromptTemplate.from_messages(
34
+ [
35
+ (
36
+ "system",
37
+ (
38
+ "You are evaluateGPT, tasked with extract and parse the task output based on the history of an agent. "
39
+ "Review the entire history of messages provided. "
40
+ "Here is the task output requirement: \n"
41
+ f"'{task_intention.replace('{', '{{').replace('}', '}}')}'.\n"
42
+ ),
43
+ ),
44
+ ("placeholder", "{messages}"),
45
+ ]
46
+ )
47
+
48
+ checker_llm = self.format_check_prompt | self.llm.with_structured_output(output_class)
49
+ result = checker_llm.invoke({"messages": [("user", str(self.log))]}).dict()
50
+ return result
Biomanus/biomni/agent/react.py ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glob
2
+ import inspect
3
+ import json
4
+ import os
5
+ import signal
6
+ from collections.abc import Sequence
7
+ from functools import wraps
8
+ from multiprocessing import Process, Queue
9
+ from typing import Annotated, TypedDict
10
+
11
+ from langchain_core.messages import BaseMessage, SystemMessage, ToolMessage
12
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
13
+ from langchain_core.runnables import RunnableConfig
14
+ from langgraph.graph import END, StateGraph
15
+ from langgraph.graph.message import add_messages
16
+
17
+ from biomni.config import default_config
18
+ from biomni.env_desc import data_lake_dict, library_content_dict
19
+ from biomni.llm import get_llm
20
+ from biomni.model.retriever import ToolRetriever
21
+ from biomni.tool.tool_registry import ToolRegistry
22
+ from biomni.utils import (
23
+ api_schema_to_langchain_tool,
24
+ function_to_api_schema,
25
+ pretty_print,
26
+ read_module2api,
27
+ )
28
+
29
+
30
+ # Define the AgentState TypedDict for our custom implementation
31
+ class AgentState(TypedDict):
32
+ """The state of the agent."""
33
+
34
+ # add_messages is a reducer that combines message sequences
35
+ messages: Annotated[Sequence[BaseMessage], add_messages]
36
+
37
+
38
+ class react:
39
+ def __init__(
40
+ self,
41
+ path: str | None = None,
42
+ llm: str | None = None,
43
+ use_tool_retriever: bool | None = None,
44
+ timeout_seconds: int | None = None,
45
+ ):
46
+ # Use default_config values for unspecified parameters
47
+ if path is None:
48
+ path = default_config.path
49
+ if llm is None:
50
+ llm = default_config.llm
51
+ if use_tool_retriever is None:
52
+ use_tool_retriever = default_config.use_tool_retriever
53
+ if timeout_seconds is None:
54
+ timeout_seconds = default_config.timeout_seconds
55
+
56
+ self.path = path
57
+ if not os.path.exists(path):
58
+ os.makedirs(path)
59
+ print(f"Created directory: {path}")
60
+ ### TODO: Download the data
61
+ else:
62
+ print(f"Data directory already exists: {path}, loading...")
63
+
64
+ module2api = read_module2api()
65
+
66
+ self.llm = get_llm(llm, config=default_config)
67
+ tools = []
68
+ for module, api_list in module2api.items():
69
+ print("Registering tools from module:", module)
70
+ tools += [api_schema_to_langchain_tool(api, mode="custom_tool", module_name=module) for api in api_list]
71
+ self.tools = tools
72
+ self.module2api = module2api
73
+ self.use_tool_retriever = use_tool_retriever
74
+
75
+ # Store dictionaries for data lake and library content
76
+ self.data_lake_dict = data_lake_dict
77
+ self.library_content_dict = library_content_dict
78
+
79
+ if self.use_tool_retriever:
80
+ self.tool_registry = ToolRegistry(module2api)
81
+ self.retriever = ToolRetriever()
82
+
83
+ self.timeout_seconds = timeout_seconds # 10 minutes default timeout
84
+
85
+ # When wrapping tools with timeout
86
+ self.tools = self._add_timeout_to_tools(self.tools)
87
+ self.prompt = ""
88
+ self.system_prompt = ""
89
+
90
+ def _add_timeout_to_tools(self, tools):
91
+ """Apply timeout wrapper to all tool functions using multiprocessing."""
92
+
93
+ def create_timed_func(original_func, timeout):
94
+ """Factory function that creates a unique timed function for each tool."""
95
+ tool_name = getattr(original_func, "__name__", "unknown")
96
+ # print(f"Applying timeout wrapper to tool: {tool_name}")
97
+
98
+ def process_func(func, args, kwargs, result_queue):
99
+ """Function to run in a separate process."""
100
+ try:
101
+ result = func(*args, **kwargs)
102
+ result_queue.put(("success", result))
103
+ except Exception as e:
104
+ result_queue.put(("error", str(e)))
105
+
106
+ @wraps(original_func)
107
+ def timed_func(*args, **kwargs):
108
+ # print(f"Executing tool with timeout: {tool_name}")
109
+ result_queue = Queue()
110
+
111
+ # Start a separate process
112
+ proc = Process(
113
+ target=process_func,
114
+ args=(original_func, args, kwargs, result_queue),
115
+ )
116
+ proc.start()
117
+
118
+ # Wait for the specified timeout
119
+ proc.join(timeout)
120
+
121
+ # Check if the process is still running after timeout
122
+ if proc.is_alive():
123
+ print(f"TIMEOUT: Tool {tool_name} execution timed out after {timeout} seconds")
124
+ # Force terminate the process
125
+ proc.terminate()
126
+ proc.join(1) # Give it a second to terminate
127
+
128
+ # If it's still not dead, kill it with more force
129
+ if proc.is_alive():
130
+ os.kill(proc.pid, signal.SIGKILL)
131
+
132
+ return f"ERROR: Tool execution timed out after {timeout} seconds. Please try with simpler inputs or break your task into smaller steps."
133
+
134
+ # Get the result from the queue
135
+ if not result_queue.empty():
136
+ status, result = result_queue.get()
137
+ if status == "success":
138
+ return result
139
+ else:
140
+ return f"Error in tool execution: {result}"
141
+
142
+ return "Error: Tool execution completed but no result was returned"
143
+
144
+ return timed_func
145
+
146
+ wrapped_tools = []
147
+ for tool in tools:
148
+ wrapped_tool = tool
149
+ wrapped_tool.func = create_timed_func(tool.func, self.timeout_seconds)
150
+ wrapped_tools.append(wrapped_tool)
151
+
152
+ return wrapped_tools
153
+
154
+ def add_tool(self, api):
155
+ function_code = inspect.getsource(api)
156
+ schema = function_to_api_schema(function_code, self.llm)
157
+ new_tool = api_schema_to_langchain_tool(schema, mode="custom_tool", module_name=api.__module__)
158
+
159
+ # Create a single wrapped tool using the existing _add_timeout_to_tools method
160
+ wrapped_tools = self._add_timeout_to_tools([new_tool])
161
+
162
+ # Get the wrapped tool and add it to our tools list
163
+ if wrapped_tools:
164
+ self.tools.append(wrapped_tools[0])
165
+
166
+ def configure(
167
+ self,
168
+ plan=False,
169
+ reflect=False,
170
+ data_lake=False,
171
+ react_code_search=False,
172
+ library_access=False,
173
+ ):
174
+ data_lake_path = self.path + "/data_lake"
175
+ data_lake_content = glob.glob(data_lake_path + "/*")
176
+ data_lake_items = [x.split("/")[-1] for x in data_lake_content]
177
+
178
+ if react_code_search:
179
+ tools = [i for i in self.tools if i.name in ["run_python_repl", "search_google"]]
180
+
181
+ prompt_modifier = """
182
+ You are a helpful biomedical assistant assigned with the task of problem-solving.
183
+
184
+ You have access to two tools:
185
+ 1) run_python_repl: to write and run python code
186
+ 2) search_google: to search google for information
187
+
188
+ You can use them to solve the problem.
189
+ """
190
+ else:
191
+ tools = self.tools
192
+ if (not plan) and (not reflect):
193
+ prompt_modifier = """You are a helpful biologist and expert geneticist.
194
+ """
195
+ elif plan and (not reflect):
196
+ prompt_modifier = """You are a helpful biologist and expert geneticist.
197
+ Given the question from the user,
198
+ - First, come up with a high level plan based on your understanding of the problem and available tools and record it in the Research Plan and Status. You can revise the plan later.
199
+ - Research Plan and Status should well organized and succinctly keep track of 1) high level plan (can be revised), 2) what steps have been done and what steps are in progress, 3) short results and conclusions of each step after it has been performed. Do not perform action in research plan.
200
+ - Research Plan and Status must only include progress that has been made by previous steps. It should not include results not directly confirmed by the previous observation.
201
+ - Follow the plan and try to achieve the goal as straightforwardly as possible. Use tools as necessary.
202
+ """
203
+ elif (not plan) and reflect:
204
+ prompt_modifier = """You are a helpful biologist and expert geneticist.
205
+ In each round after the tool is used, conduct "reflection" step: reflect on the current state of the problem and the results of the last round. What does the observation mean? If there is an error, what caused the error and how to debug?
206
+ """
207
+ else:
208
+ prompt_modifier = """You are a helpful biologist and expert geneticist.
209
+ Given the question from the user,
210
+ - First, come up with a high level plan based on your understanding of the problem and available tools and record it in the Research Plan and Status. You can revise the plan later.
211
+ - Research Plan and Status should well organized and succinctly keep track of 1) high level plan (can be revised), 2) what steps have been done and what steps are in progress, 3) short results and conclusions of each step after it has been performed. Do not perform action in research plan.
212
+ - Research Plan and Status must only include progress that has been made by previous steps. It should not include results not directly confirmed by the previous observation.
213
+ - Follow the plan and try to achieve the goal as straightforwardly as possible. Use tools as necessary.
214
+ In each round after the tool is used, conduct "reflection" step: reflect on the current state of the problem and the results of the last round. What does the observation mean? If there is an error, what caused the error and how to debug?
215
+ You have access to write_python_code and run_python_repl tool to write and run your own code if tools fail, or if the given tools are not enough. Please always make sure to write code when dealing with substantial data, including finding the length of long sequences or elements at different positions.
216
+ """
217
+
218
+ if data_lake:
219
+ # Format data lake items with descriptions
220
+ data_lake_formatted = []
221
+ for item in data_lake_items:
222
+ description = data_lake_dict.get(item, f"Data lake item: {item}")
223
+ data_lake_formatted.append(f"{item}: {description}")
224
+
225
+ prompt_modifier += """
226
+ You can also access a biological data lake at the following path: {data_lake_path}. You can use the run_python_repl tool to write code to understand the data, process and utilize it for the task.
227
+ Here is the list of datasets with their descriptions:
228
+ ----
229
+ {data_lake_formatted}
230
+ ----
231
+ """.format(
232
+ data_lake_path=data_lake_path,
233
+ data_lake_formatted="\n".join(data_lake_formatted),
234
+ )
235
+
236
+ if library_access:
237
+ # Format library content with descriptions
238
+ library_formatted = []
239
+ for lib_name, lib_desc in library_content_dict.items():
240
+ library_formatted.append(f"{lib_name}: {lib_desc}")
241
+
242
+ prompt_modifier += """
243
+ You also have access to a list of software packages that can be used to perform various tasks.
244
+ You can use the run_python_repl tool to write code to access and utilize the library for the task.
245
+ Don't forget the import statement.
246
+ Here is the list of available libraries with their descriptions:
247
+ ----
248
+ {library_formatted}
249
+ ----
250
+ """.format(library_formatted="\n".join(library_formatted))
251
+
252
+ print("=" * 25 + "System Prompt" + "=" * 25)
253
+ print(prompt_modifier)
254
+ self.system_prompt = prompt_modifier
255
+ self.prompt = ChatPromptTemplate.from_messages(
256
+ [
257
+ ("system", prompt_modifier),
258
+ MessagesPlaceholder(variable_name="messages"),
259
+ ]
260
+ )
261
+
262
+ # Store the tools for later use
263
+ self.active_tools = tools
264
+
265
+ # Create a custom implementation of the ReAct agent using LangGraph
266
+ self.app = self._create_custom_react_agent(self.llm, tools, self.prompt)
267
+
268
+ def _create_custom_react_agent(self, llm, tools, prompt):
269
+ """Create a custom ReAct agent using LangGraph."""
270
+ # Create a dictionary mapping tool names to tool objects for easy lookup
271
+ tools_by_name = {tool.name: tool for tool in tools}
272
+
273
+ # Bind the tools to the language model
274
+ llm_with_tools = llm.bind_tools(tools)
275
+
276
+ # Define the node that calls the model
277
+ def call_model(state: AgentState, config: RunnableConfig = None):
278
+ """Node that calls the language model to get the next action."""
279
+ system_message = SystemMessage(content=self.system_prompt)
280
+ messages = [system_message] + state["messages"]
281
+ response = llm_with_tools.invoke(messages, config=config)
282
+ return {"messages": [response]}
283
+
284
+ # Define the node that executes tools
285
+ def tool_node(state: AgentState):
286
+ """Node that executes tools based on the LLM's decisions."""
287
+ outputs = []
288
+ for tool_call in state["messages"][-1].tool_calls:
289
+ try:
290
+ tool_result = tools_by_name[tool_call["name"]].invoke(tool_call["args"])
291
+ outputs.append(
292
+ ToolMessage(
293
+ content=json.dumps(tool_result),
294
+ name=tool_call["name"],
295
+ tool_call_id=tool_call["id"],
296
+ )
297
+ )
298
+ except Exception as e:
299
+ # Handle any errors that occur during tool execution
300
+ outputs.append(
301
+ ToolMessage(
302
+ content=json.dumps({"error": str(e)}),
303
+ name=tool_call["name"],
304
+ tool_call_id=tool_call["id"],
305
+ )
306
+ )
307
+ return {"messages": outputs}
308
+
309
+ # Define the conditional edge that determines whether to continue or not
310
+ def should_continue(state: AgentState):
311
+ """Determine if we should continue running the graph or finish."""
312
+ messages = state["messages"]
313
+ last_message = messages[-1]
314
+ # If there is no tool call, then we finish
315
+ if not hasattr(last_message, "tool_calls") or not last_message.tool_calls:
316
+ return "end"
317
+ # Otherwise if there is, we continue
318
+ else:
319
+ return "continue"
320
+
321
+ # Define a new graph
322
+ workflow = StateGraph(AgentState)
323
+
324
+ # Define the two nodes we will cycle between
325
+ workflow.add_node("agent", call_model)
326
+ workflow.add_node("tools", tool_node)
327
+
328
+ # Set the entrypoint as `agent`
329
+ workflow.set_entry_point("agent")
330
+
331
+ # Add conditional edges
332
+ workflow.add_conditional_edges(
333
+ "agent",
334
+ should_continue,
335
+ {
336
+ "continue": "tools",
337
+ "end": END,
338
+ },
339
+ )
340
+
341
+ # Add edge from tools back to agent
342
+ workflow.add_edge("tools", "agent")
343
+
344
+ # Compile the graph
345
+ return workflow.compile()
346
+
347
+ def go(self, prompt):
348
+ """Execute the agent with the given prompt.
349
+
350
+ Args:
351
+ prompt: The user's query
352
+
353
+ """
354
+ if self.use_tool_retriever:
355
+ # Gather all available tools from the registry
356
+ all_tools = self.tool_registry.tools if hasattr(self, "tool_registry") else []
357
+
358
+ # Get data lake items with descriptions
359
+ data_lake_path = self.path + "/data_lake"
360
+ data_lake_content = glob.glob(data_lake_path + "/*")
361
+ data_lake_items = [x.split("/")[-1] for x in data_lake_content]
362
+
363
+ # Create data lake descriptions for retrieval
364
+ data_lake_descriptions = []
365
+ for item in data_lake_items:
366
+ description = self.data_lake_dict.get(item, f"Data lake item: {item}")
367
+ data_lake_descriptions.append({"name": item, "description": description})
368
+
369
+ # Libraries with descriptions
370
+ library_descriptions = []
371
+ for lib_name, lib_desc in self.library_content_dict.items():
372
+ library_descriptions.append({"name": lib_name, "description": lib_desc})
373
+
374
+ # Prepare resources for retrieval
375
+ resources = {
376
+ "tools": all_tools,
377
+ "data_lake": data_lake_descriptions,
378
+ "libraries": library_descriptions,
379
+ }
380
+
381
+ # Use prompt-based retrieval with the agent's LLM
382
+ selected_resources = self.retriever.prompt_based_retrieval(prompt, resources, llm=self.llm)
383
+ print("Using prompt-based retrieval with the agent's LLM")
384
+
385
+ # If we're using prompt or embedding based retrieval, print the selected resources
386
+ print("\nSelected tools:")
387
+ for tool in selected_resources["tools"]:
388
+ if isinstance(tool, dict):
389
+ print(f"- {tool.get('name', 'Unknown')}: {tool.get('description', '')}")
390
+ else:
391
+ print(f"- {getattr(tool, 'name', str(tool))}: {getattr(tool, 'description', '')}")
392
+
393
+ print("\nSelected data lake items:")
394
+ for item in selected_resources["data_lake"]:
395
+ if isinstance(item, dict):
396
+ name = item.get("name", "Unknown")
397
+ description = self.data_lake_dict.get(name, f"Data lake item: {name}")
398
+ print(f"- {name}: {description}")
399
+ elif isinstance(item, str) and ": " in item:
400
+ # If the item already has a description, print it as is
401
+ print(f"- {item}")
402
+ else:
403
+ description = self.data_lake_dict.get(item, f"Data lake item: {item}")
404
+ print(f"- {item}: {description}")
405
+
406
+ print("\nSelected libraries:")
407
+ for lib in selected_resources["libraries"]:
408
+ if isinstance(lib, dict):
409
+ print(f"- {lib.get('name', 'Unknown')}: {lib.get('description', '')}")
410
+ else:
411
+ print(f"- {lib}")
412
+
413
+ # Convert selected tools to langchain tool objects
414
+ tool_names = [
415
+ tool["name"] if isinstance(tool, dict) else getattr(tool, "name", str(tool))
416
+ for tool in selected_resources["tools"]
417
+ ]
418
+ retrieved_list_of_tools = []
419
+
420
+ # Get the tool objects by name
421
+ for tool_name in tool_names:
422
+ # Find the tool in the original tools list
423
+ matching_tools = [t for t in self.tools if getattr(t, "name", None) == tool_name]
424
+ if matching_tools:
425
+ retrieved_list_of_tools.append(matching_tools[0])
426
+
427
+ # Add back coding tool if not already included
428
+ if len([i for i in retrieved_list_of_tools if i.name == "run_python_repl"]) == 0:
429
+ retrieved_list_of_tools = retrieved_list_of_tools + [
430
+ i for i in self.tools if i.name == "run_python_repl"
431
+ ]
432
+
433
+ print("Retrieved tools: \n" + "\n".join([l.name + ": " + l.description for l in retrieved_list_of_tools]))
434
+ # Recreate the custom agent with the retrieved tools
435
+ self.app = self._create_custom_react_agent(self.llm, retrieved_list_of_tools, self.prompt)
436
+
437
+ # Default behavior (no tool retriever or retrieval_method is 'none')
438
+ config = {"recursion_limit": 50}
439
+ inputs = {"messages": [("user", prompt)]}
440
+ self.log = []
441
+ for s in self.app.stream(inputs, stream_mode="values", config=config):
442
+ message = s["messages"][-1]
443
+ out = pretty_print(message)
444
+ self.log.append(out)
445
+ return self.log, s["messages"][-1].content
446
+
447
+ def result_formatting(self, output_class, task_intention):
448
+ self.format_check_prompt = ChatPromptTemplate.from_messages(
449
+ [
450
+ (
451
+ "system",
452
+ (
453
+ "You are evaluateGPT, tasked with extract and parse the task output based on the history of an agent. "
454
+ "Review the entire history of messages provided. "
455
+ "Here is the task output requirement: \n"
456
+ f"'{task_intention.replace('{', '{{').replace('}', '}}')}'.\n"
457
+ ),
458
+ ),
459
+ ("placeholder", "{messages}"),
460
+ ]
461
+ )
462
+
463
+ checker_llm = self.format_check_prompt | self.llm.with_structured_output(output_class)
464
+ result = checker_llm.invoke({"messages": [("user", str(self.log))]}).dict()
465
+ return result
Biomanus/biomni/biorxiv_scripts/extract_biorxiv_tasks.py ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Script to extract common tasks, databases, and software from bioRxiv papers
3
+ using the PaperTaskExtractor and existing metadata CSV.
4
+ """
5
+
6
+ import argparse
7
+ import io
8
+ import json
9
+ import os
10
+ import random
11
+ import sys
12
+ import time
13
+ from typing import Any
14
+
15
+ import pandas as pd
16
+ import PyPDF2
17
+ import requests
18
+ from tqdm import tqdm
19
+
20
+ # Add the parent directory to the path so we can import the bioagentos package
21
+ sys.path.append("../../")
22
+ from biomni.agent.env_collection import PaperTaskExtractor
23
+
24
+
25
+ def parse_arguments():
26
+ """Parse command line arguments."""
27
+ parser = argparse.ArgumentParser(description="Extract tasks from bioRxiv papers in a specific subject area.")
28
+ parser.add_argument(
29
+ "--subject",
30
+ type=str,
31
+ required=True,
32
+ help='Subject area to search for papers (e.g., "neuroscience", "bioinformatics")',
33
+ )
34
+ parser.add_argument(
35
+ "--limit",
36
+ type=int,
37
+ default=10,
38
+ help="Maximum number of papers to process (default: 10)",
39
+ )
40
+ parser.add_argument(
41
+ "--metadata-path",
42
+ type=str,
43
+ default="/dfs/user/kexinh/BioAgentOS/data/biorxiv_metadata.csv",
44
+ help="Path to bioRxiv metadata CSV file (default: data/biorxiv_metadata.csv)",
45
+ )
46
+ parser.add_argument(
47
+ "--output-dir",
48
+ type=str,
49
+ default=None,
50
+ help="Directory to save results (default: ./biorxiv_results_{subject}_{limit})",
51
+ )
52
+ parser.add_argument(
53
+ "--model",
54
+ type=str,
55
+ default="claude-3-haiku-20240307",
56
+ help="LLM model to use for extraction (default: claude-3-haiku-20240307)",
57
+ )
58
+ parser.add_argument(
59
+ "--chunk-size",
60
+ type=int,
61
+ default=4000,
62
+ help="Chunk size for text processing (default: 4000)",
63
+ )
64
+ parser.add_argument(
65
+ "--chunk-overlap",
66
+ type=int,
67
+ default=400,
68
+ help="Chunk overlap for text processing (default: 400)",
69
+ )
70
+ parser.add_argument(
71
+ "--max-paper-length",
72
+ type=int,
73
+ default=200000,
74
+ help="Maximum paper length in characters (default: 200000)",
75
+ )
76
+ parser.add_argument("--save-pdfs", action="store_true", help="Save downloaded PDFs (default: False)")
77
+ parser.add_argument(
78
+ "--random-sample",
79
+ action="store_true",
80
+ help="Randomly sample papers instead of taking the first N (default: False)",
81
+ )
82
+ return parser.parse_args()
83
+
84
+
85
+ def load_papers_from_csv(metadata_path: str, subject: str, limit: int, random_sample: bool) -> pd.DataFrame:
86
+ """Load papers from bioRxiv metadata CSV file.
87
+
88
+ Args:
89
+ metadata_path: Path to the metadata CSV file
90
+ subject: Subject area to filter by
91
+ limit: Maximum number of papers to return
92
+ random_sample: Whether to randomly sample papers
93
+
94
+ Returns:
95
+ DataFrame of filtered papers
96
+
97
+ """
98
+ try:
99
+ # Load the metadata CSV
100
+ df_biorxiv = pd.read_csv(metadata_path)
101
+
102
+ # Filter published papers in the specified subject
103
+ if subject.lower() == "all":
104
+ filtered_df = df_biorxiv[df_biorxiv.published != "NA"]
105
+ else:
106
+ filtered_df = df_biorxiv[
107
+ (df_biorxiv.published != "NA") & (df_biorxiv.category.str.lower() == subject.lower())
108
+ ]
109
+
110
+ # Sample papers
111
+ if len(filtered_df) > limit:
112
+ filtered_df = filtered_df.sample(limit, random_state=42) if random_sample else filtered_df.head(limit)
113
+
114
+ print(f"Loaded {len(filtered_df)} papers in subject: {subject}")
115
+ return filtered_df
116
+
117
+ except Exception as e:
118
+ print(f"Error loading papers from CSV: {e}")
119
+ return pd.DataFrame()
120
+
121
+
122
+ def download_pdf(paper_doi: str, save_path: str | None = None) -> str | None:
123
+ """Download PDF for a given paper DOI.
124
+
125
+ Args:
126
+ paper_doi: DOI of the paper
127
+ save_path: Path to save the PDF (if None, won't save to disk)
128
+
129
+ Returns:
130
+ PDF text content or None if download failed
131
+
132
+ """
133
+ # Construct PDF URL from DOI
134
+ pdf_url = f"https://www.biorxiv.org/content/{paper_doi}v1.full.pdf"
135
+
136
+ try:
137
+ # Add random delay to avoid rate limiting
138
+ time.sleep(random.uniform(1, 3))
139
+
140
+ response = requests.get(pdf_url, stream=True)
141
+ response.raise_for_status()
142
+
143
+ # Save PDF if requested
144
+ if save_path:
145
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
146
+ with open(save_path, "wb") as f:
147
+ for chunk in response.iter_content(chunk_size=8192):
148
+ f.write(chunk)
149
+
150
+ # Extract text from PDF
151
+ pdf_text = extract_text_from_pdf(io.BytesIO(response.content))
152
+ return pdf_text
153
+
154
+ except requests.exceptions.RequestException as e:
155
+ print(f"Error downloading PDF for {paper_doi}: {e}")
156
+ return None
157
+
158
+
159
+ def extract_text_from_pdf(pdf_file) -> str:
160
+ """Extract text content from a PDF file.
161
+
162
+ Args:
163
+ pdf_file: File-like object containing PDF data
164
+
165
+ Returns:
166
+ Extracted text content
167
+
168
+ """
169
+ try:
170
+ pdf_reader = PyPDF2.PdfReader(pdf_file)
171
+ text = ""
172
+ for page_num in range(len(pdf_reader.pages)):
173
+ page = pdf_reader.pages[page_num]
174
+ text += page.extract_text() + "\n\n"
175
+ return text
176
+ except Exception as e:
177
+ print(f"Error extracting text from PDF: {e}")
178
+ return ""
179
+
180
+
181
+ def truncate_text(text: str, max_length: int) -> str:
182
+ """Truncate text to a maximum length while preserving complete sentences.
183
+
184
+ Args:
185
+ text: The text to truncate
186
+ max_length: Maximum length in characters
187
+
188
+ Returns:
189
+ Truncated text
190
+
191
+ """
192
+ if len(text) <= max_length:
193
+ return text
194
+
195
+ # Find the last sentence boundary before max_length
196
+ truncated = text[:max_length]
197
+ last_period = truncated.rfind(".")
198
+ last_question = truncated.rfind("?")
199
+ last_exclamation = truncated.rfind("!")
200
+
201
+ # Find the last sentence boundary
202
+ last_boundary = max(last_period, last_question, last_exclamation)
203
+
204
+ if last_boundary > 0:
205
+ return text[: last_boundary + 1]
206
+ else:
207
+ # If no sentence boundary found, just truncate at max_length
208
+ return truncated
209
+
210
+
211
+ def process_papers(papers_df: pd.DataFrame, args) -> list[dict[str, Any]]:
212
+ """Process papers using PaperTaskExtractor.
213
+
214
+ Args:
215
+ papers_df: DataFrame of paper metadata
216
+ args: Command line arguments
217
+
218
+ Returns:
219
+ List of papers with extracted tasks, databases, and software
220
+
221
+ """
222
+ # Initialize the task extractor
223
+ extractor = PaperTaskExtractor(llm=args.model, chunk_size=args.chunk_size, chunk_overlap=args.chunk_overlap)
224
+
225
+ results = []
226
+
227
+ for _, paper in tqdm(papers_df.iterrows(), total=len(papers_df), desc="Processing papers"):
228
+ paper_doi = paper.get("doi")
229
+ if not paper_doi:
230
+ continue
231
+
232
+ print(f"\nProcessing paper: {paper.get('title')} (DOI: {paper_doi})")
233
+
234
+ # Create paths
235
+ pdf_path = None
236
+ if args.save_pdfs:
237
+ pdf_path = os.path.join(args.output_dir, "pdfs", f"{paper_doi.replace('/', '_')}.pdf")
238
+
239
+ result_path = os.path.join(args.output_dir, "results", f"{paper_doi.replace('/', '_')}.json")
240
+
241
+ # Skip if already processed
242
+ if os.path.exists(result_path):
243
+ print(f"Paper already processed, loading from {result_path}")
244
+ with open(result_path) as f:
245
+ paper_result = json.load(f)
246
+ results.append(paper_result)
247
+ continue
248
+
249
+ # Download and process PDF
250
+ pdf_text = download_pdf(paper_doi, pdf_path)
251
+ if not pdf_text:
252
+ print(f"Failed to download or extract text from {paper_doi}")
253
+ continue
254
+
255
+ # Truncate text if it exceeds max length
256
+ if len(pdf_text) > args.max_paper_length:
257
+ original_length = len(pdf_text)
258
+ pdf_text = truncate_text(pdf_text, args.max_paper_length)
259
+ print(f"Truncated paper from {original_length} to {len(pdf_text)} characters")
260
+
261
+ # Extract tasks, databases, and software
262
+ try:
263
+ _, extraction_results = extractor.go(pdf_text)
264
+
265
+ # Add paper metadata to results
266
+ paper_result = {
267
+ "metadata": {
268
+ "doi": paper.get("doi"),
269
+ "title": paper.get("title"),
270
+ "authors": paper.get("authors", ""),
271
+ "abstract": paper.get("abstract", ""),
272
+ "date": paper.get("date", ""),
273
+ "category": paper.get("category", ""),
274
+ "text_length": len(pdf_text),
275
+ },
276
+ "extraction": extraction_results,
277
+ }
278
+
279
+ # Save results
280
+ os.makedirs(os.path.dirname(result_path), exist_ok=True)
281
+ with open(result_path, "w") as f:
282
+ json.dump(paper_result, f, indent=2)
283
+
284
+ results.append(paper_result)
285
+
286
+ except Exception as e:
287
+ print(f"Error processing paper {paper_doi}: {e}")
288
+
289
+ return results
290
+
291
+
292
+ def generate_summary(results: list[dict[str, Any]], output_dir: str):
293
+ """Generate summary of extracted tasks, databases, and software.
294
+
295
+ Args:
296
+ results: List of papers with extraction results
297
+ output_dir: Directory to save summary
298
+
299
+ """
300
+ # Collect all tasks, databases, and software
301
+ all_tasks = []
302
+ all_databases = []
303
+ all_software = []
304
+
305
+ for paper in results:
306
+ extraction = paper.get("extraction", {})
307
+
308
+ # Add paper metadata to each item
309
+ paper_meta = paper.get("metadata", {})
310
+ paper_id = f"{paper_meta.get('title')} ({paper_meta.get('doi')})"
311
+
312
+ # Process tasks
313
+ for task in extraction.get("tasks", []):
314
+ task["paper"] = paper_id
315
+ all_tasks.append(task)
316
+
317
+ # Process databases
318
+ for db in extraction.get("databases", []):
319
+ db["paper"] = paper_id
320
+ all_databases.append(db)
321
+
322
+ # Process software
323
+ for sw in extraction.get("software", []):
324
+ sw["paper"] = paper_id
325
+ all_software.append(sw)
326
+
327
+ # Create summary dataframes
328
+ if all_tasks:
329
+ tasks_df = pd.DataFrame(all_tasks)
330
+ tasks_df.to_csv(os.path.join(output_dir, "tasks_summary.csv"), index=False)
331
+
332
+ if all_databases:
333
+ db_df = pd.DataFrame(all_databases)
334
+ db_df.to_csv(os.path.join(output_dir, "databases_summary.csv"), index=False)
335
+
336
+ if all_software:
337
+ sw_df = pd.DataFrame(all_software)
338
+ sw_df.to_csv(os.path.join(output_dir, "software_summary.csv"), index=False)
339
+
340
+ # Generate frequency counts
341
+ task_counts = {}
342
+ for task in all_tasks:
343
+ task_name = task.get("task_name")
344
+ if task_name:
345
+ task_counts[task_name] = task_counts.get(task_name, 0) + 1
346
+
347
+ db_counts = {}
348
+ for db in all_databases:
349
+ db_name = db.get("name")
350
+ if db_name:
351
+ db_counts[db_name] = db_counts.get(db_name, 0) + 1
352
+
353
+ sw_counts = {}
354
+ for sw in all_software:
355
+ sw_name = sw.get("name")
356
+ if sw_name:
357
+ sw_counts[sw_name] = sw_counts.get(sw_name, 0) + 1
358
+
359
+ # Save frequency counts
360
+ with open(os.path.join(output_dir, "frequency_summary.json"), "w") as f:
361
+ json.dump(
362
+ {"tasks": task_counts, "databases": db_counts, "software": sw_counts},
363
+ f,
364
+ indent=2,
365
+ )
366
+
367
+ print(f"Summary files saved to {output_dir}")
368
+
369
+
370
+ def main():
371
+ """Main function to run the script."""
372
+ args = parse_arguments()
373
+
374
+ # Set default output directory if not specified
375
+ if args.output_dir is None:
376
+ # Clean subject name for directory name (replace spaces with underscores, lowercase)
377
+ clean_subject = args.subject.lower().replace(" ", "_").replace("/", "_")
378
+ args.output_dir = f"./biorxiv_results_{clean_subject}_{args.limit}"
379
+
380
+ # Create output directories
381
+ os.makedirs(args.output_dir, exist_ok=True)
382
+ os.makedirs(os.path.join(args.output_dir, "results"), exist_ok=True)
383
+ if args.save_pdfs:
384
+ os.makedirs(os.path.join(args.output_dir, "pdfs"), exist_ok=True)
385
+
386
+ # Load papers from CSV
387
+ print(f"Loading papers from {args.metadata_path} in subject area: {args.subject}")
388
+ papers_df = load_papers_from_csv(args.metadata_path, args.subject, args.limit, args.random_sample)
389
+
390
+ if len(papers_df) == 0:
391
+ print(f"No papers found for subject: {args.subject}")
392
+ return
393
+
394
+ # Process papers
395
+ results = process_papers(papers_df, args)
396
+
397
+ # Generate summary
398
+ generate_summary(results, args.output_dir)
399
+
400
+ print("Done!")
401
+
402
+
403
+ if __name__ == "__main__":
404
+ main()
Biomanus/biomni/biorxiv_scripts/generate_function.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Command-line tool to generate Python functions from task descriptions using the function_generator agent."""
3
+
4
+ import argparse
5
+ import json
6
+ import os
7
+
8
+ from biomni.agent.function_generator import FunctionGenerator
9
+ from tqdm import tqdm
10
+
11
+
12
+ def main():
13
+ """Main function for the command-line tool."""
14
+ parser = argparse.ArgumentParser(description="Generate Python functions given task descriptions")
15
+ parser.add_argument(
16
+ "--task",
17
+ "-t",
18
+ type=str,
19
+ help="JSON file containing a list of task descriptions",
20
+ )
21
+ parser.add_argument(
22
+ "--output-dir",
23
+ "-o",
24
+ type=str,
25
+ default="generated_functions",
26
+ help="Directory to save the generated functions (default: generated_functions)",
27
+ )
28
+ parser.add_argument(
29
+ "--model",
30
+ "-m",
31
+ type=str,
32
+ default="claude-3-7-sonnet-latest",
33
+ help="LLM model to use (default: claude-3-7-sonnet-latest)",
34
+ )
35
+ parser.add_argument(
36
+ "--temperature",
37
+ type=float,
38
+ default=0.7,
39
+ help="Temperature setting for the LLM (default: 0.7)",
40
+ )
41
+
42
+ args = parser.parse_args()
43
+
44
+ with open(args.task) as f_tasks:
45
+ task_list = json.load(f_tasks)
46
+ task_descriptions = list(task_list["tasks"])
47
+
48
+ # Create the output directory if it doesn't exist
49
+ os.makedirs(args.output_dir, exist_ok=True)
50
+
51
+ # Initialize the function generator agent
52
+ function_generator = FunctionGenerator(llm=args.model, temperature=args.temperature)
53
+
54
+ if not task_list:
55
+ print("No tasks found.")
56
+ else:
57
+ # tqdm shows a progress bar, file names as description
58
+ for _i, desc in enumerate(tqdm(task_descriptions, desc="Generating Python scripts given task descriptions"), 1):
59
+ generated_script_name, generated_codes = function_generator.go(desc)
60
+
61
+ # Save results
62
+ result_path = os.path.join(args.output_dir, generated_script_name)
63
+
64
+ os.makedirs(os.path.dirname(result_path), exist_ok=True)
65
+ with open(result_path, "w") as f:
66
+ f.write(generated_codes)
67
+
68
+ print("DONE")
69
+
70
+
71
+ if __name__ == "__main__":
72
+ main()
Biomanus/biomni/biorxiv_scripts/process_all_subjects.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Script to process all bioRxiv subjects and extract computational tasks from the top 100 papers in each."""
3
+
4
+ import argparse
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import time
9
+
10
+ import pandas as pd
11
+ from tqdm import tqdm
12
+
13
+ # Add the parent directory to the path so we can import the bioagentos package
14
+ sys.path.append("/dfs/user/kexinh/BioAgentOS/")
15
+
16
+ # List of all bioRxiv subjects
17
+ BIORXIV_SUBJECTS = [
18
+ "evolutionary biology",
19
+ "ecology",
20
+ "neuroscience",
21
+ "developmental biology",
22
+ "plant biology",
23
+ "microbiology",
24
+ "cancer biology",
25
+ "immunology",
26
+ "cell biology",
27
+ "biochemistry",
28
+ "genetics",
29
+ "bioinformatics",
30
+ "animal behavior and cognition",
31
+ "biophysics",
32
+ "genomics",
33
+ "systems biology",
34
+ "bioengineering",
35
+ "molecular biology",
36
+ "physiology",
37
+ "zoology",
38
+ "scientific communication and education",
39
+ "pathology",
40
+ "synthetic biology",
41
+ "paleontology",
42
+ "pharmacology and toxicology",
43
+ ]
44
+
45
+
46
+ def parse_arguments():
47
+ """Parse command line arguments."""
48
+ parser = argparse.ArgumentParser(description="Process all bioRxiv subjects and extract tasks from papers.")
49
+ parser.add_argument(
50
+ "--papers-per-subject",
51
+ type=int,
52
+ default=100,
53
+ help="Number of papers to process per subject (default: 100)",
54
+ )
55
+ parser.add_argument(
56
+ "--metadata-path",
57
+ type=str,
58
+ default="/dfs/user/kexinh/BioAgentOS/data/biorxiv_metadata.csv",
59
+ help="Path to bioRxiv metadata CSV file (default: data/biorxiv_metadata.csv)",
60
+ )
61
+ parser.add_argument(
62
+ "--output-dir",
63
+ type=str,
64
+ default="./biorxiv_all_subjects",
65
+ help="Base directory to save results (default: ./biorxiv_all_subjects)",
66
+ )
67
+ parser.add_argument(
68
+ "--model",
69
+ type=str,
70
+ default="claude-3-haiku-20240307",
71
+ help="LLM model to use for extraction (default: claude-3-haiku-20240307)",
72
+ )
73
+ parser.add_argument(
74
+ "--max-paper-length",
75
+ type=int,
76
+ default=200000,
77
+ help="Maximum paper length in characters (default: 200000)",
78
+ )
79
+ parser.add_argument(
80
+ "--chunk-size",
81
+ type=int,
82
+ default=10000,
83
+ help="Chunk size for text processing (default: 10000)",
84
+ )
85
+ parser.add_argument(
86
+ "--chunk-overlap",
87
+ type=int,
88
+ default=400,
89
+ help="Chunk overlap for text processing (default: 400)",
90
+ )
91
+ parser.add_argument("--save-pdfs", action="store_true", help="Save downloaded PDFs (default: False)")
92
+ parser.add_argument(
93
+ "--random-sample",
94
+ action="store_true",
95
+ help="Randomly sample papers instead of taking the first N (default: False)",
96
+ )
97
+ parser.add_argument(
98
+ "--subjects",
99
+ type=str,
100
+ nargs="+",
101
+ help="Specific subjects to process (default: all subjects)",
102
+ )
103
+ parser.add_argument(
104
+ "--delay",
105
+ type=int,
106
+ default=60,
107
+ help="Delay in seconds between processing subjects (default: 60)",
108
+ )
109
+ parser.add_argument(
110
+ "--summary-only",
111
+ action="store_true",
112
+ help="Only generate the combined summary (default: False)",
113
+ )
114
+ return parser.parse_args()
115
+
116
+
117
+ def check_subject_availability(metadata_path, subjects):
118
+ """Check which subjects have papers available in the metadata.
119
+
120
+ Args:
121
+ metadata_path: Path to the metadata CSV file
122
+ subjects: List of subjects to check
123
+
124
+ Returns:
125
+ List of subjects with available papers
126
+
127
+ """
128
+ try:
129
+ df_biorxiv = pd.read_csv(metadata_path)
130
+ available_subjects = []
131
+
132
+ for subject in subjects:
133
+ count = len(
134
+ df_biorxiv[(df_biorxiv.published != "NA") & (df_biorxiv.category.str.lower() == subject.lower())]
135
+ )
136
+ if count > 0:
137
+ available_subjects.append((subject, count))
138
+ print(f"Subject '{subject}' has {count} papers available")
139
+ else:
140
+ print(f"Subject '{subject}' has no papers available")
141
+
142
+ return available_subjects
143
+ except Exception as e:
144
+ print(f"Error checking subject availability: {e}")
145
+ return []
146
+
147
+
148
+ def process_subject(subject, args):
149
+ """Process a single subject by calling the extract_biorxiv_tasks.py script.
150
+
151
+ Args:
152
+ subject: Subject to process
153
+ args: Command line arguments
154
+
155
+ """
156
+ # Clean subject name for command line
157
+ subject.replace(" ", "\\ ")
158
+
159
+ # Build command
160
+ cmd = [
161
+ "python",
162
+ "BioAgentOS/bioagentos/scripts/extract_biorxiv_tasks.py",
163
+ "--subject",
164
+ f'"{subject}"',
165
+ "--limit",
166
+ str(args.papers_per_subject),
167
+ "--metadata-path",
168
+ args.metadata_path,
169
+ "--model",
170
+ args.model,
171
+ "--max-paper-length",
172
+ str(args.max_paper_length),
173
+ "--chunk-size",
174
+ str(args.chunk_size),
175
+ "--chunk-overlap",
176
+ str(args.chunk_overlap),
177
+ ]
178
+
179
+ # Add optional arguments
180
+ if args.save_pdfs:
181
+ cmd.append("--save-pdfs")
182
+ if args.random_sample:
183
+ cmd.append("--random-sample")
184
+
185
+ # Convert command list to string
186
+ cmd_str = " ".join(cmd)
187
+
188
+ print(f"\n{'=' * 80}\nProcessing subject: {subject}\n{'=' * 80}")
189
+ print(f"Running command: {cmd_str}")
190
+
191
+ # Execute command
192
+ try:
193
+ subprocess.run(cmd_str, shell=True, check=True)
194
+ print(f"Successfully processed subject: {subject}")
195
+ except subprocess.CalledProcessError as e:
196
+ print(f"Error processing subject '{subject}': {e}")
197
+
198
+
199
+ def combine_summaries(base_dir, subjects, papers_per_subject):
200
+ """Combine frequency summaries from all subjects into a single summary.
201
+
202
+ Args:
203
+ base_dir: Base directory containing subject results
204
+ subjects: List of subjects that were processed
205
+ papers_per_subject: Number of papers processed per subject
206
+
207
+ """
208
+ all_tasks = {}
209
+ all_databases = {}
210
+ all_software = {}
211
+
212
+ for subject, _ in subjects:
213
+ # Clean subject name for directory name
214
+ clean_subject = subject.lower().replace(" ", "_").replace("/", "_")
215
+ subject_dir = f"biorxiv_results_{clean_subject}_{papers_per_subject}"
216
+ summary_path = os.path.join(subject_dir, "frequency_summary.json")
217
+
218
+ try:
219
+ if os.path.exists(summary_path):
220
+ with open(summary_path) as f:
221
+ summary = json.load(f)
222
+
223
+ # Merge tasks
224
+ for task, count in summary.get("tasks", {}).items():
225
+ if task in all_tasks:
226
+ all_tasks[task] += count
227
+ else:
228
+ all_tasks[task] = count
229
+
230
+ # Merge databases
231
+ for db, count in summary.get("databases", {}).items():
232
+ if db in all_databases:
233
+ all_databases[db] += count
234
+ else:
235
+ all_databases[db] = count
236
+
237
+ # Merge software
238
+ for sw, count in summary.get("software", {}).items():
239
+ if sw in all_software:
240
+ all_software[sw] += count
241
+ else:
242
+ all_software[sw] = count
243
+ else:
244
+ print(f"Warning: Summary file not found for subject '{subject}'")
245
+ except Exception as e:
246
+ print(f"Error processing summary for subject '{subject}': {e}")
247
+
248
+ # Sort by frequency (descending)
249
+ all_tasks = dict(sorted(all_tasks.items(), key=lambda item: item[1], reverse=True))
250
+ all_databases = dict(sorted(all_databases.items(), key=lambda item: item[1], reverse=True))
251
+ all_software = dict(sorted(all_software.items(), key=lambda item: item[1], reverse=True))
252
+
253
+ # Create combined summary
254
+ combined_summary = {
255
+ "tasks": all_tasks,
256
+ "databases": all_databases,
257
+ "software": all_software,
258
+ }
259
+
260
+ # Save combined summary
261
+ combined_path = os.path.join(base_dir, "combined_summary.json")
262
+ os.makedirs(base_dir, exist_ok=True)
263
+
264
+ with open(combined_path, "w") as f:
265
+ json.dump(combined_summary, f, indent=2)
266
+
267
+ print(f"Combined summary saved to {combined_path}")
268
+
269
+ # Create CSV files for each category
270
+ tasks_df = pd.DataFrame(list(all_tasks.items()), columns=["Task", "Frequency"])
271
+ tasks_df.to_csv(os.path.join(base_dir, "tasks_frequency.csv"), index=False)
272
+
273
+ db_df = pd.DataFrame(list(all_databases.items()), columns=["Database", "Frequency"])
274
+ db_df.to_csv(os.path.join(base_dir, "databases_frequency.csv"), index=False)
275
+
276
+ sw_df = pd.DataFrame(list(all_software.items()), columns=["Software", "Frequency"])
277
+ sw_df.to_csv(os.path.join(base_dir, "software_frequency.csv"), index=False)
278
+
279
+ print(f"CSV summaries saved to {base_dir}")
280
+
281
+
282
+ def main():
283
+ """Main function to run the script."""
284
+ args = parse_arguments()
285
+
286
+ # Determine which subjects to process
287
+ subjects_to_process = args.subjects if args.subjects else BIORXIV_SUBJECTS
288
+
289
+ # Check which subjects have papers available
290
+ available_subjects = check_subject_availability(args.metadata_path, subjects_to_process)
291
+
292
+ if not available_subjects:
293
+ print("No subjects with available papers found. Exiting.")
294
+ return
295
+
296
+ if not args.summary_only:
297
+ # Process each subject
298
+ for subject, _count in tqdm(available_subjects, desc="Processing subjects"):
299
+ process_subject(subject, args)
300
+
301
+ # Add delay between subjects to avoid rate limiting
302
+ if args.delay > 0:
303
+ print(f"Waiting {args.delay} seconds before processing the next subject...")
304
+ time.sleep(args.delay)
305
+
306
+ # Combine summaries from all subjects
307
+ combine_summaries(args.output_dir, available_subjects, args.papers_per_subject)
308
+
309
+ print("All subjects processed successfully!")
310
+
311
+
312
+ if __name__ == "__main__":
313
+ import json # Import here to avoid circular import with the main script
314
+
315
+ main()