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.
Files changed (304) hide show
  1. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/PKG-INFO +5 -4
  2. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/README.md +3 -3
  3. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/install.sh +19 -11
  4. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/pyproject.toml +2 -1
  5. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/config.py +0 -1
  6. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/mcp_server.py +31 -20
  7. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cli.py +151 -108
  8. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/client.py +2 -1
  9. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/local_runner.py +6 -8
  10. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/structure_inputs.py +1 -0
  11. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/__init__.py +15 -1
  12. celltype_cli-0.2.1/src/ct/tools/_schema_contract.py +44 -0
  13. celltype_cli-0.2.1/src/ct/tools/alphafold2/implementation.py +297 -0
  14. celltype_cli-0.2.1/src/ct/tools/alphafold2/tool.yaml +70 -0
  15. celltype_cli-0.2.1/src/ct/tools/alphafold2/tool_entrypoint.py +27 -0
  16. celltype_cli-0.2.1/src/ct/tools/alphafold2-multimer/implementation.py +389 -0
  17. celltype_cli-0.2.1/src/ct/tools/alphafold2-multimer/tool.yaml +83 -0
  18. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/boltz2/implementation.py +1 -1
  19. celltype_cli-0.2.1/src/ct/tools/boltz2/tool.yaml +52 -0
  20. celltype_cli-0.2.1/src/ct/tools/boltz2/tool_entrypoint.py +27 -0
  21. celltype_cli-0.2.1/src/ct/tools/diffdock/tool.yaml +53 -0
  22. celltype_cli-0.2.1/src/ct/tools/diffdock/tool_entrypoint.py +27 -0
  23. celltype_cli-0.2.1/src/ct/tools/esm2/tool.yaml +41 -0
  24. celltype_cli-0.2.1/src/ct/tools/esm2/tool_entrypoint.py +27 -0
  25. celltype_cli-0.2.1/src/ct/tools/esmfold/tool.yaml +36 -0
  26. celltype_cli-0.2.1/src/ct/tools/esmfold/tool_entrypoint.py +27 -0
  27. celltype_cli-0.2.1/src/ct/tools/evo2/implementation.py +174 -0
  28. celltype_cli-0.2.1/src/ct/tools/evo2/tool.yaml +69 -0
  29. celltype_cli-0.2.1/src/ct/tools/evo2/tool_entrypoint.py +27 -0
  30. celltype_cli-0.2.1/src/ct/tools/genmol/tool.yaml +71 -0
  31. celltype_cli-0.2.1/src/ct/tools/genmol/tool_entrypoint.py +27 -0
  32. celltype_cli-0.2.1/src/ct/tools/molmim/tool.yaml +66 -0
  33. celltype_cli-0.2.1/src/ct/tools/molmim/tool_entrypoint.py +27 -0
  34. celltype_cli-0.2.1/src/ct/tools/msa-search/Dockerfile +23 -0
  35. celltype_cli-0.2.1/src/ct/tools/msa-search/implementation.py +428 -0
  36. celltype_cli-0.2.1/src/ct/tools/msa-search/tool.yaml +59 -0
  37. celltype_cli-0.2.1/src/ct/tools/msa-search/tool_entrypoint.py +27 -0
  38. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/openfold2/Dockerfile +1 -1
  39. celltype_cli-0.2.1/src/ct/tools/openfold2/implementation.py +508 -0
  40. celltype_cli-0.2.1/src/ct/tools/openfold2/tool.yaml +103 -0
  41. celltype_cli-0.2.1/src/ct/tools/openfold2/tool_entrypoint.py +27 -0
  42. celltype_cli-0.2.1/src/ct/tools/openfold3/implementation.py +545 -0
  43. celltype_cli-0.2.1/src/ct/tools/openfold3/tool.yaml +94 -0
  44. celltype_cli-0.2.1/src/ct/tools/openfold3/tool_entrypoint.py +27 -0
  45. celltype_cli-0.2.1/src/ct/tools/proteinmpnn/tool.yaml +50 -0
  46. celltype_cli-0.2.1/src/ct/tools/proteinmpnn/tool_entrypoint.py +27 -0
  47. celltype_cli-0.2.1/src/ct/tools/rfdiffusion/implementation.py +425 -0
  48. celltype_cli-0.2.1/src/ct/tools/rfdiffusion/tool.yaml +64 -0
  49. celltype_cli-0.2.1/src/ct/tools/rfdiffusion/tool_entrypoint.py +27 -0
  50. celltype_cli-0.2.1/src/ct/tools/tool_entrypoint.py +27 -0
  51. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/terminal.py +7 -1
  52. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cli.py +16 -0
  53. celltype_cli-0.2.0/src/ct/cloud/image_builder.py +0 -152
  54. celltype_cli-0.2.0/src/ct/cloud/manifest.py +0 -178
  55. celltype_cli-0.2.0/src/ct/cloud/weight_downloader.py +0 -217
  56. celltype_cli-0.2.0/src/ct/tools/alphafold2/implementation.py +0 -200
  57. celltype_cli-0.2.0/src/ct/tools/alphafold2/tool.yaml +0 -17
  58. celltype_cli-0.2.0/src/ct/tools/alphafold2-multimer/implementation.py +0 -184
  59. celltype_cli-0.2.0/src/ct/tools/alphafold2-multimer/tool.yaml +0 -17
  60. celltype_cli-0.2.0/src/ct/tools/boltz2/tool.yaml +0 -19
  61. celltype_cli-0.2.0/src/ct/tools/diffdock/tool.yaml +0 -20
  62. celltype_cli-0.2.0/src/ct/tools/esm2/tool.yaml +0 -18
  63. celltype_cli-0.2.0/src/ct/tools/esmfold/tool.yaml +0 -18
  64. celltype_cli-0.2.0/src/ct/tools/evo2/implementation.py +0 -63
  65. celltype_cli-0.2.0/src/ct/tools/evo2/tool.yaml +0 -19
  66. celltype_cli-0.2.0/src/ct/tools/evo2-protein-design/Dockerfile +0 -18
  67. celltype_cli-0.2.0/src/ct/tools/evo2-protein-design/implementation.py +0 -64
  68. celltype_cli-0.2.0/src/ct/tools/evo2-protein-design/tool.yaml +0 -19
  69. celltype_cli-0.2.0/src/ct/tools/genmol/tool.yaml +0 -20
  70. celltype_cli-0.2.0/src/ct/tools/molmim/tool.yaml +0 -19
  71. celltype_cli-0.2.0/src/ct/tools/msa-search/Dockerfile +0 -17
  72. celltype_cli-0.2.0/src/ct/tools/msa-search/implementation.py +0 -138
  73. celltype_cli-0.2.0/src/ct/tools/msa-search/tool.yaml +0 -18
  74. celltype_cli-0.2.0/src/ct/tools/openfold2/implementation.py +0 -200
  75. celltype_cli-0.2.0/src/ct/tools/openfold2/tool.yaml +0 -17
  76. celltype_cli-0.2.0/src/ct/tools/openfold3/implementation.py +0 -222
  77. celltype_cli-0.2.0/src/ct/tools/openfold3/tool.yaml +0 -19
  78. celltype_cli-0.2.0/src/ct/tools/proteinmpnn/tool.yaml +0 -18
  79. celltype_cli-0.2.0/src/ct/tools/rfdiffusion/implementation.py +0 -151
  80. celltype_cli-0.2.0/src/ct/tools/rfdiffusion/tool.yaml +0 -19
  81. celltype_cli-0.2.0/tests/test_image_builder.py +0 -132
  82. celltype_cli-0.2.0/tests/test_manifest.py +0 -224
  83. celltype_cli-0.2.0/tests/test_weight_management.py +0 -138
  84. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/apply.md +0 -0
  85. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/archive.md +0 -0
  86. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/bulk-archive.md +0 -0
  87. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/continue.md +0 -0
  88. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/explore.md +0 -0
  89. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/ff.md +0 -0
  90. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/new.md +0 -0
  91. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/onboard.md +0 -0
  92. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/sync.md +0 -0
  93. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/commands/opsx/verify.md +0 -0
  94. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-apply-change/SKILL.md +0 -0
  95. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-archive-change/SKILL.md +0 -0
  96. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-bulk-archive-change/SKILL.md +0 -0
  97. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-continue-change/SKILL.md +0 -0
  98. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-explore/SKILL.md +0 -0
  99. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-ff-change/SKILL.md +0 -0
  100. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-new-change/SKILL.md +0 -0
  101. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-onboard/SKILL.md +0 -0
  102. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-sync-specs/SKILL.md +0 -0
  103. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.claude/skills/openspec-verify-change/SKILL.md +0 -0
  104. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.dockerignore +0 -0
  105. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.git +0 -0
  106. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.github/workflows/ci.yml +0 -0
  107. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/.gitignore +0 -0
  108. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/CLAUDE.md +0 -0
  109. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/Dockerfile +0 -0
  110. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/Dockerfile.api +0 -0
  111. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/LICENSE +0 -0
  112. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/assets/bixbench_benchmark.png +0 -0
  113. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/assets/ct2.gif +0 -0
  114. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/docker-compose.yml +0 -0
  115. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/openspec/config.yaml +0 -0
  116. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/scripts/prepare_datasets.py +0 -0
  117. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/scripts/prepare_l1000.py +0 -0
  118. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/settings.json +0 -0
  119. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/__init__.py +0 -0
  120. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/__init__.py +0 -0
  121. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/case_studies.py +0 -0
  122. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/doctor.py +0 -0
  123. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/knowledge.py +0 -0
  124. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/loop.py +0 -0
  125. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/orchestrator.py +0 -0
  126. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/runner.py +0 -0
  127. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/sandbox.py +0 -0
  128. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/session.py +0 -0
  129. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/system_prompt.py +0 -0
  130. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/trace_store.py +0 -0
  131. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/trajectory.py +0 -0
  132. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/types.py +0 -0
  133. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/agent/workflows.py +0 -0
  134. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/__init__.py +0 -0
  135. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/app.py +0 -0
  136. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/config.py +0 -0
  137. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/api/engine.py +0 -0
  138. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/__init__.py +0 -0
  139. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/auth.py +0 -0
  140. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/router.py +0 -0
  141. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/cloud/tool_entrypoint.py +0 -0
  142. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/__init__.py +0 -0
  143. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/compute_providers.json +0 -0
  144. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/cro_database.json +0 -0
  145. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/downloader.py +0 -0
  146. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/data/loaders.py +0 -0
  147. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/__init__.py +0 -0
  148. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/benchmarks.py +0 -0
  149. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/governance.py +0 -0
  150. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/ingest.py +0 -0
  151. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/reasoning.py +0 -0
  152. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/schema_monitor.py +0 -0
  153. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/kb/substrate.py +0 -0
  154. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/models/__init__.py +0 -0
  155. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/models/llm.py +0 -0
  156. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/_compound_resolver.py +0 -0
  157. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/_container_tools.py +0 -0
  158. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/alphafold2/Dockerfile +0 -0
  159. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/alphafold2/patch_jackhmmer.py +0 -0
  160. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/alphafold2-multimer/Dockerfile +0 -0
  161. {celltype_cli-0.2.0/src/ct/tools → celltype_cli-0.2.1/src/ct/tools/alphafold2-multimer}/tool_entrypoint.py +0 -0
  162. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/biomarker.py +0 -0
  163. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/boltz2/Dockerfile +0 -0
  164. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/cellxgene.py +0 -0
  165. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/chemistry.py +0 -0
  166. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/claude.py +0 -0
  167. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/clinical.py +0 -0
  168. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/clue.py +0 -0
  169. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/code.py +0 -0
  170. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/combination.py +0 -0
  171. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/compute.py +0 -0
  172. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/cro.py +0 -0
  173. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/data_api.py +0 -0
  174. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/design.py +0 -0
  175. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/diffdock/Dockerfile +0 -0
  176. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/diffdock/implementation.py +0 -0
  177. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/dna.py +0 -0
  178. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esm2/Dockerfile +0 -0
  179. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esm2/implementation.py +0 -0
  180. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esmfold/Dockerfile +0 -0
  181. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/esmfold/implementation.py +0 -0
  182. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/evo2/Dockerfile +0 -0
  183. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/experiment.py +0 -0
  184. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/expression.py +0 -0
  185. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/files.py +0 -0
  186. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/genmol/Dockerfile +0 -0
  187. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/genmol/implementation.py +0 -0
  188. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/genomics.py +0 -0
  189. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/http_client.py +0 -0
  190. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/imaging.py +0 -0
  191. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/intel.py +0 -0
  192. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/literature.py +0 -0
  193. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/molmim/Dockerfile +0 -0
  194. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/molmim/implementation.py +0 -0
  195. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/network.py +0 -0
  196. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/notification.py +0 -0
  197. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/omics.py +0 -0
  198. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/openfold3/Dockerfile +0 -0
  199. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/ops.py +0 -0
  200. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/parity.py +0 -0
  201. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/pk.py +0 -0
  202. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/protein.py +0 -0
  203. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/proteinmpnn/Dockerfile +0 -0
  204. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/proteinmpnn/implementation.py +0 -0
  205. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/regulatory.py +0 -0
  206. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/remote_data.py +0 -0
  207. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/report.py +0 -0
  208. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/repurposing.py +0 -0
  209. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/rfdiffusion/Dockerfile +0 -0
  210. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/safety.py +0 -0
  211. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/shell.py +0 -0
  212. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/singlecell.py +0 -0
  213. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/statistics.py +0 -0
  214. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/structure.py +0 -0
  215. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/target.py +0 -0
  216. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/translational.py +0 -0
  217. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/tools/viability.py +0 -0
  218. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/__init__.py +0 -0
  219. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/markdown.py +0 -0
  220. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/status.py +0 -0
  221. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/suggestions.py +0 -0
  222. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/ui/traces.py +0 -0
  223. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/src/ct/update_checker.py +0 -0
  224. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/__init__.py +0 -0
  225. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/conftest.py +0 -0
  226. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/__init__.py +0 -0
  227. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/kras_g12c.fasta +0 -0
  228. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/sotorasib.smi +0 -0
  229. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/e2e/test_e2e_cloud.py +0 -0
  230. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/fixtures/plan_snapshot.txt +0 -0
  231. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/fixtures/trace_snapshot.txt +0 -0
  232. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_api_smoke.py +0 -0
  233. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_billing.py +0 -0
  234. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cellxgene.py +0 -0
  235. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_chemistry_new.py +0 -0
  236. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_claude.py +0 -0
  237. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cloud_auth.py +0 -0
  238. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cloud_client.py +0 -0
  239. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_clue.py +0 -0
  240. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_code.py +0 -0
  241. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_compute.py +0 -0
  242. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_compute_router.py +0 -0
  243. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_config.py +0 -0
  244. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_cro.py +0 -0
  245. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_data_api.py +0 -0
  246. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_data_api_service.py +0 -0
  247. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_design.py +0 -0
  248. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_dna.py +0 -0
  249. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_doctor.py +0 -0
  250. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_downloader.py +0 -0
  251. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_e2e_local_gpu.py +0 -0
  252. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_engine.py +0 -0
  253. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_experiment.py +0 -0
  254. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_extensibility.py +0 -0
  255. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_fallback_routing.py +0 -0
  256. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_files.py +0 -0
  257. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_future_backends_todo.py +0 -0
  258. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_gpu_registry.py +0 -0
  259. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_html_report.py +0 -0
  260. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_http_client.py +0 -0
  261. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_imaging.py +0 -0
  262. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_benchmarks.py +0 -0
  263. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_governance.py +0 -0
  264. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_ingest.py +0 -0
  265. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_reasoning.py +0 -0
  266. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_schema_monitor.py +0 -0
  267. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_kb_substrate.py +0 -0
  268. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_knowledge.py +0 -0
  269. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_llm.py +0 -0
  270. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_local_runner.py +0 -0
  271. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_mention_completer.py +0 -0
  272. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_modal_generation.py +0 -0
  273. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_network.py +0 -0
  274. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_notebook.py +0 -0
  275. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_notification.py +0 -0
  276. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_observability.py +0 -0
  277. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_omics.py +0 -0
  278. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_ops.py +0 -0
  279. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_parity_tools.py +0 -0
  280. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_parkinsons_toolchain_smoke.py +0 -0
  281. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_patent_search.py +0 -0
  282. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_plan_display.py +0 -0
  283. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_protein.py +0 -0
  284. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_registry.py +0 -0
  285. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_remote_data.py +0 -0
  286. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_repurposing.py +0 -0
  287. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_sandbox.py +0 -0
  288. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_sdk_streaming.py +0 -0
  289. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_security_mitigations.py +0 -0
  290. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_session.py +0 -0
  291. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_setup_gpu.py +0 -0
  292. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_shell.py +0 -0
  293. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_singlecell.py +0 -0
  294. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_status.py +0 -0
  295. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_stripe_integration.py +0 -0
  296. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_structure.py +0 -0
  297. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_target.py +0 -0
  298. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_terminal.py +0 -0
  299. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_terminal_integration.py +0 -0
  300. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_tools.py +0 -0
  301. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_trace_store.py +0 -0
  302. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_traces.py +0 -0
  303. {celltype_cli-0.2.0 → celltype_cli-0.2.1}/tests/test_trajectory.py +0 -0
  304. {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.0
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
- > pip install --upgrade celltype-cli
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/cli/main/install.sh | bash
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` or `pip`, and launches an interactive setup wizard.
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
- > pip install --upgrade celltype-cli
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/cli/main/install.sh | bash
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` or `pip`, and launches an interactive setup wizard.
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/cli/main/install.sh | bash
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} @ ${REPO_URL}"
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 "$REPO_URL" || pipx install --force "$REPO_URL"
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
- info ""
87
- info "Running setup wizard..."
88
- info ""
89
- ct setup
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.0"
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]
@@ -33,7 +33,6 @@ DEFAULTS = {
33
33
  "llm.api_key": None,
34
34
  "llm.openai_api_key": None,
35
35
  "llm.temperature": 0.1,
36
-
37
36
  "data.base": str(CONFIG_DIR / "data"),
38
37
  "data.depmap": None,
39
38
  "data.prism": None,
@@ -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
- # Coerce string values to numeric types when they look like numbers.
114
- # MCP sends all parameters as strings, but tools often expect int/float.
115
- for key, val in list(call_args.items()):
116
- if key.startswith("_"):
117
- continue
118
- if isinstance(val, str):
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
- except ValueError:
124
- pass
125
- try:
126
- call_args[key] = float(val)
127
- continue
128
- except ValueError:
129
- pass
130
- # Boolean coercion
131
- if val.lower() in ("true", "false"):
132
- call_args[key] = val.lower() == "true"
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 getpass
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 = getpass.getpass(" Enter your Anthropic API 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
- table.add_row(tool.name, tool_type, req, compat, tool.description[:60])
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 by running container with cache mounts
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
- JOB_TIMEOUT = 600.0 # 10 minutes max
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 from manifest, with fallback."""
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]:
@@ -8,6 +8,7 @@ from typing import Any
8
8
 
9
9
  INLINE_FILE_ARG_NAMES = {
10
10
  "target_pdb",
11
+ "pdb_text",
11
12
  "backbone_pdb",
12
13
  "protein_pdb",
13
14
  }
@@ -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 = ", ".join(f"{k}: {v}" for k, v in tool.parameters.items())
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
+ }