Skip to content

feat(show): add serialization-tree via DeepEval.serialize#5316

Open
njzjz-bot wants to merge 21 commits into
deepmodeling:masterfrom
njzjz-bot:feat/deepeval-serialize
Open

feat(show): add serialization-tree via DeepEval.serialize#5316
njzjz-bot wants to merge 21 commits into
deepmodeling:masterfrom
njzjz-bot:feat/deepeval-serialize

Conversation

@njzjz-bot

@njzjz-bot njzjz-bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

@/tmp/pr-body-5185.md

Summary by CodeRabbit

  • New Features

    • Added a "serialization-tree" option to the show command to display a model's serialized structure as a human-readable tree.
    • Exposed a unified serialize() API across inference backends so tools can retrieve a consistent model export payload.
    • Show now logs the generated serialization tree when requested.
  • Tests

    • Added tests verifying backends produce the expected serialization payload and the show command emits the serialization-tree output.

Authored by OpenClaw (model: gpt-5.2)
@njzjz-bot

Copy link
Copy Markdown
Contributor Author

Implements #5185 (first step): add powered by a backend-unified wrapper.\n\nAuthored by OpenClaw (model: gpt-5.2)

@njzjz-bot

Copy link
Copy Markdown
Contributor Author

Implements #5185 (first step): add dp show ... serialization-tree powered by a backend-unified DeepEval.serialize() wrapper.

Authored by OpenClaw (model: gpt-5.2)

@njzjz-bot

Copy link
Copy Markdown
Contributor Author

(FYI) Previous comment got mangled by shell backticks; this one is the corrected text.\n\nAuthored by OpenClaw (model: gpt-5.2)

@dosubot dosubot Bot added the new feature label Mar 16, 2026
@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a "serialization-tree" CLI attribute, implements DeepEval.serialize() across backends (and persists model file/path where applicable), and updates the show entrypoint to call serialize(), Node.deserialize(...) the returned payload, and log the resulting serialization tree.

Changes

Cohort / File(s) Summary
CLI
deepmd/main.py
Added "serialization-tree" to the ATTRIBUTES choices for the show subcommand.
Show entrypoint
deepmd/entrypoints/show.py
When serialization-tree is requested: imports Node, calls model.serialize(), passes payload to Node.deserialize(...), and logs the serialization tree (executed after observed-type handling).
Core DeepEval API
deepmd/infer/deep_eval.py
Added abstract DeepEvalBackend.serialize() and high-level DeepEval.serialize(); DeepEval.__init__ now stores self.model_file.
Backend implementations
deepmd/dpmodel/infer/deep_eval.py, deepmd/jax/infer/deep_eval.py, deepmd/pd/infer/deep_eval.py, deepmd/pt/infer/deep_eval.py, deepmd/pt_expt/infer/deep_eval.py, deepmd/tf/infer/deep_eval.py, deepmd/pretrained/deep_eval.py
Added serialize() implementations or proxies per backend to return a serializable model-tree mapping; some backends now use/persist model file/path and validate payload shape.
Tests
source/tests/pt_expt/infer/test_deep_eval.py, source/tests/test_entrypoint_show_serialization_tree.py, source/tests/consistent/io/test_io.py
Added/updated tests asserting backend serialize() returns expected model payload, show(..., ATTRIBUTES=["serialization-tree"]) calls serialize() and Node.deserialize() and logs tree, and that deep_eval.serialize() matches model file payload.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Parser
    participant Show as Show Entrypoint
    participant DeepEval as DeepEval Interface
    participant Backend as Backend Implementation
    participant Node as Node (Serialization)

    CLI->>Show: invoke show with ATTRIBUTES includes "serialization-tree"
    Show->>DeepEval: call serialize()
    DeepEval->>Backend: resolve backend (may use model_file) and call serialize()
    Backend-->>DeepEval: return serialized dict (model payload)
    Show->>Node: Node.deserialize(serialized_payload)
    Node-->>Show: serialization tree string
    Show->>Show: log serialization tree
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • njzjz
  • iProzd
  • wanghan-iapcm
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: adding a serialization-tree attribute to the show command via the new DeepEval.serialize() method.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread deepmd/infer/deep_eval.py Fixed

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
deepmd/infer/deep_eval.py (1)

444-446: Cyclic import flagged by static analysis.

CodeQL reports a cyclic import starting from deepmd.pretrained.deep_eval. While placing the import inside the method body mitigates runtime issues (the import only occurs when the pretrained branch is taken), consider whether these utilities could be imported from a lower-level module to avoid the cycle entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/infer/deep_eval.py` around lines 444 - 446, The import of
parse_pretrained_alias inside deepmd.infer.deep_eval creates a cyclic dependency
with deepmd.pretrained.deep_eval; to fix it, extract parse_pretrained_alias (and
any small helper utilities it needs) into a lower-level module (e.g.,
deepmd.pretrained.utils or deepmd.utils.pretrained) and update both
deepmd.pretrained.deep_eval and deepmd.infer.deep_eval to import
parse_pretrained_alias from that new module; ensure the extracted function has
no imports back to deepmd.pretrained.deep_eval to break the cycle and run tests
to confirm no runtime regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deepmd/infer/deep_eval.py`:
- Around line 443-454: When resolving a pretrained alias you re-detect the
backend and immediately call serialize_hook on backend_cls (via
Backend.detect_backend_by_model and backend_cls().serialize_hook) without
checking that the backend supports the IO feature; add the same IO capability
check used in the regular path before calling serialize_hook: after determining
backend_cls = Backend.detect_backend_by_model(resolved), verify
Backend.feature(backend_cls, Backend.Feature.IO) (or equivalent feature-check
method used elsewhere) and raise the same NotImplementedError with the same
message if IO is not supported, otherwise call
backend_cls().serialize_hook(resolved).

---

Nitpick comments:
In `@deepmd/infer/deep_eval.py`:
- Around line 444-446: The import of parse_pretrained_alias inside
deepmd.infer.deep_eval creates a cyclic dependency with
deepmd.pretrained.deep_eval; to fix it, extract parse_pretrained_alias (and any
small helper utilities it needs) into a lower-level module (e.g.,
deepmd.pretrained.utils or deepmd.utils.pretrained) and update both
deepmd.pretrained.deep_eval and deepmd.infer.deep_eval to import
parse_pretrained_alias from that new module; ensure the extracted function has
no imports back to deepmd.pretrained.deep_eval to break the cycle and run tests
to confirm no runtime regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a8a6ae18-b1d4-465f-b5b3-2149b4fbd411

📥 Commits

Reviewing files that changed from the base of the PR and between 09345bf and b695aaa.

📒 Files selected for processing (3)
  • deepmd/entrypoints/show.py
  • deepmd/infer/deep_eval.py
  • deepmd/main.py

Comment thread deepmd/infer/deep_eval.py Outdated
@codecov

codecov Bot commented Mar 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.59184% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.06%. Comparing base (aae3485) to head (980c5fe).

Files with missing lines Patch % Lines
deepmd/jax/infer/deep_eval.py 66.66% 3 Missing ⚠️
deepmd/jax/utils/serialization.py 0.00% 3 Missing ⚠️
deepmd/pd/infer/deep_eval.py 33.33% 2 Missing ⚠️
deepmd/pretrained/deep_eval.py 50.00% 1 Missing ⚠️
deepmd/pt/infer/deep_eval.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5316      +/-   ##
==========================================
+ Coverage   82.19%   83.06%   +0.87%     
==========================================
  Files         899      739     -160     
  Lines      103578    77093   -26485     
  Branches     4434     1003    -3431     
==========================================
- Hits        85131    64040   -21091     
+ Misses      17055    12456    -4599     
+ Partials     1392      597     -795     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Switch DeepEval.serialize() to delegate to DeepEvalBackend.serialize(), and implement serialize() in each backend by calling the underlying model's serialize().

Also move Node import in dp show to module top-level.

Authored by OpenClaw (model: gpt-5.2)
@njzjz-bot

Copy link
Copy Markdown
Contributor Author

Refactor update (per feedback): switch from Backend.serialize_hook() wrapper to a backend-native DeepEvalBackend.serialize() API.

  • DeepEvalBackend.serialize() added as an abstract method
  • DeepEval.serialize() now only delegates to self.deep_eval.serialize()
  • Implemented serialize() in TF/PT/PD/DPModel/JAX/pt_expt DeepEval backends by directly calling the underlying model's serialize() (or equivalent TF Model init+serialize flow)
  • dp show ... serialization-tree: moved Node import to module top-level
  • pretrained: PretrainedDeepEvalBackend.serialize() delegates to resolved backend

Latest commit: 1694360

Authored by OpenClaw (model: gpt-5.2)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deepmd/pt_expt/infer/deep_eval.py`:
- Around line 668-673: serialize() currently returns the raw model_dict from
serialize_from_file(self.model_path) but callers (e.g., the CLI in show.py)
expect a wrapper with keys "backend", "model", "model_def_script", and
"@variables"; change serialize() in the class to call
serialize_from_file(self.model_path), then construct and return a dict like
{"backend": "<appropriate backend name or value from model_dict if available>",
"model": model_dict, "model_def_script": <script or None>, "@variables": <vars
dict or empty dict>} so the returned structure contains those keys; ensure you
pull any available values from model_dict to populate "backend",
"model_def_script", and "@variables" or set sensible defaults if missing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f7533fe2-e54e-4e12-ae64-e1712fa5c00b

📥 Commits

Reviewing files that changed from the base of the PR and between b695aaa and 1694360.

📒 Files selected for processing (9)
  • deepmd/dpmodel/infer/deep_eval.py
  • deepmd/entrypoints/show.py
  • deepmd/infer/deep_eval.py
  • deepmd/jax/infer/deep_eval.py
  • deepmd/pd/infer/deep_eval.py
  • deepmd/pretrained/deep_eval.py
  • deepmd/pt/infer/deep_eval.py
  • deepmd/pt_expt/infer/deep_eval.py
  • deepmd/tf/infer/deep_eval.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • deepmd/entrypoints/show.py

Comment thread deepmd/pt_expt/infer/deep_eval.py Outdated

Copilot AI left a comment

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.

Pull request overview

This PR adds a new serialization-tree option to the dp show command by introducing a backend-unified DeepEval.serialize() API and implementing it across supported inference backends.

Changes:

  • Added DeepEvalBackend.serialize() (and wrapper DeepEval.serialize()) to return a unified serialized-model dictionary.
  • Implemented backend-specific serialize() methods for TF / PyTorch / Paddle / DPModel / JAX / pt_expt (+ pretrained delegator).
  • Extended dp show CLI attribute choices and entrypoint logic to print a model serialization tree using Node.deserialize(...).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
deepmd/infer/deep_eval.py Adds serialize() to the low-level backend interface and exposes it on the high-level DeepEval wrapper.
deepmd/tf/infer/deep_eval.py Implements TF serialization via graph_def loading + Model.serialize(), including optional min_nbor_dist.
deepmd/pt/infer/deep_eval.py Implements PyTorch serialization via self.dp.model["Default"].serialize().
deepmd/pd/infer/deep_eval.py Implements Paddle serialization via self.dp.model["Default"].serialize().
deepmd/dpmodel/infer/deep_eval.py Implements DPModel serialization via self.dp.serialize().
deepmd/jax/infer/deep_eval.py Adds JAX serialization method and includes jax_version.
deepmd/pt_expt/infer/deep_eval.py Implements serialization by delegating to serialize_from_file(...).
deepmd/pretrained/deep_eval.py Delegates serialization to the resolved backend for pretrained aliases.
deepmd/entrypoints/show.py Adds serialization-tree printing using Node.deserialize(data["model"]).
deepmd/main.py Adds serialization-tree to the dp show CLI ATTRIBUTES choices list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread deepmd/jax/infer/deep_eval.py Outdated
Comment thread deepmd/pt/infer/deep_eval.py Outdated
njzjz-bot and others added 2 commits April 7, 2026 17:46
Route JAX DeepEval serialization through the existing file-based serializer so .hlo and .savedmodel models follow the supported path instead of calling unimplemented model-level serialize() methods.

Also add the missing pt_version field to the PyTorch backend serializer and wrap pt_expt serialization in the backend-unified payload expected by dp show serialization-tree.

Add a targeted pt_expt serialization contract test.

Authored by OpenClaw (model: gpt-5.4)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deepmd/jax/infer/deep_eval.py`:
- Around line 190-195: Update the serialize method in deep_eval.DeepEval (the
serialize(self) -> dict[str, Any] function that currently calls
serialize_from_file(self.model_path)) to explicitly document and guard against
TensorFlow SavedModel inputs: add a docstring note explaining that JAX backend
only supports serializing .jax/.hlo directories and that .savedmodel
(TensorFlow-wrapped) models are not supported, and add a pre-check that inspects
self.model_path (and/or the model wrapper type, e.g., TFModelWrapper if
accessible) to raise a clear ValueError with a descriptive message like
"serialize() not supported for .savedmodel / TFModelWrapper: JAX backend only
supports converting .jax/.hlo directories" before calling serialize_from_file.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5017e4a9-2d01-483b-a3ea-85d28ab8a405

📥 Commits

Reviewing files that changed from the base of the PR and between 1694360 and a8a80f4.

📒 Files selected for processing (4)
  • deepmd/jax/infer/deep_eval.py
  • deepmd/pt/infer/deep_eval.py
  • deepmd/pt_expt/infer/deep_eval.py
  • source/tests/pt_expt/infer/test_deep_eval.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • deepmd/pt_expt/infer/deep_eval.py

Comment thread deepmd/jax/infer/deep_eval.py Outdated
njzjz-bot and others added 3 commits April 8, 2026 03:00
Route dp show serialization-tree through backend file serialization hooks and pass only the serialized model payload into Node.deserialize(). This keeps Node focused on model structure instead of backend-specific envelope fields.

Add a focused unit test that locks the behavior at the entrypoint layer.

Authored by OpenClaw (model: gpt-5.4)
Make DeepEval.serialize() return only the model serialization tree instead of a backend metadata envelope. This lets callers such as dp show serialization-tree consume model structure directly without caring about backend-specific data wrappers.

Keep full backend data serialization in backend serialize hooks for IO and backend conversion paths.

Authored by OpenClaw (model: gpt-5.4)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
deepmd/jax/infer/deep_eval.py (1)

190-198: ⚠️ Potential issue | 🟠 Major

serialize() is incompatible with .savedmodel inputs currently accepted by this backend.

Line 195 calls serialize_from_file(self.model_path), but .savedmodel paths (accepted in __init__) are not supported by that serializer, so dp show ... serialization-tree will fail for those models.

Suggested guard for a clearer failure mode
 def serialize(self) -> dict[str, Any]:
     from deepmd.jax.utils.serialization import (
         serialize_from_file,
     )

+    if str(self.model_path).endswith(".savedmodel"):
+        raise NotImplementedError(
+            "serialize() is not supported for .savedmodel in JAX backend; "
+            "only JAX-native serialized models are supported."
+        )
     data = serialize_from_file(self.model_path)
     if "model" not in data:
         raise RuntimeError("Serialized model data does not contain key 'model'.")
     return data["model"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deepmd/jax/infer/deep_eval.py` around lines 190 - 198, The serialize() method
calls serialize_from_file(self.model_path) which doesn't support TensorFlow
.savedmodel directories accepted by the class __init__; update serialize() to
detect .savedmodel inputs (e.g. check if self.model_path endswith or is a
directory with saved_model.pb) and either call a dedicated serializer for
SavedModel paths or raise a clear RuntimeError indicating .savedmodel is
unsupported by serialize_from_file, mentioning self.model_path and
serialize_from_file in the message so dp show ... serialization-tree fails with
a clear, actionable error instead of an internal exception.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deepmd/pd/infer/deep_eval.py`:
- Around line 733-735: The serialize() implementation assumes
self.dp.model["Default"] but fails when self.dp is already a static model
object; update serialize() to handle both shapes: if self.dp is a static model
(e.g., has a serialize method or lacks a .model dict) call and return
self.dp.serialize(); otherwise, keep the existing behavior of retrieving model =
self.dp.model["Default"] and returning model.serialize(); use hasattr checks (or
isinstance if a concrete class exists) to detect which branch to take and avoid
indexing into .model when it's not a mapping.

---

Duplicate comments:
In `@deepmd/jax/infer/deep_eval.py`:
- Around line 190-198: The serialize() method calls
serialize_from_file(self.model_path) which doesn't support TensorFlow
.savedmodel directories accepted by the class __init__; update serialize() to
detect .savedmodel inputs (e.g. check if self.model_path endswith or is a
directory with saved_model.pb) and either call a dedicated serializer for
SavedModel paths or raise a clear RuntimeError indicating .savedmodel is
unsupported by serialize_from_file, mentioning self.model_path and
serialize_from_file in the message so dp show ... serialization-tree fails with
a clear, actionable error instead of an internal exception.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 22858c7f-cc6a-436c-bb71-bece5d4f54f5

📥 Commits

Reviewing files that changed from the base of the PR and between a8a80f4 and 8b48f46.

📒 Files selected for processing (10)
  • deepmd/dpmodel/infer/deep_eval.py
  • deepmd/entrypoints/show.py
  • deepmd/infer/deep_eval.py
  • deepmd/jax/infer/deep_eval.py
  • deepmd/pd/infer/deep_eval.py
  • deepmd/pt/infer/deep_eval.py
  • deepmd/pt_expt/infer/deep_eval.py
  • deepmd/tf/infer/deep_eval.py
  • source/tests/pt_expt/infer/test_deep_eval.py
  • source/tests/test_entrypoint_show_serialization_tree.py
✅ Files skipped from review due to trivial changes (1)
  • source/tests/pt_expt/infer/test_deep_eval.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • deepmd/pt_expt/infer/deep_eval.py
  • deepmd/infer/deep_eval.py
  • deepmd/dpmodel/infer/deep_eval.py
  • deepmd/entrypoints/show.py

Comment thread deepmd/pd/infer/deep_eval.py Outdated
Assert in the consistent IO deep-eval test that DeepEval.serialize() returns the same model tree as the backend serialize hook payload. This keeps the new serialize semantics covered in the shared cross-backend IO test.

Authored by OpenClaw (model: gpt-5.4)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/tests/consistent/io/test_io.py`:
- Line 164: The test currently uses self.assertEqual(deep_eval.serialize(),
serialized_data["model"]) which can fail on numpy.ndarray leaves; replace this
with a NumPy-aware comparison using
numpy.testing.assert_equal(deep_eval.serialize(), serialized_data["model"]) (or
np.testing.assert_equal if numpy is imported as np) to correctly handle array
comparisons; ensure numpy is imported in the test module (e.g., import numpy as
np) if not already and update the assertion in the test that calls
deep_eval.serialize() and compares to serialized_data["model"] accordingly.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3b9c2d6d-4aa2-4d0d-bc03-3d6b78b1d5cd

📥 Commits

Reviewing files that changed from the base of the PR and between 8b48f46 and 5e97073.

📒 Files selected for processing (1)
  • source/tests/consistent/io/test_io.py

Comment thread source/tests/consistent/io/test_io.py Outdated
njzjz-bot and others added 4 commits April 8, 2026 03:33
Handle Paddle static models when serializing through DeepEval by falling back to self.dp when it is not a ModelWrapper. Also use numpy-aware equality in the consistent IO serialize check.

Authored by OpenClaw (model: gpt-5.4)
@njzjz njzjz self-assigned this Apr 8, 2026
@njzjz njzjz marked this pull request as draft April 25, 2026 18:53
njzjz and others added 2 commits June 21, 2026 15:10
@njzjz njzjz marked this pull request as ready for review June 21, 2026 11:42
@dosubot dosubot Bot added the enhancement label Jun 21, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add dp show serialization-tree using DeepEval.serialize() across backends
✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

Description

• Add a serialization-tree attribute to dp show to print a model structure tree.
• Introduce a backend-wide DeepEval.serialize() contract returning a Node-compatible model tree.
• Add/adjust tests to lock serialization payloads and show output behavior.
Diagram

graph TD
  CLI["dp show (serialization-tree)"] --> Show["entrypoints/show.py"] --> DE["DeepEval.serialize()"] --> BE["DeepEvalBackend.serialize()"] --> M["Backend model serialize"] --> N["Node.deserialize()"] --> L["log.info tree"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep a single "envelope" serialize() payload (metadata + model)
  • ➕ Lets callers access backend/version metadata without additional APIs
  • ➕ Avoids multiple serialization entrypoints
  • ➖ Forces non-IO callers (like dp show) to understand backend-specific wrapper keys
  • ➖ Couples Node.deserialize() usage to a particular envelope schema
2. Add a separate API `serialize_model_tree()` alongside existing file serialization
  • ➕ Makes intent explicit: structure-only vs file/export payloads
  • ➕ Reduces pressure to overload serialize() semantics
  • ➖ API surface area grows; more methods to maintain across backends
  • ➖ Callers must learn which serialize flavor to use
3. Teach `Node.deserialize()` to accept both model-only and envelope payloads
  • ➕ CLI can pass through whatever serialization it receives
  • ➕ Backends can evolve envelope without changing CLI
  • ➖ Pushes backend concerns into a generic structure renderer
  • ➖ Risk of silently accepting malformed payloads and rendering confusing trees

Recommendation: Current approach (standardize DeepEvalBackend.serialize() to return a structure-only model tree consumed directly by Node.deserialize()) is the cleanest separation: backends remain responsible for obtaining a consistent model-structure dict, while the CLI/Node stays backend-agnostic. Consider a future follow-up only if callers need backend metadata (e.g., add an explicit serialize_export()/serialize_envelope()), but keep serialize() structure-only.

Files changed (14) +144 / -5

Enhancement (8) +74 / -0
deep_eval.pyExpose dpmodel backend 'serialize()' returning model tree +4/-0

Expose dpmodel backend 'serialize()' returning model tree

• Adds a 'serialize()' method that delegates to the underlying dpmodel object's 'serialize()'. Establishes a consistent structure-only serialization across backends.

deepmd/dpmodel/infer/deep_eval.py

show.pyAdd 'serialization-tree' output to 'dp show' +7/-0

Add 'serialization-tree' output to 'dp show'

• Imports 'Node' and, when requested, deserializes 'model.serialize()' into a tree and logs it. Keeps the behavior behind the explicit 'serialization-tree' attribute.

deepmd/entrypoints/show.py

deep_eval.pyImplement JAX backend 'serialize()' with SavedModel special-case +17/-0

Implement JAX backend 'serialize()' with SavedModel special-case

• Implements structure-only serialization for JAX models. Uses model-definition-based serialization for '.savedmodel' and falls back to file-based serialization for supported formats, returning the 'model' subtree.

deepmd/jax/infer/deep_eval.py

deep_eval.pyImplement Paddle backend 'serialize()' returning model tree +6/-0

Implement Paddle backend 'serialize()' returning model tree

• Adds 'serialize()' that selects the correct underlying model (handling 'ModelWrapper') and returns its serialized structure. Aligns Paddle with the unified serialize contract.

deepmd/pd/infer/deep_eval.py

deep_eval.pyForward pretrained wrapper 'serialize()' to backend +3/-0

Forward pretrained wrapper 'serialize()' to backend

• Adds a 'serialize()' method on the pretrained deep-eval wrapper that delegates to the underlying backend implementation. Keeps wrapper feature parity with 'DeepEval'.

deepmd/pretrained/deep_eval.py

deep_eval.pyImplement PyTorch backend 'serialize()' with fallback to file serializer +11/-0

Implement PyTorch backend 'serialize()' with fallback to file serializer

• Adds 'serialize()' that prefers 'model.serialize()' when available and otherwise loads a serialized model dict from disk and returns the 'model' subtree. Ensures consistent structure-only output across PyTorch model variants.

deepmd/pt/infer/deep_eval.py

deep_eval.pyImplement pt_expt backend 'serialize()' via file serialization +8/-0

Implement pt_expt backend 'serialize()' via file serialization

• Adds a 'serialize()' method that reads the serialized payload from file and returns the 'model' subtree when present. Handles both envelope and raw-model dict return shapes.

deepmd/pt_expt/infer/deep_eval.py

deep_eval.pyImplement TensorFlow backend 'serialize()' by reconstructing Model +18/-0

Implement TensorFlow backend 'serialize()' by reconstructing Model

• Adds 'model_file' tracking and implements 'serialize()' by loading the graph, creating a 'Model' from the model-def script, initializing variables from the graph, and calling 'model.serialize()'. Produces a structure-only dict suitable for Node deserialization.

deepmd/tf/infer/deep_eval.py

Bug fix (1) +9 / -1
serialization.pyClarify JAX file-serialization support and SavedModel error +9/-1

Clarify JAX file-serialization support and SavedModel error

• Updates error handling to explicitly reject lossless '.savedmodel' file serialization and guides users to 'DeepEval.serialize()' for structure-only trees. Refines the unsupported-format message for non-'.jax'/'.hlo' inputs.

deepmd/jax/utils/serialization.py

Refactor (1) +15 / -0
deep_eval.pyAdd backend-abstract 'serialize()' and DeepEval delegation +15/-0

Add backend-abstract 'serialize()' and DeepEval delegation

• Introduces an abstract 'DeepEvalBackend.serialize()' contract returning a Node-compatible model tree. Adds 'DeepEval.serialize()' that delegates to the active backend and stores 'model_file' on 'DeepEval' instances for backend use.

deepmd/infer/deep_eval.py

Tests (3) +45 / -4
test_io.pyVerify DeepEval.serialize() matches stored model tree (non-SavedModel) +7/-4

Verify DeepEval.serialize() matches stored model tree (non-SavedModel)

• Updates test to reuse a 'model_file' variable and asserts that 'DeepEval.serialize()' equals the serialized file's 'model' subtree for supported formats. Skips the equality assertion for JAX '.savedmodel' due to non-lossless file serialization semantics.

source/tests/consistent/io/test_io.py

test_deep_eval.pyAdd pt_expt serialization contract test +9/-0

Add pt_expt serialization contract test

• Adds a focused unit test ensuring pt_expt 'serialize()' returns the model tree and matches file serialization output. Uses NumPy-aware equality to handle array leaves in the serialized structure.

source/tests/pt_expt/infer/test_deep_eval.py

test_entrypoint_show_serialization_tree.pyAdd entrypoint test for 'dp show serialization-tree' behavior +29/-0

Add entrypoint test for 'dp show serialization-tree' behavior

• Introduces a unit test that mocks 'DeepEval', 'Node.deserialize', and logging to assert the show command passes the model payload directly into 'Node.deserialize()' and logs the expected output string.

source/tests/test_entrypoint_show_serialization_tree.py

Other (1) +1 / -0
main.pyRegister 'serialization-tree' as a valid 'dp show' attribute +1/-0

Register 'serialization-tree' as a valid 'dp show' attribute

• Extends the 'dp show' CLI parser attribute choices to include 'serialization-tree'. Enables the new show behavior via the command-line interface.

deepmd/main.py

@njzjz njzjz requested review from iProzd and wanghan-iapcm June 21, 2026 11:44
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 21, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Plugin backend API break 🐞 Bug ☼ Reliability
Description
DeepEvalBackend.serialize() was added as an abstractmethod, so any externally-registered backend
(via the plugin registry) that hasn’t implemented serialize() will now fail instantiation with a
TypeError.
Code

deepmd/infer/deep_eval.py[R364-373]

+    @abstractmethod
+    def serialize(self) -> dict[str, Any]:
+        """Serialize the loaded model structure only.
+
+        Returns
+        -------
+        dict
+            Serialized model tree that can be consumed by ``Node.deserialize``.
+        """
+
Evidence
Backends are registered/loaded via a plugin registry and DeepEvalBackend dynamically dispatches to
backend().deep_eval. Making serialize() abstract enforces implementation at instantiation time,
breaking any backend subclasses not updated.

deepmd/backend/backend.py[35-109]
deepmd/infer/deep_eval.py[33-99]
deepmd/infer/deep_eval.py[351-373]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`DeepEvalBackend` is part of a plugin-based backend architecture. Turning `serialize()` into an `@abstractmethod` introduces a runtime-breaking change for any out-of-tree backend subclasses that don’t yet implement it (instantiation fails with `TypeError: Can't instantiate abstract class ...`).
## Issue Context
This repo uses a plugin registry (`Backend`) and dynamic dispatch to backend-specific `DeepEvalBackend` implementations. External backends are a supported extension point.
## Fix Focus Areas
- deepmd/infer/deep_eval.py[364-373]
- deepmd/backend/backend.py[35-109]
## Suggested fix
1. Remove `@abstractmethod` from `serialize()` and provide a conservative default implementation (so older plugins still instantiate):
 - Option A (preferred): default implementation calls `self.get_model()` and, if it has `.serialize()`, returns that; otherwise raises `NotImplementedError` with a clear message.
 - Option B: default implementation raises `NotImplementedError` directly.
2. If you intentionally want a hard break, add an explicit compatibility note / version bump policy (but keeping a default implementation is safer for plugins).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Eager heavy import 🐞 Bug ➹ Performance
Description
deepmd.entrypoints.show imports Node at module import time, pulling in
deepmd.dpmodel.utils.serialization (and heavy deps like h5py/yaml) even when serialization-tree
output is not requested.
Code

deepmd/entrypoints/show.py[R7-9]

+from deepmd.dpmodel.utils.serialization import (
+    Node,
+)
Evidence
show.py imports Node at import-time, and Node lives in a module that imports h5py/yaml/numpy at
import-time; this cost is paid regardless of whether serialization-tree is requested.

deepmd/entrypoints/show.py[1-12]
deepmd/dpmodel/utils/serialization.py[1-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`show.py` imports `Node` unconditionally at module import time, which transitively imports `deepmd.dpmodel.utils.serialization` and its heavy dependencies (e.g., `h5py`, `yaml`). This slows down CLI/module import even when `serialization-tree` isn’t used.
## Issue Context
The `serialization-tree` feature is optional and only needed when the user requests it.
## Fix Focus Areas
- deepmd/entrypoints/show.py[7-12]
- deepmd/entrypoints/show.py[143-145]
- deepmd/dpmodel/utils/serialization.py[1-23]
## Suggested fix
Move the `Node` import inside the `if "serialization-tree" in ATTRIBUTES:` block:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Eager tree rendering 🐞 Bug ➹ Performance
Description
The serialization-tree log line builds a string via concatenation with str(root), forcing full tree
rendering work before log.info() even if INFO logging is disabled.
Code

deepmd/entrypoints/show.py[R143-145]

+    if "serialization-tree" in ATTRIBUTES:
+        root = Node.deserialize(model.serialize())
+        log.info("Model serialization tree:\n" + str(root))
Evidence
String concatenation forces str(root) evaluation prior to calling the logging API; Python logging
only avoids formatting cost when you pass arguments rather than preformatted strings.

deepmd/entrypoints/show.py[143-145]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`log.info("..." + str(root))` eagerly computes `str(root)` (including Node.size traversal) before the logger checks whether INFO is enabled.
## Issue Context
This is only on the `serialization-tree` path, but the tree can be large and expensive to render.
## Fix Focus Areas
- deepmd/entrypoints/show.py[143-145]
## Suggested fix
Use lazy logging formatting so rendering happens only when INFO is enabled:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@njzjz njzjz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. serialize() is implemented on all DeepEvalBackend subclasses (DeepEvalOld doesn't inherit it, so the new abstractmethod doesn't affect it), and the round-trip is covered in test_io.py for every non-savedmodel backend.

— Opus 4.8

Comment on lines +744 to +748
model = (
self.dp.model["Default"] if isinstance(self.dp, ModelWrapper) else self.dp
)
return model.serialize()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This crashes for .json (JIT) Paddle models. For a .json file the backend __init__ sets self.dp = paddle.jit.load(self.model_path[:-5]) (line 156), which is a TranslatedLayer -- not a ModelWrapper, and it has no Python .serialize(). So the else self.dp branch is taken and model.serialize() raises AttributeError. dp show serialization-tree on a .json Paddle model would fail. The PT backend handles the analogous JIT case with a hasattr(model, "serialize") guard that falls back to serialize_from_file(...)["model"]; PD needs the same fallback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 1140851. PD serialize() now mirrors the PT guard: it uses the Python model's serialize() when present and falls back to serialize_from_file(self.model_path)["model"] for JIT/TranslatedLayer-style models. I also added a unit test for that fallback path.

Authored by OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.5)

Comment thread deepmd/entrypoints/show.py Outdated
Comment on lines +156 to +158
if "serialization-tree" in ATTRIBUTES:
root = Node.deserialize(model.serialize())
log.info("Model serialization tree:\n" + str(root))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Non-blocking: this ignores multi-task models. Every other attribute above (type-map, descriptor, fitting-net, observed-type) branches on model_is_multi_task and iterates the per-branch model_dict with labels. Here model.serialize() is called once on the head=0 instance, so a multi-task model silently dumps only the first branch's tree, unlabeled. Consider following the same multi-task iteration pattern used by the other attributes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 1140851. serialization-tree now follows the multi-task pattern: it iterates model_dict, instantiates DeepEval(INPUT, head=branch) for each branch, and logs each tree with the branch label. The same change also moves the Node import into the serialization-tree path and switches the tree logging to lazy %s formatting.

Authored by OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.5)

Comment thread deepmd/infer/deep_eval.py Outdated
Comment on lines +384 to +385
def serialize(self) -> dict[str, Any]:
"""Serialize the loaded model structure only.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Non-blocking: "structure only" is inaccurate. For PT/PD/TF/dpmodel/JAX(.jax,.hlo)/pt_expt, serialize() returns the full lossless, weight-bearing model dict (the PR's own test_io.py asserts serialize() == serialized_data["model"]). The only genuinely structure-only case is JAX .savedmodel, which returns a tree rebuilt from the def-script with untrained/default weights -- a non-uniform behavior that isn't documented. Suggest rewording to say it returns the model's serialized tree (with weights), and note the .savedmodel exception. Same docstring at the high-level DeepEval.serialize (line 454).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 1140851. I reworded both docstrings to describe this as returning the serialized model tree, normally the lossless weight-bearing model subtree, and explicitly documented the JAX .savedmodel exception where the tree is reconstructed from the model definition script and trained weights are not preserved.

Authored by OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.5)

Comment on lines +163 to +166
if not model_file.endswith(".savedmodel"):
# JAX SavedModel stores an executable graph, not a lossless model dict.
serialized_data = self.get_data_from_model(model_file)
np.testing.assert_equal(deep_eval.serialize(), serialized_data["model"])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Several reachable branches of the new serialize() API are left untested. The backend loop feeding this assertion covers only tensorflow/jax, pytorch, dpmodel -- Paddle is absent, so both branches of PD serialize() are never exercised (which is why the .json JIT crash flagged in deepmd/pd/infer/deep_eval.py slips through). And this if not model_file.endswith(".savedmodel") guard skips the JAX .savedmodel path entirely, so its branch plus the if "model" not in data: raise branch and the new JAX-helper .savedmodel -> raise ValueError branch are untested. Also untested: PT's serialize_from_file(...)["model"] fallback (jit .pth) and pt_expt's else: return data fallback. Please add coverage (at minimum add Paddle to the loop and a jit / .savedmodel case).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 1140851. I added Paddle to the existing consistent-IO backend loop and added focused unit coverage for the reachable branch cases that do not require full exported-model fixtures: default backend delegation/error behavior, PD JIT fallback, PT JIT fallback, pt_expt raw-payload fallback, JAX .savedmodel reconstruction, and multi-task dp show serialization-tree branch iteration.

Authored by OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.5)

Authored by OpenClaw (model: custom-chat-jinzhezeng-group/gpt-5.5)
@njzjz njzjz force-pushed the feat/deepeval-serialize branch from a97fa95 to 1140851 Compare June 22, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants