feat: add protpardelle#274
Conversation
Wrap sequence-conditioned all-atom Protpardelle-1c models (task "ai-allatom", e.g. cc89.yaml) under the StructureModelWrapper protocol. featurize() derives sequence conditioning (aatype/seq_mask/residue_index/ chain_index/atom37 mask) from an Atomworks structure's chain_info, and step() runs the full reverse-diffusion trajectory internally via Protpardelle.sample(gt_aatype=...), returning [batch, atoms, 3] coords. - Register PROTPARDELLE_AVAILABLE / require_protpardelle / check_protpardelle_available (catching OSError from protpardelle.env). - Add StructurePredictor.PROTPARDELLE. - Tests build a small random ai-allatom model (no weights needed); cover sequence extraction, featurization shapes, prior init, protocol conformance, and an end-to-end short-trajectory sampling smoke test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sn't just give us unguided samples
## Summary Completes the Protpardelle debugging work from ENG-75 / #257 on top of `mdc/add-protpardelle`. This PR intentionally targets `mdc/add-protpardelle` so the review contains only the final Protpardelle fixes, not the whole in-progress integration branch. Key changes: - Restores the EDM sampler/model-wrapper call shape to the protocol-compatible `step(noisy_state, t_hat, features=features)` after removing the temporary extra `eps` argument. - Implements Protpardelle step-time noise handling via `_expand_noise_level()`, broadcasting scalar or per-batch EDM timesteps to Protpardelle's expected `B x L` tensor. - Moves Protpardelle step inputs/conditioning tensors onto the wrapper device before the model forward pass. - Keeps `prot_lens_per_chain` on CPU when calling Protpardelle's sampling helper to avoid CPU/GPU device mismatch in helper-created residue indices. - Maps Atomworks selenomethionine atom name `SE` into Protpardelle atom37's methionine sulfur slot `SD`. - Fixes `ProtpardelleConditioning` immutability so dataclass construction can complete before selected conditioning fields become frozen. - Allows packaged Protpardelle config/data under `src/sampleworks/data/**` through `.actlignore` so ACTL sync includes the runtime YAML config. - Re-enables the slow `step()` behavior test and adds coverage for the MSE selenium atom mapping. ## Validation Local static checks: - `uvx ty check src/sampleworks/models/protpardelle/wrapper.py src/sampleworks/core/samplers/edm.py tests/models/protpardelle/test_protpardelle_wrapper.py` - `uvx ruff check src/sampleworks/models/protpardelle/wrapper.py src/sampleworks/core/samplers/edm.py tests/models/protpardelle/test_protpardelle_wrapper.py` Remote ACTL / Protpardelle environment checks: - `pixi run -e protpardelle-dev pytest tests/models/protpardelle -m "not slow"` - `27 passed, 1 deselected` - `pixi run -e protpardelle-dev pytest tests/models/protpardelle/test_protpardelle_wrapper.py::TestStep::test_step_returns_coords` - `1 passed` - Exact reduced Protpardelle guidance smoke command from #257 using the shared checkpoint and 1VME inputs - completed successfully with `Guidance run successfully!` - wrote results to `output/protpardelle-smoke-guided/` Non-blocking warnings observed during the smoke run were expected environment warnings for unavailable optional model/tool paths and missing mirror environment variables; they did not prevent the Protpardelle run from completing. Refs #257. Co-authored-by: xraymemory <me.anzuoni@gmail.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Protpardelle support across configuration, wrapper logic, guidance wiring, availability checks, fixtures, and tests. Also updates Pixi metadata, bundled data handling, and runner-test inputs. ChangesProtpardelle wrapper and guidance integration
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
tests/models/protpardelle/test_protpardelle_wrapper.py (1)
147-160: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep these assertions at the public wrapper boundary.
These tests reach into
_build_sampling_kwargs()and_convert_to_atom37()directly, which makes the suite brittle to internal refactors that preservefeaturize(),initialize_from_prior(), andstep()behavior. Either move the coverage to public API outcomes or promote the helpers if they are intended contract. As per coding guidelines,tests/**/*.py: "Write black-box tests that verify behavior, not implementation."Also applies to: 287-320
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/models/protpardelle/test_protpardelle_wrapper.py` around lines 147 - 160, The tests are asserting private helper behavior via ProtpardelleWrapper._build_sampling_kwargs() and _convert_to_atom37(), which makes them brittle to internal refactors. Move this coverage to black-box assertions around the public wrapper methods featurize(), initialize_from_prior(), and step(), or explicitly promote those helpers if they are meant to be part of the contract. Keep the checks focused on observable outputs and behavior at the wrapper boundary rather than internal implementation details.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pyproject.toml`:
- Around line 139-141: The pyproject.toml currently defines the
dependency-overrides table twice under tool.pixi.pypi-options, which breaks TOML
parsing. Consolidate the duplicate [tool.pixi.pypi-options.dependency-overrides]
blocks into a single table, and keep the pandas and gemmi pins together there
only if they are intended to apply globally.
In `@src/sampleworks/models/protpardelle/wrapper.py`:
- Around line 707-709: The self-conditioning cached in
features.conditioning.x_self_conditioning is being stored with its autograd
history intact, which can carry the prior graph into later sampling steps.
Update the assignment in step() to cache a detached version of x_self_cond
before writing it to features.conditioning.x_self_conditioning, so repeated
iterations do not retain gradients across steps.
In `@src/sampleworks/utils/guidance_script_utils.py`:
- Around line 223-230: In the Protpardelle branch of guidance_script_utils, the
config_path is still built from a hardcoded src/sampleworks/data path, which
breaks when the package is installed and run from outside the repo. Update the
ProtpardelleWrapper config_path handling to load cc89_epoch415.yaml as a bundled
package resource using the package’s resource-loading APIs instead of resolving
a filesystem path relative to the current working directory. Keep the change
localized to the model-loading logic around StructurePredictor.PROTPARDELLE and
ProtpardelleWrapper.
- Around line 63-67: The Protpardelle fallback in the import block for
ProtpardelleWrapper is too narrow and can still crash non-Protpardelle runs.
Update the try/except around the ProtpardelleWrapper import to catch the
additional failure modes noted in imports.py, not just ImportError, and keep the
existing fallback assignment/logging. Also make the bundled YAML path in the
guidance script resolution independent of the current checkout by resolving it
from the package/module location instead of using a repo-relative path.
In `@src/sampleworks/utils/imports.py`:
- Around line 265-270: The install hint in require_any_model() is stale because
the availability check now includes PROTPARDELLE_AVAILABLE, but the
default_message still only mentions Boltz, Protenix, and RF3. Update the
default_message text in src/sampleworks/utils/imports.py to include Protpardelle
alongside the other supported model options so the decorator’s remediation
guidance matches the current logic.
- Around line 187-230: The bare decorator usage of require_protpardelle is
broken because the function object is being passed into the message parameter,
so the wrapper is not applied. Update require_protpardelle in imports.py to
support both `@require_protpardelle` and `@require_protpardelle`("...") by detecting
when the first argument is a callable vs a custom message, or remove the bare
form from the examples/docstring so only the parenthesized usage is advertised.
In `@tests/models/protpardelle/conftest.py`:
- Around line 17-21: The `os.environ.setdefault(...)` usage is eagerly creating
a temp directory via `tempfile.mkdtemp(...)` even when
`PROTPARDELLE_MODEL_PARAMS` is already set, so update the setup in `conftest.py`
to only call `mkdtemp` when the env var is missing. Apply the same lazy pattern
in `test_protpardelle_wrapper.py` where the same `setdefault`/`mkdtemp` usage
appears, using the existing `PROTPARDELLE_MODEL_PARAMS` guard to avoid
allocating untracked temp dirs on import.
In `@tests/runs/test_runner.py`:
- Around line 29-30: The argv assertion in the test is not hermetic because
runner._build_argv() can switch to a direct Python executable when a baked pixi
env is detected. Update the test setup for the affected assertion in
test_runner.py to force the pixi code path before checking the ["pixi", "run",
"-e", "rf3", "python"] prefix, using the existing test fixture/monkeypatch
around runner._build_argv() so the environment detection cannot change the
expected argv.
---
Nitpick comments:
In `@tests/models/protpardelle/test_protpardelle_wrapper.py`:
- Around line 147-160: The tests are asserting private helper behavior via
ProtpardelleWrapper._build_sampling_kwargs() and _convert_to_atom37(), which
makes them brittle to internal refactors. Move this coverage to black-box
assertions around the public wrapper methods featurize(),
initialize_from_prior(), and step(), or explicitly promote those helpers if they
are meant to be part of the contract. Keep the checks focused on observable
outputs and behavior at the wrapper boundary rather than internal implementation
details.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8a25473f-58ce-43f8-b062-7b353055864f
⛔ Files ignored due to path filters (1)
pixi.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
.actlignorepyproject.tomlsrc/sampleworks/cli/guidance.pysrc/sampleworks/core/samplers/edm.pysrc/sampleworks/data/cc89_epoch415.yamlsrc/sampleworks/models/protpardelle/__init__.pysrc/sampleworks/models/protpardelle/wrapper.pysrc/sampleworks/utils/guidance_constants.pysrc/sampleworks/utils/guidance_script_arguments.pysrc/sampleworks/utils/guidance_script_utils.pysrc/sampleworks/utils/imports.pytests/models/protpardelle/__init__.pytests/models/protpardelle/conftest.pytests/models/protpardelle/test_protpardelle_wrapper.pytests/runs/test_runner.py
## Summary - add Protpardelle-specific CLI flags for config path and sampling controls - forward config path into ProtpardelleWrapper construction - forward sampling options into annotate_structure_for_protpardelle - keep guidance --help lightweight by lazy-loading runtime dependencies Fixes #258 ## Validation - uvx ty check src/sampleworks/utils/guidance_script_arguments.py src/sampleworks/utils/guidance_script_utils.py src/sampleworks/cli/guidance.py tests/cli/test_guidance_cli.py tests/models/protpardelle/test_protpardelle_wrapper.py - uvx ruff check src/sampleworks/utils/guidance_script_arguments.py src/sampleworks/utils/guidance_script_utils.py src/sampleworks/cli/guidance.py tests/cli/test_guidance_cli.py tests/models/protpardelle/test_protpardelle_wrapper.py - PYTHONPATH=src uvx --python 3.12 --with pytest pytest --noconftest tests/cli/test_guidance_cli.py -q - PYTHONPATH=src uvx --python 3.12 --with pytest pytest --noconftest tests/models/protpardelle/test_protpardelle_wrapper.py -q (skipped locally: Protpardelle not installed) --------- Co-authored-by: Marcus D. Collins <marcus.collins@astera.org> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/sampleworks/core/samplers/edm.py (1)
416-428: 🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift
model_wrapper.step()now breaks non-Protpardelle wrappers
src/sampleworks/core/samplers/edm.py:425-428passesepsas a third positional argument, butBoltz,Protenix, andRF3still definestep(x_t, t, *, features=...). That will raiseTypeErroron those sampling paths. Update the protocol and wrapper signatures together, or gate this call to the Protpardelle-only implementation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/sampleworks/core/samplers/edm.py` around lines 416 - 428, The `model_wrapper.step()` call in the EDM sampler is now passing `eps` positionally, which breaks wrappers like `Boltz`, `Protenix`, and `RF3` that still only accept `step(x_t, t, *, features=...)`. Fix this by updating the shared protocol and the wrapper implementations together so their `step` signatures match the new call, or by restricting the extra `eps` argument to the Protpardelle-specific path. Check the `model_wrapper.step()` invocation in the EDM sampler and the corresponding `step` methods on the wrapper classes to keep the interface consistent.
🧹 Nitpick comments (1)
src/sampleworks/cli/guidance.py (1)
13-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRedundant local
loggerimport duplicates the new top-level import.A top-level
from loguru import loggeris already added at the module level (lines 6-7), making this local re-import insidemain()dead code. Lazy-loadingget_model_and_device/run_guidanceto keep--helplight makes sense, butloguruis not a heavy dependency, so duplicating the import adds noise without benefit.♻️ Proposed fix
def main(argv: list[str] | None = None) -> int: config = GuidanceConfig.from_cli(argv) - from loguru import logger - from sampleworks.utils.guidance_script_utils import get_model_and_device, run_guidance logger.info(f"Running guidance with config: {config}")As per coding guidelines, "No dead code and no compatibility shims for hypothetical users."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/sampleworks/cli/guidance.py` around lines 13 - 16, The local re-import of logger inside main() is redundant because logger is already imported at module scope. Remove the inner from loguru import logger from main() and keep the lazy import only for get_model_and_device and run_guidance so guidance.py uses the existing top-level logger symbol consistently.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/sampleworks/core/samplers/edm.py`:
- Around line 416-428: The `model_wrapper.step()` call in the EDM sampler is now
passing `eps` positionally, which breaks wrappers like `Boltz`, `Protenix`, and
`RF3` that still only accept `step(x_t, t, *, features=...)`. Fix this by
updating the shared protocol and the wrapper implementations together so their
`step` signatures match the new call, or by restricting the extra `eps` argument
to the Protpardelle-specific path. Check the `model_wrapper.step()` invocation
in the EDM sampler and the corresponding `step` methods on the wrapper classes
to keep the interface consistent.
---
Nitpick comments:
In `@src/sampleworks/cli/guidance.py`:
- Around line 13-16: The local re-import of logger inside main() is redundant
because logger is already imported at module scope. Remove the inner from loguru
import logger from main() and keep the lazy import only for get_model_and_device
and run_guidance so guidance.py uses the existing top-level logger symbol
consistently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f581f2ae-9bd1-4cb2-9dbc-72bd9be16919
📒 Files selected for processing (7)
src/sampleworks/cli/guidance.pysrc/sampleworks/core/samplers/edm.pysrc/sampleworks/models/protpardelle/wrapper.pysrc/sampleworks/utils/guidance_script_arguments.pysrc/sampleworks/utils/guidance_script_utils.pytests/cli/test_guidance_cli.pytests/models/protpardelle/test_protpardelle_wrapper.py
🚧 Files skipped from review as they are similar to previous changes (4)
- src/sampleworks/utils/guidance_script_arguments.py
- src/sampleworks/utils/guidance_script_utils.py
- tests/models/protpardelle/test_protpardelle_wrapper.py
- src/sampleworks/models/protpardelle/wrapper.py
533d6be to
6f4451d
Compare
…ardelle temp dirs (#297) ## Summary Two P1 quick wins that live on this branch (both flagged by CodeRabbit on #274). ### #285 — `require_*` decorators don't support bare `@decorator` usage The `require_protpardelle` factory (and its siblings `require_boltz` / `require_protenix` / `require_rf3` / `require_any_model`) only worked as `@require_x("msg")`. Used bare (`@require_x`), the decorated function was passed in as `message`, so the function was never wrapped/guarded — silently unprotected. Added a `callable(message)` check that re-dispatches through the factory, so **all three** forms now work: ```python @require_protpardelle # bare @require_protpardelle() # empty parens @require_protpardelle("custom") # with message ``` The docstrings' bare examples are now accurate. Every existing call site uses `@require_x()`, so no behavior change there. ### #286 — temp dirs leaked in protpardelle tests `tests/models/protpardelle/conftest.py` and `test_protpardelle_wrapper.py` set the model-params dir with `os.environ.setdefault("PROTPARDELLE_MODEL_PARAMS", tempfile.mkdtemp(...))`. `setdefault` evaluates its default eagerly, so `mkdtemp()` ran — and leaked a dir — on **every** session even when the var was already set (e.g. real weights configured, or the other module already set it). Now guard on the var being unset, then register `atexit.register(shutil.rmtree, ..., ignore_errors=True)` so the throwaway dir is removed at process exit. Whichever module runs first owns creation+cleanup; the other no-ops. Targets `mdc/add-protpardelle` since the affected code isn't on `main` yet. Fixes #285. Fixes #286. Co-authored-by: xraymemory <me.anzuoni@gmail.com>
|
commit history got messed up. Closing this and re-opening on mdc/protpardelle |
## Summary Fixes #283 and unbreaks CI on #274 (`mdc/add-protpardelle`). The EDM sampler called `model_wrapper.step(noisy_state, t_hat, eps, features=features)`, passing a **third positional `eps`** that is not part of the `FlowModelWrapper.step(x_t, t, *, features=None)` protocol. Because boltz/protenix/rf3 (and the test mocks) implement the protocol exactly — no extra positional, no `**kwargs` — every sampler-path test failed with: ``` TypeError: <Wrapper>.step() takes 3 positional arguments but 4 positional arguments (and 1 keyword-only argument) were given src/sampleworks/core/samplers/edm.py:428 ``` That single call site accounts for **all 164 failing tests** across the `boltz-dev` / `protenix-dev` / `rf3-dev` jobs (110 `MismatchCaseWrapper` + 54 `MockFlowModelWrapper`). The extra argument was only ever absorbed by Protpardelle's `step()`, where it landed in a `sigma_float` parameter that is **never referenced** in the method body — so removing it changes no behavior. This matches @k-chrispens's note on #283 (already addressed by #267; `eps` is the noise tensor, not a noise level) and restores the resolution that was lost when the branch was force-updated. ## Changes **Protocol reconciliation (#283)** - `core/samplers/edm.py`: drop the extra `eps` positional from the `model_wrapper.step()` call. `eps` is still computed locally and used to build the noisy state and the working-frame guidance math — only the (dead) hand-off to the wrapper is removed. Deleted the temporary "I need to modify the Protocol itself" TODO. - `models/protpardelle/wrapper.py`: remove the unused `sigma_float` parameter so `step()` matches `FlowModelWrapper`. **Lint job (was failing, 8 errors)** - `cli/guidance.py`: remove unused top-level `from loguru import logger` (it's re-imported inside `main()`) — fixes F401 / F811 / I001. - `models/protpardelle/wrapper.py`: use the repo's existing leading-space single-axis jaxtyping convention (`Int[Tensor, " atoms"]`, as in `edm.py`/`step_scalers.py`) to avoid UP037 / F821. - `tests/runs/test_runner.py`: wrap an over-length inline comment (E501). ## Validation - `ruff 0.15.8 check` passes on all touched files locally (same version as CI). - Test suites can't run on this macOS checkout (pixi envs are linux-64); CI on this PR exercises them. Every failing test traced to the removed `edm.py:428` positional and the tests already call `step(x, t, features=...)`, so they should go green. Closes #283. Co-authored-by: xraymemory <me.anzuoni@gmail.com>
…ardelle temp dirs (#297) ## Summary Two P1 quick wins that live on this branch (both flagged by CodeRabbit on #274). ### #285 — `require_*` decorators don't support bare `@decorator` usage The `require_protpardelle` factory (and its siblings `require_boltz` / `require_protenix` / `require_rf3` / `require_any_model`) only worked as `@require_x("msg")`. Used bare (`@require_x`), the decorated function was passed in as `message`, so the function was never wrapped/guarded — silently unprotected. Added a `callable(message)` check that re-dispatches through the factory, so **all three** forms now work: ```python @require_protpardelle # bare @require_protpardelle() # empty parens @require_protpardelle("custom") # with message ``` The docstrings' bare examples are now accurate. Every existing call site uses `@require_x()`, so no behavior change there. ### #286 — temp dirs leaked in protpardelle tests `tests/models/protpardelle/conftest.py` and `test_protpardelle_wrapper.py` set the model-params dir with `os.environ.setdefault("PROTPARDELLE_MODEL_PARAMS", tempfile.mkdtemp(...))`. `setdefault` evaluates its default eagerly, so `mkdtemp()` ran — and leaked a dir — on **every** session even when the var was already set (e.g. real weights configured, or the other module already set it). Now guard on the var being unset, then register `atexit.register(shutil.rmtree, ..., ignore_errors=True)` so the throwaway dir is removed at process exit. Whichever module runs first owns creation+cleanup; the other no-ops. Targets `mdc/add-protpardelle` since the affected code isn't on `main` yet. Fixes #285. Fixes #286. Co-authored-by: xraymemory <me.anzuoni@gmail.com>
This is a working version of Protpardelle-1c in SampleWorks. It may still require parameter tuning and other updates--in particular it isn't clear how to use self-conditioning. However we can generate structures with this version and so I'm making this PR as a milestone.
Only a couple significant changes have been made outside the Protpardelle wrapper code itself. One is to make a features class that is unfrozen, so that we can pass self-conditioning input forward to the next Euler step during sampling. Another is that we define different sampling parameters for Protpardelle when instantiating the sampler. There is no CLI control for either change in this PR.
Summary by CodeRabbit