Skip to content

refactor: extract common extension patterns into openhands.sdk.extensions module#2791

Draft
csmith49 wants to merge 1 commit intomainfrom
extensions-module
Draft

refactor: extract common extension patterns into openhands.sdk.extensions module#2791
csmith49 wants to merge 1 commit intomainfrom
extensions-module

Conversation

@csmith49
Copy link
Copy Markdown
Collaborator

@csmith49 csmith49 commented Apr 9, 2026

  • A human has tested these changes.

Why

The plugin, marketplace, and skills modules had significant code duplication:

  • InstalledPluginInfo and InstalledSkillInfo shared 90% of their fields
  • PluginSource and skill source handling had identical logic
  • InstalledPluginsMetadata and InstalledSkillsMetadata had identical persistence code
  • Install/uninstall/enable/disable/list/load/get/update operations were copy-pasted between modules

Summary

  • Create a new openhands.sdk.extensions module with shared base classes
  • Extract ExtensionSource, ResolvedExtensionSource, ExtensionAuthor into source.py
  • Extract ExtensionCatalogEntry, ExtensionCatalog into catalog.py
  • Extract fetch_extension_with_resolution into fetch.py
  • Extract InstalledExtensionInfo, InstalledExtensionMetadata[T], InstalledExtensionManager[ItemT, InfoT] into installed.py
  • Refactor plugin/installed.py (~500 → ~150 lines) and skills/installed.py (~580 → ~200 lines) to use the new generic manager
  • Maintain full backward compatibility with legacy API and metadata formats

Issue Number

N/A - Internal refactoring task

How to Test

Run the tests for installed plugins and skills:

uv run pytest tests/sdk/plugin/test_installed_plugins.py tests/sdk/skills/test_installed_skills.py -v

All 53 tests pass.

Backward compatibility verified:

  • InstalledPluginsMetadata(plugins={...}) still works (legacy kwarg)
  • Loading metadata files with {"plugins": ...} or {"skills": ...} keys still works
  • PluginSource, PluginAuthor, InstalledPluginInfo all still work as before

Video/Screenshots

N/A - This is a refactoring PR with no UI changes.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

  • Uses Python 3.12+ generic syntax: class Foo[T: Bound]
  • The new InstalledExtensionManager is generic over both the item type (e.g., Plugin, Skill) and the info type (e.g., InstalledPluginInfo, InstalledSkillInfo)
  • The marketplace module could also be updated to use these patterns in a follow-up PR

This PR was created by an AI assistant (OpenHands) on behalf of the user.


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22-slim Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:776ea8c-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-776ea8c-python \
  ghcr.io/openhands/agent-server:776ea8c-python

All tags pushed for this build

ghcr.io/openhands/agent-server:776ea8c-golang-amd64
ghcr.io/openhands/agent-server:776ea8c-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:776ea8c-golang-arm64
ghcr.io/openhands/agent-server:776ea8c-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:776ea8c-java-amd64
ghcr.io/openhands/agent-server:776ea8c-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:776ea8c-java-arm64
ghcr.io/openhands/agent-server:776ea8c-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:776ea8c-python-amd64
ghcr.io/openhands/agent-server:776ea8c-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:776ea8c-python-arm64
ghcr.io/openhands/agent-server:776ea8c-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:776ea8c-golang
ghcr.io/openhands/agent-server:776ea8c-java
ghcr.io/openhands/agent-server:776ea8c-python

About Multi-Architecture Support

  • Each variant tag (e.g., 776ea8c-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 776ea8c-python-amd64) are also available if needed

…ions module

Create a new `openhands.sdk.extensions` module to deduplicate code between
plugin, marketplace, and skills modules. This extracts:

- ExtensionSource, ResolvedExtensionSource, ExtensionAuthor (source.py)
- ExtensionCatalogEntry, ExtensionCatalog (catalog.py)
- fetch_extension_with_resolution (fetch.py)
- InstalledExtensionInfo, InstalledExtensionMetadata[T], InstalledExtensionManager[ItemT, InfoT] (installed.py)

Key changes:
- PluginSource now extends ExtensionSource
- PluginAuthor now extends ExtensionAuthor
- InstalledPluginInfo now extends InstalledExtensionInfo
- InstalledSkillInfo now extends InstalledExtensionInfo
- Both plugin/installed.py and skills/installed.py now use InstalledExtensionManager
- Backward-compatible wrappers preserve legacy API (InstalledPluginsMetadata, InstalledSkillsMetadata)
- Legacy metadata formats ("plugins", "skills" keys) are still supported when loading

This reduces code duplication by ~400+ lines while maintaining full backward compatibility.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/extensions
   catalog.py29389%68–70
   fetch.py701677%59, 68–69, 75, 77, 79, 98, 120, 143, 185, 190–192, 225, 233, 270
   installed.py2082090%301, 330, 342–343, 402–403, 461–462, 465–467, 502–504, 508–510, 514, 518, 537
   source.py421759%53, 55, 57, 86–94, 96–97, 99, 139, 152
openhands-sdk/openhands/sdk/plugin
   installed.py59198%123
   types.py79297%87, 209
openhands-sdk/openhands/sdk/skills
   installed.py1212182%43, 50, 125, 139, 141–142, 144, 147–148, 280, 367–370, 372–373, 377–378, 383, 400–401
TOTAL22337647071% 

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