@moldesk/adapters 0.0.0-stage → 0.0.3

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bindcraft2/index.d.ts +2 -0
  3. package/dist/bindcraft2/index.js +348 -0
  4. package/dist/bindcraft2/index.test.d.ts +1 -0
  5. package/dist/bindcraft2/index.test.js +232 -0
  6. package/dist/boltz/index.d.ts +2 -0
  7. package/dist/boltz/index.js +285 -0
  8. package/dist/boltz/index.test.d.ts +1 -0
  9. package/dist/boltz/index.test.js +368 -0
  10. package/dist/catalog.d.ts +6 -0
  11. package/dist/catalog.js +37 -0
  12. package/dist/catalog.test.d.ts +1 -0
  13. package/dist/catalog.test.js +33 -0
  14. package/dist/diffdock/index.d.ts +2 -0
  15. package/dist/diffdock/index.js +470 -0
  16. package/dist/diffdock/index.test.d.ts +1 -0
  17. package/dist/diffdock/index.test.js +595 -0
  18. package/dist/index.d.ts +121 -0
  19. package/dist/index.js +2 -0
  20. package/dist/ligandmpnn/index.d.ts +2 -0
  21. package/dist/ligandmpnn/index.js +80 -0
  22. package/dist/ligandmpnn/index.test.d.ts +1 -0
  23. package/dist/ligandmpnn/index.test.js +85 -0
  24. package/dist/opendde/index.d.ts +2 -0
  25. package/dist/opendde/index.js +551 -0
  26. package/dist/opendde/index.test.d.ts +1 -0
  27. package/dist/opendde/index.test.js +577 -0
  28. package/dist/outputs.d.ts +8 -0
  29. package/dist/outputs.js +79 -0
  30. package/dist/outputs.test.d.ts +1 -0
  31. package/dist/outputs.test.js +69 -0
  32. package/dist/params.d.ts +13 -0
  33. package/dist/params.js +69 -0
  34. package/dist/params.test.d.ts +1 -0
  35. package/dist/params.test.js +41 -0
  36. package/dist/planned.d.ts +1 -0
  37. package/dist/planned.js +9 -0
  38. package/dist/proteinmpnn/index.d.ts +2 -0
  39. package/dist/proteinmpnn/index.js +84 -0
  40. package/dist/proteinmpnn/index.test.d.ts +1 -0
  41. package/dist/proteinmpnn/index.test.js +101 -0
  42. package/package.json +32 -3
  43. package/README.md +0 -3
@@ -0,0 +1,121 @@
1
+ export interface InstallContext {
2
+ manifestName: string;
3
+ modelDir: string;
4
+ assetsDir: string;
5
+ runner?: import("@moldesk/runtime").InstallCommandRunner;
6
+ /** The host platform the runtime entry being installed/verified was resolved
7
+ * for. Populated by the core installer from its own platform-selection result
8
+ * — never guessed from `process.platform` by the adapter — so `verifyInstallation`
9
+ * can pick the right probe (e.g. CUDA vs MPS) even before `installation.json`
10
+ * exists (during the pre-promotion staging-directory verify). */
11
+ platform?: import("@moldesk/registry").PlatformId;
12
+ }
13
+ export interface RunContext {
14
+ manifestName: string;
15
+ /** Absolute path to the copied input inside the run directory, never the user's original path. */
16
+ inputPath: string;
17
+ /** The run directory's `output/` subdir. `collectOutputs` must only look here. */
18
+ outputDir: string;
19
+ modelDir: string;
20
+ assetsDir: string;
21
+ /** Normalized, validated, defaulted params (see `ParamDescriptor`/`validateParams`). */
22
+ params: Record<string, unknown>;
23
+ /** Resolved runtime executable (e.g. the managed venv's `python`) for this run. */
24
+ runtimeExecutable: string;
25
+ /** The host platform the installed runtime was resolved for, when determinable.
26
+ * Lets an adapter's `command()` cross-validate a param (e.g. accelerator) against
27
+ * the *installed* runtime rather than guessing from `process.platform`. */
28
+ platform?: import("@moldesk/registry").PlatformId;
29
+ /** Staged companion inputs (see `resolveCompanionInputs`), keyed by the same
30
+ * `id` the adapter declared. Populated only when `resolveCompanionInputs`
31
+ * returned entries for this run's job file; absent for single-file models. */
32
+ companionInputs?: Array<{
33
+ id: string;
34
+ path: string;
35
+ }>;
36
+ /** Injected for testability when `collectOutputs` post-processes outputs with
37
+ * the installed runtime (e.g. OpenDDE's mmCIF → PDB export); defaults to
38
+ * `@moldesk/runtime`'s `runCommand`. */
39
+ runner?: import("@moldesk/runtime").InstallCommandRunner;
40
+ }
41
+ /**
42
+ * Minimal, structural view of an `InstalledModel` record (from `@moldesk/core`)
43
+ * that `resolveParamDefaults` needs. Defined locally rather than importing
44
+ * `InstalledModel` from `@moldesk/core` because `@moldesk/core` depends on
45
+ * `@moldesk/adapters` — importing the other way would be a package cycle.
46
+ */
47
+ export interface ResolveParamDefaultsInstalledRuntime {
48
+ kind: string;
49
+ python?: {
50
+ platform?: import("@moldesk/registry").PlatformId;
51
+ platformSelected?: boolean;
52
+ };
53
+ }
54
+ export interface ResolveParamDefaultsContext {
55
+ manifestName: string;
56
+ installed: {
57
+ runtime: ResolveParamDefaultsInstalledRuntime;
58
+ };
59
+ }
60
+ export interface ParamDescriptor {
61
+ name: string;
62
+ type: "string" | "number" | "boolean";
63
+ required?: boolean;
64
+ default?: string | number | boolean;
65
+ min?: number;
66
+ max?: number;
67
+ enum?: Array<string | number>;
68
+ description?: string;
69
+ }
70
+ export interface InstallPlanStep {
71
+ readonly id: string;
72
+ readonly description: string;
73
+ }
74
+ export interface InstallPlan {
75
+ readonly steps: readonly InstallPlanStep[];
76
+ }
77
+ export interface CollectedOutput {
78
+ id: string;
79
+ path: string;
80
+ }
81
+ export interface VerificationResult {
82
+ passed: boolean;
83
+ output?: string;
84
+ }
85
+ export interface ModelAdapterDefinition {
86
+ readonly modelName: string;
87
+ /** Params this model accepts via `--param key=value`; empty/omitted if none. */
88
+ readonly params?: ParamDescriptor[];
89
+ validateInput(inputPath: string): Promise<void>;
90
+ installPlan(context: InstallContext): Promise<InstallPlan>;
91
+ command(context: RunContext): Promise<import("@moldesk/registry").CommandSpec>;
92
+ collectOutputs(context: RunContext): Promise<CollectedOutput[]>;
93
+ verifyInstallation(context: InstallContext): Promise<VerificationResult>;
94
+ /**
95
+ * Optional hook for params whose correct default depends on which platform's
96
+ * runtime entry was actually installed (e.g. Boltz's `accelerator`: `mps` on a
97
+ * Darwin/boltz-community install, `gpu` on a Linux/CUDA install) rather than a
98
+ * single static `ParamDescriptor.default`. When present, `runModel` calls this
99
+ * *before* `validateParams`, merging the result in as additional defaults — a
100
+ * key here only fills in when the caller didn't explicitly supply `--param
101
+ * <key>=...`, and a declared `ParamDescriptor.default` only applies when this
102
+ * hook doesn't supply that key. This makes the resolved value visible in
103
+ * `run.json`'s `parameters.effective` up front, not only deep inside `command()`.
104
+ */
105
+ resolveParamDefaults?(context: ResolveParamDefaultsContext): Record<string, unknown>;
106
+ /**
107
+ * Optional hook for models whose job file references companion files that must
108
+ * be safely staged alongside the primary input (e.g. DiffDock-L's job JSON
109
+ * referencing a separate protein PDB and ligand file). Given the *original*
110
+ * (pre-copy) resolved job file path, returns the companion references to
111
+ * stage; `run.ts` then validates and copies each one via
112
+ * `@moldesk/runtime`'s `stageCompanionInputs` and passes the staged paths back
113
+ * as `RunContext.companionInputs`. Absent means unchanged single-file
114
+ * behavior — no companion staging occurs. All job-format-specific parsing
115
+ * (what a reference means, how it's found) stays here in the adapter; `core`/
116
+ * `runtime` only provide generic, validated staging.
117
+ */
118
+ resolveCompanionInputs?(inputPath: string): Promise<import("@moldesk/runtime").CompanionInputRef[]>;
119
+ }
120
+ export { adapterCatalog, getAdapter, hasAdapter, validateAdapterCatalog } from "./catalog.js";
121
+ export { validateParams, type ParamValidationResult } from "./params.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { adapterCatalog, getAdapter, hasAdapter, validateAdapterCatalog } from "./catalog.js";
2
+ export { validateParams } from "./params.js";
@@ -0,0 +1,2 @@
1
+ import type { ModelAdapterDefinition } from "../index.js";
2
+ export declare const ligandmpnnAdapter: ModelAdapterDefinition;
@@ -0,0 +1,80 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { MoldeskError } from "@moldesk/registry";
4
+ import { runCommand } from "@moldesk/runtime";
5
+ import { collectGlobOutputs } from "../outputs.js";
6
+ const CHECKPOINT_TARGET = path.join("model_params", "ligandmpnn_v_32_010_25.pt");
7
+ /** run.py writes `<out_folder>/seqs/<pdb_name>.fa` (verified against the pinned source revision). */
8
+ const OUTPUTS = [{ id: "sequences", glob: "seqs/*.fa", required: true }];
9
+ const PARAMS = [
10
+ { name: "temperature", type: "number", default: 0.1, min: 0, description: "Temperature to sample sequences" },
11
+ { name: "seed", type: "number", default: 0, description: "Random seed for torch, numpy, and python random" },
12
+ { name: "batch_size", type: "number", default: 1, min: 1, description: "Number of sequences to generate per pass" },
13
+ { name: "number_of_batches", type: "number", default: 1, min: 1, description: "Number of times to design sequences using the batch size" },
14
+ ];
15
+ export const ligandmpnnAdapter = {
16
+ modelName: "ligandmpnn",
17
+ params: PARAMS,
18
+ async validateInput(inputPath) {
19
+ if (!fs.existsSync(inputPath)) {
20
+ throw new MoldeskError({
21
+ code: "INVALID_RUN_INPUT",
22
+ message: `Input file not found: ${inputPath}.`,
23
+ remediation: "Provide a path to an existing .pdb file.",
24
+ });
25
+ }
26
+ if (path.extname(inputPath).toLowerCase() !== ".pdb") {
27
+ throw new MoldeskError({
28
+ code: "INVALID_RUN_INPUT",
29
+ message: `LigandMPNN requires a .pdb input file, got "${path.extname(inputPath) || "(no extension)"}".`,
30
+ remediation: "Provide a .pdb structure file.",
31
+ });
32
+ }
33
+ },
34
+ async installPlan(_context) {
35
+ return {
36
+ steps: [
37
+ { id: "source", description: "Fetch the pinned LigandMPNN source revision" },
38
+ { id: "checkpoint", description: "Download and checksum the LigandMPNN checkpoint" },
39
+ { id: "python", description: "Create an isolated managed Python 3.11 environment" },
40
+ { id: "dependencies", description: "Install pinned Python dependencies" },
41
+ { id: "verify", description: "Verify source, checkpoint, and Python imports" },
42
+ ],
43
+ };
44
+ },
45
+ async command(context) {
46
+ const params = context.params;
47
+ const args = [
48
+ path.join(context.modelDir, "source", "run.py"),
49
+ "--pdb_path", context.inputPath,
50
+ "--out_folder", context.outputDir,
51
+ // run.py defaults to model_type=protein_mpnn; must be set explicitly for LigandMPNN.
52
+ "--model_type", "ligand_mpnn",
53
+ "--checkpoint_ligand_mpnn", path.join(context.assetsDir, CHECKPOINT_TARGET),
54
+ ];
55
+ if (params.temperature !== undefined)
56
+ args.push("--temperature", String(params.temperature));
57
+ if (params.seed !== undefined)
58
+ args.push("--seed", String(params.seed));
59
+ if (params.batch_size !== undefined)
60
+ args.push("--batch_size", String(params.batch_size));
61
+ if (params.number_of_batches !== undefined)
62
+ args.push("--number_of_batches", String(params.number_of_batches));
63
+ return { executable: context.runtimeExecutable, args };
64
+ },
65
+ async collectOutputs(context) {
66
+ return collectGlobOutputs(context.outputDir, OUTPUTS);
67
+ },
68
+ async verifyInstallation(context) {
69
+ const requiredFiles = [
70
+ path.join(context.modelDir, "source", "run.py"),
71
+ path.join(context.assetsDir, CHECKPOINT_TARGET),
72
+ ];
73
+ const missing = requiredFiles.filter((file) => !fs.existsSync(file));
74
+ if (missing.length > 0)
75
+ return { passed: false, output: `Missing ${missing.join(", ")}` };
76
+ const python = path.join(context.modelDir, ".venv", "bin", "python");
77
+ const result = await (context.runner ?? runCommand)(python, ["-c", "import numpy, prody, torch"], { timeoutMs: 120_000 });
78
+ return { passed: result.code === 0, output: (result.code === 0 ? result.stdout : result.stderr).trim() };
79
+ },
80
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,85 @@
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 { ligandmpnnAdapter } from "./index.js";
6
+ const dirs = [];
7
+ function tempDir() {
8
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "moldesk-ligandmpnn-test-"));
9
+ dirs.push(dir);
10
+ return dir;
11
+ }
12
+ afterEach(() => {
13
+ for (const dir of dirs.splice(0))
14
+ fs.rmSync(dir, { recursive: true, force: true });
15
+ });
16
+ describe("ligandmpnnAdapter.validateInput", () => {
17
+ it("accepts an existing .pdb file", async () => {
18
+ const dir = tempDir();
19
+ const input = path.join(dir, "structure.pdb");
20
+ fs.writeFileSync(input, "ATOM\n");
21
+ await expect(ligandmpnnAdapter.validateInput(input)).resolves.toBeUndefined();
22
+ });
23
+ it("rejects a missing file", async () => {
24
+ await expect(ligandmpnnAdapter.validateInput(path.join(tempDir(), "missing.pdb"))).rejects.toMatchObject({
25
+ code: "INVALID_RUN_INPUT",
26
+ });
27
+ });
28
+ it("rejects a non-.pdb extension", async () => {
29
+ const dir = tempDir();
30
+ const input = path.join(dir, "structure.cif");
31
+ fs.writeFileSync(input, "ATOM\n");
32
+ await expect(ligandmpnnAdapter.validateInput(input)).rejects.toMatchObject({ code: "INVALID_RUN_INPUT" });
33
+ });
34
+ });
35
+ describe("ligandmpnnAdapter.command", () => {
36
+ it("always sets --model_type ligand_mpnn and an absolute checkpoint path", async () => {
37
+ const spec = await ligandmpnnAdapter.command({
38
+ manifestName: "ligandmpnn",
39
+ inputPath: "/run/input/structure.pdb",
40
+ outputDir: "/run/output",
41
+ modelDir: "/models/ligandmpnn",
42
+ assetsDir: "/models/ligandmpnn/assets",
43
+ params: { temperature: 0.1, seed: 0, batch_size: 1, number_of_batches: 1 },
44
+ runtimeExecutable: "/models/ligandmpnn/.venv/bin/python",
45
+ });
46
+ expect(spec.args).toContain("--model_type");
47
+ expect(spec.args).toContain("ligand_mpnn");
48
+ expect(spec.args).toContain("--checkpoint_ligand_mpnn");
49
+ expect(spec.args).toContain(path.join("/models/ligandmpnn/assets", "model_params", "ligandmpnn_v_32_010_25.pt"));
50
+ expect(spec.args).toContain("--temperature");
51
+ expect(spec.args).toContain("0.1");
52
+ });
53
+ });
54
+ describe("ligandmpnnAdapter.collectOutputs", () => {
55
+ it("collects generated .fa files from the seqs/ subdirectory", async () => {
56
+ const dir = tempDir();
57
+ const outputDir = path.join(dir, "output");
58
+ fs.mkdirSync(path.join(outputDir, "seqs"), { recursive: true });
59
+ fs.writeFileSync(path.join(outputDir, "seqs", "structure.fa"), ">structure\nMKV\n");
60
+ const outputs = await ligandmpnnAdapter.collectOutputs({
61
+ manifestName: "ligandmpnn",
62
+ inputPath: path.join(dir, "input", "structure.pdb"),
63
+ outputDir,
64
+ modelDir: dir,
65
+ assetsDir: path.join(dir, "assets"),
66
+ params: {},
67
+ runtimeExecutable: "python",
68
+ });
69
+ expect(outputs).toEqual([{ id: "sequences", path: path.join(outputDir, "seqs", "structure.fa") }]);
70
+ });
71
+ it("throws MISSING_REQUIRED_OUTPUT when no .fa files were produced", async () => {
72
+ const dir = tempDir();
73
+ const outputDir = path.join(dir, "output");
74
+ fs.mkdirSync(outputDir, { recursive: true });
75
+ await expect(ligandmpnnAdapter.collectOutputs({
76
+ manifestName: "ligandmpnn",
77
+ inputPath: path.join(dir, "input", "structure.pdb"),
78
+ outputDir,
79
+ modelDir: dir,
80
+ assetsDir: path.join(dir, "assets"),
81
+ params: {},
82
+ runtimeExecutable: "python",
83
+ })).rejects.toMatchObject({ code: "MISSING_REQUIRED_OUTPUT" });
84
+ });
85
+ });
@@ -0,0 +1,2 @@
1
+ import type { ModelAdapterDefinition } from "../index.js";
2
+ export declare const openddeAdapter: ModelAdapterDefinition;