mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 23:08:04 -05:00
Improve type coverage for nested runtime (#8921)
This commit is contained in:
@@ -37,7 +37,8 @@ repos:
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies:
|
||||
[types-requests, types-setuptools, types-pyyaml, types-toml]
|
||||
[types-requests, types-setuptools, types-pyyaml, types-toml, types-docker, lxml]
|
||||
# To see gaps add `--html-report mypy-report/`
|
||||
entry: mypy --config-file dev_config/python/mypy.ini openhands/
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from typing import Callable
|
||||
import typing
|
||||
from uuid import UUID
|
||||
|
||||
import docker
|
||||
@@ -164,7 +165,7 @@ class DockerRuntime(ActionExecutionClient):
|
||||
f'Container started: {self.container_name}. VSCode URL: {self.vscode_url}',
|
||||
)
|
||||
|
||||
if DEBUG_RUNTIME:
|
||||
if DEBUG_RUNTIME and self.container:
|
||||
self.log_streamer = LogStreamer(self.container, self.log)
|
||||
else:
|
||||
self.log_streamer = None
|
||||
@@ -281,7 +282,7 @@ class DockerRuntime(ActionExecutionClient):
|
||||
self.api_url = f'{self.config.sandbox.local_runtime_url}:{self._container_port}'
|
||||
|
||||
use_host_network = self.config.sandbox.use_host_network
|
||||
network_mode: str | None = 'host' if use_host_network else None
|
||||
network_mode: typing.Literal['host'] | None = 'host' if use_host_network else None
|
||||
|
||||
# Initialize port mappings
|
||||
port_mapping: dict[str, list[dict[str, str]]] | None = None
|
||||
@@ -353,6 +354,8 @@ class DockerRuntime(ActionExecutionClient):
|
||||
command = self.get_action_execution_server_startup_command()
|
||||
|
||||
try:
|
||||
if self.runtime_container_image is None:
|
||||
raise ValueError("Runtime container image is not set")
|
||||
self.container = self.docker_client.containers.run(
|
||||
self.runtime_container_image,
|
||||
command=command,
|
||||
@@ -364,7 +367,7 @@ class DockerRuntime(ActionExecutionClient):
|
||||
name=self.container_name,
|
||||
detach=True,
|
||||
environment=environment,
|
||||
volumes=volumes,
|
||||
volumes=volumes, # type: ignore
|
||||
device_requests=(
|
||||
[docker.types.DeviceRequest(capabilities=[['gpu']], count=-1)]
|
||||
if self.config.sandbox.enable_gpu
|
||||
|
||||
@@ -48,12 +48,12 @@ class ServerConfig(ServerConfigInterface):
|
||||
return config
|
||||
|
||||
|
||||
def load_server_config():
|
||||
def load_server_config() -> ServerConfig:
|
||||
config_cls = os.environ.get('OPENHANDS_CONFIG_CLS', None)
|
||||
logger.info(f'Using config class {config_cls}')
|
||||
|
||||
server_config_cls = get_impl(ServerConfig, config_cls)
|
||||
server_config = server_config_cls()
|
||||
server_config : ServerConfig = server_config_cls()
|
||||
server_config.verify_config()
|
||||
|
||||
return server_config
|
||||
|
||||
@@ -90,7 +90,8 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
"""
|
||||
Get the running agent loops directly from docker.
|
||||
"""
|
||||
names = (container.name for container in self.docker_client.containers.list())
|
||||
containers : list[Container] = self.docker_client.containers.list()
|
||||
names = (container.name or '' for container in containers)
|
||||
conversation_ids = {
|
||||
name[len('openhands-runtime-') :]
|
||||
for name in names
|
||||
@@ -282,11 +283,11 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
async def close_session(self, sid: str):
|
||||
stop_all_containers(f'openhands-runtime-{sid}')
|
||||
|
||||
async def get_agent_loop_info(self, user_id=None, filter_to_sids=None):
|
||||
async def get_agent_loop_info(self, user_id: str | None = None, filter_to_sids: set[str] | None = None) -> list[AgentLoopInfo]:
|
||||
results = []
|
||||
containers = self.docker_client.containers.list()
|
||||
containers : list[Container] = self.docker_client.containers.list()
|
||||
for container in containers:
|
||||
if not container.name.startswith('openhands-runtime-'):
|
||||
if not container.name or not container.name.startswith('openhands-runtime-'):
|
||||
continue
|
||||
conversation_id = container.name[len('openhands-runtime-') :]
|
||||
if filter_to_sids is not None and conversation_id not in filter_to_sids:
|
||||
@@ -349,11 +350,12 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
def get_nested_url_for_container(self, container: Container) -> str:
|
||||
env = container.attrs['Config']['Env']
|
||||
container_port = int(next(e[5:] for e in env if e.startswith('port=')))
|
||||
conversation_id = container.name[len('openhands-runtime-') :]
|
||||
container_name = container.name or ''
|
||||
conversation_id = container_name[len('openhands-runtime-') :]
|
||||
nested_url = f'{self.config.sandbox.local_runtime_url}:{container_port}/api/conversations/{conversation_id}'
|
||||
return nested_url
|
||||
|
||||
def _get_session_api_key_for_conversation(self, conversation_id: str):
|
||||
def _get_session_api_key_for_conversation(self, conversation_id: str) -> str:
|
||||
jwt_secret = self.config.jwt_secret.get_secret_value() # type:ignore
|
||||
conversation_key = f'{jwt_secret}:{conversation_id}'.encode()
|
||||
session_api_key = (
|
||||
@@ -363,7 +365,7 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
)
|
||||
return session_api_key
|
||||
|
||||
async def ensure_num_conversations_below_limit(self, sid: str, user_id: str | None):
|
||||
async def ensure_num_conversations_below_limit(self, sid: str, user_id: str | None) -> None:
|
||||
response_ids = await self.get_running_agent_loops(user_id)
|
||||
if len(response_ids) >= self.config.max_concurrent_conversations:
|
||||
logger.info(
|
||||
@@ -395,7 +397,7 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
)
|
||||
await self.close_session(oldest_conversation_id)
|
||||
|
||||
def _get_provider_handler(self, settings: Settings):
|
||||
def _get_provider_handler(self, settings: Settings) -> ProviderHandler:
|
||||
provider_tokens = None
|
||||
if isinstance(settings, ConversationInitData):
|
||||
provider_tokens = settings.git_provider_tokens
|
||||
@@ -405,7 +407,7 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
)
|
||||
return provider_handler
|
||||
|
||||
async def _create_runtime(self, sid: str, user_id: str | None, settings: Settings):
|
||||
async def _create_runtime(self, sid: str, user_id: str | None, settings: Settings) -> DockerRuntime:
|
||||
# This session is created here only because it is the easiest way to get a runtime, which
|
||||
# is the easiest way to create the needed docker container
|
||||
session = Session(
|
||||
@@ -480,7 +482,7 @@ class DockerNestedConversationManager(ConversationManager):
|
||||
if container:
|
||||
status = container.status
|
||||
if status == 'exited':
|
||||
await call_sync_from_async(container.start())
|
||||
await call_sync_from_async(container.start)
|
||||
return True
|
||||
return False
|
||||
except docker.errors.NotFound as e:
|
||||
|
||||
Reference in New Issue
Block a user