celltype-cli 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/PKG-INFO +5 -4
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/README.md +3 -3
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/install.sh +19 -11
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/pyproject.toml +2 -1
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/config.py +0 -1
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/mcp_server.py +31 -20
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cli.py +151 -108
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/client.py +2 -1
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/local_runner.py +6 -8
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/structure_inputs.py +1 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/__init__.py +15 -1
- celltype_cli-0.2.1/src/ct/tools/_schema_contract.py +44 -0
- celltype_cli-0.2.1/src/ct/tools/alphafold2/implementation.py +297 -0
- celltype_cli-0.2.1/src/ct/tools/alphafold2/tool.yaml +70 -0
- celltype_cli-0.2.1/src/ct/tools/alphafold2/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/alphafold2-multimer/implementation.py +389 -0
- celltype_cli-0.2.1/src/ct/tools/alphafold2-multimer/tool.yaml +83 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/boltz2/implementation.py +1 -1
- celltype_cli-0.2.1/src/ct/tools/boltz2/tool.yaml +52 -0
- celltype_cli-0.2.1/src/ct/tools/boltz2/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/diffdock/tool.yaml +53 -0
- celltype_cli-0.2.1/src/ct/tools/diffdock/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/esm2/tool.yaml +41 -0
- celltype_cli-0.2.1/src/ct/tools/esm2/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/esmfold/tool.yaml +36 -0
- celltype_cli-0.2.1/src/ct/tools/esmfold/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/evo2/implementation.py +174 -0
- celltype_cli-0.2.1/src/ct/tools/evo2/tool.yaml +69 -0
- celltype_cli-0.2.1/src/ct/tools/evo2/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/genmol/tool.yaml +71 -0
- celltype_cli-0.2.1/src/ct/tools/genmol/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/molmim/tool.yaml +66 -0
- celltype_cli-0.2.1/src/ct/tools/molmim/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/msa-search/Dockerfile +23 -0
- celltype_cli-0.2.1/src/ct/tools/msa-search/implementation.py +428 -0
- celltype_cli-0.2.1/src/ct/tools/msa-search/tool.yaml +59 -0
- celltype_cli-0.2.1/src/ct/tools/msa-search/tool_entrypoint.py +27 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/openfold2/Dockerfile +1 -1
- celltype_cli-0.2.1/src/ct/tools/openfold2/implementation.py +508 -0
- celltype_cli-0.2.1/src/ct/tools/openfold2/tool.yaml +103 -0
- celltype_cli-0.2.1/src/ct/tools/openfold2/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/openfold3/implementation.py +545 -0
- celltype_cli-0.2.1/src/ct/tools/openfold3/tool.yaml +94 -0
- celltype_cli-0.2.1/src/ct/tools/openfold3/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/proteinmpnn/tool.yaml +50 -0
- celltype_cli-0.2.1/src/ct/tools/proteinmpnn/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/rfdiffusion/implementation.py +425 -0
- celltype_cli-0.2.1/src/ct/tools/rfdiffusion/tool.yaml +64 -0
- celltype_cli-0.2.1/src/ct/tools/rfdiffusion/tool_entrypoint.py +27 -0
- celltype_cli-0.2.1/src/ct/tools/tool_entrypoint.py +27 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/terminal.py +7 -1
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cli.py +16 -0
- celltype_cli-0.2.0/src/ct/cloud/image_builder.py +0 -152
- celltype_cli-0.2.0/src/ct/cloud/manifest.py +0 -178
- celltype_cli-0.2.0/src/ct/cloud/weight_downloader.py +0 -217
- celltype_cli-0.2.0/src/ct/tools/alphafold2/implementation.py +0 -200
- celltype_cli-0.2.0/src/ct/tools/alphafold2/tool.yaml +0 -17
- celltype_cli-0.2.0/src/ct/tools/alphafold2-multimer/implementation.py +0 -184
- celltype_cli-0.2.0/src/ct/tools/alphafold2-multimer/tool.yaml +0 -17
- celltype_cli-0.2.0/src/ct/tools/boltz2/tool.yaml +0 -19
- celltype_cli-0.2.0/src/ct/tools/diffdock/tool.yaml +0 -20
- celltype_cli-0.2.0/src/ct/tools/esm2/tool.yaml +0 -18
- celltype_cli-0.2.0/src/ct/tools/esmfold/tool.yaml +0 -18
- celltype_cli-0.2.0/src/ct/tools/evo2/implementation.py +0 -63
- celltype_cli-0.2.0/src/ct/tools/evo2/tool.yaml +0 -19
- celltype_cli-0.2.0/src/ct/tools/evo2-protein-design/Dockerfile +0 -18
- celltype_cli-0.2.0/src/ct/tools/evo2-protein-design/implementation.py +0 -64
- celltype_cli-0.2.0/src/ct/tools/evo2-protein-design/tool.yaml +0 -19
- celltype_cli-0.2.0/src/ct/tools/genmol/tool.yaml +0 -20
- celltype_cli-0.2.0/src/ct/tools/molmim/tool.yaml +0 -19
- celltype_cli-0.2.0/src/ct/tools/msa-search/Dockerfile +0 -17
- celltype_cli-0.2.0/src/ct/tools/msa-search/implementation.py +0 -138
- celltype_cli-0.2.0/src/ct/tools/msa-search/tool.yaml +0 -18
- celltype_cli-0.2.0/src/ct/tools/openfold2/implementation.py +0 -200
- celltype_cli-0.2.0/src/ct/tools/openfold2/tool.yaml +0 -17
- celltype_cli-0.2.0/src/ct/tools/openfold3/implementation.py +0 -222
- celltype_cli-0.2.0/src/ct/tools/openfold3/tool.yaml +0 -19
- celltype_cli-0.2.0/src/ct/tools/proteinmpnn/tool.yaml +0 -18
- celltype_cli-0.2.0/src/ct/tools/rfdiffusion/implementation.py +0 -151
- celltype_cli-0.2.0/src/ct/tools/rfdiffusion/tool.yaml +0 -19
- celltype_cli-0.2.0/tests/test_image_builder.py +0 -132
- celltype_cli-0.2.0/tests/test_manifest.py +0 -224
- celltype_cli-0.2.0/tests/test_weight_management.py +0 -138
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/apply.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/archive.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/bulk-archive.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/continue.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/explore.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/ff.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/new.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/onboard.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/sync.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/verify.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-apply-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-archive-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-bulk-archive-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-continue-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-explore/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-ff-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-new-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-onboard/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-sync-specs/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-verify-change/SKILL.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.dockerignore +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.git +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.github/workflows/ci.yml +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.gitignore +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/CLAUDE.md +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/Dockerfile.api +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/LICENSE +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/assets/bixbench_benchmark.png +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/assets/ct2.gif +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/docker-compose.yml +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/openspec/config.yaml +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/scripts/prepare_datasets.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/scripts/prepare_l1000.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/settings.json +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/case_studies.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/doctor.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/knowledge.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/loop.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/orchestrator.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/runner.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/sandbox.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/session.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/system_prompt.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/trace_store.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/trajectory.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/types.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/workflows.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/app.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/config.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/engine.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/auth.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/router.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/tool_entrypoint.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/compute_providers.json +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/cro_database.json +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/downloader.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/loaders.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/benchmarks.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/governance.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/ingest.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/reasoning.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/schema_monitor.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/substrate.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/models/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/models/llm.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/_compound_resolver.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/_container_tools.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/alphafold2/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/alphafold2/patch_jackhmmer.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/alphafold2-multimer/Dockerfile +0 -0
- {celltype_cli-0.2.0/src/ct/tools → celltype_cli-0.2.1/src/ct/tools/alphafold2-multimer}/tool_entrypoint.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/biomarker.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/boltz2/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/cellxgene.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/chemistry.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/claude.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/clinical.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/clue.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/code.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/combination.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/compute.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/cro.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/data_api.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/design.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/diffdock/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/diffdock/implementation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/dna.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esm2/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esm2/implementation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esmfold/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esmfold/implementation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/evo2/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/experiment.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/expression.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/files.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/genmol/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/genmol/implementation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/genomics.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/http_client.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/imaging.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/intel.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/literature.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/molmim/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/molmim/implementation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/network.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/notification.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/omics.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/openfold3/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/ops.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/parity.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/pk.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/protein.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/proteinmpnn/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/proteinmpnn/implementation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/regulatory.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/remote_data.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/report.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/repurposing.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/rfdiffusion/Dockerfile +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/safety.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/shell.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/singlecell.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/statistics.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/structure.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/target.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/translational.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/viability.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/markdown.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/status.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/suggestions.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/traces.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/update_checker.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/conftest.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/__init__.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/kras_g12c.fasta +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/sotorasib.smi +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/test_e2e_cloud.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/fixtures/plan_snapshot.txt +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/fixtures/trace_snapshot.txt +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_api_smoke.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_billing.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cellxgene.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_chemistry_new.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_claude.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cloud_auth.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cloud_client.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_clue.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_code.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_compute.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_compute_router.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_config.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cro.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_data_api.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_data_api_service.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_design.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_dna.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_doctor.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_downloader.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_e2e_local_gpu.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_engine.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_experiment.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_extensibility.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_fallback_routing.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_files.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_future_backends_todo.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_gpu_registry.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_html_report.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_http_client.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_imaging.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_benchmarks.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_governance.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_ingest.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_reasoning.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_schema_monitor.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_substrate.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_knowledge.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_llm.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_local_runner.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_mention_completer.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_modal_generation.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_network.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_notebook.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_notification.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_observability.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_omics.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_ops.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_parity_tools.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_parkinsons_toolchain_smoke.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_patent_search.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_plan_display.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_protein.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_registry.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_remote_data.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_repurposing.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_sandbox.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_sdk_streaming.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_security_mitigations.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_session.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_setup_gpu.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_shell.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_singlecell.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_status.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_stripe_integration.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_structure.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_target.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_terminal.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_terminal_integration.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_tools.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_trace_store.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_traces.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_trajectory.py +0 -0
- {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_workflows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: celltype-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: CellType CLI — An autonomous agent for drug discovery research
|
|
5
5
|
Author: CellType Inc.
|
|
6
6
|
License: MIT
|
|
@@ -16,6 +16,7 @@ Requires-Dist: numpy>=1.24
|
|
|
16
16
|
Requires-Dist: openai>=1.0
|
|
17
17
|
Requires-Dist: pandas>=2.0
|
|
18
18
|
Requires-Dist: prompt-toolkit>=3.0
|
|
19
|
+
Requires-Dist: pydantic>=2.0
|
|
19
20
|
Requires-Dist: python-dotenv>=1.0
|
|
20
21
|
Requires-Dist: pyyaml>=6.0
|
|
21
22
|
Requires-Dist: rich>=13.0
|
|
@@ -76,7 +77,7 @@ Description-Content-Type: text/markdown
|
|
|
76
77
|
|
|
77
78
|
> **v0.2.0 is out** — Offload GPU-accelerated tools to CellType Cloud with no additional setup. ESMFold, Boltz-2, RFdiffusion, ProteinMPNN, and more. Update now:
|
|
78
79
|
> ```bash
|
|
79
|
-
>
|
|
80
|
+
> curl -fsSL https://raw.githubusercontent.com/celltype/celltype-agent/main/install.sh | bash
|
|
80
81
|
> ```
|
|
81
82
|
|
|
82
83
|
An autonomous agent for drug discovery research. Like Claude Code, but for biology.
|
|
@@ -114,10 +115,10 @@ CellType CLI achieves **90% accuracy** on [BixBench-Verified-50](https://hugging
|
|
|
114
115
|
### Quick install
|
|
115
116
|
|
|
116
117
|
```bash
|
|
117
|
-
curl -fsSL https://raw.githubusercontent.com/celltype/
|
|
118
|
+
curl -fsSL https://raw.githubusercontent.com/celltype/celltype-agent/main/install.sh | bash
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
Detects Python 3.10+, installs via `pipx
|
|
121
|
+
Detects Python 3.10+, installs the latest `celltype-cli` release from PyPI via `uv`, `pipx`, or `pip`, and launches the interactive setup wizard.
|
|
121
122
|
|
|
122
123
|
### Manual install
|
|
123
124
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **v0.2.0 is out** — Offload GPU-accelerated tools to CellType Cloud with no additional setup. ESMFold, Boltz-2, RFdiffusion, ProteinMPNN, and more. Update now:
|
|
4
4
|
> ```bash
|
|
5
|
-
>
|
|
5
|
+
> curl -fsSL https://raw.githubusercontent.com/celltype/celltype-agent/main/install.sh | bash
|
|
6
6
|
> ```
|
|
7
7
|
|
|
8
8
|
An autonomous agent for drug discovery research. Like Claude Code, but for biology.
|
|
@@ -40,10 +40,10 @@ CellType CLI achieves **90% accuracy** on [BixBench-Verified-50](https://hugging
|
|
|
40
40
|
### Quick install
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
curl -fsSL https://raw.githubusercontent.com/celltype/
|
|
43
|
+
curl -fsSL https://raw.githubusercontent.com/celltype/celltype-agent/main/install.sh | bash
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Detects Python 3.10+, installs via `pipx
|
|
46
|
+
Detects Python 3.10+, installs the latest `celltype-cli` release from PyPI via `uv`, `pipx`, or `pip`, and launches the interactive setup wizard.
|
|
47
47
|
|
|
48
48
|
### Manual install
|
|
49
49
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# celltype-cli — One-liner installer
|
|
3
|
-
# Usage: curl -fsSL https://raw.githubusercontent.com/celltype/
|
|
3
|
+
# Usage: curl -fsSL https://raw.githubusercontent.com/celltype/celltype-agent/main/install.sh | bash
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
PACKAGE="celltype-cli"
|
|
7
|
-
REPO_URL="git+https://github.com/celltype/cli.git"
|
|
8
7
|
MIN_PYTHON="3.10"
|
|
9
8
|
|
|
10
9
|
# ── Helpers ────────────────────────────────────────────────────
|
|
@@ -46,15 +45,15 @@ esac
|
|
|
46
45
|
|
|
47
46
|
# ── Install package ────────────────────────────────────────────
|
|
48
47
|
|
|
49
|
-
INSTALL_SPEC="${PACKAGE}
|
|
48
|
+
INSTALL_SPEC="${PACKAGE}"
|
|
50
49
|
|
|
51
50
|
if command -v uv >/dev/null 2>&1; then
|
|
52
|
-
info "Installing ${PACKAGE} via uv..."
|
|
53
|
-
uv tool install "$INSTALL_SPEC" || uv tool install --force "$INSTALL_SPEC"
|
|
51
|
+
info "Installing latest ${PACKAGE} from PyPI via uv..."
|
|
52
|
+
uv tool install "$INSTALL_SPEC" || uv tool install --upgrade "$INSTALL_SPEC" || uv tool install --force "$INSTALL_SPEC"
|
|
54
53
|
ok "Installed with uv"
|
|
55
54
|
elif command -v pipx >/dev/null 2>&1; then
|
|
56
|
-
info "Installing ${PACKAGE} via pipx..."
|
|
57
|
-
pipx install "$
|
|
55
|
+
info "Installing latest ${PACKAGE} from PyPI via pipx..."
|
|
56
|
+
pipx install "$INSTALL_SPEC" || pipx upgrade "$PACKAGE" || pipx install --force "$INSTALL_SPEC"
|
|
58
57
|
ok "Installed with pipx"
|
|
59
58
|
else
|
|
60
59
|
warn "uv/pipx not found — falling back to pip install --user"
|
|
@@ -83,7 +82,16 @@ ok "ct $(ct --version 2>/dev/null || echo '(installed)')"
|
|
|
83
82
|
|
|
84
83
|
# ── Run setup wizard ───────────────────────────────────────────
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
info "
|
|
88
|
-
info ""
|
|
89
|
-
|
|
85
|
+
if "$PYTHON" -c 'import os; fd = os.open("/dev/tty", os.O_RDONLY); os.close(fd)' 2>/dev/null; then
|
|
86
|
+
info ""
|
|
87
|
+
info "Running setup wizard..."
|
|
88
|
+
info ""
|
|
89
|
+
# When installed via `curl ... | bash`, stdin is the download pipe rather
|
|
90
|
+
# than the user's keyboard. Reattach setup to the controlling terminal so
|
|
91
|
+
# interactive prompts and masked input work normally.
|
|
92
|
+
ct setup < /dev/tty
|
|
93
|
+
else
|
|
94
|
+
warn ""
|
|
95
|
+
warn "Skipping interactive setup because no terminal is available."
|
|
96
|
+
warn "Run: ct setup"
|
|
97
|
+
fi
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "celltype-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "CellType CLI — An autonomous agent for drug discovery research"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
|
29
29
|
"markdown>=3.5",
|
|
30
30
|
"nbformat>=5.7",
|
|
31
31
|
"pyyaml>=6.0",
|
|
32
|
+
"pydantic>=2.0",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
[project.optional-dependencies]
|
|
@@ -71,6 +71,14 @@ _PY_TYPE_MAP = {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def _is_json_schema(parameters: dict) -> bool:
|
|
75
|
+
return (
|
|
76
|
+
isinstance(parameters, dict)
|
|
77
|
+
and parameters.get("type") == "object"
|
|
78
|
+
and isinstance(parameters.get("properties"), dict)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
74
82
|
def _params_to_json_schema(parameters: dict) -> dict:
|
|
75
83
|
"""Convert a ct tool parameters dict to a JSON Schema object.
|
|
76
84
|
|
|
@@ -81,6 +89,9 @@ def _params_to_json_schema(parameters: dict) -> dict:
|
|
|
81
89
|
if not parameters:
|
|
82
90
|
return {"type": "object", "properties": {}}
|
|
83
91
|
|
|
92
|
+
if _is_json_schema(parameters):
|
|
93
|
+
return parameters
|
|
94
|
+
|
|
84
95
|
properties = {}
|
|
85
96
|
for name, desc in parameters.items():
|
|
86
97
|
# Extract type hint from description if present (e.g., "gene name (str)")
|
|
@@ -110,30 +121,30 @@ def _make_tool_handler(tool_obj, session):
|
|
|
110
121
|
call_args["_session"] = session
|
|
111
122
|
call_args["_prior_results"] = {}
|
|
112
123
|
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# Try int first, then float
|
|
120
|
-
try:
|
|
121
|
-
call_args[key] = int(val)
|
|
124
|
+
# Legacy flat ct manifests describe every parameter as a string, so keep
|
|
125
|
+
# backwards-compatible coercion there. Structured JSON Schema tools should
|
|
126
|
+
# preserve the exact payload emitted by the SDK.
|
|
127
|
+
if not _is_json_schema(getattr(tool_obj, "parameters", {})):
|
|
128
|
+
for key, val in list(call_args.items()):
|
|
129
|
+
if key.startswith("_"):
|
|
122
130
|
continue
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
if isinstance(val, str):
|
|
132
|
+
try:
|
|
133
|
+
call_args[key] = int(val)
|
|
134
|
+
continue
|
|
135
|
+
except ValueError:
|
|
136
|
+
pass
|
|
137
|
+
try:
|
|
138
|
+
call_args[key] = float(val)
|
|
139
|
+
continue
|
|
140
|
+
except ValueError:
|
|
141
|
+
pass
|
|
142
|
+
if val.lower() in ("true", "false"):
|
|
143
|
+
call_args[key] = val.lower() == "true"
|
|
133
144
|
|
|
134
145
|
try:
|
|
135
146
|
# Route GPU tools through compute router
|
|
136
|
-
if getattr(tool_obj, "requires_gpu", False):
|
|
147
|
+
if getattr(tool_obj, "requires_gpu", False) or getattr(tool_obj, "cpu_only", False):
|
|
137
148
|
from ct.cloud.router import ComputeRouter
|
|
138
149
|
router = ComputeRouter(config=getattr(session, "config", None))
|
|
139
150
|
result = await asyncio.to_thread(router.route, tool_obj, **call_args)
|
|
@@ -423,11 +423,13 @@ def setup_gpu_cmd():
|
|
|
423
423
|
|
|
424
424
|
def _prompt_api_key() -> str:
|
|
425
425
|
"""Prompt user for API key with masked input."""
|
|
426
|
-
import
|
|
426
|
+
from prompt_toolkit import prompt as pt_prompt
|
|
427
|
+
|
|
427
428
|
console.print(" Get your key at: [link=https://console.anthropic.com/settings/keys]console.anthropic.com/settings/keys[/link]")
|
|
428
429
|
console.print()
|
|
430
|
+
console.print(" [dim]Pasted/typed characters will appear as *[/dim]")
|
|
429
431
|
try:
|
|
430
|
-
key =
|
|
432
|
+
key = pt_prompt(" Enter your Anthropic API key: ", is_password=True)
|
|
431
433
|
except (EOFError, KeyboardInterrupt):
|
|
432
434
|
console.print("\n [dim]Setup cancelled.[/dim]")
|
|
433
435
|
raise typer.Exit()
|
|
@@ -492,16 +494,24 @@ def tool_list(
|
|
|
492
494
|
ensure_loaded()
|
|
493
495
|
|
|
494
496
|
if gpu:
|
|
495
|
-
# Show GPU tools with hardware compatibility
|
|
497
|
+
# Show GPU tools with hardware compatibility and Docker image status
|
|
498
|
+
import subprocess as _sp
|
|
499
|
+
import json as _json
|
|
496
500
|
from ct.cloud.router import _detect_local_gpu_info
|
|
497
501
|
|
|
498
502
|
gpus = _detect_local_gpu_info()
|
|
499
503
|
best_gpu = max(gpus, key=lambda g: g.vram_mb) if gpus else None
|
|
500
504
|
|
|
505
|
+
try:
|
|
506
|
+
docker_ok = _sp.run(["docker", "info"], capture_output=True, timeout=5).returncode == 0
|
|
507
|
+
except Exception:
|
|
508
|
+
docker_ok = False
|
|
509
|
+
|
|
501
510
|
table = Table(title="GPU/High-Memory Tools")
|
|
502
511
|
table.add_column("Tool", style="cyan")
|
|
503
512
|
table.add_column("Type")
|
|
504
513
|
table.add_column("Requirement")
|
|
514
|
+
table.add_column("Pulled", style="bold")
|
|
505
515
|
table.add_column("Local", style="bold")
|
|
506
516
|
table.add_column("Description")
|
|
507
517
|
|
|
@@ -523,7 +533,24 @@ def tool_list(
|
|
|
523
533
|
else:
|
|
524
534
|
compat = "[red]no GPU[/red]"
|
|
525
535
|
|
|
526
|
-
|
|
536
|
+
pulled = "-"
|
|
537
|
+
if docker_ok and getattr(tool, "docker_image", None):
|
|
538
|
+
try:
|
|
539
|
+
res = _sp.run(
|
|
540
|
+
["docker", "image", "inspect", "--format",
|
|
541
|
+
"{{.Size}}", tool.docker_image],
|
|
542
|
+
capture_output=True, text=True, timeout=10,
|
|
543
|
+
)
|
|
544
|
+
if res.returncode == 0:
|
|
545
|
+
size_bytes = int(res.stdout.strip())
|
|
546
|
+
size_gb = size_bytes / (1024 ** 3)
|
|
547
|
+
pulled = f"[green]✓ {size_gb:.1f}GB[/green]"
|
|
548
|
+
else:
|
|
549
|
+
pulled = "[red]✗[/red]"
|
|
550
|
+
except Exception:
|
|
551
|
+
pulled = "-"
|
|
552
|
+
|
|
553
|
+
table.add_row(tool.name, tool_type, req, pulled, compat, tool.description[:60])
|
|
527
554
|
|
|
528
555
|
console.print(table)
|
|
529
556
|
if best_gpu:
|
|
@@ -543,6 +570,120 @@ def tool_list(
|
|
|
543
570
|
)
|
|
544
571
|
|
|
545
572
|
|
|
573
|
+
def _pull_colabfold_databases(console):
|
|
574
|
+
"""Download ColabFold databases (UniRef30 + EnvDB) for local MSA search."""
|
|
575
|
+
import shutil
|
|
576
|
+
from pathlib import Path
|
|
577
|
+
|
|
578
|
+
home = Path.home()
|
|
579
|
+
db_dir = Path(os.environ.get("COLABFOLD_DB", home / ".cache" / "colabfold_db"))
|
|
580
|
+
uniref_ready = db_dir / "UNIREF30_READY"
|
|
581
|
+
envdb_ready = db_dir / "COLABDB_READY"
|
|
582
|
+
|
|
583
|
+
if uniref_ready.exists() and envdb_ready.exists():
|
|
584
|
+
size = sum(f.stat().st_size for f in db_dir.rglob("*") if f.is_file()) / (1024**3)
|
|
585
|
+
console.print(f"[green]ColabFold databases already downloaded at {db_dir} ({size:.0f}GB)[/green]")
|
|
586
|
+
return
|
|
587
|
+
|
|
588
|
+
console.print(f"\n[bold]MSA-Search requires ColabFold databases for local execution.[/bold]")
|
|
589
|
+
console.print(f" Location: {db_dir}")
|
|
590
|
+
console.print(f" Download: ~220GB compressed, ~500GB extracted")
|
|
591
|
+
console.print(f" Databases: UniRef30 2302 + ColabFold EnvDB 202108\n")
|
|
592
|
+
|
|
593
|
+
if not shutil.which("mmseqs"):
|
|
594
|
+
console.print("[yellow]Installing mmseqs2...[/yellow]")
|
|
595
|
+
import subprocess
|
|
596
|
+
subprocess.check_call(
|
|
597
|
+
"cd /tmp && wget -q https://mmseqs.com/latest/mmseqs-linux-avx2.tar.gz "
|
|
598
|
+
"&& tar xzf mmseqs-linux-avx2.tar.gz "
|
|
599
|
+
"&& sudo cp mmseqs/bin/mmseqs /usr/local/bin/ "
|
|
600
|
+
"&& rm -rf mmseqs mmseqs-linux-avx2.tar.gz",
|
|
601
|
+
shell=True,
|
|
602
|
+
)
|
|
603
|
+
console.print("[green]mmseqs2 installed.[/green]")
|
|
604
|
+
|
|
605
|
+
download_tool = None
|
|
606
|
+
for tool in ["aria2c", "curl", "wget"]:
|
|
607
|
+
if shutil.which(tool):
|
|
608
|
+
download_tool = tool
|
|
609
|
+
break
|
|
610
|
+
if not download_tool:
|
|
611
|
+
console.print("[red]No download tool found. Install aria2c, curl, or wget.[/red]")
|
|
612
|
+
return
|
|
613
|
+
|
|
614
|
+
db_dir.mkdir(parents=True, exist_ok=True)
|
|
615
|
+
import subprocess
|
|
616
|
+
|
|
617
|
+
UNIREF = "uniref30_2302"
|
|
618
|
+
ENVDB = "colabfold_envdb_202108"
|
|
619
|
+
|
|
620
|
+
def _download(url, output):
|
|
621
|
+
console.print(f" Downloading {Path(output).name}...")
|
|
622
|
+
if download_tool == "aria2c":
|
|
623
|
+
subprocess.check_call(
|
|
624
|
+
["aria2c", "--max-connection-per-server=8", "--allow-overwrite=true",
|
|
625
|
+
"-o", Path(output).name, "-d", str(Path(output).parent), url])
|
|
626
|
+
elif download_tool == "curl":
|
|
627
|
+
subprocess.check_call(["curl", "-L", "-o", str(output), url])
|
|
628
|
+
else:
|
|
629
|
+
subprocess.check_call(["wget", "-O", str(output), url])
|
|
630
|
+
|
|
631
|
+
os.environ["MMSEQS_FORCE_MERGE"] = "1"
|
|
632
|
+
|
|
633
|
+
if not uniref_ready.exists():
|
|
634
|
+
tarball = db_dir / f"{UNIREF}.tar.gz"
|
|
635
|
+
if not tarball.exists():
|
|
636
|
+
_download(
|
|
637
|
+
f"https://opendata.mmseqs.org/colabfold/{UNIREF}.db.tar.gz",
|
|
638
|
+
str(tarball))
|
|
639
|
+
console.print(f" Extracting {UNIREF} (this takes ~15 minutes)...")
|
|
640
|
+
subprocess.check_call(["tar", "-xzf", str(tarball)], cwd=str(db_dir))
|
|
641
|
+
|
|
642
|
+
tax_tarball = db_dir / f"{UNIREF}_newtaxonomy.tar.gz"
|
|
643
|
+
if not tax_tarball.exists():
|
|
644
|
+
_download(
|
|
645
|
+
f"https://opendata.mmseqs.org/colabfold/{UNIREF}_newtaxonomy.tar.gz",
|
|
646
|
+
str(tax_tarball))
|
|
647
|
+
subprocess.check_call(["tar", "-xzf", str(tax_tarball)], cwd=str(db_dir))
|
|
648
|
+
|
|
649
|
+
mapping = db_dir / f"{UNIREF}_db_mapping"
|
|
650
|
+
if mapping.exists():
|
|
651
|
+
try:
|
|
652
|
+
subprocess.check_call(
|
|
653
|
+
["mmseqs", "createbintaxmapping", str(mapping), str(mapping) + ".bin"])
|
|
654
|
+
os.replace(str(mapping) + ".bin", str(mapping))
|
|
655
|
+
except Exception:
|
|
656
|
+
pass
|
|
657
|
+
for src, dst in [
|
|
658
|
+
(f"{UNIREF}_db_mapping", f"{UNIREF}_db.idx_mapping"),
|
|
659
|
+
(f"{UNIREF}_db_taxonomy", f"{UNIREF}_db.idx_taxonomy"),
|
|
660
|
+
]:
|
|
661
|
+
src_path = db_dir / src
|
|
662
|
+
dst_path = db_dir / dst
|
|
663
|
+
if src_path.exists() and not dst_path.exists():
|
|
664
|
+
dst_path.symlink_to(src_path.name)
|
|
665
|
+
|
|
666
|
+
uniref_ready.touch()
|
|
667
|
+
tarball.unlink(missing_ok=True)
|
|
668
|
+
tax_tarball.unlink(missing_ok=True)
|
|
669
|
+
console.print(f"[green] UniRef30 ready.[/green]")
|
|
670
|
+
|
|
671
|
+
if not envdb_ready.exists():
|
|
672
|
+
tarball = db_dir / f"{ENVDB}.tar.gz"
|
|
673
|
+
if not tarball.exists():
|
|
674
|
+
_download(
|
|
675
|
+
f"https://opendata.mmseqs.org/colabfold/{ENVDB}.db.tar.gz",
|
|
676
|
+
str(tarball))
|
|
677
|
+
console.print(f" Extracting {ENVDB} (this takes ~20 minutes)...")
|
|
678
|
+
subprocess.check_call(["tar", "-xzf", str(tarball)], cwd=str(db_dir))
|
|
679
|
+
envdb_ready.touch()
|
|
680
|
+
tarball.unlink(missing_ok=True)
|
|
681
|
+
console.print(f"[green] ColabFold EnvDB ready.[/green]")
|
|
682
|
+
|
|
683
|
+
console.print(f"\n[green bold]ColabFold databases ready at {db_dir}[/green bold]")
|
|
684
|
+
console.print("MSA-Search will use local MMseqs2 for fast, reliable homology search.")
|
|
685
|
+
|
|
686
|
+
|
|
546
687
|
@tool_app.command("pull")
|
|
547
688
|
def tool_pull(
|
|
548
689
|
tool_name: str = typer.Argument(..., help="Tool name (e.g., structure.esmfold)"),
|
|
@@ -626,10 +767,14 @@ def tool_pull(
|
|
|
626
767
|
raise typer.Exit(code=1)
|
|
627
768
|
console.print(f"[green]Built {docker_image}[/green]")
|
|
628
769
|
|
|
629
|
-
# Step 2: Pre-download weights
|
|
630
|
-
# This triggers the first-run weight download inside the container
|
|
770
|
+
# Step 2: Pre-download weights / databases
|
|
631
771
|
compute = config.get("compute", {})
|
|
632
772
|
cpu_only = compute.get("cpu_only", False) or not compute.get("requires_gpu", True)
|
|
773
|
+
|
|
774
|
+
if tool_name == "genomics.msa_search":
|
|
775
|
+
_pull_colabfold_databases(console)
|
|
776
|
+
return
|
|
777
|
+
|
|
633
778
|
if cpu_only:
|
|
634
779
|
console.print(f"[green]{display_name} is CPU-only, no weights to download.[/green]")
|
|
635
780
|
return
|
|
@@ -691,108 +836,6 @@ def tool_pull(
|
|
|
691
836
|
console.print(f"[green]{display_name} image built. Weights will download on first real use.[/green]")
|
|
692
837
|
|
|
693
838
|
|
|
694
|
-
@tool_app.command("build")
|
|
695
|
-
def tool_build(
|
|
696
|
-
tool_name: str = typer.Argument(..., help="Tool name (e.g., structure.esmfold)"),
|
|
697
|
-
):
|
|
698
|
-
"""Build a Docker image for a GPU tool from the manifest."""
|
|
699
|
-
from ct.cloud.manifest import get_tool_config
|
|
700
|
-
from ct.cloud.image_builder import generate_dockerfile
|
|
701
|
-
|
|
702
|
-
config = get_tool_config(tool_name)
|
|
703
|
-
if not config:
|
|
704
|
-
console.print(f"[red]Tool '{tool_name}' not found in manifest.[/red]")
|
|
705
|
-
raise typer.Exit(code=1)
|
|
706
|
-
|
|
707
|
-
display_name = config.get("display_name", tool_name)
|
|
708
|
-
docker_image = config.get("docker_image", f"celltype/{tool_name.split('.')[-1]}:latest")
|
|
709
|
-
|
|
710
|
-
console.print(f"[bold]Building Docker image for {display_name}...[/bold]")
|
|
711
|
-
|
|
712
|
-
# Generate Dockerfile
|
|
713
|
-
import tempfile
|
|
714
|
-
with tempfile.TemporaryDirectory() as tmpdir:
|
|
715
|
-
from pathlib import Path
|
|
716
|
-
tmpdir = Path(tmpdir)
|
|
717
|
-
dockerfile_content = generate_dockerfile(config)
|
|
718
|
-
(tmpdir / "Dockerfile").write_text(dockerfile_content)
|
|
719
|
-
|
|
720
|
-
# Copy entrypoint
|
|
721
|
-
entrypoint_src = Path(__file__).parent / "cloud" / "tool_entrypoint.py"
|
|
722
|
-
if entrypoint_src.exists():
|
|
723
|
-
import shutil
|
|
724
|
-
shutil.copy2(entrypoint_src, tmpdir / "tool_entrypoint.py")
|
|
725
|
-
|
|
726
|
-
# Create placeholder implementation
|
|
727
|
-
impl_key = tool_name.split(".")[-1]
|
|
728
|
-
(tmpdir / "implementation.py").write_text(
|
|
729
|
-
f'"""Placeholder implementation for {display_name}."""\n\n'
|
|
730
|
-
f'def run(**kwargs):\n'
|
|
731
|
-
f' return {{"summary": "{display_name} placeholder"}}\n'
|
|
732
|
-
)
|
|
733
|
-
|
|
734
|
-
# Build
|
|
735
|
-
import subprocess
|
|
736
|
-
result = subprocess.run(
|
|
737
|
-
["docker", "build", "-t", docker_image, "."],
|
|
738
|
-
cwd=tmpdir,
|
|
739
|
-
capture_output=True,
|
|
740
|
-
text=True,
|
|
741
|
-
timeout=600,
|
|
742
|
-
)
|
|
743
|
-
|
|
744
|
-
if result.returncode != 0:
|
|
745
|
-
console.print(f"[red]Build failed:[/red]\n{result.stderr[:500]}")
|
|
746
|
-
raise typer.Exit(code=1)
|
|
747
|
-
|
|
748
|
-
console.print(f"[green]Built {docker_image}[/green]")
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
@tool_app.command("setup")
|
|
752
|
-
def tool_setup(
|
|
753
|
-
tool_name: str = typer.Argument(..., help="Tool name (e.g., structure.esmfold)"),
|
|
754
|
-
):
|
|
755
|
-
"""Pull Docker image and download model weights for a GPU tool."""
|
|
756
|
-
from ct.cloud.manifest import get_tool_config
|
|
757
|
-
|
|
758
|
-
config = get_tool_config(tool_name)
|
|
759
|
-
if not config:
|
|
760
|
-
console.print(f"[red]Tool '{tool_name}' not found in manifest.[/red]")
|
|
761
|
-
raise typer.Exit(code=1)
|
|
762
|
-
|
|
763
|
-
display_name = config.get("display_name", tool_name)
|
|
764
|
-
docker_image = config.get("docker_image", "")
|
|
765
|
-
|
|
766
|
-
console.print(f"[bold]Setting up {display_name}...[/bold]")
|
|
767
|
-
|
|
768
|
-
# Step 1: Pull Docker image
|
|
769
|
-
if docker_image:
|
|
770
|
-
console.print(f"\n[bold]Step 1: Pulling Docker image {docker_image}...[/bold]")
|
|
771
|
-
import subprocess
|
|
772
|
-
result = subprocess.run(
|
|
773
|
-
["docker", "pull", docker_image],
|
|
774
|
-
capture_output=True,
|
|
775
|
-
text=True,
|
|
776
|
-
timeout=600,
|
|
777
|
-
)
|
|
778
|
-
if result.returncode != 0:
|
|
779
|
-
console.print(f"[yellow]Warning: Could not pull {docker_image}. You may need to build it first.[/yellow]")
|
|
780
|
-
else:
|
|
781
|
-
console.print(f"[green]Pulled {docker_image}[/green]")
|
|
782
|
-
|
|
783
|
-
# Step 2: Download weights
|
|
784
|
-
console.print(f"\n[bold]Step 2: Downloading model weights...[/bold]")
|
|
785
|
-
from ct.cloud.weight_downloader import pull_tool_weights
|
|
786
|
-
result = pull_tool_weights(tool_name)
|
|
787
|
-
|
|
788
|
-
if "error" in result:
|
|
789
|
-
console.print(f"[red]{result['summary']}[/red]")
|
|
790
|
-
raise typer.Exit(code=1)
|
|
791
|
-
|
|
792
|
-
console.print(f"[green]{result['summary']}[/green]")
|
|
793
|
-
console.print(f"\n[bold green]{display_name} is ready![/bold green]")
|
|
794
|
-
|
|
795
|
-
|
|
796
839
|
# ─── Knowledge subcommands ────────────────────────────────────
|
|
797
840
|
|
|
798
841
|
knowledge_app = typer.Typer(help="Manage knowledge substrate, ingestion, and quality gates")
|
|
@@ -15,7 +15,8 @@ logger = logging.getLogger("ct.cloud.client")
|
|
|
15
15
|
|
|
16
16
|
# Polling interval for job status
|
|
17
17
|
JOB_POLL_INTERVAL = 2.0
|
|
18
|
-
|
|
18
|
+
# OpenFold3 and other large structure jobs can exceed 10 minutes end-to-end.
|
|
19
|
+
JOB_TIMEOUT = 1800.0 # 30 minutes max
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class CloudClient:
|
|
@@ -100,14 +100,7 @@ class LocalRunner:
|
|
|
100
100
|
return None
|
|
101
101
|
|
|
102
102
|
def _get_timeout(self, tool) -> int:
|
|
103
|
-
"""Get per-tool timeout
|
|
104
|
-
try:
|
|
105
|
-
from ct.cloud.manifest import get_tool_config
|
|
106
|
-
config = get_tool_config(tool.name)
|
|
107
|
-
if config:
|
|
108
|
-
return config.get("execution", {}).get("timeout_s", 600)
|
|
109
|
-
except Exception:
|
|
110
|
-
pass
|
|
103
|
+
"""Get per-tool timeout. Default 600s for all tools."""
|
|
111
104
|
return 600
|
|
112
105
|
|
|
113
106
|
# Weight mounts are handled by _get_cache_mounts() which mounts
|
|
@@ -146,6 +139,11 @@ class LocalRunner:
|
|
|
146
139
|
resolved.mkdir(parents=True, exist_ok=True)
|
|
147
140
|
mounts.extend(["-v", f"{resolved}:{container_path}"])
|
|
148
141
|
|
|
142
|
+
# ColabFold databases for MSA search (read-only mount, don't create if missing)
|
|
143
|
+
colabfold_db = Path(os.environ.get("COLABFOLD_DB", home / ".cache" / "colabfold_db"))
|
|
144
|
+
if colabfold_db.exists() and colabfold_db.joinpath("uniref30_2302_db.dbtype").exists():
|
|
145
|
+
mounts.extend(["-v", f"{colabfold_db}:/vol/colabfold_db:ro"])
|
|
146
|
+
|
|
149
147
|
return mounts
|
|
150
148
|
|
|
151
149
|
def _get_gpu_flags(self, tool) -> list[str]:
|
|
@@ -189,7 +189,7 @@ class ToolRegistry:
|
|
|
189
189
|
continue
|
|
190
190
|
lines.append(f"\n## {cat}")
|
|
191
191
|
for tool in cat_tools:
|
|
192
|
-
params =
|
|
192
|
+
params = _summarize_tool_parameters(tool.parameters)
|
|
193
193
|
lines.append(f"- **{tool.name}**({params}): {tool.description}")
|
|
194
194
|
if tool.usage_guide:
|
|
195
195
|
lines.append(f" USE WHEN: {tool.usage_guide}")
|
|
@@ -210,6 +210,20 @@ class ToolRegistry:
|
|
|
210
210
|
registry = ToolRegistry()
|
|
211
211
|
|
|
212
212
|
|
|
213
|
+
def _summarize_tool_parameters(parameters: dict) -> str:
|
|
214
|
+
if not isinstance(parameters, dict):
|
|
215
|
+
return ""
|
|
216
|
+
if parameters.get("type") == "object" and isinstance(parameters.get("properties"), dict):
|
|
217
|
+
parts = []
|
|
218
|
+
required = set(parameters.get("required", []))
|
|
219
|
+
for name, spec in parameters["properties"].items():
|
|
220
|
+
type_name = spec.get("type", "any") if isinstance(spec, dict) else "any"
|
|
221
|
+
suffix = " [required]" if name in required else ""
|
|
222
|
+
parts.append(f"{name}: {type_name}{suffix}")
|
|
223
|
+
return ", ".join(parts)
|
|
224
|
+
return ", ".join(f"{k}: {v}" for k, v in parameters.items())
|
|
225
|
+
|
|
226
|
+
|
|
213
227
|
# Import tool modules to trigger registration
|
|
214
228
|
def _load_tools():
|
|
215
229
|
"""Import all tool modules to register them."""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Shared schema and error helpers for structured GPU tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ToolInputModel(BaseModel):
|
|
11
|
+
"""Base input model for structured GPU tools."""
|
|
12
|
+
|
|
13
|
+
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ToolOutputModel(BaseModel):
|
|
17
|
+
"""Base output model for structured GPU tools."""
|
|
18
|
+
|
|
19
|
+
model_config = ConfigDict(extra="forbid")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ToolErrorModel(BaseModel):
|
|
23
|
+
"""Structured tool error payload for agent-friendly failures."""
|
|
24
|
+
|
|
25
|
+
summary: str
|
|
26
|
+
error: str
|
|
27
|
+
isError: bool = True
|
|
28
|
+
detail: str | None = None
|
|
29
|
+
|
|
30
|
+
model_config = ConfigDict(extra="allow")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def structured_error(summary: str, error: str, **extra: Any) -> dict[str, Any]:
|
|
34
|
+
payload = ToolErrorModel(summary=summary, error=error, **extra)
|
|
35
|
+
return payload.model_dump(exclude_none=True)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def export_tool_contract(input_model: type[BaseModel], output_model: type[BaseModel]) -> dict[str, Any]:
|
|
39
|
+
"""Expose JSON Schema for the current tool contract."""
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
"input_schema": input_model.model_json_schema(),
|
|
43
|
+
"output_schema": output_model.model_json_schema(),
|
|
44
|
+
}
|