mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Revise Scenario 1 design to align with engineer's sandbox spec approach
Major changes to align with existing OpenHands infrastructure: - Use enhanced sandbox specifications instead of parallel CustomAgentSpec system - Implement permissions as core M1 requirement (not M4 afterthought) - Support both pre-built image registration and secure user upload workflows - Extend existing SandboxSpecService rather than creating separate infrastructure - Integrate with existing conversation creation API via sandbox_spec_id parameter - Address V0 security issues with comprehensive validation and approval workflows - Maintain backward compatibility with existing sandbox system This approach is more pragmatic and leverages proven OpenHands infrastructure while addressing the engineer's specific requirements for permissions and security. Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
9f04705b30
commit
635a29a47c
@ -12,21 +12,25 @@ Users building agents that require:
|
||||
- Custom Docker base images with specific OS configurations
|
||||
- Proprietary or licensed software installations
|
||||
|
||||
Currently have no supported path to deploy their agents within the OpenHands ecosystem.
|
||||
Currently have no supported path to deploy their agents to OpenHands Enterprise.
|
||||
|
||||
### 1.2 Proposed Solution
|
||||
|
||||
We propose extending the V1 architecture to support **Custom Agent Runtime Images** - allowing users to package their agents with custom dependencies into Docker images that integrate seamlessly with OpenHands' existing infrastructure.
|
||||
We propose extending the existing **Sandbox Specification System** to support custom agent runtime images with proper permissions and security controls. This approach builds directly on OpenHands' current sandbox infrastructure rather than creating parallel systems.
|
||||
|
||||
Users will be able to:
|
||||
1. Create custom Docker images containing their agent code and dependencies
|
||||
2. Register these images with OpenHands through a declarative specification
|
||||
3. Deploy conversations using their custom agent images instead of the default agent server
|
||||
4. Maintain full compatibility with OpenHands' HTTP API and tooling ecosystem
|
||||
2. Register these images as enhanced sandbox specifications with rich metadata
|
||||
3. Deploy conversations using their custom sandbox specs (with proper permissions)
|
||||
4. Maintain full compatibility with existing sandbox management and API infrastructure
|
||||
|
||||
The solution leverages the existing V1 architecture's separation between the main server and agent server, requiring minimal changes to core OpenHands components while providing maximum flexibility for custom agent deployment.
|
||||
The solution extends the current `SandboxSpecService` with:
|
||||
- **Permission-based access control** to limit custom specs to authorized users
|
||||
- **Enhanced sandbox specifications** that include agent-specific metadata and requirements
|
||||
- **Secure image management** with validation and approval workflows
|
||||
- **Integrated deployment** through existing conversation creation APIs
|
||||
|
||||
**Trade-offs**: This approach requires users to build and maintain Docker images, increasing complexity compared to simple Python package deployment. However, it provides the necessary isolation and dependency management for complex agent requirements that cannot be satisfied by dynamic package installation.
|
||||
**Trade-offs**: This approach requires users to build and maintain Docker images, increasing complexity compared to simple Python package deployment. However, it provides the necessary isolation and dependency management for complex agent requirements while leveraging proven sandbox infrastructure.
|
||||
|
||||
## 2. User Interface
|
||||
|
||||
@ -58,30 +62,42 @@ ENV CUSTOM_AGENT_MODULE=my_custom_agent
|
||||
ENV CUSTOM_AGENT_CLASS=MySpecializedAgent
|
||||
```
|
||||
|
||||
### 2.2 Agent Registration
|
||||
### 2.2 Enhanced Sandbox Spec Registration
|
||||
|
||||
Users register their custom agent image through a configuration file:
|
||||
Users register their custom agent image as an enhanced sandbox specification:
|
||||
|
||||
```yaml
|
||||
# custom-agent-spec.yaml
|
||||
# enhanced-sandbox-spec.yaml
|
||||
apiVersion: openhands.ai/v1
|
||||
kind: CustomAgentSpec
|
||||
kind: SandboxSpec
|
||||
metadata:
|
||||
name: specialized-ml-agent
|
||||
version: "1.0.0"
|
||||
owner: user@company.com
|
||||
permissions:
|
||||
users: ["user@company.com", "team-lead@company.com"]
|
||||
groups: ["ml-team", "data-science"]
|
||||
spec:
|
||||
image: "myregistry/specialized-ml-agent:v1.0.0"
|
||||
description: "ML agent with TensorFlow and custom data processing tools"
|
||||
capabilities:
|
||||
- machine_learning
|
||||
- data_analysis
|
||||
- custom_visualization
|
||||
# Agent-specific metadata
|
||||
agent:
|
||||
capabilities:
|
||||
- machine_learning
|
||||
- data_analysis
|
||||
- custom_visualization
|
||||
type: "custom"
|
||||
module: "agents.specialized_ml_agent"
|
||||
class: "SpecializedMLAgent"
|
||||
requirements:
|
||||
memory: "4Gi"
|
||||
cpu: "2"
|
||||
environment:
|
||||
TENSORFLOW_VERSION: "2.15.0"
|
||||
CUSTOM_MODEL_PATH: "/app/models"
|
||||
# Agent server configuration
|
||||
CUSTOM_AGENT_MODULE: "agents.specialized_ml_agent"
|
||||
CUSTOM_AGENT_CLASS: "SpecializedMLAgent"
|
||||
ports:
|
||||
- name: agent-server
|
||||
port: 8000
|
||||
@ -89,17 +105,17 @@ spec:
|
||||
port: 6006
|
||||
```
|
||||
|
||||
### 2.3 Conversation Creation with Custom Agent
|
||||
### 2.3 Conversation Creation with Custom Sandbox Spec
|
||||
|
||||
Users can create conversations using their custom agent through the API:
|
||||
Users create conversations using their custom sandbox specs through the existing API:
|
||||
|
||||
```bash
|
||||
# Create conversation with custom agent
|
||||
# Create conversation with custom sandbox spec
|
||||
curl -X POST "https://api.openhands.ai/api/conversations" \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"agent_spec": "specialized-ml-agent:v1.0.0",
|
||||
"sandbox_spec_id": "specialized-ml-agent:v1.0.0",
|
||||
"initial_message": "Analyze this dataset and create a predictive model",
|
||||
"workspace": {
|
||||
"type": "local",
|
||||
@ -108,84 +124,166 @@ curl -X POST "https://api.openhands.ai/api/conversations" \
|
||||
}'
|
||||
```
|
||||
|
||||
### 2.4 Image Management Workflows
|
||||
|
||||
#### 2.4.1 Pre-built Image Approach
|
||||
|
||||
For organizations that want to manage custom agent images centrally:
|
||||
|
||||
```bash
|
||||
# Admin registers pre-built image as sandbox spec
|
||||
curl -X POST "https://api.openhands.ai/api/sandbox-specs" \
|
||||
-H "Authorization: Bearer $ADMIN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "company-ml-agent",
|
||||
"version": "1.0.0",
|
||||
"image": "company-registry/ml-agent:v1.0.0",
|
||||
"permissions": {
|
||||
"groups": ["ml-team", "data-science"]
|
||||
},
|
||||
"agent": {
|
||||
"type": "custom",
|
||||
"capabilities": ["machine_learning", "data_analysis"]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
#### 2.4.2 User Upload Approach
|
||||
|
||||
For users who want to upload their own custom images:
|
||||
|
||||
```bash
|
||||
# User uploads custom image (with security validation)
|
||||
curl -X POST "https://api.openhands.ai/api/sandbox-specs/upload" \
|
||||
-H "Authorization: Bearer $API_KEY" \
|
||||
-F "dockerfile=@Dockerfile" \
|
||||
-F "context=@agent-context.tar.gz" \
|
||||
-F "spec=@sandbox-spec.yaml"
|
||||
```
|
||||
|
||||
## 3. Other Context
|
||||
|
||||
### 3.1 Docker Image Architecture
|
||||
### 3.1 Current Sandbox Specification System
|
||||
|
||||
Custom agent images follow a layered architecture:
|
||||
- **Base Layer**: OpenHands agent server runtime (`ghcr.io/openhands/agent-server`)
|
||||
- **Dependencies Layer**: Custom system packages and tools
|
||||
- **Agent Layer**: Custom agent implementation and configuration
|
||||
- **Runtime Layer**: Environment variables and startup configuration
|
||||
OpenHands V1 uses a sandbox specification system to manage container deployments:
|
||||
|
||||
### 3.2 Agent Server Integration
|
||||
- **Single Default Spec**: Currently only one sandbox spec exists, shared by all users
|
||||
- **SandboxSpecService**: Manages sandbox specifications and container creation
|
||||
- **SandboxSpecInfo**: Contains image, environment, and resource configuration
|
||||
- **No Permissions**: Current system lacks user-based access control
|
||||
|
||||
The software-agent-sdk provides the foundation for custom agents through:
|
||||
- **AgentBase**: Abstract base class defining the agent interface
|
||||
- **Tool System**: Extensible tool registration and execution framework
|
||||
- **HTTP API**: Standard endpoints for conversation management and agent interaction
|
||||
- **Event System**: Structured event handling for actions and observations
|
||||
The existing system provides the foundation but needs enhancement for custom agents:
|
||||
- **Permission Layer**: Required to control access to custom specs
|
||||
- **Rich Metadata**: Need agent-specific information beyond basic container config
|
||||
- **Image Management**: Need secure workflows for custom image registration
|
||||
|
||||
### 3.3 Container Orchestration
|
||||
### 3.2 Enhanced Sandbox Specification Architecture
|
||||
|
||||
Custom agent images integrate with OpenHands' existing container orchestration:
|
||||
- **Sandbox Service**: Manages container lifecycle and resource allocation
|
||||
- **Network Isolation**: Maintains security boundaries between conversations
|
||||
- **Resource Management**: Enforces memory and CPU limits per agent instance
|
||||
- **Health Monitoring**: Tracks agent server availability and performance
|
||||
Our proposal extends the existing system with:
|
||||
|
||||
#### 3.2.1 Permission-Based Access Control
|
||||
- **User Permissions**: Individual user access to specific sandbox specs
|
||||
- **Group Permissions**: Team-based access control for organizational specs
|
||||
- **Owner Management**: Spec ownership and delegation capabilities
|
||||
- **Admin Override**: Administrative access for spec management
|
||||
|
||||
#### 3.2.2 Agent-Specific Metadata
|
||||
- **Agent Configuration**: Module, class, and capability information
|
||||
- **Resource Requirements**: Memory, CPU, and storage specifications
|
||||
- **Environment Variables**: Agent-specific configuration and secrets
|
||||
- **Port Mappings**: Additional ports for agent services (e.g., TensorBoard)
|
||||
|
||||
#### 3.2.3 Image Management Integration
|
||||
- **Registry Support**: Integration with Docker registries for image storage
|
||||
- **Security Validation**: Image scanning and approval workflows
|
||||
- **Version Management**: Support for multiple versions of custom specs
|
||||
- **Build Integration**: Optional image building from Dockerfile uploads
|
||||
|
||||
### 3.3 Existing Container Orchestration Integration
|
||||
|
||||
The enhanced system leverages existing OpenHands infrastructure:
|
||||
|
||||
- **Sandbox Service**: Extended to support permission checks and enhanced specs
|
||||
- **Container Management**: Same lifecycle management with additional metadata
|
||||
- **Network Isolation**: Maintains existing security boundaries
|
||||
- **Resource Enforcement**: Enhanced with custom resource requirements
|
||||
- **Health Monitoring**: Extended to track custom agent-specific metrics
|
||||
|
||||
## 4. Technical Design
|
||||
|
||||
### 4.1 Custom Agent Image Specification
|
||||
### 4.1 Enhanced Sandbox Specification Model
|
||||
|
||||
#### 4.1.1 Image Structure
|
||||
#### 4.1.1 Extended SandboxSpecInfo Structure
|
||||
|
||||
Custom agent images must conform to the following structure:
|
||||
The existing `SandboxSpecInfo` model is enhanced to support custom agents:
|
||||
|
||||
```python
|
||||
# openhands/app_server/sandbox/sandbox_spec_models.py (enhanced)
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
class AgentMetadata(BaseModel):
|
||||
"""Agent-specific metadata for custom agents."""
|
||||
type: str = Field(default="default", description="Agent type (default|custom)")
|
||||
capabilities: List[str] = Field(default_factory=list, description="Agent capabilities")
|
||||
module: Optional[str] = Field(description="Python module containing agent class")
|
||||
class_name: Optional[str] = Field(description="Agent class name")
|
||||
|
||||
class PermissionSpec(BaseModel):
|
||||
"""Permission specification for sandbox spec access."""
|
||||
users: List[str] = Field(default_factory=list, description="Authorized user emails")
|
||||
groups: List[str] = Field(default_factory=list, description="Authorized group names")
|
||||
owner: Optional[str] = Field(description="Spec owner")
|
||||
|
||||
class EnhancedSandboxSpecInfo(BaseModel):
|
||||
"""Enhanced sandbox specification with agent metadata and permissions."""
|
||||
|
||||
# Existing fields from SandboxSpecInfo
|
||||
id: str = Field(description="Docker image identifier")
|
||||
command: List[str] = Field(default_factory=lambda: ['--port', '8000'])
|
||||
initial_env: Dict[str, str] = Field(default_factory=dict)
|
||||
working_dir: str = Field(default="/workspace/project")
|
||||
|
||||
# Enhanced fields
|
||||
name: str = Field(description="Human-readable spec name")
|
||||
version: str = Field(description="Spec version")
|
||||
description: Optional[str] = Field(description="Spec description")
|
||||
|
||||
# Agent-specific metadata
|
||||
agent: AgentMetadata = Field(default_factory=AgentMetadata)
|
||||
|
||||
# Permission and access control
|
||||
permissions: PermissionSpec = Field(default_factory=PermissionSpec)
|
||||
|
||||
# Resource requirements
|
||||
memory_limit: Optional[str] = Field(description="Memory limit (e.g., '4Gi')")
|
||||
cpu_limit: Optional[str] = Field(description="CPU limit (e.g., '2')")
|
||||
|
||||
# Additional ports for custom services
|
||||
ports: List[Dict[str, any]] = Field(
|
||||
default_factory=lambda: [{"name": "agent-server", "port": 8000}]
|
||||
)
|
||||
```
|
||||
|
||||
#### 4.1.2 Custom Agent Image Structure
|
||||
|
||||
Custom agent images extend the base agent server with this structure:
|
||||
|
||||
```
|
||||
/app/
|
||||
├── config/
|
||||
│ ├── agent_config.json # Agent configuration
|
||||
│ └── tool_registry.json # Custom tool definitions
|
||||
│ └── tool_registry.json # Custom tool definitions (optional)
|
||||
├── agents/
|
||||
│ └── custom_agent.py # Agent implementation
|
||||
├── tools/
|
||||
├── tools/ # Custom tools (optional)
|
||||
│ ├── __init__.py
|
||||
│ └── custom_tools.py # Custom tool implementations
|
||||
│ └── custom_tools.py
|
||||
└── startup/
|
||||
└── init_agent.py # Agent initialization script
|
||||
```
|
||||
|
||||
#### 4.1.2 Agent Configuration Schema
|
||||
|
||||
```python
|
||||
# agent_config.json
|
||||
{
|
||||
"agent": {
|
||||
"name": "SpecializedMLAgent",
|
||||
"version": "1.0.0",
|
||||
"description": "ML agent with TensorFlow capabilities",
|
||||
"module": "agents.custom_agent",
|
||||
"class": "SpecializedMLAgent"
|
||||
},
|
||||
"capabilities": [
|
||||
"machine_learning",
|
||||
"data_analysis",
|
||||
"visualization"
|
||||
],
|
||||
"tools": [
|
||||
{"name": "TerminalTool"},
|
||||
{"name": "FileEditorTool"},
|
||||
{"name": "TensorFlowTool", "module": "tools.custom_tools"},
|
||||
{"name": "DataVisualizationTool", "module": "tools.custom_tools"}
|
||||
],
|
||||
"environment": {
|
||||
"TENSORFLOW_VERSION": "2.15.0",
|
||||
"MODEL_CACHE_DIR": "/app/models"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Agent Implementation Interface
|
||||
|
||||
#### 4.2.1 Custom Agent Base Class
|
||||
@ -365,72 +463,81 @@ async def startup_event():
|
||||
print("Using default OpenHands agent")
|
||||
```
|
||||
|
||||
### 4.4 Sandbox Service Integration
|
||||
### 4.4 Enhanced Sandbox Service Integration
|
||||
|
||||
#### 4.4.1 Custom Agent Spec Model
|
||||
#### 4.4.1 Permission-Aware Sandbox Service
|
||||
|
||||
```python
|
||||
# openhands/app_server/sandbox/custom_agent_models.py
|
||||
from pydantic import BaseModel, Field
|
||||
# openhands/app_server/sandbox/enhanced_sandbox_spec_service.py
|
||||
from openhands.app_server.sandbox.sandbox_spec_service import SandboxSpecService
|
||||
from openhands.app_server.sandbox.sandbox_spec_models import SandboxSpecInfo, EnhancedSandboxSpecInfo
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
class CustomAgentSpec(BaseModel):
|
||||
"""Specification for custom agent deployment."""
|
||||
class EnhancedSandboxSpecService(SandboxSpecService):
|
||||
"""Enhanced sandbox service with permissions and custom agent support."""
|
||||
|
||||
name: str = Field(description="Unique name for the custom agent")
|
||||
version: str = Field(description="Version of the custom agent")
|
||||
image: str = Field(description="Docker image containing the custom agent")
|
||||
description: Optional[str] = Field(description="Human-readable description")
|
||||
def __init__(self, spec_registry: Dict[str, EnhancedSandboxSpecInfo]):
|
||||
super().__init__()
|
||||
self.spec_registry = spec_registry
|
||||
|
||||
capabilities: List[str] = Field(
|
||||
default_factory=list,
|
||||
description="List of agent capabilities"
|
||||
)
|
||||
def get_available_sandbox_specs(self, user_email: str, user_groups: List[str]) -> List[str]:
|
||||
"""Get sandbox specs available to the user based on permissions."""
|
||||
available_specs = []
|
||||
|
||||
for spec_key, spec in self.spec_registry.items():
|
||||
if self._has_permission(spec, user_email, user_groups):
|
||||
available_specs.append(spec_key)
|
||||
|
||||
return available_specs
|
||||
|
||||
requirements: Dict[str, str] = Field(
|
||||
default_factory=dict,
|
||||
description="Resource requirements (memory, cpu)"
|
||||
)
|
||||
|
||||
environment: Dict[str, str] = Field(
|
||||
default_factory=dict,
|
||||
description="Environment variables for the agent"
|
||||
)
|
||||
|
||||
ports: List[Dict[str, any]] = Field(
|
||||
default_factory=lambda: [{"name": "agent-server", "port": 8000}],
|
||||
description="Port configurations"
|
||||
)
|
||||
```
|
||||
|
||||
#### 4.4.2 Custom Sandbox Spec Service
|
||||
|
||||
```python
|
||||
# openhands/app_server/sandbox/custom_agent_sandbox_service.py
|
||||
from openhands.app_server.sandbox.sandbox_spec_service import SandboxSpecService
|
||||
from openhands.app_server.sandbox.sandbox_spec_models import SandboxSpecInfo
|
||||
from openhands.app_server.sandbox.custom_agent_models import CustomAgentSpec
|
||||
|
||||
class CustomAgentSandboxService(SandboxSpecService):
|
||||
"""Sandbox service for custom agent deployments."""
|
||||
|
||||
def __init__(self, custom_agent_registry: Dict[str, CustomAgentSpec]):
|
||||
self.custom_agent_registry = custom_agent_registry
|
||||
|
||||
def create_custom_agent_sandbox_spec(
|
||||
self,
|
||||
agent_spec_name: str,
|
||||
agent_version: str = "latest"
|
||||
def get_sandbox_spec_by_id(
|
||||
self,
|
||||
spec_id: str,
|
||||
user_email: str,
|
||||
user_groups: List[str]
|
||||
) -> SandboxSpecInfo:
|
||||
"""Create sandbox specification for custom agent."""
|
||||
"""Get sandbox spec by ID with permission check."""
|
||||
|
||||
if spec_id not in self.spec_registry:
|
||||
# Fall back to default specs for backward compatibility
|
||||
return super().get_default_sandbox_specs()[0]
|
||||
|
||||
enhanced_spec = self.spec_registry[spec_id]
|
||||
|
||||
# Check permissions
|
||||
if not self._has_permission(enhanced_spec, user_email, user_groups):
|
||||
raise PermissionError(f"User {user_email} does not have access to spec {spec_id}")
|
||||
|
||||
# Convert to SandboxSpecInfo for existing infrastructure
|
||||
return self._convert_to_sandbox_spec_info(enhanced_spec)
|
||||
|
||||
spec_key = f"{agent_spec_name}:{agent_version}"
|
||||
if spec_key not in self.custom_agent_registry:
|
||||
raise ValueError(f"Custom agent spec not found: {spec_key}")
|
||||
def _has_permission(
|
||||
self,
|
||||
spec: EnhancedSandboxSpecInfo,
|
||||
user_email: str,
|
||||
user_groups: List[str]
|
||||
) -> bool:
|
||||
"""Check if user has permission to use the sandbox spec."""
|
||||
|
||||
# Owner always has access
|
||||
if spec.permissions.owner == user_email:
|
||||
return True
|
||||
|
||||
# Check user permissions
|
||||
if user_email in spec.permissions.users:
|
||||
return True
|
||||
|
||||
# Check group permissions
|
||||
for group in user_groups:
|
||||
if group in spec.permissions.groups:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
custom_spec = self.custom_agent_registry[spec_key]
|
||||
|
||||
# Build environment variables
|
||||
def _convert_to_sandbox_spec_info(self, enhanced_spec: EnhancedSandboxSpecInfo) -> SandboxSpecInfo:
|
||||
"""Convert enhanced spec to standard SandboxSpecInfo."""
|
||||
|
||||
# Build environment variables including agent configuration
|
||||
env_vars = {
|
||||
'OPENVSCODE_SERVER_ROOT': '/openhands/.openvscode-server',
|
||||
'OH_ENABLE_VNC': '0',
|
||||
@ -439,119 +546,231 @@ class CustomAgentSandboxService(SandboxSpecService):
|
||||
'OH_BASH_EVENTS_DIR': '/workspace/bash_events',
|
||||
'PYTHONUNBUFFERED': '1',
|
||||
'ENV_LOG_LEVEL': '20',
|
||||
# Custom agent environment
|
||||
'CUSTOM_AGENT_MODULE': custom_spec.name,
|
||||
'CUSTOM_AGENT_CLASS': custom_spec.name,
|
||||
**custom_spec.environment
|
||||
**enhanced_spec.initial_env
|
||||
}
|
||||
|
||||
# Add custom agent configuration if specified
|
||||
if enhanced_spec.agent.type == "custom":
|
||||
env_vars.update({
|
||||
'CUSTOM_AGENT_MODULE': enhanced_spec.agent.module,
|
||||
'CUSTOM_AGENT_CLASS': enhanced_spec.agent.class_name,
|
||||
})
|
||||
|
||||
return SandboxSpecInfo(
|
||||
id=custom_spec.image,
|
||||
command=['--port', '8000'],
|
||||
id=enhanced_spec.id,
|
||||
command=enhanced_spec.command,
|
||||
initial_env=env_vars,
|
||||
working_dir='/workspace/project',
|
||||
# Resource requirements
|
||||
memory_limit=custom_spec.requirements.get('memory', '2Gi'),
|
||||
cpu_limit=custom_spec.requirements.get('cpu', '1'),
|
||||
working_dir=enhanced_spec.working_dir,
|
||||
)
|
||||
|
||||
def register_sandbox_spec(
|
||||
self,
|
||||
spec: EnhancedSandboxSpecInfo,
|
||||
admin_user: str
|
||||
) -> str:
|
||||
"""Register a new sandbox spec (admin only)."""
|
||||
|
||||
spec_key = f"{spec.name}:{spec.version}"
|
||||
|
||||
# Validate spec
|
||||
self._validate_sandbox_spec(spec)
|
||||
|
||||
# Store in registry
|
||||
self.spec_registry[spec_key] = spec
|
||||
|
||||
return spec_key
|
||||
|
||||
def _validate_sandbox_spec(self, spec: EnhancedSandboxSpecInfo) -> None:
|
||||
"""Validate sandbox spec for security and correctness."""
|
||||
|
||||
# Image validation
|
||||
if not spec.id or not spec.id.strip():
|
||||
raise ValueError("Image ID cannot be empty")
|
||||
|
||||
# Permission validation
|
||||
if not spec.permissions.owner:
|
||||
raise ValueError("Sandbox spec must have an owner")
|
||||
|
||||
# Agent validation for custom agents
|
||||
if spec.agent.type == "custom":
|
||||
if not spec.agent.module or not spec.agent.class_name:
|
||||
raise ValueError("Custom agents must specify module and class_name")
|
||||
```
|
||||
|
||||
### 4.5 API Integration
|
||||
### 4.5 Enhanced API Integration
|
||||
|
||||
#### 4.5.1 Custom Agent Conversation Creation
|
||||
#### 4.5.1 Enhanced Conversation Creation
|
||||
|
||||
```python
|
||||
# openhands/server/routes/custom_agent_routes.py
|
||||
# openhands/server/routes/conversation_routes.py (enhanced)
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional, Dict, Any, List
|
||||
from uuid import UUID
|
||||
|
||||
from openhands.app_server.sandbox.custom_agent_sandbox_service import CustomAgentSandboxService
|
||||
from openhands.app_server.sandbox.enhanced_sandbox_spec_service import EnhancedSandboxSpecService
|
||||
from openhands.server.session.agent_session import AgentSession
|
||||
from openhands.server.auth import get_current_user, get_user_groups
|
||||
|
||||
router = APIRouter(prefix="/api/custom-agents", tags=["Custom Agents"])
|
||||
|
||||
class CreateCustomAgentConversationRequest(BaseModel):
|
||||
agent_spec: str # Format: "agent-name:version"
|
||||
# Enhanced conversation creation request
|
||||
class CreateConversationRequest(BaseModel):
|
||||
initial_message: str
|
||||
workspace_config: Optional[Dict[str, Any]] = None
|
||||
|
||||
class CreateCustomAgentConversationResponse(BaseModel):
|
||||
conversation_id: UUID
|
||||
agent_spec: str
|
||||
status: str
|
||||
# New field for custom sandbox spec
|
||||
sandbox_spec_id: Optional[str] = None
|
||||
|
||||
@router.post("/conversations")
|
||||
async def create_custom_agent_conversation(
|
||||
request: CreateCustomAgentConversationRequest,
|
||||
sandbox_service: CustomAgentSandboxService = Depends(get_custom_agent_sandbox_service)
|
||||
) -> CreateCustomAgentConversationResponse:
|
||||
"""Create a new conversation with a custom agent."""
|
||||
async def create_conversation(
|
||||
request: CreateConversationRequest,
|
||||
current_user: str = Depends(get_current_user),
|
||||
user_groups: List[str] = Depends(get_user_groups),
|
||||
sandbox_service: EnhancedSandboxSpecService = Depends(get_enhanced_sandbox_service)
|
||||
) -> ConversationResponse:
|
||||
"""Create conversation with optional custom sandbox spec."""
|
||||
|
||||
try:
|
||||
# Parse agent spec
|
||||
agent_name, agent_version = request.agent_spec.split(":", 1)
|
||||
except ValueError:
|
||||
agent_name = request.agent_spec
|
||||
agent_version = "latest"
|
||||
if request.sandbox_spec_id:
|
||||
# Use custom sandbox spec with permission check
|
||||
sandbox_spec = sandbox_service.get_sandbox_spec_by_id(
|
||||
request.sandbox_spec_id,
|
||||
current_user,
|
||||
user_groups
|
||||
)
|
||||
else:
|
||||
# Use default sandbox spec
|
||||
sandbox_spec = sandbox_service.get_default_sandbox_specs()[0]
|
||||
|
||||
# Create custom sandbox specification
|
||||
try:
|
||||
sandbox_spec = sandbox_service.create_custom_agent_sandbox_spec(
|
||||
agent_name, agent_version
|
||||
# Create sandbox and conversation
|
||||
sandbox = await sandbox_service.create_sandbox(sandbox_spec)
|
||||
await wait_for_agent_server_ready(sandbox)
|
||||
|
||||
conversation = await create_conversation_with_sandbox(
|
||||
sandbox=sandbox,
|
||||
initial_message=request.initial_message,
|
||||
workspace_config=request.workspace_config
|
||||
)
|
||||
|
||||
return ConversationResponse(
|
||||
conversation_id=conversation.id,
|
||||
status="created",
|
||||
sandbox_spec_id=request.sandbox_spec_id or "default"
|
||||
)
|
||||
|
||||
except PermissionError as e:
|
||||
raise HTTPException(status_code=403, detail=str(e))
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
```
|
||||
|
||||
# Create sandbox with custom spec
|
||||
sandbox = await sandbox_service.create_sandbox(sandbox_spec)
|
||||
#### 4.5.2 Sandbox Spec Management API
|
||||
|
||||
# Wait for agent server to be ready
|
||||
await wait_for_agent_server_ready(sandbox)
|
||||
```python
|
||||
# openhands/server/routes/sandbox_spec_routes.py (new)
|
||||
from fastapi import APIRouter, HTTPException, Depends, UploadFile, File
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
import yaml
|
||||
|
||||
# Create conversation
|
||||
conversation = await create_conversation_with_sandbox(
|
||||
sandbox=sandbox,
|
||||
initial_message=request.initial_message,
|
||||
workspace_config=request.workspace_config
|
||||
)
|
||||
from openhands.app_server.sandbox.enhanced_sandbox_spec_service import EnhancedSandboxSpecService
|
||||
from openhands.app_server.sandbox.sandbox_spec_models import EnhancedSandboxSpecInfo
|
||||
from openhands.server.auth import get_current_user, get_user_groups, require_admin
|
||||
|
||||
return CreateCustomAgentConversationResponse(
|
||||
conversation_id=conversation.id,
|
||||
agent_spec=request.agent_spec,
|
||||
status="created"
|
||||
)
|
||||
router = APIRouter(prefix="/api/sandbox-specs", tags=["Sandbox Specs"])
|
||||
|
||||
@router.get("/")
|
||||
async def list_available_sandbox_specs(
|
||||
current_user: str = Depends(get_current_user),
|
||||
user_groups: List[str] = Depends(get_user_groups),
|
||||
sandbox_service: EnhancedSandboxSpecService = Depends(get_enhanced_sandbox_service)
|
||||
) -> List[str]:
|
||||
"""List sandbox specs available to the current user."""
|
||||
|
||||
return sandbox_service.get_available_sandbox_specs(current_user, user_groups)
|
||||
|
||||
@router.post("/")
|
||||
async def register_sandbox_spec(
|
||||
spec_data: EnhancedSandboxSpecInfo,
|
||||
current_user: str = Depends(require_admin),
|
||||
sandbox_service: EnhancedSandboxSpecService = Depends(get_enhanced_sandbox_service)
|
||||
) -> Dict[str, str]:
|
||||
"""Register a new sandbox spec (admin only)."""
|
||||
|
||||
try:
|
||||
spec_key = sandbox_service.register_sandbox_spec(spec_data, current_user)
|
||||
return {"spec_id": spec_key, "status": "registered"}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@router.post("/upload")
|
||||
async def upload_custom_image(
|
||||
dockerfile: UploadFile = File(...),
|
||||
context: UploadFile = File(...),
|
||||
spec: UploadFile = File(...),
|
||||
current_user: str = Depends(get_current_user),
|
||||
sandbox_service: EnhancedSandboxSpecService = Depends(get_enhanced_sandbox_service)
|
||||
) -> Dict[str, str]:
|
||||
"""Upload custom image with Dockerfile and context (with security validation)."""
|
||||
|
||||
try:
|
||||
# Parse spec file
|
||||
spec_content = await spec.read()
|
||||
spec_data = yaml.safe_load(spec_content)
|
||||
|
||||
# Validate user has permission to create specs
|
||||
if not _can_user_create_specs(current_user):
|
||||
raise HTTPException(status_code=403, detail="User not authorized to create custom specs")
|
||||
|
||||
# Security validation of Dockerfile
|
||||
dockerfile_content = await dockerfile.read()
|
||||
_validate_dockerfile_security(dockerfile_content)
|
||||
|
||||
# Build image (implementation depends on build system)
|
||||
image_id = await _build_custom_image(dockerfile_content, context, current_user)
|
||||
|
||||
# Create enhanced spec
|
||||
enhanced_spec = EnhancedSandboxSpecInfo(**spec_data)
|
||||
enhanced_spec.id = image_id
|
||||
enhanced_spec.permissions.owner = current_user
|
||||
|
||||
# Register the spec
|
||||
spec_key = sandbox_service.register_sandbox_spec(enhanced_spec, current_user)
|
||||
|
||||
return {"spec_id": spec_key, "image_id": image_id, "status": "uploaded"}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Upload failed: {str(e)}")
|
||||
```
|
||||
|
||||
## 5. Implementation Plan
|
||||
|
||||
All implementation must pass existing lints and tests. New functionality requires comprehensive test coverage including unit tests, integration tests, and end-to-end scenarios.
|
||||
|
||||
### 5.1 Foundation Models and Services (M1)
|
||||
### 5.1 Enhanced Sandbox Models and Permissions (M1)
|
||||
|
||||
#### 5.1.1 Custom Agent Specification Models
|
||||
#### 5.1.1 Enhanced Sandbox Specification Models
|
||||
|
||||
* `openhands/app_server/sandbox/custom_agent_models.py`
|
||||
* `tests/unit/app_server/sandbox/test_custom_agent_models.py`
|
||||
* `openhands/app_server/sandbox/sandbox_spec_models.py` (enhanced)
|
||||
* `tests/unit/app_server/sandbox/test_enhanced_sandbox_spec_models.py`
|
||||
|
||||
Define Pydantic models for custom agent specifications including image references, resource requirements, and environment configuration.
|
||||
Extend existing `SandboxSpecInfo` with `EnhancedSandboxSpecInfo` including agent metadata, permissions, and resource requirements. This is the **core requirement** identified by the engineer.
|
||||
|
||||
#### 5.1.2 Custom Agent Registry Service
|
||||
#### 5.1.2 Permission System Foundation
|
||||
|
||||
* `openhands/app_server/sandbox/custom_agent_registry.py`
|
||||
* `tests/unit/app_server/sandbox/test_custom_agent_registry.py`
|
||||
* `openhands/server/auth/permissions.py`
|
||||
* `tests/unit/server/auth/test_permissions.py`
|
||||
|
||||
Implement registry service for managing custom agent specifications with CRUD operations and validation.
|
||||
Implement user and group-based permission system for sandbox spec access control. This addresses the **security concerns** from V0 mentioned by the engineer.
|
||||
|
||||
### 5.2 Sandbox Integration (M2)
|
||||
**Demo**: Create enhanced sandbox specs with permission restrictions and verify access control works correctly.
|
||||
|
||||
#### 5.2.1 Custom Agent Sandbox Service
|
||||
### 5.2 Enhanced Sandbox Service (M2)
|
||||
|
||||
* `openhands/app_server/sandbox/custom_agent_sandbox_service.py`
|
||||
* `tests/unit/app_server/sandbox/test_custom_agent_sandbox_service.py`
|
||||
#### 5.2.1 Permission-Aware Sandbox Service
|
||||
|
||||
Extend sandbox service to support custom agent image deployment with resource management and environment configuration.
|
||||
* `openhands/app_server/sandbox/enhanced_sandbox_spec_service.py`
|
||||
* `tests/unit/app_server/sandbox/test_enhanced_sandbox_spec_service.py`
|
||||
|
||||
Extend existing `SandboxSpecService` with permission checks and enhanced spec management. This **builds on existing infrastructure** as the engineer suggested.
|
||||
|
||||
#### 5.2.2 Agent Server Startup Integration
|
||||
|
||||
@ -560,40 +779,58 @@ Extend sandbox service to support custom agent image deployment with resource ma
|
||||
|
||||
Implement custom agent loading mechanism in agent server startup process with configuration-driven agent instantiation.
|
||||
|
||||
**Demo**: Deploy a simple custom agent with additional Python packages and verify it responds to basic queries through the existing HTTP API.
|
||||
**Demo**: Deploy custom agents using enhanced sandbox specs and verify permission-based access control works end-to-end.
|
||||
|
||||
### 5.3 API Endpoints (M3)
|
||||
### 5.3 Image Management and API Integration (M3)
|
||||
|
||||
#### 5.3.1 Custom Agent Management API
|
||||
#### 5.3.1 Secure Image Management
|
||||
|
||||
* `openhands/server/routes/custom_agent_routes.py`
|
||||
* `tests/unit/server/routes/test_custom_agent_routes.py`
|
||||
* `openhands/app_server/sandbox/image_builder.py`
|
||||
* `openhands/app_server/security/dockerfile_validator.py`
|
||||
* `tests/unit/app_server/sandbox/test_image_builder.py`
|
||||
* `tests/unit/app_server/security/test_dockerfile_validator.py`
|
||||
|
||||
Implement REST API endpoints for custom agent registration, conversation creation, and status management.
|
||||
Implement both **pre-built image registration** and **secure user upload** workflows as identified by the engineer. This addresses the security issues from V0.
|
||||
|
||||
#### 5.3.2 Agent Specification Validation
|
||||
#### 5.3.2 Enhanced Conversation API
|
||||
|
||||
* `openhands/app_server/sandbox/custom_agent_validator.py`
|
||||
* `tests/unit/app_server/sandbox/test_custom_agent_validator.py`
|
||||
* `openhands/server/routes/conversation_routes.py` (enhanced)
|
||||
* `openhands/server/routes/sandbox_spec_routes.py` (new)
|
||||
* `tests/unit/server/routes/test_enhanced_conversation_routes.py`
|
||||
* `tests/unit/server/routes/test_sandbox_spec_routes.py`
|
||||
|
||||
Add validation logic for custom agent specifications including image accessibility, resource limits, and security constraints.
|
||||
Enhance existing conversation creation API to support `sandbox_spec_id` parameter and add new sandbox spec management endpoints.
|
||||
|
||||
**Demo**: Create conversations with custom agents through API endpoints and demonstrate tool execution with custom dependencies.
|
||||
**Demo**: Create conversations with custom sandbox specs through existing API endpoints and demonstrate both pre-built and user-uploaded image workflows.
|
||||
|
||||
### 5.4 Advanced Features (M4)
|
||||
### 5.4 Advanced Security and Management (M4)
|
||||
|
||||
#### 5.4.1 Resource Management and Monitoring
|
||||
#### 5.4.1 Image Security Validation
|
||||
|
||||
* `openhands/app_server/sandbox/custom_agent_monitor.py`
|
||||
* `tests/unit/app_server/sandbox/test_custom_agent_monitor.py`
|
||||
* `openhands/app_server/security/image_scanner.py`
|
||||
* `openhands/app_server/security/security_policies.py`
|
||||
* `tests/unit/app_server/security/test_image_scanner.py`
|
||||
|
||||
Implement resource monitoring and management for custom agent containers including memory/CPU usage tracking and automatic scaling.
|
||||
Implement comprehensive security validation including image vulnerability scanning, Dockerfile analysis, and approval workflows.
|
||||
|
||||
#### 5.4.2 Security and Isolation
|
||||
#### 5.4.2 Spec Registry and Lifecycle Management
|
||||
|
||||
* `openhands/app_server/security/custom_agent_security.py`
|
||||
* `tests/unit/app_server/security/test_custom_agent_security.py`
|
||||
* `openhands/app_server/sandbox/spec_registry.py`
|
||||
* `openhands/app_server/sandbox/spec_lifecycle.py`
|
||||
* `tests/unit/app_server/sandbox/test_spec_registry.py`
|
||||
|
||||
Add security validation for custom agent images including vulnerability scanning integration and network isolation policies.
|
||||
Add persistent storage for enhanced sandbox specs, version management, and lifecycle policies (deprecation, cleanup).
|
||||
|
||||
**Demo**: Deploy multiple custom agents simultaneously with different resource requirements and demonstrate proper isolation and resource management.
|
||||
**Demo**: Deploy multiple custom agents with different permission levels, demonstrate security validation workflows, and show proper spec lifecycle management.
|
||||
|
||||
---
|
||||
|
||||
## Key Alignment with Engineer's Approach
|
||||
|
||||
This revised implementation plan directly addresses the engineer's requirements:
|
||||
|
||||
1. **✅ Uses existing sandbox specs system** - Enhanced rather than replaced
|
||||
2. **✅ Permissions as core requirement** - Moved to M1 instead of M4
|
||||
3. **✅ Two image management approaches** - Pre-built registration and secure user uploads
|
||||
4. **✅ Security-first design** - Addresses V0 security issues with comprehensive validation
|
||||
5. **✅ Minimal infrastructure changes** - Builds on existing `SandboxSpecService` and conversation APIs
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user