Skip to content

chore(sdk): deprecate AgentBase.model_dump_succint - #4328

Merged
VascoSch92 merged 1 commit into
OpenHands:mainfrom
AzeelSajjad:chore/deprecate-model-dump-succint
Aug 3, 2026
Merged

VascoSch92 merged 1 commit into
OpenHands:mainfrom
AzeelSajjad:chore/deprecate-model-dump-succint

Conversation

@AzeelSajjad

Copy link
Copy Markdown

HUMAN:

For #4224 I picked up the model_dump_succint item. I went with a deprecation rather than a deletion — removing it outright would pull a public method out from under anyone still using it with no warning period, and that isn't how this repo treats public API.


AGENT:

Why

AgentBase.model_dump_succint() is the last unchecked dead-code item on #4224. It has no callers anywhere in the tree — git grep finds only the definition.

It cannot be deleted in a single PR, which is presumably why #4225 cleared the other five items and left this one. AgentBase is exported via openhands.sdk.__all__, so check_sdk_api_breakage.py governs its public members, and the deprecation scan runs against the baseline tree rather than the PR:

:1382   old_root = _load_prev_from_pypi(griffe_module, baseline, cfg)
:1278   source_root = _get_source_root(old_root)   # deprecation scan

So the @deprecated marker has to exist in an already-published release before removal is permitted. A PR that adds the marker and deletes the method together still fails, because the baseline it is compared against has no marker. Deleting it today gives:

Removed 'AgentBase.model_dump_succint' without prior deprecation.

This PR therefore starts the runway instead of removing.

Question for maintainers — the reason this is worth a look rather than a rubber stamp. The issue says "remove", and this PR does not remove. There is an escape hatch: _ACCEPTED_REMOVED_MEMBERS in check_sdk_api_breakage.py already allowlists two removals (DockerWorkspace.mount_dir / DockerDevWorkspace.mount_dir, from #3822, shipped with release-note-required). If you would rather allowlist this one and take the deletion now, say so and I will swap this PR for the removal. If you would rather leave the method alone entirely, that is a fine answer too and I will close this. I picked the deprecation route because adding an entry to that allowlist is accepting a breaking change on behalf of downstream clients, which felt like your call and not mine.

Summary

  • Adds @deprecated(deprecated_in="1.40.0", removed_in="1.45.0", ...) to AgentBase.model_dump_succint, using the canonical helper in openhands.sdk.utils.deprecation.
  • 1.45.0 is exactly the minimum legal target: _minimum_removed_in("1.40.0") returns "1.45.0" for DEPRECATION_RUNWAY_MINOR_RELEASES = 5.
  • deprecated_in="1.40.0" rather than 1.41.0 because the decorator only warns when current_version >= deprecated_in, so anchoring ahead of the current version leaves it untestable in-tree. This matches existing entries, which anchor at or below the version current when they were written.
  • No runtime behavior change. The method body is byte-identical; only a decorator and a one-line docstring were added. The diff is additive-only.
  • The succint typo is deliberately left alone — correcting it would add a new public symbol rather than remove one.
  • Three tests covering warning emission with both version strings asserted, equivalence to model_dump(exclude_none=True), and the explicit exclude_none=False override.
  • I did not hand-write a .. deprecated:: docstring block: the deprecation package injects its own at decoration time, so a manual one renders twice.

Follow-up note for whoever does the deletion: check_deprecations.py goes red on every PR once pyproject reaches 1.45.0, while the api-breakage gate rejects the removal until then. So the deletion has to ride in the 1.45.0 release PR itself. That is pre-existing behavior — the four existing 1.36.0 → 1.41.0 deprecations hit the same wall at the next release — not something introduced here.

Issue Number

Part of #4224

How to Test

Full SDK suite (baseline on main before this change was 5650 passed / 9 skipped / 13 xfailed — the delta is exactly the three new tests):

uv run pytest tests/sdk/ -q
5653 passed, 9 skipped, 13 xfailed, 61 warnings in 333.40s (0:05:33)

Focused tests:

uv run pytest tests/sdk/agent/test_model_dump_succint_deprecation.py -v
3 passed

Both policy gates:

uv run python .github/scripts/check_deprecations.py
# exit 0 — openhands-sdk: checked 5 deprecation metadata entries against version 1.40.0

uv run python .github/scripts/check_sdk_api_breakage.py
# exit 0 — No breaking changes detected

Lint:

make lint
# All checks passed!

The warning and docstring, confirmed at runtime:

>>> AgentBase.model_dump_succint.__doc__
'Like model_dump, but excludes None fields by default.\n\n.. deprecated:: 1.40.0\n   This will be removed in 1.45.0. Use model_dump(exclude_none=True) instead.'

Exactly one .. deprecated:: directive, and the emitted DeprecatedWarning reads:

model_dump_succint is deprecated as of 1.40.0 and will be removed in 1.45.0. Use model_dump(exclude_none=True) instead.

Unreferenced public method flagged in OpenHands#4224. Public API removal policy
requires 5 minor releases of runway, so mark it deprecated in 1.40.0 for
removal in 1.45.0 rather than deleting it now.

@all-hands-bot all-hands-bot left a comment

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 review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Verdict

No material findings. The change is correct, minimal, and well-tested.

Risk Assessment: LOW — Additive-only change with no runtime behavior modification. The method body is byte-identical; only a decorator and an import were added, plus a new test file.

What this PR does

Adds @deprecated(deprecated_in="1.40.0", removed_in="1.45.0", details="Use model_dump(exclude_none=True) instead.") to AgentBase.model_dump_succint, starting the deprecation runway required before the method can be removed under the SDK's public-API compatibility policy (DEPRECATION_RUNWAY_MINOR_RELEASES = 5).

Verification performed

  • Diff is additive-only. Confirmed via the PR diff: a new import, the @deprecated decorator, and a new test file. The method body and docstring source are unchanged (the deprecation package injects the .. deprecated:: block at decoration time, so there is no manual docstring duplication).
  • Runway is the legal minimum. _minimum_removed_in("1.40.0") returns "1.45.0" (1.40 + 5 minors), matching removed_in.
  • Version anchoring is consistent with existing entries. deprecated_in="1.40.0" is at the current version because the deprecation decorator only warns when current_version >= deprecated_in; anchoring ahead would leave the warning untestable in-tree. The existing warn_deprecated entries follow the same pattern (anchored at or below the version current when written).
  • Both policy gates pass. check_deprecations.py -> exit 0 (openhands-sdk: 5 deprecation metadata entries). check_sdk_api_breakage.py -> exit 0 (No breaking changes detected).
  • Tests pass. tests/sdk/agent/test_model_dump_succint_deprecation.py -> 3 passed. Coverage is appropriate and proportionate: warning emission with both version strings asserted, equivalence to model_dump(exclude_none=True), and the explicit exclude_none=False override path.
  • Runtime behavior confirmed. Exactly one .. deprecated:: directive in the docstring; the emitted DeprecatedWarning reads model_dump_succint is deprecated as of 1.40.0 and will be removed in 1.45.0. Use model_dump(exclude_none=True) instead.; model_dump_succint() == model_dump(exclude_none=True).
  • No callers exist. git grep model_dump_succint finds only the definition and the new test file.

Notes

  • The succint typo is deliberately left alone — correcting it would introduce a new public symbol rather than deprecate one, which is the right call for a removal-track PR.
  • This is the first use of the @deprecated decorator (vs warn_deprecated) in the SDK. The decorator is the correct tool for direct method annotation, and the canonical helper in openhands.sdk.utils.deprecation is used as intended.
  • The author explicitly flagged the maintainer decision point (deprecation vs allowlisted removal via _ACCEPTED_REMOVED_MEMBERS); that's a policy call for maintainers, not a code issue.

@VascoSch92
VascoSch92 merged commit 8ce9300 into OpenHands:main Aug 3, 2026
32 checks passed
@VascoSch92

Copy link
Copy Markdown
Member

Thanks :-)

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.

3 participants