Skip to content

fix: propagate fields in composite ModelCard synthesis#29

Open
zubatyuk wants to merge 2 commits intoNVIDIA:mainfrom
zubatyuk:fix/composite-model-card-synthesis
Open

fix: propagate fields in composite ModelCard synthesis#29
zubatyuk wants to merge 2 commits intoNVIDIA:mainfrom
zubatyuk:fix/composite-model-card-synthesis

Conversation

@zubatyuk
Copy link
Copy Markdown
Collaborator

ALCHEMI Toolkit Pull Request

Description

ComposableModelWrapper._build_model_card() only passed 7 of 16 ModelCard boolean fields to the synthesized card. The remaining fields silently defaulted to False, making wrapper.input_data() and wrapper.model_card incorrect for composites that include charge-requiring sub-models like PME or Ewald.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Performance improvement
  • Documentation update
  • Refactoring (no functional changes)
  • CI/CD or infrastructure change

Related Issues

None.

Changes Made

  • Propagate needs_node_charges and needs_system_charges with any() aggregation in _build_model_card(), so the composite correctly reports input requirements from sub-models.
  • Propagate includes_dispersion and includes_long_range_electrostatics with any() aggregation, so the composite card reflects physics metadata that was already read for double-counting warnings but never written to the synthesized card.
  • Add a comment block explaining why supports_node/edge/graph_embeddings, supports_hessians, and supports_dipoles are intentionally kept False on composites.
  • Update example comment in examples/advanced/07_composable_model_composition.py to accurately describe the composite card as "aggregated sub-model requirements, additive capabilities, and metadata" rather than "union of sub-model capabilities."

Testing

  • Unit tests pass locally (make pytest)
  • Linting passes (make lint)
  • New tests added for new functionality meets coverage expectations?

New tests in test/models/test_composable.py::TestModelCardSynthesis:

  • test_needs_node_charges_is_any / test_needs_node_charges_false_when_none_need / test_needs_system_charges_is_any — verify needs_* aggregation.
  • test_input_data_includes_node_charges / test_input_data_includes_graph_charges — integration tests verifying wrapper.input_data() includes charge fields when a sub-model requires them.
  • test_includes_dispersion_propagated / test_includes_long_range_electrostatics_propagated — verify includes_* metadata propagation.
  • test_embedding_support_stays_false — negative test confirming embedding support is not propagated even when a child model supports it.
  • test_model_card_policy_covers_all_booleans — future-proofing test that maintains two explicit sets (_PROPAGATED and _INTENTIONALLY_FALSE) covering every ModelCard boolean field. If a new boolean is added to ModelCard, this test fails and forces an explicit composite aggregation decision.

Checklist

  • I have read and understand the Contributing Guidelines
  • I have updated the CHANGELOG.md
  • I have performed a self-review of my code
  • I have added docstrings to new functions/classes
  • I have updated the documentation (if applicable)

Additional Notes

The composite's forward() dispatches directly to child models, so the missing fields did not break the forward path itself. The bug affected any generic code that inspects the composite's model_card or calls input_data() to determine required batch fields.

Mock helpers in tests use model_copy(update={...}) instead of manually rebuilding partial ModelCard instances, avoiding accidental field omissions in test setup.

@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot bot commented Mar 25, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR fixes ComposableModelWrapper._build_model_card() to propagate four previously-dropped ModelCard boolean fields: needs_node_charges, needs_system_charges, includes_dispersion, and includes_long_range_electrostatics. The fix is correct — any() is the right aggregation for requirement/capability flags, the double-counting warnings for includes_* fields were already in place before the propagation assignment, and the intentionally-suppressed fields (supports_*_embeddings, supports_hessians, supports_dipoles) are clearly documented. Tests are thorough and the policy-completeness sentinel test is a solid safeguard against future regressions.

Important Files Changed

Filename Overview
nvalchemi/models/composable.py Propagates needs_node_charges, needs_system_charges, includes_dispersion, and includes_long_range_electrostatics with any() aggregation in _build_model_card(); adds comment block explaining why embedding/hessian/dipole fields stay False.
test/models/test_composable.py Adds four mock model subclasses and ten new test cases covering needs_* aggregation, input_data() integration, includes_* metadata propagation, negative embedding test, and a future-proofing policy-completeness test.
examples/advanced/07_composable_model_composition.py Minor comment correction: "union of sub-model capabilities" replaced with "aggregated sub-model requirements, additive capabilities, and metadata" to accurately describe the synthesized ModelCard.

Reviews (4): Last reviewed commit: "fix(test): harden ModelCard boolean-fiel..." | Re-trigger Greptile

Comment on lines +662 to +666
bool_fields = {
name
for name, info in ModelCard.model_fields.items()
if info.annotation is bool
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Fragile boolean-field detection via info.annotation is bool

FieldInfo.annotation for Annotated[bool, Field(...)] fields is currently unwrapped to the bare bool type by Pydantic v2, so info.annotation is bool works today. However, this is an undocumented internal detail. If Pydantic changes this behaviour in a future major version, or if a future contributor adds a ModelCard boolean field using a slightly different annotation style (e.g. bool | None), the set comprehension could silently produce an incorrect bool_fields, causing the completeness assertion to fail with a confusing message.

A more explicit and forward-compatible approach is to unwrap Annotated yourself:

Suggested change
bool_fields = {
name
for name, info in ModelCard.model_fields.items()
if info.annotation is bool
}
import typing
bool_fields = {
name
for name, info in ModelCard.model_fields.items()
if (
info.annotation is bool
or (
typing.get_origin(info.annotation) is typing.Annotated
and typing.get_args(info.annotation)[0] is bool
)
)
}

This makes the intent explicit and handles both plain bool and Annotated[bool, ...] fields without relying on Pydantic's internal unwrapping.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e1279a5.

@dallasfoster
Copy link
Copy Markdown
Collaborator

/ok to test d608b78

zubatyuk added 2 commits April 1, 2026 22:30
…elCard synthesis

Signed-off-by: Roman Zubatyuk <rzubatiuk@nvidia.com>
… types

The policy completeness test used `info.annotation is bool` to discover
ModelCard boolean fields. This relies on Pydantic v2 internally
unwrapping Annotated types, which is undocumented. Add an explicit
check for `Annotated[bool, ...]` so the test remains correct if a
future ModelCard field uses `Annotated[bool, Field(...)]`.

Signed-off-by: Roman Zubatyuk <rzubatiuk@nvidia.com>
@zubatyuk zubatyuk force-pushed the fix/composite-model-card-synthesis branch from d608b78 to c514feb Compare April 2, 2026 02:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants