Skip to content

Conversation

@simonrosenberg
Copy link
Collaborator

@simonrosenberg simonrosenberg commented Nov 21, 2025

PR Summary: Allow user to specify their own agent at runtime

Overview

Users can specify their own agent factory methods in agent delegation.


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:9cd58ec-python

Run

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

All tags pushed for this build

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

About Multi-Architecture Support

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

@simonrosenberg simonrosenberg self-assigned this Nov 21, 2025
@simonrosenberg
Copy link
Collaborator Author

@OpenHands /codereview-roasted Read the diff and write a code review. Specifically

  1. how the agent_type "default" logic can be simplified
  2. how the agent registration pattern could perhaps be closer to the tools registration pattern in the repo
    Point out any other code issue

@openhands-ai
Copy link

openhands-ai bot commented Nov 21, 2025

I'm on it! simonrosenberg can track my progress at all-hands.dev

@openhands-ai
Copy link

openhands-ai bot commented Nov 21, 2025

Code Review Summary

I've completed a comprehensive code review of PR #1223 focusing on the specific issues you requested. Here are my findings:

🔍 Requested Analysis

1. Agent Type "default" Logic Simplification

Problem: The current logic in impl.py (lines 109-113) is unnecessarily complex with three levels of default handling:

agent_type = "default"
if action.agent_types is not None and i < len(action.agent_types):
    agent_type = action.agent_types[i] or "default"

Recommendation: Simplify to a single expression or extract to a helper function that clearly handles the default case without nested checks.

2. Agent Registration vs Tool Registration Pattern Mismatch

Key differences found:

Feature Tool Registry Agent Registry
Thread Safety ✅ Uses RLock ❌ None
Registration Types 3 types (instance, subclass, callable) 1 type (callable only)
Pattern Resolver-based with ConversationState Simple dict lookup
Default Handling Raises errors Pre-registers at import time

Recommendation: Either align agent registration with the robust tool registry pattern, or simplify tool registry if the complexity isn't needed. Inconsistent patterns create cognitive friction.

🔴 Critical Issues

  1. Module-level side effects (registration.py:113-117): The "default" agent is registered at import time, creating a special case that's treated both as a sentinel value AND a real agent type
  2. No thread safety: Unlike tool registry, agent registry lacks RLock protection
  3. Poor data structure choice: "default" creates edge cases throughout - it's checked in multiple places, creating unnecessary complexity

🟡 Other Issues Found

  1. Error handling: get_agent_factory() raises errors instead of sensible defaults
  2. Verbose message building: Lines 137-145 in impl.py could be simplified with helper functions
  3. Type ambiguity: agent_types: list[str | None] | None allows None in the list unnecessarily
  4. Missing tests: No tests for the new registration system
  5. Overly complex example: The example tries to demonstrate too much at once

💡 Key Insight

The fundamental problem is treating "default" as both a sentinel value and a registered agent type. This creates special cases everywhere. Better design: make default behavior implicit when no agent_type is specified, rather than requiring "default" to be passed around and registered.


Full detailed review: /workspace/project/CODE_REVIEW.md

The review follows the "Linus Torvalds roasted" style you requested - direct, technically rigorous, focusing on fundamental engineering principles over style preferences.

View full conversation

@simonrosenberg
Copy link
Collaborator Author

@OpenHands /codereview-roasted Read the diff and write a code review

@simonrosenberg simonrosenberg marked this pull request as ready for review November 21, 2025 16:40
@openhands-ai
Copy link

openhands-ai bot commented Nov 21, 2025

I'm on it! simonrosenberg can track my progress at all-hands.dev

@openhands-ai
Copy link

openhands-ai bot commented Nov 21, 2025

I've completed a comprehensive code review of PR #1223 "feat: allow delegator to spawn pre-defined subagents". Since you requested a code review (not code changes), I have not modified any code - only provided detailed feedback.

Summary of Code Review Findings

I analyzed the diff and provided a Linus-style "roasted" code review focusing on your specific questions:

1. Agent Type "default" Logic Simplification

Critical Issue Found: The current _resolve_agent_type() method (lines 77-85 in impl.py) has unnecessary complexity with three levels of None-checking. The root problem is treating "default" as a special None value instead of a normal string, which creates ripple effects throughout the codebase with special-case handling.

Recommendation: Change _resolve_agent_type() to always return a string (including "default"), eliminating the None/empty string special cases entirely. This would simplify the code from ~9 lines with 3 conditionals to ~5 lines with clearer logic.

2. Agent Registration Pattern vs Tools Registration Pattern

Key Gap Identified: The agent registration is much simpler than the tool registration pattern, but in an awkward way:

Feature Tool Registry Agent Registry
Multiple registration types ✅ (instance, class, callable) ❌ (only callable)
Parameter support ✅ Full params dict ❌ Only LLM
ConversationState access ✅ Passed to factory ❌ Not available
Type validation ✅ Validates returns ❌ No validation

Recommendation: Either keep it simple but make it consistent (remove special cases, add validation), or elevate agent registration to SDK-level like tools. Currently it's in an uncomfortable middle ground.

3. Other Code Issues Found

  • No type safety: Factory functions aren't validated to return Agent instances
  • Global mutable state: Registry lacks clear lifecycle management
  • Dynamic description generation: Tool description rebuilt on every create() call
  • Missing test coverage: Only happy-path tests, no error cases
  • Unnecessary indirection: create_default_agent() wrapper adds no value

Overall Assessment

🟡 Acceptable - Core functionality is sound and solves a real user need, but the implementation has unnecessary complexity from special-case handling. The code would benefit from:

  1. HIGH Priority: Eliminate None handling for default - use "default" string everywhere
  2. HIGH Priority: Add validation that factories return Agent instances
  3. MEDIUM Priority: Add negative test cases for error conditions

The review is available above with detailed line-by-line analysis, code examples, and specific recommendations. No code was modified per the code review instructions.

View full conversation

@github-actions
Copy link
Contributor

github-actions bot commented Nov 21, 2025

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-tools/openhands/tools/delegate
   definition.py25772%80–81, 84, 87–88, 97, 100
   impl.py12010016%30, 32–33, 42–43, 47, 53–54, 57–60, 62, 74–75, 79–81, 85–86, 93–95, 104–105, 115–119, 121, 125–127, 129–130, 136, 138, 144, 147, 152, 157, 161, 166–168, 185–186, 193–195, 204, 206–208, 211–216, 218, 225–226, 228–229, 232–235, 237–238, 242–245, 248–250, 255–256, 259–260, 263, 265–269, 271, 274–276, 278–279, 282, 284, 289–291
   registration.py402537%70–72, 74, 93–94, 96–98, 100–102, 107, 112–113, 115–116, 120–121, 124, 126–127, 129, 134–135
TOTAL12444577853% 

@openhands-ai
Copy link

openhands-ai bot commented Nov 21, 2025

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • Check Documented Examples

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #1223 at branch `allow-delegator-to-load-predefined-subagents`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

simonrosenberg and others added 2 commits November 21, 2025 18:19
Move the delegate tool description from an inline f-string to a
separate Jinja template file for better maintainability and
separation of concerns.

Changes:
- Create delegate/templates/delegate_tool_description.j2
- Update definition.py to use render_template()
- Add .j2 file inclusion to MANIFEST.in for openhands-tools

Co-authored-by: openhands <openhands@all-hands.dev>
@simonrosenberg simonrosenberg requested a review from enyst November 24, 2025 15:38
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

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

Love this PR, thank you! I think people are asking for this 😄

@simonrosenberg
Copy link
Collaborator Author

@OpenHands solve the conflicts with the PR

@openhands-ai
Copy link

openhands-ai bot commented Nov 25, 2025

I'm on it! simonrosenberg can track my progress at all-hands.dev

Resolved conflict in 25_agent_delegation.py by integrating cost reporting
from main branch into both the simple delegation and user-defined agents examples.

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link

openhands-ai bot commented Nov 25, 2025

✅ Conflicts Resolved Successfully

I've successfully resolved the merge conflicts in PR #1223 and pushed the changes to update the branch.

Summary of Changes

Conflict Location: examples/01_standalone_sdk/25_agent_delegation.py

Resolution:

  • The conflict was between:

    • Main branch: Added cost reporting (EXAMPLE_COST) at the end of the simple delegation example
    • Current branch: Extended the example with a second part demonstrating user-defined agent types
  • I merged both changes by:

    1. Added cost reporting for the simple delegation example (line 83-85) with label "EXAMPLE_COST (simple delegation)"
    2. Kept all the extended functionality for user-defined agent types (lines 90-196)
    3. Added cost reporting for the user-defined agent types example (line 192-194) with label "EXAMPLE_COST (user-defined agents)"

Additional Merged Changes from Main

The merge also brought in several other updates from main:

  • Workflow improvements for eval and examples testing
  • New test infrastructure for running examples via pytest
  • Bug fix for file download API endpoint
  • Dependency updates

All pre-commit checks passed, and the changes have been committed and pushed to allow-delegator-to-load-predefined-subagents.

The PR is now up to date with main and ready for review!

View full conversation

@simonrosenberg simonrosenberg merged commit 3ec7bb2 into main Nov 25, 2025
22 checks passed
@simonrosenberg simonrosenberg deleted the allow-delegator-to-load-predefined-subagents branch November 25, 2025 15:37
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.

4 participants