@moldesk/registry 0.0.1 → 0.0.2

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.
@@ -0,0 +1,14 @@
1
+ export interface MoldeskErrorData {
2
+ code: string;
3
+ message: string;
4
+ remediation?: string;
5
+ cause?: unknown;
6
+ details?: Record<string, unknown>;
7
+ }
8
+ export declare class MoldeskError extends Error {
9
+ readonly code: string;
10
+ readonly remediation?: string;
11
+ readonly cause?: unknown;
12
+ readonly details?: Record<string, unknown>;
13
+ constructor(data: MoldeskErrorData);
14
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,14 @@
1
+ export class MoldeskError extends Error {
2
+ code;
3
+ remediation;
4
+ cause;
5
+ details;
6
+ constructor(data) {
7
+ super(data.message);
8
+ this.name = "MoldeskError";
9
+ this.code = data.code;
10
+ this.remediation = data.remediation;
11
+ this.cause = data.cause;
12
+ this.details = data.details;
13
+ }
14
+ }
package/dist/index.d.ts CHANGED
@@ -1,14 +1,7 @@
1
- export interface ModelManifest {
2
- name: string;
3
- displayName: string;
4
- version: string;
5
- description: string;
6
- homepage?: string;
7
- license?: string;
8
- compatibility?: {
9
- cpu?: boolean;
10
- gpu?: "required" | "recommended" | "unsupported";
11
- };
12
- }
13
- export declare function listAvailableModels(): ModelManifest[];
14
- export declare function listInstalledModels(): ModelManifest[];
1
+ export { MoldeskError } from "./errors.js";
2
+ export type { MoldeskErrorData } from "./errors.js";
3
+ export { allowedCommandTokens, commandSpecSchema, modelManifestV1Schema, } from "./schema.js";
4
+ export type { AssetSpec, CommandSpec, HardwareRequirements, InputSpec, ModelCategory, ModelManifestV1, ModelStatus, OutputSpec, PlatformId, PostInstallHook, PythonRequirement, PythonRuntimeSpec, DockerRuntimeSpec, RequirementLevel, RuntimeKind, RuntimeSpec, SourceSpec, } from "./schema.js";
5
+ export type { LegacyModelManifest } from "./types.js";
6
+ export { getModelsDir, listAvailableModels, loadManifestFile, parseManifest, } from "./loader.js";
7
+ export { detectHostPlatformId, effectiveRuntimeSource, selectPythonRuntime, } from "./runtime-selection.js";
package/dist/index.js CHANGED
@@ -1,29 +1,4 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import YAML from "yaml";
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- function repoModelsDir() {
7
- // packages/registry/src -> repo root -> models
8
- return path.resolve(__dirname, "../../../models");
9
- }
10
- export function listAvailableModels() {
11
- const modelsDir = repoModelsDir();
12
- if (!fs.existsSync(modelsDir))
13
- return [];
14
- return fs
15
- .readdirSync(modelsDir, { withFileTypes: true })
16
- .filter((entry) => entry.isDirectory())
17
- .map((entry) => {
18
- const manifestPath = path.join(modelsDir, entry.name, "manifest.yaml");
19
- if (!fs.existsSync(manifestPath))
20
- return null;
21
- const raw = fs.readFileSync(manifestPath, "utf-8");
22
- return YAML.parse(raw);
23
- })
24
- .filter((manifest) => manifest !== null);
25
- }
26
- export function listInstalledModels() {
27
- // No models are installed yet — installation support lands with `moldesk install`.
28
- return [];
29
- }
1
+ export { MoldeskError } from "./errors.js";
2
+ export { allowedCommandTokens, commandSpecSchema, modelManifestV1Schema, } from "./schema.js";
3
+ export { getModelsDir, listAvailableModels, loadManifestFile, parseManifest, } from "./loader.js";
4
+ export { detectHostPlatformId, effectiveRuntimeSource, selectPythonRuntime, } from "./runtime-selection.js";
@@ -0,0 +1,5 @@
1
+ import { type ModelManifestV1 } from "./schema.js";
2
+ export declare function parseManifest(raw: unknown, manifestPath: string): ModelManifestV1;
3
+ export declare function loadManifestFile(manifestPath: string): ModelManifestV1;
4
+ export declare function listAvailableModels(modelsDir?: string): ModelManifestV1[];
5
+ export declare function getModelsDir(): string;
package/dist/loader.js ADDED
@@ -0,0 +1,128 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import YAML from "yaml";
5
+ import { z } from "zod";
6
+ import { MoldeskError } from "./errors.js";
7
+ import { modelManifestV1Schema } from "./schema.js";
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ function packagedModelsDir() {
10
+ return path.resolve(__dirname, "models");
11
+ }
12
+ function sourceModelsDir() {
13
+ // packages/registry/src -> repo root -> models (dev fallback only)
14
+ return path.resolve(__dirname, "../../../models");
15
+ }
16
+ function resolveModelsDir() {
17
+ const override = process.env["MOLDESK_MODELS_DIR"];
18
+ if (override && fs.existsSync(override))
19
+ return override;
20
+ if (fs.existsSync(packagedModelsDir()))
21
+ return packagedModelsDir();
22
+ if (fs.existsSync(sourceModelsDir()))
23
+ return sourceModelsDir();
24
+ throw new MoldeskError({
25
+ code: "REGISTRY_NOT_FOUND",
26
+ message: "MoleculeDesk model registry is missing from this installation.",
27
+ remediation: "Reinstall @moldesk/cli. Package maintainers should verify that @moldesk/registry includes dist/models.",
28
+ details: { packagedModelsDir: packagedModelsDir(), sourceModelsDir: sourceModelsDir() },
29
+ });
30
+ }
31
+ function toMoldeskError(manifestPath, error) {
32
+ if (error instanceof z.ZodError) {
33
+ const first = error.issues[0];
34
+ const field = first && first.path.length > 0 ? first.path.join(".") : "(root)";
35
+ return new MoldeskError({
36
+ code: "INVALID_MANIFEST",
37
+ message: `Invalid manifest ${manifestPath}: ${field}: ${first?.message ?? "validation failed"}`,
38
+ remediation: "Fix the manifest field and re-run the registry build. See docs/v0.1-implementation-spec.md Step 1.",
39
+ details: { manifestPath, field, issues: error.issues },
40
+ });
41
+ }
42
+ return new MoldeskError({
43
+ code: "INVALID_MANIFEST",
44
+ message: `Invalid manifest ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`,
45
+ remediation: "Ensure the file is valid YAML matching ModelManifestV1.",
46
+ details: { manifestPath },
47
+ cause: error,
48
+ });
49
+ }
50
+ export function parseManifest(raw, manifestPath) {
51
+ if (typeof raw !== "object" || raw === null) {
52
+ throw new MoldeskError({
53
+ code: "INVALID_MANIFEST",
54
+ message: `Invalid manifest ${manifestPath}: root must be a mapping`,
55
+ remediation: "Ensure the manifest is a YAML mapping with schemaVersion: 1.",
56
+ details: { manifestPath },
57
+ });
58
+ }
59
+ const record = raw;
60
+ if (record["schemaVersion"] !== 1) {
61
+ throw new MoldeskError({
62
+ code: "UNKNOWN_SCHEMA_VERSION",
63
+ message: `Unknown schema version in ${manifestPath}: ${String(record["schemaVersion"])}`,
64
+ remediation: "Set schemaVersion: 1. Only v1 manifests are supported in v0.1.",
65
+ details: { manifestPath, schemaVersion: record["schemaVersion"] },
66
+ });
67
+ }
68
+ if ("version" in record && !("modelVersion" in record)) {
69
+ throw new MoldeskError({
70
+ code: "INVALID_MANIFEST",
71
+ message: `Invalid manifest ${manifestPath}: ambiguous top-level "version" field; use modelVersion + adapterVersion`,
72
+ remediation: "Rename version to modelVersion and add adapterVersion.",
73
+ details: { manifestPath, field: "version" },
74
+ });
75
+ }
76
+ try {
77
+ return modelManifestV1Schema.parse(raw);
78
+ }
79
+ catch (error) {
80
+ throw toMoldeskError(manifestPath, error);
81
+ }
82
+ }
83
+ export function loadManifestFile(manifestPath) {
84
+ let raw;
85
+ try {
86
+ raw = YAML.parse(fs.readFileSync(manifestPath, "utf-8"));
87
+ }
88
+ catch (error) {
89
+ throw new MoldeskError({
90
+ code: "INVALID_MANIFEST",
91
+ message: `Invalid manifest ${manifestPath}: YAML parse failed`,
92
+ remediation: "Fix YAML syntax.",
93
+ details: { manifestPath },
94
+ cause: error,
95
+ });
96
+ }
97
+ return parseManifest(raw, manifestPath);
98
+ }
99
+ export function listAvailableModels(modelsDir) {
100
+ const dir = modelsDir ?? resolveModelsDir();
101
+ if (!fs.existsSync(dir))
102
+ return [];
103
+ const manifests = [];
104
+ const seen = new Set();
105
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
106
+ if (!entry.isDirectory())
107
+ continue;
108
+ const manifestPath = path.join(dir, entry.name, "manifest.yaml");
109
+ if (!fs.existsSync(manifestPath))
110
+ continue;
111
+ const manifest = loadManifestFile(manifestPath);
112
+ if (seen.has(manifest.name)) {
113
+ throw new MoldeskError({
114
+ code: "DUPLICATE_MODEL_ID",
115
+ message: `Duplicate model id "${manifest.name}" in ${manifestPath}`,
116
+ remediation: "Rename one model so every manifest name is unique.",
117
+ details: { manifestPath, name: manifest.name },
118
+ });
119
+ }
120
+ seen.add(manifest.name);
121
+ manifests.push(manifest);
122
+ }
123
+ manifests.sort((a, b) => a.name.localeCompare(b.name));
124
+ return manifests;
125
+ }
126
+ export function getModelsDir() {
127
+ return resolveModelsDir();
128
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,72 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import YAML from "yaml";
6
+ import { MoldeskError } from "./errors.js";
7
+ import { listAvailableModels } from "./loader.js";
8
+ const tempDirs = [];
9
+ function makeTempDir(prefix) {
10
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
11
+ tempDirs.push(dir);
12
+ return dir;
13
+ }
14
+ afterEach(() => {
15
+ for (const dir of tempDirs.splice(0))
16
+ fs.rmSync(dir, { recursive: true, force: true });
17
+ });
18
+ function writeManifest(dir, name, overrides = {}) {
19
+ const base = {
20
+ schemaVersion: 1,
21
+ name,
22
+ displayName: name,
23
+ modelVersion: "1.0.0",
24
+ adapterVersion: "0.1.0",
25
+ description: `${name} model`,
26
+ category: "other",
27
+ status: "beta",
28
+ runtimes: [{ kind: "python", python: "3.11", installer: "uv", requirements: [{ name: "torch" }] }],
29
+ hardware: {},
30
+ input: { formats: [".pdb"], required: true },
31
+ outputs: [{ id: "out", glob: "*.txt", required: true }],
32
+ ...overrides,
33
+ };
34
+ const modelDir = path.join(dir, name);
35
+ fs.mkdirSync(modelDir, { recursive: true });
36
+ fs.writeFileSync(path.join(modelDir, "manifest.yaml"), YAML.stringify(base));
37
+ }
38
+ describe("listAvailableModels", () => {
39
+ it("discovers validated manifests sorted by name", () => {
40
+ const dir = makeTempDir("moldesk-registry-");
41
+ writeManifest(dir, "zebra");
42
+ writeManifest(dir, "alpha");
43
+ const models = listAvailableModels(dir);
44
+ expect(models.map((m) => m.name)).toEqual(["alpha", "zebra"]);
45
+ });
46
+ it("throws DUPLICATE_MODEL_ID on duplicate names", () => {
47
+ const dir = makeTempDir("moldesk-dup-");
48
+ writeManifest(dir, "dir-a", { name: "same" });
49
+ writeManifest(dir, "dir-b", { name: "same" });
50
+ try {
51
+ listAvailableModels(dir);
52
+ expect.unreachable();
53
+ }
54
+ catch (error) {
55
+ expect(error).toBeInstanceOf(MoldeskError);
56
+ expect(error.code).toBe("DUPLICATE_MODEL_ID");
57
+ }
58
+ });
59
+ it("loads default manifests relative to the module, not cwd", () => {
60
+ const originalCwd = process.cwd();
61
+ const unrelatedCwd = makeTempDir("moldesk-unrelated-cwd-");
62
+ try {
63
+ process.chdir(unrelatedCwd);
64
+ const models = listAvailableModels();
65
+ const names = models.map((m) => m.name).sort();
66
+ expect(names).toEqual(["bindcraft2", "boltz", "diffdock", "ligandmpnn", "opendde", "proteinmpnn"]);
67
+ }
68
+ finally {
69
+ process.chdir(originalCwd);
70
+ }
71
+ });
72
+ });
@@ -0,0 +1,178 @@
1
+ schemaVersion: 1
2
+ name: bindcraft2
3
+ displayName: BindCraft2
4
+ modelVersion: 1.0.3
5
+ adapterVersion: 0.1.0
6
+ description: Bounded local protein-binder campaigns with AlphaFold and ProteinMPNN/HyperMPNN.
7
+ category: sequence-design
8
+ status: planned
9
+ license: BindCraft2 Source-Available License (Hosting-Restricted)
10
+ homepage: https://github.com/PacesaLab/BindCraft2
11
+ source:
12
+ repository: https://github.com/PacesaLab/BindCraft2
13
+ revision: 5bac63cd2d4e7ed3d5077ba4fc5966e0352038e4
14
+ # Linux dependency resolution passed; no live accelerator verification yet.
15
+ runtimes:
16
+ - kind: python
17
+ python: "3.12"
18
+ installer: uv
19
+ platforms: [linux-x64]
20
+ estimatedDownloadBytes: 10000000000
21
+ estimatedDiskBytes: 20000000000
22
+ nvidiaGpu: required
23
+ cuda:
24
+ level: required
25
+ minDriverCudaVersion: "12.1"
26
+ binaryOnly: [jaxlib, jax-cuda12-plugin, jax-cuda12-pjrt, cuequivariance-ops-cu12]
27
+ requirements:
28
+ - name: bindcraft
29
+ source: https://github.com/PacesaLab/BindCraft2
30
+ revision: 5bac63cd2d4e7ed3d5077ba4fc5966e0352038e4
31
+ extras: [cuda12]
32
+ - name: absl-py
33
+ version: "2.5.0"
34
+ - name: biotite
35
+ version: "1.7.1"
36
+ - name: biotraj
37
+ version: "1.2.2"
38
+ - name: certifi
39
+ version: "2026.7.22"
40
+ - name: charset-normalizer
41
+ version: "3.5.2"
42
+ - name: contourpy
43
+ version: "1.4.0"
44
+ - name: cuequivariance
45
+ version: "0.11.1"
46
+ - name: cuequivariance-jax
47
+ version: "0.11.1"
48
+ - name: cuequivariance-ops-cu12
49
+ version: "0.11.1"
50
+ hashes: ["dfb05a8a87ba657e5aa0019a27ceaccfd0152c69646ce5039bcc9eafc9fc0c33"]
51
+ - name: cycler
52
+ version: "0.12.1"
53
+ - name: dm-haiku
54
+ version: "0.0.17"
55
+ - name: einops
56
+ version: "0.8.2"
57
+ - name: fonttools
58
+ version: "4.66.1"
59
+ - name: idna
60
+ version: "3.20"
61
+ - name: jax
62
+ version: "0.11.2"
63
+ - name: jax-cuda12-pjrt
64
+ version: "0.11.2"
65
+ hashes: ["2133b3afc63307c016ae4980f166b2b3de199759363ead74f7d550eaae953463"]
66
+ - name: jax-cuda12-plugin
67
+ version: "0.11.2"
68
+ hashes: ["ae67a4cc49b34c575713a8311f7597f6700ec5a2b6afbdbc3db5550fb022873c"]
69
+ - name: jaxlib
70
+ version: "0.11.2"
71
+ hashes: ["6338a1f0956f60d02529404ac33a8070015a52635cdb6bbb15d0d199757077bb"]
72
+ - name: jmp
73
+ version: "0.0.4"
74
+ - name: kiwisolver
75
+ version: "1.5.1"
76
+ - name: matplotlib
77
+ version: "3.11.2"
78
+ - name: ml-collections
79
+ version: "1.1.0"
80
+ - name: ml-dtypes
81
+ version: "0.6.0"
82
+ - name: mpmath
83
+ version: "1.3.0"
84
+ - name: msgpack
85
+ version: "1.2.3"
86
+ - name: networkx
87
+ version: "3.7"
88
+ - name: numpy
89
+ version: "2.5.3"
90
+ - name: nvidia-cublas-cu12
91
+ version: "12.9.2.10"
92
+ - name: nvidia-cuda-cccl-cu12
93
+ version: "12.9.27"
94
+ - name: nvidia-cuda-cupti-cu12
95
+ version: "12.9.79"
96
+ - name: nvidia-cuda-nvcc-cu12
97
+ version: "12.9.86"
98
+ - name: nvidia-cuda-nvrtc-cu12
99
+ version: "12.9.86"
100
+ - name: nvidia-cuda-runtime-cu12
101
+ version: "12.9.79"
102
+ - name: nvidia-cudnn-cu12
103
+ version: "9.27.0.42"
104
+ - name: nvidia-cufft-cu12
105
+ version: "11.4.1.4"
106
+ - name: nvidia-cusolver-cu12
107
+ version: "11.7.5.82"
108
+ - name: nvidia-cusparse-cu12
109
+ version: "12.5.10.65"
110
+ - name: nvidia-ml-py
111
+ version: "13.615.71"
112
+ - name: nvidia-nccl-cu12
113
+ version: "2.32.3"
114
+ - name: nvidia-nvjitlink-cu12
115
+ version: "12.9.86"
116
+ - name: nvidia-nvshmem-cu12
117
+ version: "3.8.0"
118
+ - name: opt-einsum
119
+ version: "3.4.0"
120
+ - name: optax
121
+ version: "0.2.8"
122
+ - name: packaging
123
+ version: "26.3"
124
+ - name: pillow
125
+ version: "12.3.0"
126
+ - name: platformdirs
127
+ version: "4.12.3"
128
+ - name: pyparsing
129
+ version: "3.3.3"
130
+ - name: python-dateutil
131
+ version: "2.9.0.post0"
132
+ - name: pyyaml
133
+ version: "6.0.3"
134
+ - name: requests
135
+ version: "2.34.2"
136
+ - name: scipy
137
+ version: "1.18.1"
138
+ - name: six
139
+ version: "1.17.0"
140
+ - name: sympy
141
+ version: "1.14.0"
142
+ - name: tabulate
143
+ version: "0.10.0"
144
+ - name: tqdm
145
+ version: "4.70.1"
146
+ - name: urllib3
147
+ version: "2.8.0"
148
+ hardware:
149
+ platforms: [linux-x64]
150
+ nvidiaGpu: required
151
+ cuda:
152
+ level: required
153
+ minDriverCudaVersion: "12.1"
154
+ minRamGb: 24
155
+ minDiskGb: 25
156
+ assets:
157
+ - id: alphafold-parameters
158
+ url: https://storage.googleapis.com/alphafold/alphafold_params_2022-12-06.tar?generation=1670353726160664
159
+ source: DeepMind AlphaFold parameters 2022-12-06
160
+ license: CC-BY-4.0
161
+ sha256: 36d4b0220f3c735f3296d301152b738c9776d16981d054845a68a1370b26cfe3
162
+ sizeBytes: 5587968000
163
+ target: alphafold
164
+ archive: tar
165
+ input:
166
+ formats: [.json]
167
+ required: true
168
+ outputs:
169
+ - { id: campaign, glob: moldesk-campaign.json, required: true }
170
+ - { id: metadata, glob: campaign_metadata.json, required: true }
171
+ - { id: trajectories, glob: "1_Trajectories/!_Trajectories.csv", required: true }
172
+ - { id: trajectory_structures, glob: "1_Trajectories/*/*.cif", required: false }
173
+ - { id: refolded, glob: "2_Refolded/!_Refolded.csv", required: false }
174
+ - { id: refolded_structures, glob: "2_Refolded/*.cif", required: false }
175
+ - { id: ranked, glob: "3_Ranked/!_Ranked.csv", required: false }
176
+ - { id: accepted_structures, glob: "3_Ranked/*.cif", required: false }
177
+ - { id: upstream_summary, glob: summary.csv, required: true }
178
+ - { id: summary, glob: moldesk-summary.json, required: false }
@@ -0,0 +1,155 @@
1
+ schemaVersion: 1
2
+ name: boltz
3
+ displayName: Boltz-2
4
+ modelVersion: 2.2.1
5
+ adapterVersion: 0.2.0
6
+ description: Joint biomolecular structure and binding-affinity prediction (Boltz team, MIT).
7
+ category: structure-prediction
8
+ status: beta
9
+ license: MIT
10
+ homepage: https://github.com/jwohlwend/boltz
11
+ source:
12
+ repository: https://github.com/jwohlwend/boltz
13
+ revision: cb04aeccdd480fd4db707f0bbafde538397fa2ac
14
+ # Darwin/MPS was verified end-to-end with a real install + prediction on
15
+ # 2026-09-23 (see models/boltz/README.md for full evidence: process device
16
+ # banner, checksummed outputs, negative-path test). Linux/CUDA is
17
+ # structurally implemented and unit-tested but not live-verified — no NVIDIA
18
+ # hardware was available to run it; do not treat it as verified per rule 13.
19
+ adapterVerification:
20
+ lastVerifiedAt: "2026-09-23T11:22:33.984Z"
21
+ platforms:
22
+ - darwin-arm64
23
+ notes: >-
24
+ Real moldesk install + moldesk run on Apple M3 (macOS, Darwin 25.6.0),
25
+ re-verified after fixing two P1 review findings (checkpoints now
26
+ populated/checksum-verified at install time via manifest `assets:`, not
27
+ lazily on first run; CUDA raised from recommended to required to match
28
+ the mandatory CUDA probe) and three P2 findings (verifyInstallation now
29
+ asserts the exact pinned boltz-community version, absence of official
30
+ boltz, and CLI mps-accelerator support; YAML input validation uses a
31
+ real parser, not a regex; distribution version is explicitly asserted
32
+ since git-sourced installs don't get PyPI hash enforcement). Installed
33
+ boltz-community==2.10.12 + torch==2.14.0 (not official boltz), ran
34
+ boltz-fix-macos-libomp, verifyInstallation's MPS probe passed with a
35
+ real tensor round-trip plus the strengthened assertions above. A live
36
+ prediction against examples/boltz/protein.yaml completed in ~88s (all
37
+ checkpoints already cache-verified, zero network downloads during the
38
+ run — confirmed via log inspection) with PyTorch Lightning reporting
39
+ "GPU available: True (mps), used: True". Outputs: a parseable mmCIF and
40
+ confidence JSON, both checksummed in run.json. Negative tests: (1)
41
+ --param accelerator=gpu on this CUDA-less install failed closed
42
+ instantly with an actionable error; (2) a malformed YAML job
43
+ ("sequences: [") was rejected with a real parser error before any run
44
+ started. Linux/CUDA path not covered by this verification — no NVIDIA
45
+ hardware available.
46
+ runtimes:
47
+ # Linux/CUDA: official `jwohlwend/boltz`. `--accelerator` on this pinned CLI only
48
+ # accepts gpu|cpu|tpu (no "mps" choice) — verified against src/boltz/main.py at
49
+ # this revision. `nvidiaGpu`/`cuda` are `required`, not `recommended`: the
50
+ # adapter's verifyInstallation runs a mandatory CUDA probe on every non-Darwin
51
+ # install (torch.cuda.is_available(), device count, CUDA build) with no CPU
52
+ # verification path implemented — declaring CUDA merely "recommended" would
53
+ # contradict what install actually enforces and let a CPU-only Linux host pass
54
+ # compatibility checks only to fail install. A genuine CPU-only Linux path is
55
+ # not implemented in this pass; `--param accelerator=cpu` is exposed by the
56
+ # pinned CLI but running it still requires this install to pass its CUDA probe.
57
+ - kind: python
58
+ python: "3.11"
59
+ installer: uv
60
+ platforms:
61
+ - linux-x64
62
+ source:
63
+ repository: https://github.com/jwohlwend/boltz
64
+ revision: cb04aeccdd480fd4db707f0bbafde538397fa2ac
65
+ requirements:
66
+ - name: boltz
67
+ version: 2.2.1
68
+ source: https://github.com/jwohlwend/boltz
69
+ revision: cb04aeccdd480fd4db707f0bbafde538397fa2ac
70
+ extras:
71
+ - cuda
72
+ nvidiaGpu: required
73
+ cuda:
74
+ level: required
75
+ # Darwin/MPS: `Novel-Therapeutics/boltz-community` fork, which adds an "mps"
76
+ # `--accelerator` choice (verified against src/boltz/main.py at this revision) and
77
+ # ships the `boltz-fix-macos-libomp` console script this runtime entry's
78
+ # `postInstall` runs after dependency install. No `nvidiaGpu`/`cuda` here: this
79
+ # platform-runtime entry genuinely has no CUDA requirement.
80
+ - kind: python
81
+ python: "3.11"
82
+ installer: uv
83
+ platforms:
84
+ - darwin-arm64
85
+ source:
86
+ repository: https://github.com/Novel-Therapeutics/boltz-community
87
+ revision: 401f18150cc7184b7e1bb37239a25035ef6b3a57
88
+ requirements:
89
+ - name: boltz-community
90
+ version: 2.10.12
91
+ source: https://github.com/Novel-Therapeutics/boltz-community
92
+ revision: 401f18150cc7184b7e1bb37239a25035ef6b3a57
93
+ # Newest stable PyPI torch as of 2026-09-23, satisfies boltz-community's own
94
+ # `torch>=2.2` pyproject constraint and its README's documented "PyTorch 2.6+"
95
+ # compatibility statement. cp311 wheel to match this entry's `python: "3.11"`.
96
+ # No credible MPS-blocking regression found for 2.14.0 on shipping macOS
97
+ # versions (see models/boltz/README.md for research notes).
98
+ - name: torch
99
+ version: "2.14.0"
100
+ hashes:
101
+ - ae530ddd3f3b94248b77f1fd3313c4f3405bd0d17283d505b81574816767133a
102
+ postInstall:
103
+ - boltz-fix-macos-libomp
104
+ # Boltz-2's own CLI (both distributions, identical logic) lazily downloads these
105
+ # from HuggingFace's *mutable* `resolve/main/...` URLs on first prediction — that
106
+ # both defers download past install time and has no fixed content to verify
107
+ # against. Since upstream provides no immutable/checksum-pinned download itself,
108
+ # these assets are instead pinned here to an exact HuggingFace repo commit
109
+ # (boltz-community/boltz-2 @ 6fdef46d763fee7fbb83ca5501ccceff43b85607) with sha256
110
+ # digests read from that commit's `X-Linked-ETag` (HF's convention for an LFS
111
+ # file's sha256) and independently confirmed by hashing a real download of each
112
+ # file byte-for-byte. Materialized by the existing checksum-verified asset
113
+ # pipeline into `assets/boltz-cache/`, which is what the adapter passes as
114
+ # `--cache` — so by the time a prediction runs, Boltz's own downloader finds
115
+ # every file already present and verified, and performs no network access.
116
+ assets:
117
+ - id: boltz2-ccd
118
+ url: https://huggingface.co/boltz-community/boltz-2/resolve/6fdef46d763fee7fbb83ca5501ccceff43b85607/mols.tar
119
+ source: https://huggingface.co/boltz-community/boltz-2
120
+ sha256: 39e076d96dbec6b4e86982bbda16f3a53a2a60c9bdc17828d88f6f9a0c7d1fd7
121
+ sizeBytes: 1855662080
122
+ archive: tar
123
+ target: boltz-cache
124
+ - id: boltz2-conf-checkpoint
125
+ url: https://huggingface.co/boltz-community/boltz-2/resolve/6fdef46d763fee7fbb83ca5501ccceff43b85607/boltz2_conf.ckpt
126
+ source: https://huggingface.co/boltz-community/boltz-2
127
+ sha256: 090e82ac8c92f5e943fa1b39e7410a44027bea7243c0bbb3caa67a77fc1428e1
128
+ sizeBytes: 2286561469
129
+ target: boltz-cache/boltz2_conf.ckpt
130
+ - id: boltz2-affinity-checkpoint
131
+ url: https://huggingface.co/boltz-community/boltz-2/resolve/6fdef46d763fee7fbb83ca5501ccceff43b85607/boltz2_aff.ckpt
132
+ source: https://huggingface.co/boltz-community/boltz-2
133
+ sha256: dcc5cd3722b1c9eaa34267e4ae32f55cbbf1963f4c19319381ccfa30fdd2ca9e
134
+ sizeBytes: 2062139170
135
+ target: boltz-cache/boltz2_aff.ckpt
136
+ hardware:
137
+ platforms:
138
+ - darwin-arm64
139
+ - linux-x64
140
+ input:
141
+ formats:
142
+ - .yaml
143
+ - .yml
144
+ - .fasta
145
+ required: true
146
+ outputs:
147
+ - id: structures
148
+ glob: "boltz_results_*/predictions/*/*.cif"
149
+ required: true
150
+ - id: confidence
151
+ glob: "boltz_results_*/predictions/*/confidence_*.json"
152
+ required: true
153
+ - id: affinity
154
+ glob: "boltz_results_*/predictions/*/affinity_*.json"
155
+ required: false