From 29ba94fc0f6e102e80a9d6c6a36aebb19b8468c4 Mon Sep 17 00:00:00 2001 From: tofarr Date: Mon, 24 Feb 2025 15:35:10 +0000 Subject: [PATCH 1/9] Fix file descriptor leak (#6897) Co-authored-by: openhands --- openhands/llm/llm.py | 13 +++-- openhands/utils/ensure_httpx_close.py | 43 ++++++++++++++ tests/unit/test_ensure_httpx_close.py | 84 +++++++++++++++++++++++++++ tests/unit/test_llm.py | 26 +++++++++ 4 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 openhands/utils/ensure_httpx_close.py create mode 100644 tests/unit/test_ensure_httpx_close.py diff --git a/openhands/llm/llm.py b/openhands/llm/llm.py index 66bc6f99cb09..944e4660b47e 100644 --- a/openhands/llm/llm.py +++ b/openhands/llm/llm.py @@ -8,6 +8,7 @@ import requests from openhands.core.config import LLMConfig +from openhands.utils.ensure_httpx_close import EnsureHttpxClose with warnings.catch_warnings(): warnings.simplefilter('ignore') @@ -230,9 +231,9 @@ def wrapper(*args, **kwargs): # Record start time for latency measurement start_time = time.time() - - # we don't support streaming here, thus we get a ModelResponse - resp: ModelResponse = self._completion_unwrapped(*args, **kwargs) + with EnsureHttpxClose(): + # we don't support streaming here, thus we get a ModelResponse + resp: ModelResponse = self._completion_unwrapped(*args, **kwargs) # Calculate and record latency latency = time.time() - start_time @@ -287,7 +288,11 @@ def wrapper(*args, **kwargs): 'messages': messages, 'response': resp, 'args': args, - 'kwargs': {k: v for k, v in kwargs.items() if k != 'messages'}, + 'kwargs': { + k: v + for k, v in kwargs.items() + if k not in ('messages', 'client') + }, 'timestamp': time.time(), 'cost': cost, } diff --git a/openhands/utils/ensure_httpx_close.py b/openhands/utils/ensure_httpx_close.py new file mode 100644 index 000000000000..f60a7189d565 --- /dev/null +++ b/openhands/utils/ensure_httpx_close.py @@ -0,0 +1,43 @@ +""" +LiteLLM currently have an issue where HttpHandlers are being created but not +closed. We have submitted a PR to them, (https://github.com/BerriAI/litellm/pull/8711) +and their dev team say they are in the process of a refactor that will fix this, but +in the meantime, we need to manage the lifecycle of the httpx.Client manually. + +We can't simply pass in our own client object, because all the different implementations use +different types of client object. + +So we monkey patch the httpx.Client class to track newly created instances and close these +when the operations complete. (This is relatively safe, as if the client is reused after this +then is will transparently reopen) + +Hopefully, this will be fixed soon and we can remove this abomination. +""" + +from dataclasses import dataclass, field +from functools import wraps +from typing import Callable + +from httpx import Client + + +@dataclass +class EnsureHttpxClose: + clients: list[Client] = field(default_factory=list) + original_init: Callable | None = None + + def __enter__(self): + self.original_init = Client.__init__ + + @wraps(Client.__init__) + def init_wrapper(*args, **kwargs): + self.clients.append(args[0]) + return self.original_init(*args, **kwargs) # type: ignore + + Client.__init__ = init_wrapper + + def __exit__(self, type, value, traceback): + Client.__init__ = self.original_init + while self.clients: + client = self.clients.pop() + client.close() diff --git a/tests/unit/test_ensure_httpx_close.py b/tests/unit/test_ensure_httpx_close.py new file mode 100644 index 000000000000..7ef50b5535a5 --- /dev/null +++ b/tests/unit/test_ensure_httpx_close.py @@ -0,0 +1,84 @@ +from httpx import Client + +from openhands.utils.ensure_httpx_close import EnsureHttpxClose + + +def test_ensure_httpx_close_basic(): + """Test basic functionality of EnsureHttpxClose.""" + clients = [] + ctx = EnsureHttpxClose() + with ctx: + # Create a client - should be tracked + client = Client() + assert client in ctx.clients + assert len(ctx.clients) == 1 + clients.append(client) + + # After context exit, client should be closed + assert client.is_closed + + +def test_ensure_httpx_close_multiple_clients(): + """Test EnsureHttpxClose with multiple clients.""" + ctx = EnsureHttpxClose() + with ctx: + client1 = Client() + client2 = Client() + assert len(ctx.clients) == 2 + assert client1 in ctx.clients + assert client2 in ctx.clients + + assert client1.is_closed + assert client2.is_closed + + +def test_ensure_httpx_close_nested(): + """Test nested usage of EnsureHttpxClose.""" + outer_ctx = EnsureHttpxClose() + with outer_ctx: + client1 = Client() + assert client1 in outer_ctx.clients + + inner_ctx = EnsureHttpxClose() + with inner_ctx: + client2 = Client() + assert client2 in inner_ctx.clients + # Since both contexts are using the same monkey-patched __init__, + # both contexts will track all clients created while they are active + assert client2 in outer_ctx.clients + + # After inner context, client2 should be closed + assert client2.is_closed + # client1 should still be open since outer context is still active + assert not client1.is_closed + + # After outer context, both clients should be closed + assert client1.is_closed + assert client2.is_closed + + +def test_ensure_httpx_close_exception(): + """Test EnsureHttpxClose when an exception occurs.""" + client = None + ctx = EnsureHttpxClose() + try: + with ctx: + client = Client() + raise ValueError('Test exception') + except ValueError: + pass + + # Client should be closed even if an exception occurred + assert client is not None + assert client.is_closed + + +def test_ensure_httpx_close_restore_init(): + """Test that the original __init__ is restored after context exit.""" + original_init = Client.__init__ + ctx = EnsureHttpxClose() + with ctx: + assert Client.__init__ != original_init + + # Original __init__ should be restored + assert Client.__init__ == original_init diff --git a/tests/unit/test_llm.py b/tests/unit/test_llm.py index 0ec7fe252192..57906c2c776c 100644 --- a/tests/unit/test_llm.py +++ b/tests/unit/test_llm.py @@ -1,4 +1,6 @@ import copy +import tempfile +from pathlib import Path from unittest.mock import MagicMock, patch import pytest @@ -489,3 +491,27 @@ def test_llm_token_usage(mock_litellm_completion, default_config): assert usage_entry_2['cache_read_tokens'] == 1 assert usage_entry_2['cache_write_tokens'] == 3 assert usage_entry_2['response_id'] == 'test-response-usage-2' + + +@patch('openhands.llm.llm.litellm_completion') +def test_completion_with_log_completions(mock_litellm_completion, default_config): + with tempfile.TemporaryDirectory() as temp_dir: + default_config.log_completions = True + default_config.log_completions_folder = temp_dir + mock_response = { + 'choices': [{'message': {'content': 'This is a mocked response.'}}] + } + mock_litellm_completion.return_value = mock_response + + test_llm = LLM(config=default_config) + response = test_llm.completion( + messages=[{'role': 'user', 'content': 'Hello!'}], + stream=False, + drop_params=True, + ) + assert ( + response['choices'][0]['message']['content'] == 'This is a mocked response.' + ) + files = list(Path(temp_dir).iterdir()) + # Expect a log to be generated + assert len(files) == 1 From 8ad89e368a6c4a78d680e500fe4d536a8f8dbeb6 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Mon, 24 Feb 2025 16:35:16 +0100 Subject: [PATCH 2/9] Small rename to long term memory (#6914) --- .github/workflows/py-unit-tests.yml | 2 +- openhands/memory/__init__.py | 2 +- openhands/memory/{memory.py => long_term_memory.py} | 0 .../unit/{test_memory.py => test_long_term_memory.py} | 10 ++++++---- 4 files changed, 8 insertions(+), 6 deletions(-) rename openhands/memory/{memory.py => long_term_memory.py} (100%) rename tests/unit/{test_memory.py => test_long_term_memory.py} (96%) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index f6f68051409f..a7dcfb84acd9 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -48,7 +48,7 @@ jobs: - name: Build Environment run: make build - name: Run Tests - run: poetry run pytest --forked -n auto --cov=openhands --cov-report=xml -svv ./tests/unit --ignore=tests/unit/test_memory.py + run: poetry run pytest --forked -n auto --cov=openhands --cov-report=xml -svv ./tests/unit --ignore=tests/unit/test_long_term_memory.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: diff --git a/openhands/memory/__init__.py b/openhands/memory/__init__.py index 89109ea270a1..7529ea33ece4 100644 --- a/openhands/memory/__init__.py +++ b/openhands/memory/__init__.py @@ -1,4 +1,4 @@ from openhands.memory.condenser import Condenser -from openhands.memory.memory import LongTermMemory +from openhands.memory.long_term_memory import LongTermMemory __all__ = ['LongTermMemory', 'Condenser'] diff --git a/openhands/memory/memory.py b/openhands/memory/long_term_memory.py similarity index 100% rename from openhands/memory/memory.py rename to openhands/memory/long_term_memory.py diff --git a/tests/unit/test_memory.py b/tests/unit/test_long_term_memory.py similarity index 96% rename from tests/unit/test_memory.py rename to tests/unit/test_long_term_memory.py index 96c06e0fd41c..1845022e02c1 100644 --- a/tests/unit/test_memory.py +++ b/tests/unit/test_long_term_memory.py @@ -7,7 +7,7 @@ from openhands.core.config import AgentConfig, LLMConfig from openhands.events.event import Event, EventSource from openhands.events.stream import EventStream -from openhands.memory.memory import LongTermMemory +from openhands.memory.long_term_memory import LongTermMemory from openhands.storage.files import FileStore @@ -154,7 +154,7 @@ def test_load_events_into_index_with_invalid_json( """Test loading events with malformed event data.""" # Simulate an event that causes event_to_memory to raise a JSONDecodeError with patch( - 'openhands.memory.memory.event_to_memory', + 'openhands.memory.long_term_memory.event_to_memory', side_effect=json.JSONDecodeError('Expecting value', '', 0), ): event = _create_action_event('invalid_action') @@ -190,7 +190,8 @@ def test_search_returns_correct_results(long_term_memory: LongTermMemory): MagicMock(get_text=MagicMock(return_value='result2')), ] with patch( - 'openhands.memory.memory.VectorIndexRetriever', return_value=mock_retriever + 'openhands.memory.long_term_memory.VectorIndexRetriever', + return_value=mock_retriever, ): results = long_term_memory.search(query='test query', k=2) assert results == ['result1', 'result2'] @@ -201,7 +202,8 @@ def test_search_with_no_results(long_term_memory: LongTermMemory): mock_retriever = MagicMock() mock_retriever.retrieve.return_value = [] with patch( - 'openhands.memory.memory.VectorIndexRetriever', return_value=mock_retriever + 'openhands.memory.long_term_memory.VectorIndexRetriever', + return_value=mock_retriever, ): results = long_term_memory.search(query='no results', k=5) assert results == [] From aa15c9d3855ddee1137dcf2928fc42cc589bf9ce Mon Sep 17 00:00:00 2001 From: Christopher Pereira Date: Mon, 24 Feb 2025 12:36:55 -0300 Subject: [PATCH 3/9] Handle Docker version string with +dfsg1 (#6732) Co-authored-by: Christoper Pereira --- openhands/runtime/builder/docker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/builder/docker.py b/openhands/runtime/builder/docker.py index 4c2c13f90f04..041ff622370f 100644 --- a/openhands/runtime/builder/docker.py +++ b/openhands/runtime/builder/docker.py @@ -67,7 +67,7 @@ def build( """ self.docker_client = docker.from_env() version_info = self.docker_client.version() - server_version = version_info.get('Version', '').replace('-', '.') + server_version = version_info.get('Version', '').split('+')[0].replace('-', '.') if tuple(map(int, server_version.split('.'))) < (18, 9): raise AgentRuntimeBuildError( 'Docker server version must be >= 18.09 to use BuildKit' From 0217a7cfbd6b2960838e6a2a4c24e9c1b211a27a Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 24 Feb 2025 12:51:48 -0500 Subject: [PATCH 4/9] chore: Make remote runtime class default to None (#6919) --- openhands/core/config/sandbox_config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openhands/core/config/sandbox_config.py b/openhands/core/config/sandbox_config.py index 7c1b1adaba64..820c33c70a04 100644 --- a/openhands/core/config/sandbox_config.py +++ b/openhands/core/config/sandbox_config.py @@ -53,11 +53,11 @@ class SandboxConfig(BaseModel): remote_runtime_api_timeout: int = Field(default=10) remote_runtime_enable_retries: bool = Field(default=False) remote_runtime_class: str | None = Field( - default='sysbox' + default=None ) # can be "None" (default to gvisor) or "sysbox" (support docker inside runtime + more stable) enable_auto_lint: bool = Field( - default=False # once enabled, OpenHands would lint files after editing - ) + default=False + ) # once enabled, OpenHands would lint files after editing use_host_network: bool = Field(default=False) runtime_extra_build_args: list[str] | None = Field(default=None) initialize_plugins: bool = Field(default=True) From 65622976150caa733756eebd0e2042f5f53c093d Mon Sep 17 00:00:00 2001 From: Mateusz Kwiatkowski Date: Mon, 24 Feb 2025 19:07:28 +0100 Subject: [PATCH 5/9] Replace shebang with /usr/bin/env bash for improved portability (#6876) Co-authored-by: Xingyao Wang --- Makefile | 2 +- build.sh | 2 +- containers/build.sh | 2 +- containers/dev/dev.sh | 2 +- evaluation/benchmarks/EDA/scripts/run_infer.sh | 2 +- evaluation/benchmarks/agent_bench/scripts/run_infer.sh | 2 +- evaluation/benchmarks/aider_bench/scripts/run_infer.sh | 2 +- evaluation/benchmarks/biocoder/scripts/run_infer.sh | 2 +- evaluation/benchmarks/bird/scripts/run_infer.sh | 2 +- evaluation/benchmarks/browsing_delegation/scripts/run_infer.sh | 2 +- evaluation/benchmarks/commit0_bench/scripts/run_infer.sh | 2 +- evaluation/benchmarks/discoverybench/scripts/run_infer.sh | 2 +- evaluation/benchmarks/gaia/scripts/run_infer.sh | 2 +- evaluation/benchmarks/gorilla/scripts/run_infer.sh | 2 +- evaluation/benchmarks/gpqa/scripts/run_infer.sh | 2 +- evaluation/benchmarks/humanevalfix/scripts/run_infer.sh | 2 +- evaluation/benchmarks/logic_reasoning/scripts/run_infer.sh | 2 +- evaluation/benchmarks/miniwob/scripts/run_infer.sh | 2 +- evaluation/benchmarks/mint/scripts/run_infer.sh | 2 +- evaluation/benchmarks/ml_bench/scripts/cleanup.sh | 2 +- evaluation/benchmarks/ml_bench/scripts/run_analysis.sh | 2 +- evaluation/benchmarks/ml_bench/scripts/run_infer.sh | 2 +- evaluation/benchmarks/scienceagentbench/scripts/run_infer.sh | 2 +- .../benchmarks/swe_bench/scripts/docker/pull_all_eval_docker.sh | 2 +- .../benchmarks/swe_bench/scripts/docker/push_eval_docker.sh | 2 +- .../scripts/eval/convert_oh_folder_to_swebench_submission.sh | 2 +- evaluation/benchmarks/swe_bench/scripts/eval_infer.sh | 2 +- evaluation/benchmarks/swe_bench/scripts/eval_infer_remote.sh | 2 +- evaluation/benchmarks/swe_bench/scripts/run_infer.sh | 2 +- .../benchmarks/swe_bench/scripts/setup/instance_swe_entry.sh | 2 +- .../benchmarks/swe_bench/scripts/setup/prepare_swe_utils.sh | 2 +- evaluation/benchmarks/swe_bench/scripts/setup/swe_entry.sh | 2 +- evaluation/benchmarks/the_agent_company/scripts/run_infer.sh | 2 +- evaluation/benchmarks/toolqa/scripts/run_infer.sh | 2 +- evaluation/benchmarks/visualwebarena/scripts/run_infer.sh | 2 +- evaluation/benchmarks/webarena/scripts/run_infer.sh | 2 +- evaluation/integration_tests/scripts/run_infer.sh | 2 +- evaluation/regression/cases/hello-name/start/hello_world.sh | 2 +- evaluation/utils/scripts/cleanup_remote_runtime.sh | 2 +- 39 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 08ada5d64c29..2fde9c14d5ad 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SHELL=/bin/bash +SHELL=/usr/bin/env bash # Makefile for OpenHands project # Variables diff --git a/build.sh b/build.sh index b0c667b35f41..599c63161a21 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e poetry build -v diff --git a/containers/build.sh b/containers/build.sh index 9b6fcfb2e40d..baaedc115fd2 100755 --- a/containers/build.sh +++ b/containers/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail # Initialize variables with default values diff --git a/containers/dev/dev.sh b/containers/dev/dev.sh index 2c9a9db4efa4..ee481f25264b 100755 --- a/containers/dev/dev.sh +++ b/containers/dev/dev.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -o pipefail function get_docker() { diff --git a/evaluation/benchmarks/EDA/scripts/run_infer.sh b/evaluation/benchmarks/EDA/scripts/run_infer.sh index 9897ad3c6104..a072e7157cec 100755 --- a/evaluation/benchmarks/EDA/scripts/run_infer.sh +++ b/evaluation/benchmarks/EDA/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/agent_bench/scripts/run_infer.sh b/evaluation/benchmarks/agent_bench/scripts/run_infer.sh index 6a22cdcc4506..5a1006d530fc 100755 --- a/evaluation/benchmarks/agent_bench/scripts/run_infer.sh +++ b/evaluation/benchmarks/agent_bench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/aider_bench/scripts/run_infer.sh b/evaluation/benchmarks/aider_bench/scripts/run_infer.sh index 34249e94c527..59d53cfb1980 100755 --- a/evaluation/benchmarks/aider_bench/scripts/run_infer.sh +++ b/evaluation/benchmarks/aider_bench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/biocoder/scripts/run_infer.sh b/evaluation/benchmarks/biocoder/scripts/run_infer.sh index 76c4f007dcef..bfd44df9d5d2 100755 --- a/evaluation/benchmarks/biocoder/scripts/run_infer.sh +++ b/evaluation/benchmarks/biocoder/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/bird/scripts/run_infer.sh b/evaluation/benchmarks/bird/scripts/run_infer.sh index 835f511652f3..2eef8849e3c7 100755 --- a/evaluation/benchmarks/bird/scripts/run_infer.sh +++ b/evaluation/benchmarks/bird/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/browsing_delegation/scripts/run_infer.sh b/evaluation/benchmarks/browsing_delegation/scripts/run_infer.sh index 78d19fe1b7aa..cdaf01dcd62b 100755 --- a/evaluation/benchmarks/browsing_delegation/scripts/run_infer.sh +++ b/evaluation/benchmarks/browsing_delegation/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/commit0_bench/scripts/run_infer.sh b/evaluation/benchmarks/commit0_bench/scripts/run_infer.sh index 93df2208b097..601499b90e92 100755 --- a/evaluation/benchmarks/commit0_bench/scripts/run_infer.sh +++ b/evaluation/benchmarks/commit0_bench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/discoverybench/scripts/run_infer.sh b/evaluation/benchmarks/discoverybench/scripts/run_infer.sh index 0c693a75796b..415fb4824974 100755 --- a/evaluation/benchmarks/discoverybench/scripts/run_infer.sh +++ b/evaluation/benchmarks/discoverybench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/gaia/scripts/run_infer.sh b/evaluation/benchmarks/gaia/scripts/run_infer.sh index 217809880d40..2605bd38f10c 100755 --- a/evaluation/benchmarks/gaia/scripts/run_infer.sh +++ b/evaluation/benchmarks/gaia/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/gorilla/scripts/run_infer.sh b/evaluation/benchmarks/gorilla/scripts/run_infer.sh index 2efcdc957979..7abbbd85de63 100755 --- a/evaluation/benchmarks/gorilla/scripts/run_infer.sh +++ b/evaluation/benchmarks/gorilla/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/gpqa/scripts/run_infer.sh b/evaluation/benchmarks/gpqa/scripts/run_infer.sh index dbd7cda98fc3..5d93f99f0371 100755 --- a/evaluation/benchmarks/gpqa/scripts/run_infer.sh +++ b/evaluation/benchmarks/gpqa/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/humanevalfix/scripts/run_infer.sh b/evaluation/benchmarks/humanevalfix/scripts/run_infer.sh index bf36e92bc083..fbd83e648a11 100755 --- a/evaluation/benchmarks/humanevalfix/scripts/run_infer.sh +++ b/evaluation/benchmarks/humanevalfix/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/logic_reasoning/scripts/run_infer.sh b/evaluation/benchmarks/logic_reasoning/scripts/run_infer.sh index 5a93a65ca87d..8504dd77cddd 100755 --- a/evaluation/benchmarks/logic_reasoning/scripts/run_infer.sh +++ b/evaluation/benchmarks/logic_reasoning/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/miniwob/scripts/run_infer.sh b/evaluation/benchmarks/miniwob/scripts/run_infer.sh index e261a123654f..f4e18a5c4856 100755 --- a/evaluation/benchmarks/miniwob/scripts/run_infer.sh +++ b/evaluation/benchmarks/miniwob/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/mint/scripts/run_infer.sh b/evaluation/benchmarks/mint/scripts/run_infer.sh index 52ab0cb81b35..067bda46fb6b 100755 --- a/evaluation/benchmarks/mint/scripts/run_infer.sh +++ b/evaluation/benchmarks/mint/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/ml_bench/scripts/cleanup.sh b/evaluation/benchmarks/ml_bench/scripts/cleanup.sh index c6c90f662c9f..0990f371ff34 100644 --- a/evaluation/benchmarks/ml_bench/scripts/cleanup.sh +++ b/evaluation/benchmarks/ml_bench/scripts/cleanup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Step 1: Stop all running containers echo "Stopping all running containers..." diff --git a/evaluation/benchmarks/ml_bench/scripts/run_analysis.sh b/evaluation/benchmarks/ml_bench/scripts/run_analysis.sh index d5fe6365ca86..a69179ee2ca0 100644 --- a/evaluation/benchmarks/ml_bench/scripts/run_analysis.sh +++ b/evaluation/benchmarks/ml_bench/scripts/run_analysis.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash RESULT_FILE=$1 MODEL_CONFIG=$2 diff --git a/evaluation/benchmarks/ml_bench/scripts/run_infer.sh b/evaluation/benchmarks/ml_bench/scripts/run_infer.sh index e6932851731d..d00b5fc891b3 100755 --- a/evaluation/benchmarks/ml_bench/scripts/run_infer.sh +++ b/evaluation/benchmarks/ml_bench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/scienceagentbench/scripts/run_infer.sh b/evaluation/benchmarks/scienceagentbench/scripts/run_infer.sh index 945f7dccdaba..ac1196b1458d 100755 --- a/evaluation/benchmarks/scienceagentbench/scripts/run_infer.sh +++ b/evaluation/benchmarks/scienceagentbench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/swe_bench/scripts/docker/pull_all_eval_docker.sh b/evaluation/benchmarks/swe_bench/scripts/docker/pull_all_eval_docker.sh index 98b7d8250e5d..8c525a3041f3 100755 --- a/evaluation/benchmarks/swe_bench/scripts/docker/pull_all_eval_docker.sh +++ b/evaluation/benchmarks/swe_bench/scripts/docker/pull_all_eval_docker.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e LEVEL=$1 diff --git a/evaluation/benchmarks/swe_bench/scripts/docker/push_eval_docker.sh b/evaluation/benchmarks/swe_bench/scripts/docker/push_eval_docker.sh index 3f98eb6be167..1c5c9a07e98e 100644 --- a/evaluation/benchmarks/swe_bench/scripts/docker/push_eval_docker.sh +++ b/evaluation/benchmarks/swe_bench/scripts/docker/push_eval_docker.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # This is ONLY used for pushing docker images created by https://github.com/princeton-nlp/SWE-bench/blob/main/docs/20240627_docker/README.md diff --git a/evaluation/benchmarks/swe_bench/scripts/eval/convert_oh_folder_to_swebench_submission.sh b/evaluation/benchmarks/swe_bench/scripts/eval/convert_oh_folder_to_swebench_submission.sh index 044f9972f4eb..ed50fb35e7b3 100755 --- a/evaluation/benchmarks/swe_bench/scripts/eval/convert_oh_folder_to_swebench_submission.sh +++ b/evaluation/benchmarks/swe_bench/scripts/eval/convert_oh_folder_to_swebench_submission.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash FOLDER_PATH=$1 NEW_FOLDER_PATH=${FOLDER_PATH}.swebench_submission diff --git a/evaluation/benchmarks/swe_bench/scripts/eval_infer.sh b/evaluation/benchmarks/swe_bench/scripts/eval_infer.sh index b39986615ef8..6672e99d2f71 100755 --- a/evaluation/benchmarks/swe_bench/scripts/eval_infer.sh +++ b/evaluation/benchmarks/swe_bench/scripts/eval_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash PROCESS_FILEPATH=$1 if [ -z "$PROCESS_FILEPATH" ]; then diff --git a/evaluation/benchmarks/swe_bench/scripts/eval_infer_remote.sh b/evaluation/benchmarks/swe_bench/scripts/eval_infer_remote.sh index 68280978368e..1ec07182acfe 100755 --- a/evaluation/benchmarks/swe_bench/scripts/eval_infer_remote.sh +++ b/evaluation/benchmarks/swe_bench/scripts/eval_infer_remote.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail INPUT_FILE=$1 diff --git a/evaluation/benchmarks/swe_bench/scripts/run_infer.sh b/evaluation/benchmarks/swe_bench/scripts/run_infer.sh index 73e8bd3a3e55..d0bed0179266 100755 --- a/evaluation/benchmarks/swe_bench/scripts/run_infer.sh +++ b/evaluation/benchmarks/swe_bench/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/swe_bench/scripts/setup/instance_swe_entry.sh b/evaluation/benchmarks/swe_bench/scripts/setup/instance_swe_entry.sh index 6da2e46477d8..6bacc93fde78 100755 --- a/evaluation/benchmarks/swe_bench/scripts/setup/instance_swe_entry.sh +++ b/evaluation/benchmarks/swe_bench/scripts/setup/instance_swe_entry.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash source ~/.bashrc SWEUTIL_DIR=/swe_util diff --git a/evaluation/benchmarks/swe_bench/scripts/setup/prepare_swe_utils.sh b/evaluation/benchmarks/swe_bench/scripts/setup/prepare_swe_utils.sh index 7091b6f586b7..c5726a402f06 100755 --- a/evaluation/benchmarks/swe_bench/scripts/setup/prepare_swe_utils.sh +++ b/evaluation/benchmarks/swe_bench/scripts/setup/prepare_swe_utils.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e EVAL_WORKSPACE="evaluation/benchmarks/swe_bench/eval_workspace" diff --git a/evaluation/benchmarks/swe_bench/scripts/setup/swe_entry.sh b/evaluation/benchmarks/swe_bench/scripts/setup/swe_entry.sh index 23fc3a3c5066..03e0de7a23b2 100755 --- a/evaluation/benchmarks/swe_bench/scripts/setup/swe_entry.sh +++ b/evaluation/benchmarks/swe_bench/scripts/setup/swe_entry.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e diff --git a/evaluation/benchmarks/the_agent_company/scripts/run_infer.sh b/evaluation/benchmarks/the_agent_company/scripts/run_infer.sh index e266e5990b1a..0f8c9370c17d 100755 --- a/evaluation/benchmarks/the_agent_company/scripts/run_infer.sh +++ b/evaluation/benchmarks/the_agent_company/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ################################################################################################## # Adapted from https://github.com/TheAgentCompany/TheAgentCompany/blob/main/evaluation/run_eval.sh diff --git a/evaluation/benchmarks/toolqa/scripts/run_infer.sh b/evaluation/benchmarks/toolqa/scripts/run_infer.sh index 7f5635dd9f24..20614b3cb2b7 100755 --- a/evaluation/benchmarks/toolqa/scripts/run_infer.sh +++ b/evaluation/benchmarks/toolqa/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/visualwebarena/scripts/run_infer.sh b/evaluation/benchmarks/visualwebarena/scripts/run_infer.sh index 9c1f6dc3e8d6..d49ead43b6fc 100755 --- a/evaluation/benchmarks/visualwebarena/scripts/run_infer.sh +++ b/evaluation/benchmarks/visualwebarena/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/benchmarks/webarena/scripts/run_infer.sh b/evaluation/benchmarks/webarena/scripts/run_infer.sh index e3e08dcd48e5..8751f96613c2 100755 --- a/evaluation/benchmarks/webarena/scripts/run_infer.sh +++ b/evaluation/benchmarks/webarena/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/integration_tests/scripts/run_infer.sh b/evaluation/integration_tests/scripts/run_infer.sh index 32702afa9013..5696a46e62e8 100755 --- a/evaluation/integration_tests/scripts/run_infer.sh +++ b/evaluation/integration_tests/scripts/run_infer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail source "evaluation/utils/version_control.sh" diff --git a/evaluation/regression/cases/hello-name/start/hello_world.sh b/evaluation/regression/cases/hello-name/start/hello_world.sh index 2a43bb269b6c..1a2a680f62a4 100644 --- a/evaluation/regression/cases/hello-name/start/hello_world.sh +++ b/evaluation/regression/cases/hello-name/start/hello_world.sh @@ -1,2 +1,2 @@ -#!/bin/bash +#!/usr/bin/env bash echo "hello world" diff --git a/evaluation/utils/scripts/cleanup_remote_runtime.sh b/evaluation/utils/scripts/cleanup_remote_runtime.sh index 34685b11aeda..5bc22d7cec8a 100755 --- a/evaluation/utils/scripts/cleanup_remote_runtime.sh +++ b/evaluation/utils/scripts/cleanup_remote_runtime.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # API base URL From f4c5bbda19f08d19ba4bf139367e5cee0c2b271a Mon Sep 17 00:00:00 2001 From: tofarr Date: Mon, 24 Feb 2025 18:47:13 +0000 Subject: [PATCH 6/9] Revert "Fix file descriptor leak (#6897)" (#6921) --- openhands/llm/llm.py | 13 ++--- openhands/utils/ensure_httpx_close.py | 43 -------------- tests/unit/test_ensure_httpx_close.py | 84 --------------------------- tests/unit/test_llm.py | 26 --------- 4 files changed, 4 insertions(+), 162 deletions(-) delete mode 100644 openhands/utils/ensure_httpx_close.py delete mode 100644 tests/unit/test_ensure_httpx_close.py diff --git a/openhands/llm/llm.py b/openhands/llm/llm.py index 944e4660b47e..66bc6f99cb09 100644 --- a/openhands/llm/llm.py +++ b/openhands/llm/llm.py @@ -8,7 +8,6 @@ import requests from openhands.core.config import LLMConfig -from openhands.utils.ensure_httpx_close import EnsureHttpxClose with warnings.catch_warnings(): warnings.simplefilter('ignore') @@ -231,9 +230,9 @@ def wrapper(*args, **kwargs): # Record start time for latency measurement start_time = time.time() - with EnsureHttpxClose(): - # we don't support streaming here, thus we get a ModelResponse - resp: ModelResponse = self._completion_unwrapped(*args, **kwargs) + + # we don't support streaming here, thus we get a ModelResponse + resp: ModelResponse = self._completion_unwrapped(*args, **kwargs) # Calculate and record latency latency = time.time() - start_time @@ -288,11 +287,7 @@ def wrapper(*args, **kwargs): 'messages': messages, 'response': resp, 'args': args, - 'kwargs': { - k: v - for k, v in kwargs.items() - if k not in ('messages', 'client') - }, + 'kwargs': {k: v for k, v in kwargs.items() if k != 'messages'}, 'timestamp': time.time(), 'cost': cost, } diff --git a/openhands/utils/ensure_httpx_close.py b/openhands/utils/ensure_httpx_close.py deleted file mode 100644 index f60a7189d565..000000000000 --- a/openhands/utils/ensure_httpx_close.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -LiteLLM currently have an issue where HttpHandlers are being created but not -closed. We have submitted a PR to them, (https://github.com/BerriAI/litellm/pull/8711) -and their dev team say they are in the process of a refactor that will fix this, but -in the meantime, we need to manage the lifecycle of the httpx.Client manually. - -We can't simply pass in our own client object, because all the different implementations use -different types of client object. - -So we monkey patch the httpx.Client class to track newly created instances and close these -when the operations complete. (This is relatively safe, as if the client is reused after this -then is will transparently reopen) - -Hopefully, this will be fixed soon and we can remove this abomination. -""" - -from dataclasses import dataclass, field -from functools import wraps -from typing import Callable - -from httpx import Client - - -@dataclass -class EnsureHttpxClose: - clients: list[Client] = field(default_factory=list) - original_init: Callable | None = None - - def __enter__(self): - self.original_init = Client.__init__ - - @wraps(Client.__init__) - def init_wrapper(*args, **kwargs): - self.clients.append(args[0]) - return self.original_init(*args, **kwargs) # type: ignore - - Client.__init__ = init_wrapper - - def __exit__(self, type, value, traceback): - Client.__init__ = self.original_init - while self.clients: - client = self.clients.pop() - client.close() diff --git a/tests/unit/test_ensure_httpx_close.py b/tests/unit/test_ensure_httpx_close.py deleted file mode 100644 index 7ef50b5535a5..000000000000 --- a/tests/unit/test_ensure_httpx_close.py +++ /dev/null @@ -1,84 +0,0 @@ -from httpx import Client - -from openhands.utils.ensure_httpx_close import EnsureHttpxClose - - -def test_ensure_httpx_close_basic(): - """Test basic functionality of EnsureHttpxClose.""" - clients = [] - ctx = EnsureHttpxClose() - with ctx: - # Create a client - should be tracked - client = Client() - assert client in ctx.clients - assert len(ctx.clients) == 1 - clients.append(client) - - # After context exit, client should be closed - assert client.is_closed - - -def test_ensure_httpx_close_multiple_clients(): - """Test EnsureHttpxClose with multiple clients.""" - ctx = EnsureHttpxClose() - with ctx: - client1 = Client() - client2 = Client() - assert len(ctx.clients) == 2 - assert client1 in ctx.clients - assert client2 in ctx.clients - - assert client1.is_closed - assert client2.is_closed - - -def test_ensure_httpx_close_nested(): - """Test nested usage of EnsureHttpxClose.""" - outer_ctx = EnsureHttpxClose() - with outer_ctx: - client1 = Client() - assert client1 in outer_ctx.clients - - inner_ctx = EnsureHttpxClose() - with inner_ctx: - client2 = Client() - assert client2 in inner_ctx.clients - # Since both contexts are using the same monkey-patched __init__, - # both contexts will track all clients created while they are active - assert client2 in outer_ctx.clients - - # After inner context, client2 should be closed - assert client2.is_closed - # client1 should still be open since outer context is still active - assert not client1.is_closed - - # After outer context, both clients should be closed - assert client1.is_closed - assert client2.is_closed - - -def test_ensure_httpx_close_exception(): - """Test EnsureHttpxClose when an exception occurs.""" - client = None - ctx = EnsureHttpxClose() - try: - with ctx: - client = Client() - raise ValueError('Test exception') - except ValueError: - pass - - # Client should be closed even if an exception occurred - assert client is not None - assert client.is_closed - - -def test_ensure_httpx_close_restore_init(): - """Test that the original __init__ is restored after context exit.""" - original_init = Client.__init__ - ctx = EnsureHttpxClose() - with ctx: - assert Client.__init__ != original_init - - # Original __init__ should be restored - assert Client.__init__ == original_init diff --git a/tests/unit/test_llm.py b/tests/unit/test_llm.py index 57906c2c776c..0ec7fe252192 100644 --- a/tests/unit/test_llm.py +++ b/tests/unit/test_llm.py @@ -1,6 +1,4 @@ import copy -import tempfile -from pathlib import Path from unittest.mock import MagicMock, patch import pytest @@ -491,27 +489,3 @@ def test_llm_token_usage(mock_litellm_completion, default_config): assert usage_entry_2['cache_read_tokens'] == 1 assert usage_entry_2['cache_write_tokens'] == 3 assert usage_entry_2['response_id'] == 'test-response-usage-2' - - -@patch('openhands.llm.llm.litellm_completion') -def test_completion_with_log_completions(mock_litellm_completion, default_config): - with tempfile.TemporaryDirectory() as temp_dir: - default_config.log_completions = True - default_config.log_completions_folder = temp_dir - mock_response = { - 'choices': [{'message': {'content': 'This is a mocked response.'}}] - } - mock_litellm_completion.return_value = mock_response - - test_llm = LLM(config=default_config) - response = test_llm.completion( - messages=[{'role': 'user', 'content': 'Hello!'}], - stream=False, - drop_params=True, - ) - assert ( - response['choices'][0]['message']['content'] == 'This is a mocked response.' - ) - files = list(Path(temp_dir).iterdir()) - # Expect a log to be generated - assert len(files) == 1 From fa50e0c9b9021bec1f0f6f83dc2004b99ab4dabb Mon Sep 17 00:00:00 2001 From: celek Date: Mon, 24 Feb 2025 14:31:02 -0500 Subject: [PATCH 7/9] add extended generic section (#5932) Co-authored-by: Christophe Elek Co-authored-by: Engel Nyst --- openhands/core/config/__init__.py | 2 + openhands/core/config/app_config.py | 2 + openhands/core/config/extended_config.py | 40 ++++++ openhands/core/config/utils.py | 5 + tests/unit/test_config_extended.py | 169 +++++++++++++++++++++++ 5 files changed, 218 insertions(+) create mode 100644 openhands/core/config/extended_config.py create mode 100644 tests/unit/test_config_extended.py diff --git a/openhands/core/config/__init__.py b/openhands/core/config/__init__.py index d653f3e70ac4..e927c4f3b0c1 100644 --- a/openhands/core/config/__init__.py +++ b/openhands/core/config/__init__.py @@ -5,6 +5,7 @@ OH_MAX_ITERATIONS, get_field_info, ) +from openhands.core.config.extended_config import ExtendedConfig from openhands.core.config.llm_config import LLMConfig from openhands.core.config.sandbox_config import SandboxConfig from openhands.core.config.security_config import SecurityConfig @@ -28,6 +29,7 @@ 'LLMConfig', 'SandboxConfig', 'SecurityConfig', + 'ExtendedConfig', 'load_app_config', 'load_from_env', 'load_from_toml', diff --git a/openhands/core/config/app_config.py b/openhands/core/config/app_config.py index 7abf0936f86b..98bc5d0c2771 100644 --- a/openhands/core/config/app_config.py +++ b/openhands/core/config/app_config.py @@ -9,6 +9,7 @@ OH_MAX_ITERATIONS, model_defaults_to_dict, ) +from openhands.core.config.extended_config import ExtendedConfig from openhands.core.config.llm_config import LLMConfig from openhands.core.config.sandbox_config import SandboxConfig from openhands.core.config.security_config import SecurityConfig @@ -52,6 +53,7 @@ class AppConfig(BaseModel): default_agent: str = Field(default=OH_DEFAULT_AGENT) sandbox: SandboxConfig = Field(default_factory=SandboxConfig) security: SecurityConfig = Field(default_factory=SecurityConfig) + extended: ExtendedConfig = Field(default_factory=lambda: ExtendedConfig({})) runtime: str = Field(default='docker') file_store: str = Field(default='local') file_store_path: str = Field(default='/tmp/openhands_file_store') diff --git a/openhands/core/config/extended_config.py b/openhands/core/config/extended_config.py new file mode 100644 index 000000000000..071a6872d2b0 --- /dev/null +++ b/openhands/core/config/extended_config.py @@ -0,0 +1,40 @@ +from pydantic import RootModel + + +class ExtendedConfig(RootModel[dict]): + """Configuration for extended functionalities. + + This is implemented as a root model so that the entire input is stored + as the root value. This allows arbitrary keys to be stored and later + accessed via attribute or dictionary-style access. + """ + + @property + def root(self) -> dict: # type annotation to help mypy + return super().root + + def __str__(self) -> str: + # Use the root dict to build a string representation. + attr_str = [f'{k}={repr(v)}' for k, v in self.root.items()] + return f"ExtendedConfig({', '.join(attr_str)})" + + def __repr__(self) -> str: + return self.__str__() + + @classmethod + def from_dict(cls, data: dict) -> 'ExtendedConfig': + # Create an instance directly by wrapping the input dict. + return cls(data) + + def __getitem__(self, key: str) -> object: + # Provide dictionary-like access via the root dict. + return self.root[key] + + def __getattr__(self, key: str) -> object: + # Fallback for attribute access using the root dict. + try: + return self.root[key] + except KeyError as e: + raise AttributeError( + f"'ExtendedConfig' object has no attribute '{key}'" + ) from e diff --git a/openhands/core/config/utils.py b/openhands/core/config/utils.py index f057eb6ad2fe..b1e9a6ba09b9 100644 --- a/openhands/core/config/utils.py +++ b/openhands/core/config/utils.py @@ -19,6 +19,7 @@ OH_DEFAULT_AGENT, OH_MAX_ITERATIONS, ) +from openhands.core.config.extended_config import ExtendedConfig from openhands.core.config.llm_config import LLMConfig from openhands.core.config.sandbox_config import SandboxConfig from openhands.core.config.security_config import SecurityConfig @@ -134,6 +135,10 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml') -> None: for key, value in toml_config.items(): if isinstance(value, dict): try: + if key.lower() == 'extended': + # For ExtendedConfig (RootModel), pass the entire dict as the root value + cfg.extended = ExtendedConfig(value) + continue if key is not None and key.lower() == 'agent': # Every entry here is either a field for the default `agent` config group, or itself a group # The best way to tell the difference is to try to parse it as an AgentConfig object diff --git a/tests/unit/test_config_extended.py b/tests/unit/test_config_extended.py new file mode 100644 index 000000000000..8dbfa6b9bfe9 --- /dev/null +++ b/tests/unit/test_config_extended.py @@ -0,0 +1,169 @@ +import os + +import pytest + +from openhands.core.config.app_config import AppConfig +from openhands.core.config.extended_config import ExtendedConfig +from openhands.core.config.utils import load_from_toml + + +def test_extended_config_from_dict(): + """ + Test that ExtendedConfig.from_dict successfully creates an instance + from a dictionary containing arbitrary extra keys. + """ + data = {'foo': 'bar', 'baz': 123, 'flag': True} + ext_cfg = ExtendedConfig.from_dict(data) + + # Check that the keys are accessible both as attributes and via __getitem__ + assert ext_cfg.foo == 'bar' + assert ext_cfg['baz'] == 123 + assert ext_cfg.flag is True + # Verify the root dictionary contains all keys + assert ext_cfg.root == data + + +def test_extended_config_empty(): + """ + Test that an empty ExtendedConfig can be created and accessed. + """ + ext_cfg = ExtendedConfig.from_dict({}) + assert ext_cfg.root == {} + + # Creating directly should also work + ext_cfg2 = ExtendedConfig({}) + assert ext_cfg2.root == {} + + +def test_extended_config_str_and_repr(): + """ + Test that __str__ and __repr__ return the correct string representations + of the ExtendedConfig instance. + """ + data = {'alpha': 'test', 'beta': 42} + ext_cfg = ExtendedConfig.from_dict(data) + string_repr = str(ext_cfg) + repr_str = repr(ext_cfg) + + # Ensure the representations include our key/value pairs + assert "alpha='test'" in string_repr + assert 'beta=42' in string_repr + + # __repr__ should match __str__ + assert string_repr == repr_str + + +def test_extended_config_getitem_and_getattr(): + """ + Test that __getitem__ and __getattr__ can be used to access values + in the ExtendedConfig instance. + """ + data = {'key1': 'value1', 'key2': 2} + ext_cfg = ExtendedConfig.from_dict(data) + + # Attribute access + assert ext_cfg.key1 == 'value1' + # Dictionary-style access + assert ext_cfg['key2'] == 2 + + +def test_extended_config_invalid_key(): + """ + Test that accessing a non-existent key via attribute access raises AttributeError. + """ + data = {'existing': 'yes'} + ext_cfg = ExtendedConfig.from_dict(data) + + with pytest.raises(AttributeError): + _ = ext_cfg.nonexistent + + with pytest.raises(KeyError): + _ = ext_cfg['nonexistent'] + + +def test_app_config_extended_from_toml(tmp_path: os.PathLike) -> None: + """ + Test that the [extended] section in a TOML file is correctly loaded into + AppConfig.extended and that it accepts arbitrary keys. + """ + # Create a temporary TOML file with multiple sections including [extended] + config_content = """ +[core] +workspace_base = "/tmp/workspace" + +[llm] +model = "test-model" +api_key = "toml-api-key" + +[extended] +custom1 = "custom_value" +custom2 = 42 +llm = "overridden" # even a key like 'llm' is accepted in extended + +[agent] +memory_enabled = true +""" + config_file = tmp_path / 'config.toml' + config_file.write_text(config_content) + + # Load the TOML into the AppConfig instance + config = AppConfig() + load_from_toml(config, str(config_file)) + + # Verify that extended section is applied + assert config.extended.custom1 == 'custom_value' + assert config.extended.custom2 == 42 + # Even though 'llm' is defined in extended, it should not affect the main llm config. + assert config.get_llm_config().model == 'test-model' + + +def test_app_config_extended_default(tmp_path: os.PathLike) -> None: + """ + Test that if there is no [extended] section in the TOML file, + AppConfig.extended remains its default (empty) ExtendedConfig. + """ + config_content = """ +[core] +workspace_base = "/tmp/workspace" + +[llm] +model = "test-model" +api_key = "toml-api-key" + +[agent] +memory_enabled = true +""" + config_file = tmp_path / 'config.toml' + config_file.write_text(config_content) + + config = AppConfig() + load_from_toml(config, str(config_file)) + + # Extended config should be empty + assert config.extended.root == {} + + +def test_app_config_extended_random_keys(tmp_path: os.PathLike) -> None: + """ + Test that the extended section accepts arbitrary keys, + including ones not defined in any schema. + """ + config_content = """ +[core] +workspace_base = "/tmp/workspace" + +[extended] +random_key = "random_value" +another_key = 3.14 +""" + config_file = tmp_path / 'config.toml' + config_file.write_text(config_content) + + config = AppConfig() + load_from_toml(config, str(config_file)) + + # Verify that extended config holds the arbitrary keys with correct values. + assert config.extended.random_key == 'random_value' + assert config.extended.another_key == 3.14 + # Verify the root dictionary contains all keys + assert config.extended.root == {'random_key': 'random_value', 'another_key': 3.14} From 6787a3adf711a63d4baa6419b07133ebdc304448 Mon Sep 17 00:00:00 2001 From: mamoodi Date: Mon, 24 Feb 2025 15:34:34 -0500 Subject: [PATCH 8/9] Release 0.26.0 (#6915) --- Development.md | 2 +- README.md | 6 +++--- containers/dev/compose.yml | 2 +- docker-compose.yml | 2 +- .../current/usage/how-to/cli-mode.md | 4 ++-- .../current/usage/how-to/headless-mode.md | 4 ++-- .../current/usage/installation.mdx | 6 +++--- .../current/usage/runtimes.md | 2 +- .../current/usage/how-to/cli-mode.md | 4 ++-- .../current/usage/how-to/headless-mode.md | 4 ++-- .../current/usage/installation.mdx | 6 +++--- .../current/usage/runtimes.md | 2 +- docs/modules/usage/how-to/cli-mode.md | 4 ++-- docs/modules/usage/how-to/headless-mode.md | 4 ++-- docs/modules/usage/installation.mdx | 6 +++--- docs/modules/usage/runtimes.md | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- pyproject.toml | 4 +--- 19 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Development.md b/Development.md index 06bf415a7921..2c4edf1b3d9b 100644 --- a/Development.md +++ b/Development.md @@ -100,7 +100,7 @@ poetry run pytest ./tests/unit/test_*.py To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image. -Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.25-nikolaik` +Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.26-nikolaik` ## Develop inside Docker container diff --git a/README.md b/README.md index 51cf52943e94..ecb8ac105bc9 100644 --- a/README.md +++ b/README.md @@ -43,17 +43,17 @@ See the [Running OpenHands](https://docs.all-hands.dev/modules/usage/installatio system requirements and more information. ```bash -docker pull docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik +docker pull docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik docker run -it --rm --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e LOG_ALL_EVENTS=true \ -v /var/run/docker.sock:/var/run/docker.sock \ -v ~/.openhands-state:/.openhands-state \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ --name openhands-app \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 + docker.all-hands.dev/all-hands-ai/openhands:0.26 ``` You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)! diff --git a/containers/dev/compose.yml b/containers/dev/compose.yml index 34245f04efaf..43a6a2593302 100644 --- a/containers/dev/compose.yml +++ b/containers/dev/compose.yml @@ -11,7 +11,7 @@ services: - BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"} - SANDBOX_API_HOSTNAME=host.docker.internal # - - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.25-nikolaik} + - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.26-nikolaik} - SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} - WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace} ports: diff --git a/docker-compose.yml b/docker-compose.yml index 6407f78b4030..f4cedf06f2f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: image: openhands:latest container_name: openhands-app-${DATE:-} environment: - - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik} + - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik} #- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of openhands-state for this user - WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace} ports: diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md index c00beec86d72..f64f049ccda8 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md @@ -52,7 +52,7 @@ LLM_API_KEY="sk_test_12345" ```bash docker run -it \ --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e SANDBOX_USER_ID=$(id -u) \ -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \ -e LLM_API_KEY=$LLM_API_KEY \ @@ -61,7 +61,7 @@ docker run -it \ -v /var/run/docker.sock:/var/run/docker.sock \ --add-host host.docker.internal:host-gateway \ --name openhands-app-$(date +%Y%m%d%H%M%S) \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 \ + docker.all-hands.dev/all-hands-ai/openhands:0.26 \ python -m openhands.core.cli ``` diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md index 1151e1e60df8..9e911c966900 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md @@ -46,7 +46,7 @@ LLM_API_KEY="sk_test_12345" ```bash docker run -it \ --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e SANDBOX_USER_ID=$(id -u) \ -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \ -e LLM_API_KEY=$LLM_API_KEY \ @@ -56,6 +56,6 @@ docker run -it \ -v /var/run/docker.sock:/var/run/docker.sock \ --add-host host.docker.internal:host-gateway \ --name openhands-app-$(date +%Y%m%d%H%M%S) \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 \ + docker.all-hands.dev/all-hands-ai/openhands:0.26 \ python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue ``` diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx index 79a9bf0acdb7..309fcd7c7b1f 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/installation.mdx @@ -13,16 +13,16 @@ La façon la plus simple d'exécuter OpenHands est avec Docker. ```bash -docker pull docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik +docker pull docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik docker run -it --rm --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e LOG_ALL_EVENTS=true \ -v /var/run/docker.sock:/var/run/docker.sock \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ --name openhands-app \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 + docker.all-hands.dev/all-hands-ai/openhands:0.26 ``` Vous pouvez également exécuter OpenHands en mode [headless scriptable](https://docs.all-hands.dev/modules/usage/how-to/headless-mode), en tant que [CLI interactive](https://docs.all-hands.dev/modules/usage/how-to/cli-mode), ou en utilisant l'[Action GitHub OpenHands](https://docs.all-hands.dev/modules/usage/how-to/github-action). diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md index d256032b4c4c..014a6c922450 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/runtimes.md @@ -13,7 +13,7 @@ C'est le Runtime par défaut qui est utilisé lorsque vous démarrez OpenHands. ``` docker run # ... - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -v /var/run/docker.sock:/var/run/docker.sock \ # ... ``` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md index 4aafa581294b..d24f18168814 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/cli-mode.md @@ -50,7 +50,7 @@ LLM_API_KEY="sk_test_12345" ```bash docker run -it \ --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e SANDBOX_USER_ID=$(id -u) \ -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \ -e LLM_API_KEY=$LLM_API_KEY \ @@ -59,7 +59,7 @@ docker run -it \ -v /var/run/docker.sock:/var/run/docker.sock \ --add-host host.docker.internal:host-gateway \ --name openhands-app-$(date +%Y%m%d%H%M%S) \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 \ + docker.all-hands.dev/all-hands-ai/openhands:0.26 \ python -m openhands.core.cli ``` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md index a164f3a9ba8b..433346c8ec10 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/headless-mode.md @@ -47,7 +47,7 @@ LLM_API_KEY="sk_test_12345" ```bash docker run -it \ --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e SANDBOX_USER_ID=$(id -u) \ -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \ -e LLM_API_KEY=$LLM_API_KEY \ @@ -57,6 +57,6 @@ docker run -it \ -v /var/run/docker.sock:/var/run/docker.sock \ --add-host host.docker.internal:host-gateway \ --name openhands-app-$(date +%Y%m%d%H%M%S) \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 \ + docker.all-hands.dev/all-hands-ai/openhands:0.26 \ python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue ``` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx index 4988d8d4c7da..525c2ce45aa2 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/installation.mdx @@ -11,16 +11,16 @@ 在 Docker 中运行 OpenHands 是最简单的方式。 ```bash -docker pull docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik +docker pull docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik docker run -it --rm --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e LOG_ALL_EVENTS=true \ -v /var/run/docker.sock:/var/run/docker.sock \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ --name openhands-app \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 + docker.all-hands.dev/all-hands-ai/openhands:0.26 ``` 你也可以在可脚本化的[无头模式](https://docs.all-hands.dev/modules/usage/how-to/headless-mode)下运行 OpenHands,作为[交互式 CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode),或使用 [OpenHands GitHub Action](https://docs.all-hands.dev/modules/usage/how-to/github-action)。 diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md index e2d4bde47a2e..e9e63ee64c06 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/runtimes.md @@ -11,7 +11,7 @@ ``` docker run # ... - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -v /var/run/docker.sock:/var/run/docker.sock \ # ... ``` diff --git a/docs/modules/usage/how-to/cli-mode.md b/docs/modules/usage/how-to/cli-mode.md index 630f63b9697a..f3fda164f4e8 100644 --- a/docs/modules/usage/how-to/cli-mode.md +++ b/docs/modules/usage/how-to/cli-mode.md @@ -35,7 +35,7 @@ To run OpenHands in CLI mode with Docker: ```bash docker run -it \ --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e SANDBOX_USER_ID=$(id -u) \ -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \ -e LLM_API_KEY=$LLM_API_KEY \ @@ -45,7 +45,7 @@ docker run -it \ -v ~/.openhands-state:/.openhands-state \ --add-host host.docker.internal:host-gateway \ --name openhands-app-$(date +%Y%m%d%H%M%S) \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 \ + docker.all-hands.dev/all-hands-ai/openhands:0.26 \ python -m openhands.core.cli ``` diff --git a/docs/modules/usage/how-to/headless-mode.md b/docs/modules/usage/how-to/headless-mode.md index e68b1e494934..36a6815e9f5e 100644 --- a/docs/modules/usage/how-to/headless-mode.md +++ b/docs/modules/usage/how-to/headless-mode.md @@ -32,7 +32,7 @@ To run OpenHands in Headless mode with Docker: ```bash docker run -it \ --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e SANDBOX_USER_ID=$(id -u) \ -e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \ -e LLM_API_KEY=$LLM_API_KEY \ @@ -43,7 +43,7 @@ docker run -it \ -v ~/.openhands-state:/.openhands-state \ --add-host host.docker.internal:host-gateway \ --name openhands-app-$(date +%Y%m%d%H%M%S) \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 \ + docker.all-hands.dev/all-hands-ai/openhands:0.26 \ python -m openhands.core.main -t "write a bash script that prints hi" ``` diff --git a/docs/modules/usage/installation.mdx b/docs/modules/usage/installation.mdx index 610be444fef4..8220f2aa604b 100644 --- a/docs/modules/usage/installation.mdx +++ b/docs/modules/usage/installation.mdx @@ -58,17 +58,17 @@ A system with a modern processor and a minimum of **4GB RAM** is recommended to The easiest way to run OpenHands is in Docker. ```bash -docker pull docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik +docker pull docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik docker run -it --rm --pull=always \ - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -e LOG_ALL_EVENTS=true \ -v /var/run/docker.sock:/var/run/docker.sock \ -v ~/.openhands-state:/.openhands-state \ -p 3000:3000 \ --add-host host.docker.internal:host-gateway \ --name openhands-app \ - docker.all-hands.dev/all-hands-ai/openhands:0.25 + docker.all-hands.dev/all-hands-ai/openhands:0.26 ``` You'll find OpenHands running at http://localhost:3000! diff --git a/docs/modules/usage/runtimes.md b/docs/modules/usage/runtimes.md index 1fb2d0f4236d..3ab64177e006 100644 --- a/docs/modules/usage/runtimes.md +++ b/docs/modules/usage/runtimes.md @@ -16,7 +16,7 @@ some flags being passed to `docker run` that make this possible: ``` docker run # ... - -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.25-nikolaik \ + -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.26-nikolaik \ -v /var/run/docker.sock:/var/run/docker.sock \ # ... ``` diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 12eb28118046..8a17cc3b4659 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "openhands-frontend", - "version": "0.25.0", + "version": "0.26.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openhands-frontend", - "version": "0.25.0", + "version": "0.26.0", "dependencies": { "@heroui/react": "2.6.14", "@monaco-editor/react": "^4.7.0-rc.0", diff --git a/frontend/package.json b/frontend/package.json index 4fc89d93629d..083c61843738 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "openhands-frontend", - "version": "0.25.0", + "version": "0.26.0", "private": true, "type": "module", "engines": { diff --git a/pyproject.toml b/pyproject.toml index 09d72d3a087c..d2dc591c1a73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "openhands-ai" -version = "0.25.0" +version = "0.26.0" description = "OpenHands: Code Less, Make More" authors = ["OpenHands"] license = "MIT" @@ -109,7 +109,6 @@ reportlab = "*" [tool.coverage.run] concurrency = ["gevent"] - [tool.poetry.group.runtime.dependencies] jupyterlab = "*" notebook = "*" @@ -138,7 +137,6 @@ ignore = ["D1"] [tool.ruff.lint.pydocstyle] convention = "google" - [tool.poetry.group.evaluation.dependencies] streamlit = "*" whatthepatch = "*" From f35ed5e277749b616ec79efde0203a624ad9eafc Mon Sep 17 00:00:00 2001 From: mamoodi Date: Mon, 24 Feb 2025 16:03:10 -0500 Subject: [PATCH 9/9] Add documentation checkbox to PR template (#6924) --- .github/pull_request_template.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index dace0fffec3a..08a5edea6b2a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,12 @@ -**End-user friendly description of the problem this fixes or functionality that this introduces** +- [ ] This change is worth documenting at https://docs.all-hands.dev/ +- [ ] Include this change in the Release Notes. If checked, you **must** provide an **end-user friendly** description for your change below -- [ ] Include this change in the Release Notes. If checked, you must provide an **end-user friendly** description for your change below +**End-user friendly description of the problem this fixes or functionality that this introduces.** ---- -**Give a summary of what the PR does, explaining any non-trivial design decisions** +--- +**Give a summary of what the PR does, explaining any non-trivial design decisions.** --- -**Link of any specific issues this addresses** +**Link of any specific issues this addresses.**