Skip to content

Conversation

@rbren
Copy link
Contributor

@rbren rbren commented Nov 22, 2025

Summary

This PR adds server-side automatic conversion of plain string secrets to StaticSecret objects in the UpdateSecretsRequest model. This provides backward compatibility for clients that send plain string secrets instead of properly formatted SecretSource objects.

Changes

Core Implementation

  • Added field validator to UpdateSecretsRequest.secrets that automatically converts plain strings to StaticSecret objects
  • Handles multiple input formats:
    • Plain strings: "secret-value"StaticSecret(value=SecretStr("secret-value"))
    • Dict with value field: {"value": "secret-value"}StaticSecret(value=SecretStr("secret-value"))
    • Proper SecretSource objects: passed through unchanged

Testing

  • 7 comprehensive unit tests for model validation covering:
    • Plain string conversion
    • Proper SecretSource objects (no conversion)
    • Mixed formats (strings + proper objects)
    • Dict without kind field conversion
    • Invalid dict handling
    • Empty secrets dict
    • Invalid input types
  • 2 integration tests for API endpoint functionality:
    • String conversion through HTTP API
    • Mixed format handling through HTTP API

Problem Solved

Fixes the issue where clients sending plain string secrets receive a 422 validation error:

{'detail': [{'type': 'union_tag_invalid', 'loc': ['body', 'secrets', 'GITHUB_TOKEN'], 
'msg': "Input tag 'str' found using kind_of() does not match any of the expected tags: 
'LookupSecret', 'StaticSecret'", 'input': 'ghp_...', 'ctx': {'discriminator': 'kind_of()', 
'tag': 'str', 'expected_tags': "'LookupSecret', 'StaticSecret'"}}]}

Backward Compatibility

Fully backward compatible - existing clients using proper SecretSource objects continue to work unchanged

Enables legacy clients - clients sending plain strings now work automatically

Testing Results

All tests pass:

  • 7/7 model validation tests ✅
  • 2/2 API integration tests ✅
  • Pre-commit hooks pass ✅
  • Type checking passes ✅

Related

This is the server-side solution that complements the client-side fix in PR #1231. Together, they provide a complete solution:

  • Client-side: Ensures new SDK versions send properly formatted secrets
  • Server-side: Accepts both formats for backward compatibility

Review Notes

The field validator uses Pydantic's validation system to safely convert string inputs to the expected StaticSecret format before the discriminated union validation occurs. This approach is clean, type-safe, and maintains the existing API contract while adding flexibility.

@rbren can click here to continue refining the PR


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.12-nodejs22 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:d7ed6b0-python

Run

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

All tags pushed for this build

ghcr.io/openhands/agent-server:d7ed6b0-golang-amd64
ghcr.io/openhands/agent-server:d7ed6b0-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:d7ed6b0-golang-arm64
ghcr.io/openhands/agent-server:d7ed6b0-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:d7ed6b0-java-amd64
ghcr.io/openhands/agent-server:d7ed6b0-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:d7ed6b0-java-arm64
ghcr.io/openhands/agent-server:d7ed6b0-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:d7ed6b0-python-amd64
ghcr.io/openhands/agent-server:d7ed6b0-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-amd64
ghcr.io/openhands/agent-server:d7ed6b0-python-arm64
ghcr.io/openhands/agent-server:d7ed6b0-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-arm64
ghcr.io/openhands/agent-server:d7ed6b0-golang
ghcr.io/openhands/agent-server:d7ed6b0-java
ghcr.io/openhands/agent-server:d7ed6b0-python

About Multi-Architecture Support

  • Each variant tag (e.g., d7ed6b0-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., d7ed6b0-python-amd64) are also available if needed

- Add field validator to UpdateSecretsRequest.secrets that automatically converts plain strings to StaticSecret objects
- Validator handles multiple input formats: plain strings, dict with value field, proper SecretSource objects
- Enables backward compatibility for clients sending plain string secrets
- Add comprehensive unit tests for model validation (7 test cases)
- Add integration tests for API endpoint with string conversion (2 test cases)
- All tests passing with proper type safety

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

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   models.py1131487%56–57, 171–172, 174–176, 178–179, 181–182, 190, 193–194
TOTAL12202560754% 

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