From 149f60aef4b951996a44adb464d015391915183a Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 26 Nov 2024 16:00:29 +0000 Subject: [PATCH 01/90] feat: Add LocalRuntime and rename EventStreamRuntime to LocalDockerRuntime - Add new LocalRuntime implementation that runs action_execution_server directly on host - Rename EventStreamRuntime to LocalDockerRuntime for clarity - Move runtime implementations to dedicated directories - Update documentation to reflect runtime changes --- openhands/runtime/README.md | 24 ++- openhands/runtime/impl/__init__.py | 4 + openhands/runtime/impl/docker/__init__.py | 3 + .../{eventstream => docker}/containers.py | 0 .../docker_runtime.py} | 10 +- openhands/runtime/impl/local/__init__.py | 3 + openhands/runtime/impl/local/local_runtime.py | 190 ++++++++++++++++++ 7 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 openhands/runtime/impl/__init__.py create mode 100644 openhands/runtime/impl/docker/__init__.py rename openhands/runtime/impl/{eventstream => docker}/containers.py (100%) rename openhands/runtime/impl/{eventstream/eventstream_runtime.py => docker/docker_runtime.py} (98%) create mode 100644 openhands/runtime/impl/local/__init__.py create mode 100644 openhands/runtime/impl/local/local_runtime.py diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index ca084706fb3e..053961b17123 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -3,7 +3,7 @@ ## Introduction The OpenHands Runtime folder contains the core components responsible for executing actions and managing the runtime environment for the OpenHands project. This README provides an overview of the main components and their interactions. -You can learn more about how the runtime works in the [EventStream Runtime](https://docs.all-hands.dev/modules/usage/architecture/runtime) documentation. +You can learn more about how the runtime works in the [Runtime Architecture](https://docs.all-hands.dev/modules/usage/architecture/runtime) documentation. ## Main Components @@ -76,9 +76,9 @@ Key features of the `ActionExecutor` class: ## Runtime Types -### EventStream Runtime +### Local Docker Runtime -The EventStream Runtime is designed for local execution using Docker containers: +The Local Docker Runtime (formerly EventStream Runtime) is designed for local execution using Docker containers: - Creates and manages a Docker container for each session - Executes actions within the container @@ -89,9 +89,27 @@ Key features: - Real-time logging and debugging capabilities - Direct access to the local file system - Faster execution due to local resources +- Container isolation for security This is the default runtime used within OpenHands. +### Local Runtime + +The Local Runtime is designed for direct execution on the local machine: + +- Runs the action_execution_server directly on the host +- No Docker container overhead +- Direct access to local system resources +- Ideal for development and testing when Docker is not available or desired + +Key features: +- Minimal setup required +- Direct access to local resources +- No container overhead +- Fastest execution speed + +Note: This runtime provides less isolation than the Local Docker Runtime. + ### Remote Runtime The Remote Runtime is designed for execution in a remote environment: diff --git a/openhands/runtime/impl/__init__.py b/openhands/runtime/impl/__init__.py new file mode 100644 index 000000000000..1241f8ba672c --- /dev/null +++ b/openhands/runtime/impl/__init__.py @@ -0,0 +1,4 @@ +from openhands.runtime.impl.docker import LocalDockerRuntime +from openhands.runtime.impl.local import LocalRuntime + +__all__ = ["LocalDockerRuntime", "LocalRuntime"] \ No newline at end of file diff --git a/openhands/runtime/impl/docker/__init__.py b/openhands/runtime/impl/docker/__init__.py new file mode 100644 index 000000000000..cfbbb7bc2f0c --- /dev/null +++ b/openhands/runtime/impl/docker/__init__.py @@ -0,0 +1,3 @@ +from openhands.runtime.impl.docker.docker_runtime import LocalDockerRuntime + +__all__ = ["LocalDockerRuntime"] \ No newline at end of file diff --git a/openhands/runtime/impl/eventstream/containers.py b/openhands/runtime/impl/docker/containers.py similarity index 100% rename from openhands/runtime/impl/eventstream/containers.py rename to openhands/runtime/impl/docker/containers.py diff --git a/openhands/runtime/impl/eventstream/eventstream_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py similarity index 98% rename from openhands/runtime/impl/eventstream/eventstream_runtime.py rename to openhands/runtime/impl/docker/docker_runtime.py index 30f78f88a2de..894500cb920d 100644 --- a/openhands/runtime/impl/eventstream/eventstream_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -40,7 +40,7 @@ RuntimeNotFoundError, ) from openhands.runtime.builder import DockerRuntimeBuilder -from openhands.runtime.impl.eventstream.containers import remove_all_containers +from openhands.runtime.impl.docker.containers import remove_all_containers from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils import find_available_tcp_port from openhands.runtime.utils.request import send_request @@ -120,9 +120,9 @@ def close(self, timeout: float = 5.0): self.log_generator.close() -class EventStreamRuntime(Runtime): - """This runtime will subscribe the event stream. - When receive an event, it will send the event to runtime-client which run inside the docker environment. +class LocalDockerRuntime(Runtime): + """This runtime runs the action_execution_server inside a Docker container. + When receiving an event, it will send the event to the server via HTTP. Args: config (AppConfig): The application configuration. @@ -133,7 +133,7 @@ class EventStreamRuntime(Runtime): """ # Need to provide this method to allow inheritors to init the Runtime - # without initting the EventStreamRuntime. + # without initting the LocalDockerRuntime. def init_base_runtime( self, config: AppConfig, diff --git a/openhands/runtime/impl/local/__init__.py b/openhands/runtime/impl/local/__init__.py new file mode 100644 index 000000000000..fe8396f79ce6 --- /dev/null +++ b/openhands/runtime/impl/local/__init__.py @@ -0,0 +1,3 @@ +from openhands.runtime.impl.local.local_runtime import LocalRuntime + +__all__ = ["LocalRuntime"] \ No newline at end of file diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py new file mode 100644 index 000000000000..8c754da9eb73 --- /dev/null +++ b/openhands/runtime/impl/local/local_runtime.py @@ -0,0 +1,190 @@ +""" +This runtime runs the action_execution_server directly on the local machine without Docker. +""" + +import os +import subprocess +import threading +from functools import lru_cache +from pathlib import Path +from typing import Callable + +import requests +import tenacity + +from openhands.core.config import AppConfig +from openhands.core.logger import openhands_logger as logger +from openhands.events import EventStream +from openhands.events.action import Action +from openhands.events.observation import ErrorObservation, Observation +from openhands.events.serialization import event_to_dict, observation_from_dict +from openhands.runtime.base import Runtime, RuntimeDisconnectedError +from openhands.runtime.plugins import PluginRequirement +from openhands.runtime.utils import find_available_tcp_port +from openhands.runtime.utils.request import send_request +from openhands.utils.async_utils import call_sync_from_async +from openhands.utils.tenacity_stop import stop_if_should_exit + + +class LocalRuntime(Runtime): + """This runtime will run the action_execution_server directly on the local machine. + When receiving an event, it will send the event to the server via HTTP. + + Args: + config (AppConfig): The application configuration. + event_stream (EventStream): The event stream to subscribe to. + sid (str, optional): The session ID. Defaults to 'default'. + plugins (list[PluginRequirement] | None, optional): List of plugin requirements. Defaults to None. + env_vars (dict[str, str] | None, optional): Environment variables to set. Defaults to None. + """ + + def __init__( + self, + config: AppConfig, + event_stream: EventStream, + sid: str = "default", + plugins: list[PluginRequirement] | None = None, + env_vars: dict[str, str] | None = None, + status_callback: Callable | None = None, + attach_to_existing: bool = False, + headless_mode: bool = True, + ): + self.config = config + self._host_port = 30000 # initial dummy value + self._runtime_initialized: bool = False + self.api_url = f"{self.config.sandbox.local_runtime_url}:{self._host_port}" + self.session = requests.Session() + self.status_callback = status_callback + self.server_process = None + self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time + + super().__init__( + config, + event_stream, + sid, + plugins, + env_vars, + status_callback, + attach_to_existing, + headless_mode, + ) + + async def connect(self): + """Start the action_execution_server on the local machine.""" + self.send_status_message("STATUS$STARTING_RUNTIME") + + self._host_port = self._find_available_port() + self.api_url = f"{self.config.sandbox.local_runtime_url}:{self._host_port}" + + plugin_arg = "" + if self.plugins is not None and len(self.plugins) > 0: + plugin_arg = f"--plugins {' '.join([plugin.name for plugin in self.plugins])} " + + if self.config.sandbox.browsergym_eval_env is not None: + browsergym_arg = f"--browsergym-eval-env {self.config.sandbox.browsergym_eval_env}" + else: + browsergym_arg = "" + + # Start the server process + cmd = ( + f"python -u -m openhands.runtime.action_execution_server {self._host_port} " + f"--working-dir {self.config.workspace_mount_path_in_sandbox} " + f"{plugin_arg}" + f"--username {'openhands' if self.config.run_as_openhands else 'root'} " + f"--user-id {self.config.sandbox.user_id} " + f"{browsergym_arg}" + ) + + self.log("debug", f"Starting server with command: {cmd}") + self.server_process = subprocess.Popen( + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + bufsize=1, + ) + + # Start a thread to read and log server output + def log_output(): + while True: + line = self.server_process.stdout.readline() + if not line: + break + self.log("debug", f"Server: {line.strip()}") + + log_thread = threading.Thread(target=log_output, daemon=True) + log_thread.start() + + self.log("info", f"Waiting for server to become ready at {self.api_url}...") + self.send_status_message("STATUS$WAITING_FOR_CLIENT") + + await call_sync_from_async(self._wait_until_alive) + + if not self.attach_to_existing: + await call_sync_from_async(self.setup_initial_env) + + self.log( + "debug", + f"Server initialized with plugins: {[plugin.name for plugin in self.plugins]}", + ) + if not self.attach_to_existing: + self.send_status_message(" ") + self._runtime_initialized = True + + def _find_available_port(self) -> int: + """Find an available port to use for the server.""" + return find_available_tcp_port() + + @tenacity.retry( + wait=tenacity.wait_exponential(multiplier=0.1, min=0.1, max=1), + stop=stop_if_should_exit, + before_sleep=lambda retry_state: logger.debug( + f"Waiting for server to be ready... (attempt {retry_state.attempt_number})" + ), + ) + def _wait_until_alive(self): + """Wait until the server is ready to accept requests.""" + if self.server_process and self.server_process.poll() is not None: + raise RuntimeError("Server process died") + + try: + response = self.session.get(f"{self.api_url}/health") + response.raise_for_status() + return True + except Exception as e: + self.log("debug", f"Server not ready yet: {e}") + raise + + async def execute_action(self, action: Action) -> Observation: + """Execute an action by sending it to the server.""" + if not self._runtime_initialized: + return ErrorObservation("Runtime not initialized") + + if self.server_process and self.server_process.poll() is not None: + return ErrorObservation("Server process died") + + with self.action_semaphore: + try: + response = await send_request( + self.session, + "POST", + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.RequestException as e: + if isinstance(e, requests.exceptions.ConnectionError): + raise RuntimeDisconnectedError("Server connection lost") + return ErrorObservation(f"Failed to execute action: {e}") + + def close(self): + """Stop the server process.""" + if self.server_process: + self.server_process.terminate() + try: + self.server_process.wait(timeout=5) + except subprocess.TimeoutExpired: + self.server_process.kill() + self.server_process = None + + super().close() \ No newline at end of file From 39911f4d673df812dcfa5e8e76abaccd789bb2a8 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 26 Nov 2024 16:18:02 +0000 Subject: [PATCH 02/90] test: Add LocalRuntime to test configuration - Update imports to use LocalDockerRuntime and LocalRuntime - Add LocalRuntime to get_runtime_classes() - Update _close_test_runtime to handle LocalDockerRuntime --- tests/runtime/conftest.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 2a57a9c820b7..18b474a23557 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -12,7 +12,8 @@ from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream from openhands.runtime.base import Runtime -from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime +from openhands.runtime.impl.docker import LocalDockerRuntime +from openhands.runtime.impl.local import LocalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime from openhands.runtime.plugins import AgentSkillsRequirement, JupyterRequirement @@ -62,7 +63,7 @@ def _remove_folder(folder: str) -> bool: def _close_test_runtime(runtime: Runtime): - if isinstance(runtime, EventStreamRuntime): + if isinstance(runtime, LocalDockerRuntime): runtime.close(rm_all_containers=False) else: runtime.close() @@ -129,7 +130,9 @@ def cleanup(): def get_runtime_classes(): runtime = TEST_RUNTIME if runtime.lower() == 'eventstream': - return [EventStreamRuntime] + return [LocalDockerRuntime] # Previously EventStreamRuntime + elif runtime.lower() == 'local': + return [LocalRuntime] elif runtime.lower() == 'remote': return [RemoteRuntime] elif runtime.lower() == 'runloop': From 1c9b6ca40407673a1a321330c7f97743fc21e2ed Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 26 Nov 2024 16:21:06 +0000 Subject: [PATCH 03/90] fix: Fix mypy errors in LocalRuntime - Add proper type hints for server_process - Fix stdout access safety - Fix async/await type hints - Improve error handling --- openhands/runtime/impl/local/local_runtime.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 8c754da9eb73..0f967e958359 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -7,10 +7,11 @@ import threading from functools import lru_cache from pathlib import Path -from typing import Callable +from typing import Callable, Optional import requests import tenacity +from requests import Response from openhands.core.config import AppConfig from openhands.core.logger import openhands_logger as logger @@ -55,7 +56,7 @@ def __init__( self.api_url = f"{self.config.sandbox.local_runtime_url}:{self._host_port}" self.session = requests.Session() self.status_callback = status_callback - self.server_process = None + self.server_process: Optional[subprocess.Popen[str]] = None self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time super().__init__( @@ -106,11 +107,12 @@ async def connect(self): # Start a thread to read and log server output def log_output(): - while True: - line = self.server_process.stdout.readline() - if not line: - break - self.log("debug", f"Server: {line.strip()}") + if self.server_process and self.server_process.stdout: + while True: + line = self.server_process.stdout.readline() + if not line: + break + self.log("debug", f"Server: {line.strip()}") log_thread = threading.Thread(target=log_output, daemon=True) log_thread.start() @@ -160,21 +162,21 @@ async def execute_action(self, action: Action) -> Observation: if not self._runtime_initialized: return ErrorObservation("Runtime not initialized") - if self.server_process and self.server_process.poll() is not None: + if self.server_process is None or self.server_process.poll() is not None: return ErrorObservation("Server process died") with self.action_semaphore: try: - response = await send_request( - self.session, - "POST", - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + response = await call_sync_from_async( + lambda: self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) ) return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") except requests.exceptions.RequestException as e: - if isinstance(e, requests.exceptions.ConnectionError): - raise RuntimeDisconnectedError("Server connection lost") return ErrorObservation(f"Failed to execute action: {e}") def close(self): From 52d6c60c5bf84b4045b27f62c4f82f78e6d41c9c Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 26 Nov 2024 16:32:11 +0000 Subject: [PATCH 04/90] Fix pr #5284: feat: Add LocalRuntime and rename EventStreamRuntime to LocalDockerRuntime --- openhands/runtime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index 053961b17123..2c35dc6f0ecc 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -108,7 +108,7 @@ Key features: - No container overhead - Fastest execution speed -Note: This runtime provides less isolation than the Local Docker Runtime. +Important: This runtime provides no isolation as it runs directly on the host machine. All actions are executed with the same permissions as the user running OpenHands. For secure execution with proper isolation, use the Local Docker Runtime instead. ### Remote Runtime From e1ac04204c967d47ea15cbf36b734fbaae107044 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 29 Nov 2024 04:16:38 +0000 Subject: [PATCH 05/90] Fix pr #5284: feat: Add LocalRuntime and rename EventStreamRuntime to LocalDockerRuntime --- openhands/runtime/impl/__init__.py | 4 +- openhands/runtime/impl/docker/__init__.py | 4 +- .../runtime/impl/docker/docker_runtime.py | 66 +------------------ .../runtime/impl/eventstream/__init__.py | 3 + .../impl/eventstream/eventstream_runtime.py | 8 +++ openhands/runtime/impl/modal/modal_runtime.py | 6 +- .../runtime/impl/runloop/runloop_runtime.py | 6 +- openhands/runtime/utils/log_buffer.py | 64 ++++++++++++++++++ 8 files changed, 85 insertions(+), 76 deletions(-) create mode 100644 openhands/runtime/impl/eventstream/__init__.py create mode 100644 openhands/runtime/impl/eventstream/eventstream_runtime.py create mode 100644 openhands/runtime/utils/log_buffer.py diff --git a/openhands/runtime/impl/__init__.py b/openhands/runtime/impl/__init__.py index 1241f8ba672c..8ad1f314a038 100644 --- a/openhands/runtime/impl/__init__.py +++ b/openhands/runtime/impl/__init__.py @@ -1,4 +1,4 @@ -from openhands.runtime.impl.docker import LocalDockerRuntime +from openhands.runtime.impl.docker import DockerRuntime from openhands.runtime.impl.local import LocalRuntime -__all__ = ["LocalDockerRuntime", "LocalRuntime"] \ No newline at end of file +__all__ = ["DockerRuntime", "LocalRuntime"] diff --git a/openhands/runtime/impl/docker/__init__.py b/openhands/runtime/impl/docker/__init__.py index cfbbb7bc2f0c..c54d8e2efeb7 100644 --- a/openhands/runtime/impl/docker/__init__.py +++ b/openhands/runtime/impl/docker/__init__.py @@ -1,3 +1,3 @@ -from openhands.runtime.impl.docker.docker_runtime import LocalDockerRuntime +from openhands.runtime.impl.docker.docker_runtime import DockerRuntime -__all__ = ["LocalDockerRuntime"] \ No newline at end of file +__all__ = ["DockerRuntime"] diff --git a/openhands/runtime/impl/docker/docker_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py index 894500cb920d..613200a86b60 100644 --- a/openhands/runtime/impl/docker/docker_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -43,6 +43,7 @@ from openhands.runtime.impl.docker.containers import remove_all_containers from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils import find_available_tcp_port +from openhands.runtime.utils.log_buffer import LogBuffer from openhands.runtime.utils.request import send_request from openhands.runtime.utils.runtime_build import build_runtime_image from openhands.utils.async_utils import call_sync_from_async @@ -57,70 +58,7 @@ def remove_all_runtime_containers(): atexit.register(remove_all_runtime_containers) - -class LogBuffer: - """Synchronous buffer for Docker container logs. - - This class provides a thread-safe way to collect, store, and retrieve logs - from a Docker container. It uses a list to store log lines and provides methods - for appending, retrieving, and clearing logs. - """ - - def __init__(self, container: docker.models.containers.Container, logFn: Callable): - self.init_msg = 'Runtime client initialized.' - - self.buffer: list[str] = [] - self.lock = threading.Lock() - self._stop_event = threading.Event() - self.log_generator = container.logs(stream=True, follow=True) - self.log_stream_thread = threading.Thread(target=self.stream_logs) - self.log_stream_thread.daemon = True - self.log_stream_thread.start() - self.log = logFn - - def append(self, log_line: str): - with self.lock: - self.buffer.append(log_line) - - def get_and_clear(self) -> list[str]: - with self.lock: - logs = list(self.buffer) - self.buffer.clear() - return logs - - def stream_logs(self): - """Stream logs from the Docker container in a separate thread. - - This method runs in its own thread to handle the blocking - operation of reading log lines from the Docker SDK's synchronous generator. - """ - try: - for log_line in self.log_generator: - if self._stop_event.is_set(): - break - if log_line: - decoded_line = log_line.decode('utf-8').rstrip() - self.append(decoded_line) - except Exception as e: - self.log('error', f'Error streaming docker logs: {e}') - - def __del__(self): - if self.log_stream_thread.is_alive(): - self.log( - 'warn', - "LogBuffer was not properly closed. Use 'log_buffer.close()' for clean shutdown.", - ) - self.close(timeout=5) - - def close(self, timeout: float = 5.0): - self._stop_event.set() - self.log_stream_thread.join(timeout) - # Close the log generator to release the file descriptor - if hasattr(self.log_generator, 'close'): - self.log_generator.close() - - -class LocalDockerRuntime(Runtime): +class DockerRuntime(Runtime): """This runtime runs the action_execution_server inside a Docker container. When receiving an event, it will send the event to the server via HTTP. diff --git a/openhands/runtime/impl/eventstream/__init__.py b/openhands/runtime/impl/eventstream/__init__.py new file mode 100644 index 000000000000..f78079307cc0 --- /dev/null +++ b/openhands/runtime/impl/eventstream/__init__.py @@ -0,0 +1,3 @@ +from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime + +__all__ = ['EventStreamRuntime'] diff --git a/openhands/runtime/impl/eventstream/eventstream_runtime.py b/openhands/runtime/impl/eventstream/eventstream_runtime.py new file mode 100644 index 000000000000..aeda603c1efc --- /dev/null +++ b/openhands/runtime/impl/eventstream/eventstream_runtime.py @@ -0,0 +1,8 @@ +from openhands.runtime.impl.docker.docker_runtime import DockerRuntime + + +class EventStreamRuntime(DockerRuntime): + """Default runtime that uses Docker containers for sandboxed execution.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) diff --git a/openhands/runtime/impl/modal/modal_runtime.py b/openhands/runtime/impl/modal/modal_runtime.py index fb036a6d72be..ccf8fd590ffc 100644 --- a/openhands/runtime/impl/modal/modal_runtime.py +++ b/openhands/runtime/impl/modal/modal_runtime.py @@ -10,12 +10,10 @@ from openhands.core.config import AppConfig from openhands.events import EventStream -from openhands.runtime.impl.eventstream.eventstream_runtime import ( - EventStreamRuntime, - LogBuffer, -) +from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.log_buffer import LogBuffer from openhands.runtime.utils.runtime_build import ( BuildFromImageType, prep_build_folder, diff --git a/openhands/runtime/impl/runloop/runloop_runtime.py b/openhands/runtime/impl/runloop/runloop_runtime.py index 76f9b254fdcf..9ec32d33d38e 100644 --- a/openhands/runtime/impl/runloop/runloop_runtime.py +++ b/openhands/runtime/impl/runloop/runloop_runtime.py @@ -12,12 +12,10 @@ from openhands.core.config import AppConfig from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream -from openhands.runtime.impl.eventstream.eventstream_runtime import ( - EventStreamRuntime, - LogBuffer, -) +from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.log_buffer import LogBuffer from openhands.runtime.utils.request import send_request from openhands.utils.tenacity_stop import stop_if_should_exit diff --git a/openhands/runtime/utils/log_buffer.py b/openhands/runtime/utils/log_buffer.py new file mode 100644 index 000000000000..141d91dde220 --- /dev/null +++ b/openhands/runtime/utils/log_buffer.py @@ -0,0 +1,64 @@ +import threading +from typing import Callable + + +class LogBuffer: + """Synchronous buffer for Docker container logs. + + This class provides a thread-safe way to collect, store, and retrieve logs + from a Docker container. It uses a list to store log lines and provides methods + for appending, retrieving, and clearing logs. + """ + + def __init__(self, container, logFn: Callable): + self.init_msg = 'Runtime client initialized.' + + self.buffer: list[str] = [] + self.lock = threading.Lock() + self._stop_event = threading.Event() + self.log_generator = container.logs(stream=True, follow=True) + self.log_stream_thread = threading.Thread(target=self.stream_logs) + self.log_stream_thread.daemon = True + self.log_stream_thread.start() + self.log = logFn + + def append(self, log_line: str): + with self.lock: + self.buffer.append(log_line) + + def get_and_clear(self) -> list[str]: + with self.lock: + logs = list(self.buffer) + self.buffer.clear() + return logs + + def stream_logs(self): + """Stream logs from the Docker container in a separate thread. + + This method runs in its own thread to handle the blocking + operation of reading log lines from the Docker SDK's synchronous generator. + """ + try: + for log_line in self.log_generator: + if self._stop_event.is_set(): + break + if log_line: + decoded_line = log_line.decode('utf-8').rstrip() + self.append(decoded_line) + except Exception as e: + self.log('error', f'Error streaming docker logs: {e}') + + def __del__(self): + if self.log_stream_thread.is_alive(): + self.log( + 'warn', + "LogBuffer was not properly closed. Use 'log_buffer.close()' for clean shutdown.", + ) + self.close(timeout=5) + + def close(self, timeout: float = 5.0): + self._stop_event.set() + self.log_stream_thread.join(timeout) + # Close the log generator to release the file descriptor + if hasattr(self.log_generator, 'close'): + self.log_generator.close() From 5ce262b72fc4f4cc1f36dfd9f4261c4dd5c80c40 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:21:15 +0100 Subject: [PATCH 06/90] Delete openhands/runtime/impl/eventstream/__init__.py --- openhands/runtime/impl/eventstream/__init__.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 openhands/runtime/impl/eventstream/__init__.py diff --git a/openhands/runtime/impl/eventstream/__init__.py b/openhands/runtime/impl/eventstream/__init__.py deleted file mode 100644 index f78079307cc0..000000000000 --- a/openhands/runtime/impl/eventstream/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime - -__all__ = ['EventStreamRuntime'] From 24f29c6baef2f063fa7c138f38859b9a0da6bd66 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:21:43 +0100 Subject: [PATCH 07/90] Delete openhands/runtime/impl/eventstream/eventstream_runtime.py --- openhands/runtime/impl/eventstream/eventstream_runtime.py | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 openhands/runtime/impl/eventstream/eventstream_runtime.py diff --git a/openhands/runtime/impl/eventstream/eventstream_runtime.py b/openhands/runtime/impl/eventstream/eventstream_runtime.py deleted file mode 100644 index aeda603c1efc..000000000000 --- a/openhands/runtime/impl/eventstream/eventstream_runtime.py +++ /dev/null @@ -1,8 +0,0 @@ -from openhands.runtime.impl.docker.docker_runtime import DockerRuntime - - -class EventStreamRuntime(DockerRuntime): - """Default runtime that uses Docker containers for sandboxed execution.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) From 910cc6835df8a2ec33fabbc561a7c1fccea01196 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:23:38 +0100 Subject: [PATCH 08/90] Update openhands/runtime/impl/modal/modal_runtime.py --- openhands/runtime/impl/modal/modal_runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/impl/modal/modal_runtime.py b/openhands/runtime/impl/modal/modal_runtime.py index ccf8fd590ffc..06395029065e 100644 --- a/openhands/runtime/impl/modal/modal_runtime.py +++ b/openhands/runtime/impl/modal/modal_runtime.py @@ -10,7 +10,7 @@ from openhands.core.config import AppConfig from openhands.events import EventStream -from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime +from openhands.runtime.impl.docker.docker_runtime import DockerRuntime from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils.command import get_remote_startup_command from openhands.runtime.utils.log_buffer import LogBuffer From 60839c478326293b6dec70d5bd17a9bf0b662508 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:24:23 +0100 Subject: [PATCH 09/90] Update openhands/runtime/impl/runloop/runloop_runtime.py --- openhands/runtime/impl/runloop/runloop_runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/impl/runloop/runloop_runtime.py b/openhands/runtime/impl/runloop/runloop_runtime.py index 9ec32d33d38e..b9e2adfaeadf 100644 --- a/openhands/runtime/impl/runloop/runloop_runtime.py +++ b/openhands/runtime/impl/runloop/runloop_runtime.py @@ -12,7 +12,7 @@ from openhands.core.config import AppConfig from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream -from openhands.runtime.impl.eventstream.eventstream_runtime import EventStreamRuntime +from openhands.runtime.impl.docker.docker_runtime import DockerRuntime from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils.command import get_remote_startup_command from openhands.runtime.utils.log_buffer import LogBuffer From c6aae8bf351f8942360bfc72c645822a4b69b6fe Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:25:25 +0100 Subject: [PATCH 10/90] Update tests/runtime/conftest.py --- tests/runtime/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 5812d1229817..ccafc1e0e385 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -131,7 +131,7 @@ def cleanup(): def get_runtime_classes() -> list[type[Runtime]]: runtime = TEST_RUNTIME if runtime.lower() == 'eventstream': - return [LocalDockerRuntime] # Previously EventStreamRuntime + return [DockerRuntime] elif runtime.lower() == 'local': return [LocalRuntime] elif runtime.lower() == 'remote': From 7fc2c8764afa1fed8b8bbcaf70addaf4cd4778f9 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:25:44 +0100 Subject: [PATCH 11/90] Update tests/runtime/conftest.py --- tests/runtime/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index ccafc1e0e385..0af2198d9fbb 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -63,7 +63,7 @@ def _remove_folder(folder: str) -> bool: def _close_test_runtime(runtime: Runtime) -> None: - if isinstance(runtime, LocalDockerRuntime): + if isinstance(runtime, DockerRuntime): runtime.close(rm_all_containers=False) else: runtime.close() From 3dec4cd6d22dcb84c986fe4089d448b56ebec577 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Fri, 29 Nov 2024 05:26:21 +0100 Subject: [PATCH 12/90] Update tests/runtime/conftest.py --- tests/runtime/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 0af2198d9fbb..5d78a15114eb 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -12,7 +12,7 @@ from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream from openhands.runtime.base import Runtime -from openhands.runtime.impl.docker import LocalDockerRuntime +from openhands.runtime.impl.docker import DockerRuntime from openhands.runtime.impl.local import LocalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime From 18987b8fa0ea10fb023d7310f5ab174e46991172 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 29 Nov 2024 22:35:24 +0000 Subject: [PATCH 13/90] Fix pr #5284: feat: Add LocalRuntime and rename EventStreamRuntime to LocalDockerRuntime --- openhands/runtime/__init__.py | 8 +++----- openhands/runtime/impl/docker/docker_runtime.py | 4 ++-- openhands/runtime/impl/modal/modal_runtime.py | 2 +- openhands/runtime/impl/runloop/runloop_runtime.py | 6 +++--- openhands/runtime/plugins/agent_skills/utils/config.py | 4 ++-- tests/runtime/conftest.py | 2 +- tests/runtime/test_bash.py | 2 +- tests/runtime/test_browsing.py | 2 +- tests/runtime/test_edit.py | 2 +- tests/runtime/test_env_vars.py | 2 +- tests/runtime/test_images.py | 2 +- tests/runtime/test_ipython.py | 2 +- tests/runtime/test_stress_remote_runtime.py | 2 +- 13 files changed, 19 insertions(+), 21 deletions(-) diff --git a/openhands/runtime/__init__.py b/openhands/runtime/__init__.py index 16534daf6b56..f997d93092c7 100644 --- a/openhands/runtime/__init__.py +++ b/openhands/runtime/__init__.py @@ -1,8 +1,6 @@ from openhands.core.logger import openhands_logger as logger from openhands.runtime.impl.e2b.sandbox import E2BBox -from openhands.runtime.impl.eventstream.eventstream_runtime import ( - EventStreamRuntime, -) +from openhands.runtime.impl.docker.docker_runtime import DockerRuntime from openhands.runtime.impl.modal.modal_runtime import ModalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime @@ -11,7 +9,7 @@ def get_runtime_cls(name: str): # Local imports to avoid circular imports if name == 'eventstream': - return EventStreamRuntime + return DockerRuntime elif name == 'e2b': return E2BBox elif name == 'remote': @@ -30,6 +28,6 @@ def get_runtime_cls(name: str): 'RemoteRuntime', 'ModalRuntime', 'RunloopRuntime', - 'EventStreamRuntime', + 'DockerRuntime', 'get_runtime_cls', ] diff --git a/openhands/runtime/impl/docker/docker_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py index 613200a86b60..00652353ca2a 100644 --- a/openhands/runtime/impl/docker/docker_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -71,7 +71,7 @@ class DockerRuntime(Runtime): """ # Need to provide this method to allow inheritors to init the Runtime - # without initting the LocalDockerRuntime. + # without initting the DockerRuntime. def init_base_runtime( self, config: AppConfig, @@ -395,7 +395,7 @@ def _wait_until_alive(self): pass def close(self, rm_all_containers: bool | None = None): - """Closes the EventStreamRuntime and associated objects + """Closes the DockerRuntime and associated objects Parameters: - rm_all_containers (bool): Whether to remove all containers with the 'openhands-sandbox-' prefix diff --git a/openhands/runtime/impl/modal/modal_runtime.py b/openhands/runtime/impl/modal/modal_runtime.py index 06395029065e..b9f581769bd1 100644 --- a/openhands/runtime/impl/modal/modal_runtime.py +++ b/openhands/runtime/impl/modal/modal_runtime.py @@ -50,7 +50,7 @@ def __init__(self, sandbox: modal.Sandbox): self.log_stream_thread.start() -class ModalRuntime(EventStreamRuntime): +class ModalRuntime(DockerRuntime): """This runtime will subscribe the event stream. When receive an event, it will send the event to runtime-client which run inside the Modal sandbox environment. diff --git a/openhands/runtime/impl/runloop/runloop_runtime.py b/openhands/runtime/impl/runloop/runloop_runtime.py index b9e2adfaeadf..04e41537b67f 100644 --- a/openhands/runtime/impl/runloop/runloop_runtime.py +++ b/openhands/runtime/impl/runloop/runloop_runtime.py @@ -93,8 +93,8 @@ def close(self, timeout: float = 5.0): self.log_stream_thread.join(timeout) -class RunloopRuntime(EventStreamRuntime): - """The RunloopRuntime class is an EventStreamRuntime that utilizes Runloop Devbox as a runtime environment.""" +class RunloopRuntime(DockerRuntime): + """The RunloopRuntime class is a DockerRuntime that utilizes Runloop Devbox as a runtime environment.""" _sandbox_port: int = 4444 _vscode_port: int = 4445 @@ -227,7 +227,7 @@ async def connect(self): logger.info(f'Container started. Server url: {self.api_url}') # End Runloop connect - # NOTE: Copied from EventStreamRuntime + # NOTE: Copied from DockerRuntime logger.info('Waiting for client to become ready...') self.send_status_message('STATUS$WAITING_FOR_CLIENT') self._wait_until_alive() diff --git a/openhands/runtime/plugins/agent_skills/utils/config.py b/openhands/runtime/plugins/agent_skills/utils/config.py index f0084c540393..2b0f0f4ead26 100644 --- a/openhands/runtime/plugins/agent_skills/utils/config.py +++ b/openhands/runtime/plugins/agent_skills/utils/config.py @@ -5,9 +5,9 @@ # ================================================================================================== # OPENAI -# TODO: Move this to EventStream Actions when EventStreamRuntime is fully implemented +# TODO: Move this to EventStream Actions when DockerRuntime is fully implemented # NOTE: we need to get env vars inside functions because they will be set in IPython -# AFTER the agentskills is imported (the case for EventStreamRuntime) +# AFTER the agentskills is imported (the case for DockerRuntime) # ================================================================================================== def _get_openai_api_key(): return os.getenv('OPENAI_API_KEY', os.getenv('SANDBOX_ENV_OPENAI_API_KEY', '')) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 5d78a15114eb..d9856f4045be 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -176,7 +176,7 @@ def runtime_cls(request): # TODO: We will change this to `run_as_user` when `ServerRuntime` is deprecated. -# since `EventStreamRuntime` supports running as an arbitrary user. +# since `DockerRuntime` supports running as an arbitrary user. @pytest.fixture(scope='module', params=get_run_as_openhands()) def run_as_openhands(request): time.sleep(1) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index f8ff95d9a6b2..e68346546bb3 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -1,4 +1,4 @@ -"""Bash-related tests for the EventStreamRuntime, which connects to the ActionExecutor running in the sandbox.""" +"""Bash-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import os from pathlib import Path diff --git a/tests/runtime/test_browsing.py b/tests/runtime/test_browsing.py index f24e37cd06bf..8f3c6bbc2a57 100644 --- a/tests/runtime/test_browsing.py +++ b/tests/runtime/test_browsing.py @@ -1,4 +1,4 @@ -"""Browsing-related tests for the EventStreamRuntime, which connects to the ActionExecutor running in the sandbox.""" +"""Browsing-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import json diff --git a/tests/runtime/test_edit.py b/tests/runtime/test_edit.py index 99a7ce113b18..c507166a840d 100644 --- a/tests/runtime/test_edit.py +++ b/tests/runtime/test_edit.py @@ -1,4 +1,4 @@ -"""Edit-related tests for the EventStreamRuntime.""" +"""Edit-related tests for the DockerRuntime.""" import os diff --git a/tests/runtime/test_env_vars.py b/tests/runtime/test_env_vars.py index de65bf8101ed..898003ff66c7 100644 --- a/tests/runtime/test_env_vars.py +++ b/tests/runtime/test_env_vars.py @@ -1,4 +1,4 @@ -"""Env vars related tests for the EventStreamRuntime, which connects to the ActionExecutor running in the sandbox.""" +"""Env vars related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import os from unittest.mock import patch diff --git a/tests/runtime/test_images.py b/tests/runtime/test_images.py index 1dd7e295c415..b7ab82b54b3c 100644 --- a/tests/runtime/test_images.py +++ b/tests/runtime/test_images.py @@ -1,4 +1,4 @@ -"""Image-related tests for the EventStreamRuntime, which connects to the ActionExecutor running in the sandbox.""" +"""Image-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import pytest from conftest import _close_test_runtime, _load_runtime diff --git a/tests/runtime/test_ipython.py b/tests/runtime/test_ipython.py index 3e22e3e3d3a1..d9786529d96c 100644 --- a/tests/runtime/test_ipython.py +++ b/tests/runtime/test_ipython.py @@ -1,4 +1,4 @@ -"""Test the EventStreamRuntime, which connects to the ActionExecutor running in the sandbox.""" +"""Test the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import pytest from conftest import ( diff --git a/tests/runtime/test_stress_remote_runtime.py b/tests/runtime/test_stress_remote_runtime.py index 367af20467be..6f2f011ee2a3 100644 --- a/tests/runtime/test_stress_remote_runtime.py +++ b/tests/runtime/test_stress_remote_runtime.py @@ -1,4 +1,4 @@ -"""Bash-related tests for the EventStreamRuntime, which connects to the ActionExecutor running in the sandbox.""" +"""Bash-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import asyncio import os From 0b1e1aa4648e4d83b0021ffa970a1a1277e4821c Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sat, 30 Nov 2024 02:57:14 +0100 Subject: [PATCH 14/90] Update openhands/runtime/__init__.py --- openhands/runtime/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openhands/runtime/__init__.py b/openhands/runtime/__init__.py index f997d93092c7..1a9142981d25 100644 --- a/openhands/runtime/__init__.py +++ b/openhands/runtime/__init__.py @@ -1,5 +1,4 @@ from openhands.core.logger import openhands_logger as logger -from openhands.runtime.impl.e2b.sandbox import E2BBox from openhands.runtime.impl.docker.docker_runtime import DockerRuntime from openhands.runtime.impl.modal.modal_runtime import ModalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime From a136ab1aa9b2a6d51242a39ba51de0370393fabb Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sat, 30 Nov 2024 02:57:34 +0100 Subject: [PATCH 15/90] Update openhands/runtime/__init__.py --- openhands/runtime/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openhands/runtime/__init__.py b/openhands/runtime/__init__.py index 1a9142981d25..42beb3b8504c 100644 --- a/openhands/runtime/__init__.py +++ b/openhands/runtime/__init__.py @@ -1,5 +1,6 @@ from openhands.core.logger import openhands_logger as logger from openhands.runtime.impl.docker.docker_runtime import DockerRuntime +from openhands.runtime.impl.e2b.sandbox import E2BBox from openhands.runtime.impl.modal.modal_runtime import ModalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime From e3560ae3c100ad61d3fc6d7274930b164c975002 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 1 Dec 2024 00:26:51 +0800 Subject: [PATCH 16/90] Update openhands/runtime/README.md --- openhands/runtime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index 2c35dc6f0ecc..06cee3f54dde 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -76,7 +76,7 @@ Key features of the `ActionExecutor` class: ## Runtime Types -### Local Docker Runtime +### Docker Runtime The Local Docker Runtime (formerly EventStream Runtime) is designed for local execution using Docker containers: From 598112c5303fb02e4e67917600edc1002a584ad4 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 1 Dec 2024 00:27:00 +0800 Subject: [PATCH 17/90] Update openhands/runtime/README.md --- openhands/runtime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index 06cee3f54dde..4b235e3ae2bb 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -78,7 +78,7 @@ Key features of the `ActionExecutor` class: ### Docker Runtime -The Local Docker Runtime (formerly EventStream Runtime) is designed for local execution using Docker containers: +The Docker Runtime (formerly EventStream Runtime) is designed for local execution using Docker containers: - Creates and manages a Docker container for each session - Executes actions within the container From f2c2311ba6153d9a26f9a4ef801d2c2992ce74ad Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 30 Nov 2024 16:31:40 +0000 Subject: [PATCH 18/90] Fix pr #5284: feat: Add LocalRuntime and rename EventStreamRuntime to LocalDockerRuntime --- openhands/runtime/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openhands/runtime/__init__.py b/openhands/runtime/__init__.py index 42beb3b8504c..89eca1162a3f 100644 --- a/openhands/runtime/__init__.py +++ b/openhands/runtime/__init__.py @@ -1,6 +1,7 @@ from openhands.core.logger import openhands_logger as logger from openhands.runtime.impl.docker.docker_runtime import DockerRuntime from openhands.runtime.impl.e2b.sandbox import E2BBox +from openhands.runtime.impl.local.local_runtime import LocalRuntime from openhands.runtime.impl.modal.modal_runtime import ModalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime @@ -19,6 +20,8 @@ def get_runtime_cls(name: str): return ModalRuntime elif name == 'runloop': return RunloopRuntime + elif name == 'local': + return LocalRuntime else: raise ValueError(f'Runtime {name} not supported') @@ -29,5 +32,6 @@ def get_runtime_cls(name: str): 'ModalRuntime', 'RunloopRuntime', 'DockerRuntime', + 'LocalRuntime', 'get_runtime_cls', ] From 724c1b0e5a1b6b89ad3d23c0fb22296ba3f3721a Mon Sep 17 00:00:00 2001 From: openhands Date: Sun, 1 Dec 2024 01:19:15 +0000 Subject: [PATCH 19/90] Fix pr #5284: feat: Add LocalRuntime and rename EventStreamRuntime to DockerRuntime --- openhands/runtime/action_execution_server.py | 73 +++++++- openhands/runtime/impl/local/local_runtime.py | 162 +++++++++++++++++- openhands/runtime/utils/bash.py | 2 +- poetry.lock | 17 +- pyproject.toml | 3 + 5 files changed, 245 insertions(+), 12 deletions(-) diff --git a/openhands/runtime/action_execution_server.py b/openhands/runtime/action_execution_server.py index ba03613dde53..6ca4a919e04a 100644 --- a/openhands/runtime/action_execution_server.py +++ b/openhands/runtime/action_execution_server.py @@ -431,11 +431,6 @@ async def get_server_info(): return response @app.post('/execute_action') - async def execute_action(action_request: ActionRequest): - assert client is not None - try: - action = event_from_dict(action_request.action) - if not isinstance(action, Action): raise HTTPException(status_code=400, detail='Invalid action type') client.last_execution_time = time.time() observation = await client.run_action(action) @@ -538,6 +533,74 @@ async def download_file(path: str): async def alive(): return {'status': 'ok'} + @app.post('/execute_action') + async def execute_action(action_request: ActionRequest): + if client is None: + raise HTTPException(status_code=500, detail='Runtime client not initialized') + action = event_from_dict(action_request.action) + observation = await client.run_action(action) + return event_to_dict(observation) + + @app.post('/action') + async def execute_action(request: ActionRequest): + if client is None: + raise HTTPException(status_code=500, detail='Runtime client not initialized') + action = event_from_dict(request.action) + observation = await client.run_action(action) + data = await request.json() + host_src = data['host_src'] + sandbox_dest = data['sandbox_dest'] + recursive = data.get('recursive', False) + try: + shutil.copy2(host_src, sandbox_dest) + return {'status': 'ok'} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + @app.get('/list_files') + async def list_files(path: str | None = None): + if client is None: + raise HTTPException(status_code=500, detail='Runtime client not initialized') + try: + if path is None: + path = client.initial_pwd + files = os.listdir(path) + return files + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + @app.post('/copy_from') + async def copy_from(request: Request): + if client is None: + raise HTTPException(status_code=500, detail='Runtime client not initialized') + data = await request.json() + path = data['path'] + try: + # Create a temporary directory to store the files + with tempfile.TemporaryDirectory() as temp_dir: + # Create a zip file + zip_path = os.path.join(temp_dir, 'files.zip') + with ZipFile(zip_path, 'w') as zip_file: + if os.path.isfile(path): + zip_file.write(path, os.path.basename(path)) + else: + for root, _, files in os.walk(path): + for file in files: + file_path = os.path.join(root, file) + zip_file.write(file_path, os.path.relpath(file_path, path)) + return {'path': zip_path} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + @app.get('/vscode_url') + async def vscode_url(): + if client is None: + raise HTTPException(status_code=500, detail='Runtime client not initialized') + if 'vscode' not in client.plugins: + raise HTTPException(status_code=404, detail='VSCode plugin not found') + vscode_plugin: VSCodePlugin = client.plugins['vscode'] # type: ignore + return {'url': vscode_plugin.url} + # ================================ # VSCode-specific operations # ================================ diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 0f967e958359..d3716169a0db 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -16,8 +16,23 @@ from openhands.core.config import AppConfig from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream -from openhands.events.action import Action -from openhands.events.observation import ErrorObservation, Observation +from openhands.events.action import ( + Action, + BrowseInteractiveAction, + BrowseURLAction, + CmdRunAction, + FileReadAction, + FileWriteAction, + IPythonRunCellAction, +) +from openhands.events.observation import ( + BrowserOutputObservation, + CmdOutputObservation, + ErrorObservation, + FileReadObservation, + FileWriteObservation, + Observation, +) from openhands.events.serialization import event_to_dict, observation_from_dict from openhands.runtime.base import Runtime, RuntimeDisconnectedError from openhands.runtime.plugins import PluginRequirement @@ -150,7 +165,7 @@ def _wait_until_alive(self): raise RuntimeError("Server process died") try: - response = self.session.get(f"{self.api_url}/health") + response = self.session.get(f"{self.api_url}/alive") response.raise_for_status() return True except Exception as e: @@ -169,7 +184,7 @@ async def execute_action(self, action: Action) -> Observation: try: response = await call_sync_from_async( lambda: self.session.post( - f"{self.api_url}/action", + f"{self.api_url}/execute_action", json={"action": event_to_dict(action)}, ) ) @@ -189,4 +204,141 @@ def close(self): self.server_process.kill() self.server_process = None - super().close() \ No newline at end of file + super().close() + + def run(self, action: CmdRunAction) -> Observation: + """Execute a command in the local machine.""" + try: + response = self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + return ErrorObservation(f"Failed to execute command: {e}") + + def run_ipython(self, action: IPythonRunCellAction) -> Observation: + """Execute a Python cell in IPython.""" + try: + response = self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + return ErrorObservation(f"Failed to execute IPython cell: {e}") + + def read(self, action: FileReadAction) -> Observation: + """Read a file from the local machine.""" + try: + response = self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + return ErrorObservation(f"Failed to read file: {e}") + + def write(self, action: FileWriteAction) -> Observation: + """Write to a file in the local machine.""" + try: + response = self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + return ErrorObservation(f"Failed to write file: {e}") + + def browse(self, action: BrowseURLAction) -> Observation: + """Browse a URL.""" + try: + response = self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + return ErrorObservation(f"Failed to browse URL: {e}") + + def browse_interactive(self, action: BrowseInteractiveAction) -> Observation: + """Execute interactive browser actions.""" + try: + response = self.session.post( + f"{self.api_url}/action", + json={"action": event_to_dict(action)}, + ) + return observation_from_dict(response.json()) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + return ErrorObservation(f"Failed to execute browser action: {e}") + + def copy_to(self, host_src: str, sandbox_dest: str, recursive: bool = False): + """Copy a file or directory from host to sandbox.""" + try: + response = self.session.post( + f"{self.api_url}/copy_to", + json={ + "host_src": host_src, + "sandbox_dest": sandbox_dest, + "recursive": recursive, + }, + ) + response.raise_for_status() + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + raise RuntimeError(f"Failed to copy file: {e}") + + def list_files(self, path: str | None = None) -> list[str]: + """List files in the sandbox.""" + try: + response = self.session.get( + f"{self.api_url}/list_files", + params={"path": path} if path else None, + ) + response.raise_for_status() + return response.json() + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + raise RuntimeError(f"Failed to list files: {e}") + + def copy_from(self, path: str) -> Path: + """Copy a file or directory from sandbox to host.""" + try: + response = self.session.post( + f"{self.api_url}/copy_from", + json={"path": path}, + ) + response.raise_for_status() + return Path(response.json()["path"]) + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + raise RuntimeError(f"Failed to copy file: {e}") + + @property + def vscode_url(self) -> str | None: + """Get the VSCode URL.""" + if not self.vscode_enabled: + return None + try: + response = self.session.get(f"{self.api_url}/vscode_url") + response.raise_for_status() + return response.json()["url"] + except requests.exceptions.ConnectionError: + raise RuntimeDisconnectedError("Server connection lost") + except requests.exceptions.RequestException as e: + raise RuntimeError(f"Failed to get VSCode URL: {e}") diff --git a/openhands/runtime/utils/bash.py b/openhands/runtime/utils/bash.py index a5019315a038..4f0670edf991 100644 --- a/openhands/runtime/utils/bash.py +++ b/openhands/runtime/utils/bash.py @@ -73,7 +73,7 @@ def __init__(self, work_dir: str, username: str): self._pwd = work_dir self.shell = pexpect.spawn( - f'su {username}', + f'sudo -u {username} bash', encoding='utf-8', codec_errors='replace', echo=False, diff --git a/poetry.lock b/poetry.lock index d97ef683fe6b..acaaaaec23a0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -7145,6 +7145,21 @@ files = [ py = "*" pytest = ">=3.10" +[[package]] +name = "pytest-rerunfailures" +version = "15.0" +description = "pytest plugin to re-run tests to eliminate flaky failures" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest-rerunfailures-15.0.tar.gz", hash = "sha256:2d9ac7baf59f4c13ac730b47f6fa80e755d1ba0581da45ce30b72fb3542b4474"}, + {file = "pytest_rerunfailures-15.0-py3-none-any.whl", hash = "sha256:dd150c4795c229ef44320adc9a0c0532c51b78bb7a6843a8c53556b9a611df1a"}, +] + +[package.dependencies] +packaging = ">=17.1" +pytest = ">=7.4,<8.2.2 || >8.2.2" + [[package]] name = "pytest-xdist" version = "3.6.1" @@ -10350,4 +10365,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "ff370b7b5077720b73fe3b90cc1b7fb9c7a262bfbd35885bb717369061e8a466" +content-hash = "e390a4ae2715e170e7b9a18caa8b656c7e0cdf7c407309cb71d869db7535c63d" diff --git a/pyproject.toml b/pyproject.toml index ec148baadc5c..c63ebf763966 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,7 @@ pygithub = "^2.5.0" openhands-aci = "^0.1.1" python-socketio = "^5.11.4" redis = "^5.2.0" +pytest-rerunfailures = "^15.0" [tool.poetry.group.llama-index.dependencies] llama-index = "*" @@ -98,6 +99,7 @@ reportlab = "*" concurrency = ["gevent"] + [tool.poetry.group.runtime.dependencies] jupyterlab = "*" notebook = "*" @@ -129,6 +131,7 @@ ignore = ["D1"] convention = "google" + [tool.poetry.group.evaluation.dependencies] streamlit = "*" whatthepatch = "*" From 474984a5198a2b0442c67ec10f023958d14ff082 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 2 Dec 2024 12:54:04 -0500 Subject: [PATCH 20/90] add local runtime test to ci --- .github/workflows/ghcr-build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index 7948291d6ec3..415e307ea69b 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -275,7 +275,13 @@ jobs: run: pipx install poetry - name: Install Python dependencies using Poetry run: make install-python-dependencies - - name: Run runtime tests + - name: Run local runtime tests + run: | + TEST_RUNTIME=local \ + TEST_IN_CI=true \ + RUN_AS_OPENHANDS=false \ + poetry run pytest -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py + - name: Run docker runtime tests run: | # We install pytest-xdist in order to run tests across CPUs poetry run pip install pytest-xdist From c78239af80732738914acd554b18adb79487f793 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 2 Dec 2024 12:59:14 -0500 Subject: [PATCH 21/90] tweak configs in local runtime --- openhands/runtime/impl/local/local_runtime.py | 188 ++++++++++-------- 1 file changed, 106 insertions(+), 82 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index d3716169a0db..ee6813f1eab6 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -2,16 +2,15 @@ This runtime runs the action_execution_server directly on the local machine without Docker. """ -import os +import shutil import subprocess +import tempfile import threading -from functools import lru_cache from pathlib import Path from typing import Callable, Optional import requests import tenacity -from requests import Response from openhands.core.config import AppConfig from openhands.core.logger import openhands_logger as logger @@ -26,18 +25,13 @@ IPythonRunCellAction, ) from openhands.events.observation import ( - BrowserOutputObservation, - CmdOutputObservation, ErrorObservation, - FileReadObservation, - FileWriteObservation, Observation, ) from openhands.events.serialization import event_to_dict, observation_from_dict from openhands.runtime.base import Runtime, RuntimeDisconnectedError from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils import find_available_tcp_port -from openhands.runtime.utils.request import send_request from openhands.utils.async_utils import call_sync_from_async from openhands.utils.tenacity_stop import stop_if_should_exit @@ -58,7 +52,7 @@ def __init__( self, config: AppConfig, event_stream: EventStream, - sid: str = "default", + sid: str = 'default', plugins: list[PluginRequirement] | None = None, env_vars: dict[str, str] | None = None, status_callback: Callable | None = None, @@ -66,9 +60,32 @@ def __init__( headless_mode: bool = True, ): self.config = config + if self.config.run_as_openhands: + raise RuntimeError( + 'Local runtime does not support running as openhands. It only supports running as root.' + ) + if self.config.sandbox.user_id != 0: + logger.warning( + 'Local runtime does not support running as a non-root user. Setting user ID to 0.' + ) + self.config.sandbox.user_id = 0 + + self._temp_workspace: str | None = None + if self.config.workspace_base is not None: + logger.warning( + 'Workspace base path is set. It will be set to the default path for the agent to run in.' + ) + self.config.workspace_mount_path_in_sandbox = self.config.workspace_base + else: + logger.warning( + 'Workspace base path is NOT set. Agent will run in a temporary directory.' + ) + self._temp_workspace = tempfile.mkdtemp() + self.config.workspace_mount_path_in_sandbox = self._temp_workspace + self._host_port = 30000 # initial dummy value self._runtime_initialized: bool = False - self.api_url = f"{self.config.sandbox.local_runtime_url}:{self._host_port}" + self.api_url = f'{self.config.sandbox.local_runtime_url}:{self._host_port}' self.session = requests.Session() self.status_callback = status_callback self.server_process: Optional[subprocess.Popen[str]] = None @@ -87,31 +104,35 @@ def __init__( async def connect(self): """Start the action_execution_server on the local machine.""" - self.send_status_message("STATUS$STARTING_RUNTIME") + self.send_status_message('STATUS$STARTING_RUNTIME') self._host_port = self._find_available_port() - self.api_url = f"{self.config.sandbox.local_runtime_url}:{self._host_port}" + self.api_url = f'{self.config.sandbox.local_runtime_url}:{self._host_port}' - plugin_arg = "" + plugin_arg = '' if self.plugins is not None and len(self.plugins) > 0: - plugin_arg = f"--plugins {' '.join([plugin.name for plugin in self.plugins])} " + plugin_arg = ( + f"--plugins {' '.join([plugin.name for plugin in self.plugins])} " + ) if self.config.sandbox.browsergym_eval_env is not None: - browsergym_arg = f"--browsergym-eval-env {self.config.sandbox.browsergym_eval_env}" + browsergym_arg = ( + f'--browsergym-eval-env {self.config.sandbox.browsergym_eval_env}' + ) else: - browsergym_arg = "" + browsergym_arg = '' # Start the server process cmd = ( - f"python -u -m openhands.runtime.action_execution_server {self._host_port} " - f"--working-dir {self.config.workspace_mount_path_in_sandbox} " - f"{plugin_arg}" - f"--username {'openhands' if self.config.run_as_openhands else 'root'} " - f"--user-id {self.config.sandbox.user_id} " - f"{browsergym_arg}" + f'python -u -m openhands.runtime.action_execution_server {self._host_port} ' + f'--working-dir {self.config.workspace_mount_path_in_sandbox} ' + f'{plugin_arg}' + f'--username root ' + f'--user-id {self.config.sandbox.user_id} ' + f'{browsergym_arg}' ) - self.log("debug", f"Starting server with command: {cmd}") + self.log('debug', f'Starting server with command: {cmd}') self.server_process = subprocess.Popen( cmd.split(), stdout=subprocess.PIPE, @@ -127,13 +148,13 @@ def log_output(): line = self.server_process.stdout.readline() if not line: break - self.log("debug", f"Server: {line.strip()}") + self.log('debug', f'Server: {line.strip()}') log_thread = threading.Thread(target=log_output, daemon=True) log_thread.start() - self.log("info", f"Waiting for server to become ready at {self.api_url}...") - self.send_status_message("STATUS$WAITING_FOR_CLIENT") + self.log('info', f'Waiting for server to become ready at {self.api_url}...') + self.send_status_message('STATUS$WAITING_FOR_CLIENT') await call_sync_from_async(self._wait_until_alive) @@ -141,11 +162,11 @@ def log_output(): await call_sync_from_async(self.setup_initial_env) self.log( - "debug", - f"Server initialized with plugins: {[plugin.name for plugin in self.plugins]}", + 'debug', + f'Server initialized with plugins: {[plugin.name for plugin in self.plugins]}', ) if not self.attach_to_existing: - self.send_status_message(" ") + self.send_status_message(' ') self._runtime_initialized = True def _find_available_port(self) -> int: @@ -156,43 +177,43 @@ def _find_available_port(self) -> int: wait=tenacity.wait_exponential(multiplier=0.1, min=0.1, max=1), stop=stop_if_should_exit, before_sleep=lambda retry_state: logger.debug( - f"Waiting for server to be ready... (attempt {retry_state.attempt_number})" + f'Waiting for server to be ready... (attempt {retry_state.attempt_number})' ), ) def _wait_until_alive(self): """Wait until the server is ready to accept requests.""" if self.server_process and self.server_process.poll() is not None: - raise RuntimeError("Server process died") + raise RuntimeError('Server process died') try: - response = self.session.get(f"{self.api_url}/alive") + response = self.session.get(f'{self.api_url}/alive') response.raise_for_status() return True except Exception as e: - self.log("debug", f"Server not ready yet: {e}") + self.log('debug', f'Server not ready yet: {e}') raise async def execute_action(self, action: Action) -> Observation: """Execute an action by sending it to the server.""" if not self._runtime_initialized: - return ErrorObservation("Runtime not initialized") + return ErrorObservation('Runtime not initialized') if self.server_process is None or self.server_process.poll() is not None: - return ErrorObservation("Server process died") + return ErrorObservation('Server process died') with self.action_semaphore: try: response = await call_sync_from_async( lambda: self.session.post( - f"{self.api_url}/execute_action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/execute_action', + json={'action': event_to_dict(action)}, ) ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to execute action: {e}") + return ErrorObservation(f'Failed to execute action: {e}') def close(self): """Stop the server process.""" @@ -204,130 +225,133 @@ def close(self): self.server_process.kill() self.server_process = None + if self._temp_workspace: + shutil.rmtree(self._temp_workspace) + super().close() def run(self, action: CmdRunAction) -> Observation: """Execute a command in the local machine.""" try: response = self.session.post( - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/action', + json={'action': event_to_dict(action)}, ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to execute command: {e}") + return ErrorObservation(f'Failed to execute command: {e}') def run_ipython(self, action: IPythonRunCellAction) -> Observation: """Execute a Python cell in IPython.""" try: response = self.session.post( - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/action', + json={'action': event_to_dict(action)}, ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to execute IPython cell: {e}") + return ErrorObservation(f'Failed to execute IPython cell: {e}') def read(self, action: FileReadAction) -> Observation: """Read a file from the local machine.""" try: response = self.session.post( - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/action', + json={'action': event_to_dict(action)}, ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to read file: {e}") + return ErrorObservation(f'Failed to read file: {e}') def write(self, action: FileWriteAction) -> Observation: """Write to a file in the local machine.""" try: response = self.session.post( - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/action', + json={'action': event_to_dict(action)}, ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to write file: {e}") + return ErrorObservation(f'Failed to write file: {e}') def browse(self, action: BrowseURLAction) -> Observation: """Browse a URL.""" try: response = self.session.post( - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/action', + json={'action': event_to_dict(action)}, ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to browse URL: {e}") + return ErrorObservation(f'Failed to browse URL: {e}') def browse_interactive(self, action: BrowseInteractiveAction) -> Observation: """Execute interactive browser actions.""" try: response = self.session.post( - f"{self.api_url}/action", - json={"action": event_to_dict(action)}, + f'{self.api_url}/action', + json={'action': event_to_dict(action)}, ) return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - return ErrorObservation(f"Failed to execute browser action: {e}") + return ErrorObservation(f'Failed to execute browser action: {e}') def copy_to(self, host_src: str, sandbox_dest: str, recursive: bool = False): """Copy a file or directory from host to sandbox.""" try: response = self.session.post( - f"{self.api_url}/copy_to", + f'{self.api_url}/copy_to', json={ - "host_src": host_src, - "sandbox_dest": sandbox_dest, - "recursive": recursive, + 'host_src': host_src, + 'sandbox_dest': sandbox_dest, + 'recursive': recursive, }, ) response.raise_for_status() except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - raise RuntimeError(f"Failed to copy file: {e}") + raise RuntimeError(f'Failed to copy file: {e}') def list_files(self, path: str | None = None) -> list[str]: """List files in the sandbox.""" try: response = self.session.get( - f"{self.api_url}/list_files", - params={"path": path} if path else None, + f'{self.api_url}/list_files', + params={'path': path} if path else None, ) response.raise_for_status() return response.json() except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - raise RuntimeError(f"Failed to list files: {e}") + raise RuntimeError(f'Failed to list files: {e}') def copy_from(self, path: str) -> Path: """Copy a file or directory from sandbox to host.""" try: response = self.session.post( - f"{self.api_url}/copy_from", - json={"path": path}, + f'{self.api_url}/copy_from', + json={'path': path}, ) response.raise_for_status() - return Path(response.json()["path"]) + return Path(response.json()['path']) except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - raise RuntimeError(f"Failed to copy file: {e}") + raise RuntimeError(f'Failed to copy file: {e}') @property def vscode_url(self) -> str | None: @@ -335,10 +359,10 @@ def vscode_url(self) -> str | None: if not self.vscode_enabled: return None try: - response = self.session.get(f"{self.api_url}/vscode_url") + response = self.session.get(f'{self.api_url}/vscode_url') response.raise_for_status() - return response.json()["url"] + return response.json()['url'] except requests.exceptions.ConnectionError: - raise RuntimeDisconnectedError("Server connection lost") + raise RuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: - raise RuntimeError(f"Failed to get VSCode URL: {e}") + raise RuntimeError(f'Failed to get VSCode URL: {e}') From e24df6ec947d44b8289728db2b69192d76036100 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 2 Dec 2024 12:59:26 -0500 Subject: [PATCH 22/90] update logging --- openhands/runtime/impl/local/local_runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index ee6813f1eab6..f3747b5d423a 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -73,7 +73,7 @@ def __init__( self._temp_workspace: str | None = None if self.config.workspace_base is not None: logger.warning( - 'Workspace base path is set. It will be set to the default path for the agent to run in.' + f'Workspace base path is set to {self.config.workspace_base}. It will be used as the path for the agent to run in.' ) self.config.workspace_mount_path_in_sandbox = self.config.workspace_base else: From 90a12e615a61a22a6e28a0eaa9fbb449b2cb108a Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Tue, 3 Dec 2024 02:01:10 +0800 Subject: [PATCH 23/90] Update openhands/runtime/README.md --- openhands/runtime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index 4b235e3ae2bb..3eb5757aac3a 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -95,7 +95,7 @@ This is the default runtime used within OpenHands. ### Local Runtime -The Local Runtime is designed for direct execution on the local machine: +The Local Runtime is designed for direct execution on the local machine. Currently only supports running as `root`: - Runs the action_execution_server directly on the host - No Docker container overhead From d609f4794b2f69bf12d0252f97f2dc1bd147fc52 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Tue, 3 Dec 2024 02:01:22 +0800 Subject: [PATCH 24/90] Update openhands/runtime/README.md --- openhands/runtime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index 3eb5757aac3a..bac826e9c732 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -108,7 +108,7 @@ Key features: - No container overhead - Fastest execution speed -Important: This runtime provides no isolation as it runs directly on the host machine. All actions are executed with the same permissions as the user running OpenHands. For secure execution with proper isolation, use the Local Docker Runtime instead. +**Important: This runtime provides no isolation as it runs directly on the host machine. All actions are executed with the same permissions as the user running OpenHands. For secure execution with proper isolation, use the Docker Runtime instead.** ### Remote Runtime From 36f3ac12a9d9a8d152d186534c8c51cc9b888ac0 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 11:26:41 -0500 Subject: [PATCH 25/90] remove extra --- openhands/runtime/action_execution_server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openhands/runtime/action_execution_server.py b/openhands/runtime/action_execution_server.py index d10e465cb05c..63d2d1cbdf3e 100644 --- a/openhands/runtime/action_execution_server.py +++ b/openhands/runtime/action_execution_server.py @@ -624,7 +624,6 @@ async def alive(): return {'status': 'not initialized'} return {'status': 'ok'} - @app.post('/execute_action') # ================================ # VSCode-specific operations # ================================ From 89df7214e1697e2a5360c5398e6ce9519cd0b678 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 11:27:44 -0500 Subject: [PATCH 26/90] remove unncessary init --- openhands/runtime/impl/__init__.py | 4 ---- openhands/runtime/impl/docker/__init__.py | 3 --- openhands/runtime/impl/local/__init__.py | 3 --- 3 files changed, 10 deletions(-) delete mode 100644 openhands/runtime/impl/__init__.py delete mode 100644 openhands/runtime/impl/docker/__init__.py delete mode 100644 openhands/runtime/impl/local/__init__.py diff --git a/openhands/runtime/impl/__init__.py b/openhands/runtime/impl/__init__.py deleted file mode 100644 index 8ad1f314a038..000000000000 --- a/openhands/runtime/impl/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from openhands.runtime.impl.docker import DockerRuntime -from openhands.runtime.impl.local import LocalRuntime - -__all__ = ["DockerRuntime", "LocalRuntime"] diff --git a/openhands/runtime/impl/docker/__init__.py b/openhands/runtime/impl/docker/__init__.py deleted file mode 100644 index c54d8e2efeb7..000000000000 --- a/openhands/runtime/impl/docker/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from openhands.runtime.impl.docker.docker_runtime import DockerRuntime - -__all__ = ["DockerRuntime"] diff --git a/openhands/runtime/impl/local/__init__.py b/openhands/runtime/impl/local/__init__.py deleted file mode 100644 index fe8396f79ce6..000000000000 --- a/openhands/runtime/impl/local/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from openhands.runtime.impl.local.local_runtime import LocalRuntime - -__all__ = ["LocalRuntime"] \ No newline at end of file From 4938ce3dc630547793b770a5b7bc133b85a7ce21 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 12:31:58 -0500 Subject: [PATCH 27/90] consolidate runtime startup command into an util function --- .../runtime/impl/docker/docker_runtime.py | 31 +++------- openhands/runtime/impl/modal/modal_runtime.py | 26 ++------- .../runtime/impl/remote/remote_runtime.py | 22 ++----- .../runtime/impl/runloop/runloop_runtime.py | 28 ++------- openhands/runtime/utils/command.py | 57 +++++++++++++------ 5 files changed, 62 insertions(+), 102 deletions(-) diff --git a/openhands/runtime/impl/docker/docker_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py index 6eb51418b733..4945e6620649 100644 --- a/openhands/runtime/impl/docker/docker_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -21,6 +21,7 @@ from openhands.runtime.impl.docker.containers import remove_all_containers from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils import find_available_tcp_port +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.runtime.utils.log_streamer import LogStreamer from openhands.runtime.utils.runtime_build import build_runtime_image from openhands.utils.async_utils import call_sync_from_async @@ -186,11 +187,7 @@ def _init_docker_client() -> docker.DockerClient: def _init_container(self): self.log('debug', 'Preparing to start container...') self.send_status_message('STATUS$PREPARING_CONTAINER') - plugin_arg = '' - if self.plugins is not None and len(self.plugins) > 0: - plugin_arg = ( - f'--plugins {" ".join([plugin.name for plugin in self.plugins])} ' - ) + self._host_port = self._find_available_port(EXECUTION_SERVER_PORT_RANGE) self._container_port = self._host_port self._vscode_port = self._find_available_port(VSCODE_PORT_RANGE) @@ -203,8 +200,6 @@ def _init_container(self): use_host_network = self.config.sandbox.use_host_network network_mode: str | None = 'host' if use_host_network else None - use_host_network = self.config.sandbox.use_host_network - # Initialize port mappings port_mapping: dict[str, list[dict[str, str]]] | None = None if not use_host_network: @@ -257,26 +252,16 @@ def _init_container(self): f'Sandbox workspace: {self.config.workspace_mount_path_in_sandbox}', ) - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_arg = ( - f'--browsergym-eval-env {self.config.sandbox.browsergym_eval_env}' - ) - else: - browsergym_arg = '' + command = get_action_execution_server_startup_command( + server_port=self._container_port, + plugins=self.plugins, + app_config=self.config, + ) try: self.container = self.docker_client.containers.run( self.runtime_container_image, - command=( - f'/openhands/micromamba/bin/micromamba run -n openhands ' - f'poetry run ' - f'python -u -m openhands.runtime.action_execution_server {self._container_port} ' - f'--working-dir "{self.config.workspace_mount_path_in_sandbox}" ' - f'{plugin_arg}' - f'--username {"openhands" if self.config.run_as_openhands else "root"} ' - f'--user-id {self.config.sandbox.user_id} ' - f'{browsergym_arg}' - ), + command=command, network_mode=network_mode, ports=port_mapping, working_dir='/openhands/code/', # do not change this! diff --git a/openhands/runtime/impl/modal/modal_runtime.py b/openhands/runtime/impl/modal/modal_runtime.py index 473c4ae97b10..6c2be0739615 100644 --- a/openhands/runtime/impl/modal/modal_runtime.py +++ b/openhands/runtime/impl/modal/modal_runtime.py @@ -13,7 +13,7 @@ ActionExecutionClient, ) from openhands.runtime.plugins import PluginRequirement -from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.runtime.utils.runtime_build import ( BuildFromImageType, prep_build_folder, @@ -203,11 +203,6 @@ def _init_sandbox( ): try: self.log('debug', 'Preparing to start container...') - plugin_args = [] - if plugins is not None and len(plugins) > 0: - plugin_args.append('--plugins') - plugin_args.extend([plugin.name for plugin in plugins]) - # Combine environment variables environment: dict[str, str | None] = { 'port': str(self.container_port), @@ -216,24 +211,13 @@ def _init_sandbox( if self.config.debug: environment['DEBUG'] = 'true' - browsergym_args = [] - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_args = [ - '-browsergym-eval-env', - self.config.sandbox.browsergym_eval_env, - ] - env_secret = modal.Secret.from_dict(environment) self.log('debug', f'Sandbox workspace: {sandbox_workspace_dir}') - sandbox_start_cmd = get_remote_startup_command( - self.container_port, - sandbox_workspace_dir, - 'openhands' if self.config.run_as_openhands else 'root', - self.config.sandbox.user_id, - plugin_args, - browsergym_args, - is_root=not self.config.run_as_openhands, # is_root=True when running as root + sandbox_start_cmd = get_action_execution_server_startup_command( + server_port=self.container_port, + plugins=self.plugins, + app_config=self.config, ) self.log('debug', f'Starting container with command: {sandbox_start_cmd}') self.sandbox = modal.Sandbox.create( diff --git a/openhands/runtime/impl/remote/remote_runtime.py b/openhands/runtime/impl/remote/remote_runtime.py index 0e0b7adc79e6..ebc1a86b384b 100644 --- a/openhands/runtime/impl/remote/remote_runtime.py +++ b/openhands/runtime/impl/remote/remote_runtime.py @@ -19,7 +19,7 @@ ActionExecutionClient, ) from openhands.runtime.plugins import PluginRequirement -from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.runtime.utils.request import send_request from openhands.runtime.utils.runtime_build import build_runtime_image from openhands.utils.async_utils import call_sync_from_async @@ -194,22 +194,10 @@ def _build_runtime(self): def _start_runtime(self): # Prepare the request body for the /start endpoint - plugin_args = [] - if self.plugins is not None and len(self.plugins) > 0: - plugin_args = ['--plugins'] + [plugin.name for plugin in self.plugins] - browsergym_args = [] - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_args = [ - '--browsergym-eval-env' - ] + self.config.sandbox.browsergym_eval_env.split(' ') - command = get_remote_startup_command( - self.port, - self.config.workspace_mount_path_in_sandbox, - 'openhands' if self.config.run_as_openhands else 'root', - self.config.sandbox.user_id, - plugin_args, - browsergym_args, - is_root=not self.config.run_as_openhands, # is_root=True when running as root + command = get_action_execution_server_startup_command( + server_port=self.port, + plugins=self.plugins, + app_config=self.config, ) start_request = { 'image': self.container_image, diff --git a/openhands/runtime/impl/runloop/runloop_runtime.py b/openhands/runtime/impl/runloop/runloop_runtime.py index 93f019561ff0..51628f54056d 100644 --- a/openhands/runtime/impl/runloop/runloop_runtime.py +++ b/openhands/runtime/impl/runloop/runloop_runtime.py @@ -13,7 +13,7 @@ ActionExecutionClient, ) from openhands.runtime.plugins import PluginRequirement -from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.utils.tenacity_stop import stop_if_should_exit CONTAINER_NAME_PREFIX = 'openhands-runtime-' @@ -78,28 +78,10 @@ def _wait_for_devbox(self, devbox: DevboxView) -> DevboxView: def _create_new_devbox(self) -> DevboxView: # Note: Runloop connect - sandbox_workspace_dir = self.config.workspace_mount_path_in_sandbox - plugin_args = [] - if self.plugins is not None and len(self.plugins) > 0: - plugin_args.append('--plugins') - plugin_args.extend([plugin.name for plugin in self.plugins]) - - browsergym_args = [] - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_args = [ - '-browsergym-eval-env', - self.config.sandbox.browsergym_eval_env, - ] - - # Copied from EventstreamRuntime - start_command = get_remote_startup_command( - self._sandbox_port, - sandbox_workspace_dir, - 'openhands' if self.config.run_as_openhands else 'root', - self.config.sandbox.user_id, - plugin_args, - browsergym_args, - is_root=not self.config.run_as_openhands, # is_root=True when running as root + start_command = get_action_execution_server_startup_command( + server_port=self._sandbox_port, + plugins=self.plugins, + app_config=self.config, ) # Add some additional commands based on our image diff --git a/openhands/runtime/utils/command.py b/openhands/runtime/utils/command.py index 3a32d45fb7e1..415f9fcb7f78 100644 --- a/openhands/runtime/utils/command.py +++ b/openhands/runtime/utils/command.py @@ -1,31 +1,52 @@ -def get_remote_startup_command( - port: int, - sandbox_workspace_dir: str, - username: str, - user_id: int, - plugin_args: list[str], - browsergym_args: list[str], - is_root: bool = False, +from openhands.core.config import AppConfig +from openhands.runtime.plugins import PluginRequirement + +DEFAULT_PYTHON_PREFIX = [ + '/openhands/micromamba/bin/micromamba', + 'run', + '-n', + 'openhands', + 'poetry', + 'run', +] + + +def get_action_execution_server_startup_command( + server_port: int, + plugins: list[PluginRequirement], + app_config: AppConfig, + python_prefix: list[str] = DEFAULT_PYTHON_PREFIX, ): + sandbox_config = app_config.sandbox + + # Plugin args + plugin_args = [] + if plugins is not None and len(plugins) > 0: + plugin_args = ['--plugins'] + [plugin.name for plugin in plugins] + + # Browsergym stuffs + browsergym_args = [] + if sandbox_config.browsergym_eval_env is not None: + browsergym_args = [ + '--browsergym-eval-env' + ] + sandbox_config.browsergym_eval_env.split(' ') + + is_root = not app_config.run_as_openhands + base_cmd = [ - '/openhands/micromamba/bin/micromamba', - 'run', - '-n', - 'openhands', - 'poetry', - 'run', + *python_prefix, 'python', '-u', '-m', 'openhands.runtime.action_execution_server', - str(port), + str(server_port), '--working-dir', - sandbox_workspace_dir, + app_config.workspace_mount_path_in_sandbox, *plugin_args, '--username', - username, + 'openhands' if app_config.run_as_openhands else 'root', '--user-id', - str(user_id), + str(sandbox_config.user_id), *browsergym_args, ] From d3370e6110ed0e209b344a5051f9f539abbb4f6a Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 12:33:14 -0500 Subject: [PATCH 28/90] wip --- openhands/runtime/impl/local/local_runtime.py | 75 ++++++++++++------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 9623911b4484..deb69cce2239 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -30,14 +30,23 @@ Observation, ) from openhands.events.serialization import event_to_dict, observation_from_dict -from openhands.runtime.base import Runtime +from openhands.runtime.impl.action_execution.action_execution_client import ( + ActionExecutionClient, +) +from openhands.runtime.impl.docker.docker_runtime import ( + APP_PORT_RANGE_1, + APP_PORT_RANGE_2, + EXECUTION_SERVER_PORT_RANGE, + VSCODE_PORT_RANGE, +) from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils import find_available_tcp_port +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.utils.async_utils import call_sync_from_async from openhands.utils.tenacity_stop import stop_if_should_exit -class LocalRuntime(Runtime): +class LocalRuntime(ActionExecutionClient): """This runtime will run the action_execution_server directly on the local machine. When receiving an event, it will send the event to the server via HTTP. @@ -84,8 +93,10 @@ def __init__( self._temp_workspace = tempfile.mkdtemp() self.config.workspace_mount_path_in_sandbox = self._temp_workspace - self._host_port = 30000 # initial dummy value - self._runtime_initialized: bool = False + self._host_port = -1 + self._vscode_port = -1 + self._app_ports: list[int] = [] + self.api_url = f'{self.config.sandbox.local_runtime_url}:{self._host_port}' self.session = requests.Session() self.status_callback = status_callback @@ -102,35 +113,33 @@ def __init__( attach_to_existing, headless_mode, ) + logger.warning( + 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' + 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' + 'are running in a controlled environment.' + ) + + def _get_action_execution_server_host(self): + return self.api_url async def connect(self): """Start the action_execution_server on the local machine.""" self.send_status_message('STATUS$STARTING_RUNTIME') - self._host_port = self._find_available_port() + self._host_port = self._find_available_port(EXECUTION_SERVER_PORT_RANGE) + self._vscode_port = self._find_available_port(VSCODE_PORT_RANGE) + self._app_ports = [ + self._find_available_port(APP_PORT_RANGE_1), + self._find_available_port(APP_PORT_RANGE_2), + ] self.api_url = f'{self.config.sandbox.local_runtime_url}:{self._host_port}' - plugin_arg = '' - if self.plugins is not None and len(self.plugins) > 0: - plugin_arg = ( - f"--plugins {' '.join([plugin.name for plugin in self.plugins])} " - ) - - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_arg = ( - f'--browsergym-eval-env {self.config.sandbox.browsergym_eval_env}' - ) - else: - browsergym_arg = '' - # Start the server process - cmd = ( - f'python -u -m openhands.runtime.action_execution_server {self._host_port} ' - f'--working-dir {self.config.workspace_mount_path_in_sandbox} ' - f'{plugin_arg}' - f'--username root ' - f'--user-id {self.config.sandbox.user_id} ' - f'{browsergym_arg}' + cmd = get_action_execution_server_startup_command( + server_port=self._host_port, + plugins=self.plugins, + app_config=self.config, + python_prefix=[], # empty ) self.log('debug', f'Starting server with command: {cmd}') @@ -170,9 +179,12 @@ def log_output(): self.send_status_message(' ') self._runtime_initialized = True - def _find_available_port(self) -> int: - """Find an available port to use for the server.""" - return find_available_tcp_port() + def _find_available_port(self, port_range, max_attempts=5): + port = port_range[1] + for _ in range(max_attempts): + port = find_available_tcp_port(port_range[0], port_range[1]) + return port + return port @tenacity.retry( wait=tenacity.wait_exponential(multiplier=0.1, min=0.1, max=1), @@ -367,3 +379,10 @@ def vscode_url(self) -> str | None: raise AgentRuntimeDisconnectedError('Server connection lost') except requests.exceptions.RequestException as e: raise RuntimeError(f'Failed to get VSCode URL: {e}') + + @property + def web_hosts(self): + hosts: dict[str, int] = {} + for port in self._app_ports: + hosts[f'http://localhost:{port}'] = port + return hosts From 3876a00aa574d12075807268c0fd1427831b475a Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 12:31:58 -0500 Subject: [PATCH 29/90] consolidate runtime startup command into an util function --- .../runtime/impl/docker/docker_runtime.py | 31 +++------- openhands/runtime/impl/modal/modal_runtime.py | 26 ++------- .../runtime/impl/remote/remote_runtime.py | 22 ++----- .../runtime/impl/runloop/runloop_runtime.py | 28 ++------- openhands/runtime/utils/command.py | 57 +++++++++++++------ 5 files changed, 62 insertions(+), 102 deletions(-) diff --git a/openhands/runtime/impl/docker/docker_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py index 6eb51418b733..4945e6620649 100644 --- a/openhands/runtime/impl/docker/docker_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -21,6 +21,7 @@ from openhands.runtime.impl.docker.containers import remove_all_containers from openhands.runtime.plugins import PluginRequirement from openhands.runtime.utils import find_available_tcp_port +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.runtime.utils.log_streamer import LogStreamer from openhands.runtime.utils.runtime_build import build_runtime_image from openhands.utils.async_utils import call_sync_from_async @@ -186,11 +187,7 @@ def _init_docker_client() -> docker.DockerClient: def _init_container(self): self.log('debug', 'Preparing to start container...') self.send_status_message('STATUS$PREPARING_CONTAINER') - plugin_arg = '' - if self.plugins is not None and len(self.plugins) > 0: - plugin_arg = ( - f'--plugins {" ".join([plugin.name for plugin in self.plugins])} ' - ) + self._host_port = self._find_available_port(EXECUTION_SERVER_PORT_RANGE) self._container_port = self._host_port self._vscode_port = self._find_available_port(VSCODE_PORT_RANGE) @@ -203,8 +200,6 @@ def _init_container(self): use_host_network = self.config.sandbox.use_host_network network_mode: str | None = 'host' if use_host_network else None - use_host_network = self.config.sandbox.use_host_network - # Initialize port mappings port_mapping: dict[str, list[dict[str, str]]] | None = None if not use_host_network: @@ -257,26 +252,16 @@ def _init_container(self): f'Sandbox workspace: {self.config.workspace_mount_path_in_sandbox}', ) - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_arg = ( - f'--browsergym-eval-env {self.config.sandbox.browsergym_eval_env}' - ) - else: - browsergym_arg = '' + command = get_action_execution_server_startup_command( + server_port=self._container_port, + plugins=self.plugins, + app_config=self.config, + ) try: self.container = self.docker_client.containers.run( self.runtime_container_image, - command=( - f'/openhands/micromamba/bin/micromamba run -n openhands ' - f'poetry run ' - f'python -u -m openhands.runtime.action_execution_server {self._container_port} ' - f'--working-dir "{self.config.workspace_mount_path_in_sandbox}" ' - f'{plugin_arg}' - f'--username {"openhands" if self.config.run_as_openhands else "root"} ' - f'--user-id {self.config.sandbox.user_id} ' - f'{browsergym_arg}' - ), + command=command, network_mode=network_mode, ports=port_mapping, working_dir='/openhands/code/', # do not change this! diff --git a/openhands/runtime/impl/modal/modal_runtime.py b/openhands/runtime/impl/modal/modal_runtime.py index 473c4ae97b10..6c2be0739615 100644 --- a/openhands/runtime/impl/modal/modal_runtime.py +++ b/openhands/runtime/impl/modal/modal_runtime.py @@ -13,7 +13,7 @@ ActionExecutionClient, ) from openhands.runtime.plugins import PluginRequirement -from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.runtime.utils.runtime_build import ( BuildFromImageType, prep_build_folder, @@ -203,11 +203,6 @@ def _init_sandbox( ): try: self.log('debug', 'Preparing to start container...') - plugin_args = [] - if plugins is not None and len(plugins) > 0: - plugin_args.append('--plugins') - plugin_args.extend([plugin.name for plugin in plugins]) - # Combine environment variables environment: dict[str, str | None] = { 'port': str(self.container_port), @@ -216,24 +211,13 @@ def _init_sandbox( if self.config.debug: environment['DEBUG'] = 'true' - browsergym_args = [] - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_args = [ - '-browsergym-eval-env', - self.config.sandbox.browsergym_eval_env, - ] - env_secret = modal.Secret.from_dict(environment) self.log('debug', f'Sandbox workspace: {sandbox_workspace_dir}') - sandbox_start_cmd = get_remote_startup_command( - self.container_port, - sandbox_workspace_dir, - 'openhands' if self.config.run_as_openhands else 'root', - self.config.sandbox.user_id, - plugin_args, - browsergym_args, - is_root=not self.config.run_as_openhands, # is_root=True when running as root + sandbox_start_cmd = get_action_execution_server_startup_command( + server_port=self.container_port, + plugins=self.plugins, + app_config=self.config, ) self.log('debug', f'Starting container with command: {sandbox_start_cmd}') self.sandbox = modal.Sandbox.create( diff --git a/openhands/runtime/impl/remote/remote_runtime.py b/openhands/runtime/impl/remote/remote_runtime.py index 0e0b7adc79e6..ebc1a86b384b 100644 --- a/openhands/runtime/impl/remote/remote_runtime.py +++ b/openhands/runtime/impl/remote/remote_runtime.py @@ -19,7 +19,7 @@ ActionExecutionClient, ) from openhands.runtime.plugins import PluginRequirement -from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.runtime.utils.request import send_request from openhands.runtime.utils.runtime_build import build_runtime_image from openhands.utils.async_utils import call_sync_from_async @@ -194,22 +194,10 @@ def _build_runtime(self): def _start_runtime(self): # Prepare the request body for the /start endpoint - plugin_args = [] - if self.plugins is not None and len(self.plugins) > 0: - plugin_args = ['--plugins'] + [plugin.name for plugin in self.plugins] - browsergym_args = [] - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_args = [ - '--browsergym-eval-env' - ] + self.config.sandbox.browsergym_eval_env.split(' ') - command = get_remote_startup_command( - self.port, - self.config.workspace_mount_path_in_sandbox, - 'openhands' if self.config.run_as_openhands else 'root', - self.config.sandbox.user_id, - plugin_args, - browsergym_args, - is_root=not self.config.run_as_openhands, # is_root=True when running as root + command = get_action_execution_server_startup_command( + server_port=self.port, + plugins=self.plugins, + app_config=self.config, ) start_request = { 'image': self.container_image, diff --git a/openhands/runtime/impl/runloop/runloop_runtime.py b/openhands/runtime/impl/runloop/runloop_runtime.py index 93f019561ff0..51628f54056d 100644 --- a/openhands/runtime/impl/runloop/runloop_runtime.py +++ b/openhands/runtime/impl/runloop/runloop_runtime.py @@ -13,7 +13,7 @@ ActionExecutionClient, ) from openhands.runtime.plugins import PluginRequirement -from openhands.runtime.utils.command import get_remote_startup_command +from openhands.runtime.utils.command import get_action_execution_server_startup_command from openhands.utils.tenacity_stop import stop_if_should_exit CONTAINER_NAME_PREFIX = 'openhands-runtime-' @@ -78,28 +78,10 @@ def _wait_for_devbox(self, devbox: DevboxView) -> DevboxView: def _create_new_devbox(self) -> DevboxView: # Note: Runloop connect - sandbox_workspace_dir = self.config.workspace_mount_path_in_sandbox - plugin_args = [] - if self.plugins is not None and len(self.plugins) > 0: - plugin_args.append('--plugins') - plugin_args.extend([plugin.name for plugin in self.plugins]) - - browsergym_args = [] - if self.config.sandbox.browsergym_eval_env is not None: - browsergym_args = [ - '-browsergym-eval-env', - self.config.sandbox.browsergym_eval_env, - ] - - # Copied from EventstreamRuntime - start_command = get_remote_startup_command( - self._sandbox_port, - sandbox_workspace_dir, - 'openhands' if self.config.run_as_openhands else 'root', - self.config.sandbox.user_id, - plugin_args, - browsergym_args, - is_root=not self.config.run_as_openhands, # is_root=True when running as root + start_command = get_action_execution_server_startup_command( + server_port=self._sandbox_port, + plugins=self.plugins, + app_config=self.config, ) # Add some additional commands based on our image diff --git a/openhands/runtime/utils/command.py b/openhands/runtime/utils/command.py index 3a32d45fb7e1..415f9fcb7f78 100644 --- a/openhands/runtime/utils/command.py +++ b/openhands/runtime/utils/command.py @@ -1,31 +1,52 @@ -def get_remote_startup_command( - port: int, - sandbox_workspace_dir: str, - username: str, - user_id: int, - plugin_args: list[str], - browsergym_args: list[str], - is_root: bool = False, +from openhands.core.config import AppConfig +from openhands.runtime.plugins import PluginRequirement + +DEFAULT_PYTHON_PREFIX = [ + '/openhands/micromamba/bin/micromamba', + 'run', + '-n', + 'openhands', + 'poetry', + 'run', +] + + +def get_action_execution_server_startup_command( + server_port: int, + plugins: list[PluginRequirement], + app_config: AppConfig, + python_prefix: list[str] = DEFAULT_PYTHON_PREFIX, ): + sandbox_config = app_config.sandbox + + # Plugin args + plugin_args = [] + if plugins is not None and len(plugins) > 0: + plugin_args = ['--plugins'] + [plugin.name for plugin in plugins] + + # Browsergym stuffs + browsergym_args = [] + if sandbox_config.browsergym_eval_env is not None: + browsergym_args = [ + '--browsergym-eval-env' + ] + sandbox_config.browsergym_eval_env.split(' ') + + is_root = not app_config.run_as_openhands + base_cmd = [ - '/openhands/micromamba/bin/micromamba', - 'run', - '-n', - 'openhands', - 'poetry', - 'run', + *python_prefix, 'python', '-u', '-m', 'openhands.runtime.action_execution_server', - str(port), + str(server_port), '--working-dir', - sandbox_workspace_dir, + app_config.workspace_mount_path_in_sandbox, *plugin_args, '--username', - username, + 'openhands' if app_config.run_as_openhands else 'root', '--user-id', - str(user_id), + str(sandbox_config.user_id), *browsergym_args, ] From 383baa68acfbc5b9abe069061df451392ce0c3f7 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 13:59:54 -0500 Subject: [PATCH 30/90] wip --- openhands/runtime/impl/local/local_runtime.py | 151 +++--------------- openhands/runtime/utils/runtime_init.py | 5 + 2 files changed, 23 insertions(+), 133 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index deb69cce2239..8c11ec641949 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -103,6 +103,14 @@ def __init__( self.server_process: Optional[subprocess.Popen[str]] = None self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time + # A temporary directory is created for the agent to run in + # This is used for the local runtime only + self._temp_workspace = tempfile.mkdtemp( + prefix=f'openhands_workspace_{sid}', + ) + self.config.workspace_mount_path_in_sandbox = self._temp_workspace + + # Initialize the action_execution_server super().__init__( config, event_stream, @@ -113,10 +121,12 @@ def __init__( attach_to_existing, headless_mode, ) + logger.warning( 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' - 'are running in a controlled environment.' + 'are running in a controlled environment.\n' + f'Temp workspace: {self._temp_workspace}' ) def _get_action_execution_server_host(self): @@ -243,142 +253,17 @@ def close(self): super().close() - def run(self, action: CmdRunAction) -> Observation: - """Execute a command in the local machine.""" - try: - response = self.session.post( - f'{self.api_url}/action', - json={'action': event_to_dict(action)}, - ) - return observation_from_dict(response.json()) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to execute command: {e}') - - def run_ipython(self, action: IPythonRunCellAction) -> Observation: - """Execute a Python cell in IPython.""" - try: - response = self.session.post( - f'{self.api_url}/action', - json={'action': event_to_dict(action)}, - ) - return observation_from_dict(response.json()) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to execute IPython cell: {e}') - - def read(self, action: FileReadAction) -> Observation: - """Read a file from the local machine.""" - try: - response = self.session.post( - f'{self.api_url}/action', - json={'action': event_to_dict(action)}, - ) - return observation_from_dict(response.json()) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to read file: {e}') - - def write(self, action: FileWriteAction) -> Observation: - """Write to a file in the local machine.""" - try: - response = self.session.post( - f'{self.api_url}/action', - json={'action': event_to_dict(action)}, - ) - return observation_from_dict(response.json()) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to write file: {e}') - - def browse(self, action: BrowseURLAction) -> Observation: - """Browse a URL.""" - try: - response = self.session.post( - f'{self.api_url}/action', - json={'action': event_to_dict(action)}, - ) - return observation_from_dict(response.json()) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to browse URL: {e}') - - def browse_interactive(self, action: BrowseInteractiveAction) -> Observation: - """Execute interactive browser actions.""" - try: - response = self.session.post( - f'{self.api_url}/action', - json={'action': event_to_dict(action)}, - ) - return observation_from_dict(response.json()) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to execute browser action: {e}') - - def copy_to(self, host_src: str, sandbox_dest: str, recursive: bool = False): - """Copy a file or directory from host to sandbox.""" - try: - response = self.session.post( - f'{self.api_url}/copy_to', - json={ - 'host_src': host_src, - 'sandbox_dest': sandbox_dest, - 'recursive': recursive, - }, - ) - response.raise_for_status() - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - raise RuntimeError(f'Failed to copy file: {e}') - - def list_files(self, path: str | None = None) -> list[str]: - """List files in the sandbox.""" - try: - response = self.session.get( - f'{self.api_url}/list_files', - params={'path': path} if path else None, - ) - response.raise_for_status() - return response.json() - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - raise RuntimeError(f'Failed to list files: {e}') - - def copy_from(self, path: str) -> Path: - """Copy a file or directory from sandbox to host.""" - try: - response = self.session.post( - f'{self.api_url}/copy_from', - json={'path': path}, - ) - response.raise_for_status() - return Path(response.json()['path']) - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - raise RuntimeError(f'Failed to copy file: {e}') + # Cleanup the temp workspace + if self._temp_workspace: + shutil.rmtree(self._temp_workspace) @property def vscode_url(self) -> str | None: - """Get the VSCode URL.""" - if not self.vscode_enabled: + token = super().get_vscode_token() + if not token: return None - try: - response = self.session.get(f'{self.api_url}/vscode_url') - response.raise_for_status() - return response.json()['url'] - except requests.exceptions.ConnectionError: - raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - raise RuntimeError(f'Failed to get VSCode URL: {e}') + vscode_url = f'http://localhost:{self._vscode_port}/?tkn={token}&folder={self.config.workspace_mount_path_in_sandbox}' + return vscode_url @property def web_hosts(self): diff --git a/openhands/runtime/utils/runtime_init.py b/openhands/runtime/utils/runtime_init.py index b38ab7ca3495..511a523831fa 100644 --- a/openhands/runtime/utils/runtime_init.py +++ b/openhands/runtime/utils/runtime_init.py @@ -1,3 +1,4 @@ +import os import subprocess from openhands.core.logger import openhands_logger as logger @@ -31,6 +32,10 @@ def init_user_and_working_directory( Returns: int | None: The user ID if it was updated, None otherwise. """ + # if username is CURRENT_USER, then we don't need to do anything + # This is specific to the local runtime + if username == os.getenv('USER') and username not in ['root', 'openhands']: + return None # First create the working directory, independent of the user logger.debug(f'Client working directory: {initial_cwd}') From efe96221e1434ba18e650fda084a3e9fd11342e3 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 14:10:26 -0500 Subject: [PATCH 31/90] replace run_as_openhands with more general run_as_user --- .github/workflows/ghcr-build.yml | 6 +- config.template.toml | 2 +- containers/app/Dockerfile | 2 +- containers/app/entrypoint.sh | 2 +- .../current/usage/configuration-options.md | 6 +- .../current/usage/custom_sandbox_guide.md | 4 +- .../current/usage/configuration-options.md | 6 +- .../current/usage/custom_sandbox_guide.md | 4 +- docs/modules/usage/configuration-options.md | 6 +- evaluation/benchmarks/EDA/run_infer.py | 2 +- .../benchmarks/agent_bench/run_infer.py | 2 +- .../benchmarks/aider_bench/run_infer.py | 2 +- evaluation/benchmarks/biocoder/run_infer.py | 2 +- evaluation/benchmarks/bird/run_infer.py | 2 +- .../browsing_delegation/run_infer.py | 2 +- .../benchmarks/commit0_bench/run_infer.py | 2 +- .../benchmarks/discoverybench/run_infer.py | 2 +- evaluation/benchmarks/gaia/run_infer.py | 2 +- evaluation/benchmarks/gorilla/run_infer.py | 2 +- evaluation/benchmarks/gpqa/run_infer.py | 2 +- .../benchmarks/humanevalfix/run_infer.py | 2 +- .../benchmarks/logic_reasoning/run_infer.py | 2 +- evaluation/benchmarks/miniwob/run_infer.py | 2 +- evaluation/benchmarks/mint/run_infer.py | 2 +- evaluation/benchmarks/ml_bench/run_infer.py | 2 +- .../benchmarks/scienceagentbench/run_infer.py | 2 +- evaluation/benchmarks/swe_bench/eval_infer.py | 2 +- evaluation/benchmarks/swe_bench/run_infer.py | 2 +- .../benchmarks/the_agent_company/run_infer.py | 2 +- evaluation/benchmarks/toolqa/run_infer.py | 2 +- evaluation/benchmarks/webarena/run_infer.py | 2 +- evaluation/integration_tests/run_infer.py | 2 +- openhands/core/config/app_config.py | 5 +- openhands/core/config/utils.py | 17 +++- openhands/runtime/impl/local/local_runtime.py | 32 +++----- openhands/runtime/utils/command.py | 4 +- tests/runtime/conftest.py | 15 ++-- tests/runtime/test_bash.py | 80 +++++++++---------- tests/runtime/test_browsergym_envs.py | 2 +- tests/runtime/test_browsing.py | 4 +- tests/runtime/test_edit.py | 12 +-- tests/runtime/test_env_vars.py | 4 +- tests/runtime/test_ipython.py | 22 ++--- tests/runtime/test_stress_remote_runtime.py | 2 +- 44 files changed, 143 insertions(+), 140 deletions(-) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index 625ade07a770..2fc087c2ad1e 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -279,7 +279,7 @@ jobs: run: | TEST_RUNTIME=local \ TEST_IN_CI=true \ - RUN_AS_OPENHANDS=false \ + RUN_AS_USER=root \ poetry run pytest -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Run docker runtime tests run: | @@ -296,7 +296,7 @@ jobs: SANDBOX_USER_ID=$(id -u) \ SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \ TEST_IN_CI=true \ - RUN_AS_OPENHANDS=false \ + RUN_AS_USER=root \ poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 @@ -373,7 +373,7 @@ jobs: SANDBOX_USER_ID=$(id -u) \ SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \ TEST_IN_CI=true \ - RUN_AS_OPENHANDS=true \ + RUN_AS_USER=openhands \ poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 diff --git a/config.template.toml b/config.template.toml index de0ebf3a578f..270aeadc7b6d 100644 --- a/config.template.toml +++ b/config.template.toml @@ -64,7 +64,7 @@ workspace_base = "./workspace" #workspace_mount_rewrite = "" # Run as openhands -#run_as_openhands = true +#run_as_user = "openhands" # Runtime environment #runtime = "eventstream" diff --git a/containers/app/Dockerfile b/containers/app/Dockerfile index adcda79f338e..0e1e4515e1c4 100644 --- a/containers/app/Dockerfile +++ b/containers/app/Dockerfile @@ -34,7 +34,7 @@ WORKDIR /app ARG OPENHANDS_BUILD_VERSION #re-declare for this section -ENV RUN_AS_OPENHANDS=true +ENV RUN_AS_USER=openhands # A random number--we need this to be different from the user's UID on the host machine ENV OPENHANDS_USER_ID=42420 ENV SANDBOX_LOCAL_RUNTIME_URL=http://host.docker.internal diff --git a/containers/app/entrypoint.sh b/containers/app/entrypoint.sh index 380490449a99..84ee6c6ede37 100644 --- a/containers/app/entrypoint.sh +++ b/containers/app/entrypoint.sh @@ -25,7 +25,7 @@ fi if [[ "$SANDBOX_USER_ID" -eq 0 ]]; then echo "Running OpenHands as root" - export RUN_AS_OPENHANDS=false + export RUN_AS_USER=root mkdir -p /root/.cache/ms-playwright/ if [ -d "/home/openhands/.cache/ms-playwright/" ]; then mv /home/openhands/.cache/ms-playwright/ /root/.cache/ diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md index 36024b2f3c15..72ebeb131f1d 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md @@ -158,9 +158,9 @@ Les options de configuration de base sont définies dans la section `[core]` du - Description : Chemin pour réécrire le chemin de montage de l'espace de travail. Vous pouvez généralement ignorer cela, cela fait référence à des cas spéciaux d'exécution à l'intérieur d'un autre conteneur. **Divers** -- `run_as_openhands` - - Type : `bool` - - Valeur par défaut : `true` +- `run_as_user` + - Type : `str` + - Valeur par défaut : `openhands` - Description : Exécuter en tant qu'OpenHands - `runtime` diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md index bf4a0b328c67..c97bf14f9114 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md @@ -41,7 +41,7 @@ Créez un fichier ```config.toml``` dans le répertoire OpenHands et entrez ces ```toml [core] workspace_base="./workspace" -run_as_openhands=true +run_as_user="openhands" sandbox_base_container_image="image_personnalisée" ``` @@ -87,7 +87,7 @@ Si vous voyez cette erreur dans la sortie de la console, il s'agit du fait que O ```toml [core] workspace_base="./workspace" -run_as_openhands=true +run_as_user="openhands" sandbox_base_container_image="image_personnalisée" sandbox_user_id="1001" ``` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md index 0cff6a5f933d..19a92d300faa 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md @@ -155,9 +155,9 @@ - 描述: 重写工作区挂载路径的路径。通常可以忽略这个,它指的是在另一个容器内运行的特殊情况。 **其他** -- `run_as_openhands` - - 类型: `bool` - - 默认值: `true` +- `run_as_user` + - 类型: `str` + - 默认值: `openhands` - 描述: 以 OpenHands 身份运行 - `runtime` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md index 04e05f915d94..3c62700b4677 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md @@ -57,7 +57,7 @@ docker build -t custom_image . ``` [core] workspace_base="./workspace" -run_as_openhands=true +run_as_user="openhands" sandbox_base_container_image="custom_image" ``` @@ -88,7 +88,7 @@ sandbox_base_container_image="custom_image" ``` [core] workspace_base="./workspace" -run_as_openhands=true +run_as_user="openhands" sandbox_base_container_image="custom_image" sandbox_user_id="1001" ``` diff --git a/docs/modules/usage/configuration-options.md b/docs/modules/usage/configuration-options.md index 29c9b6759507..15be982291fb 100644 --- a/docs/modules/usage/configuration-options.md +++ b/docs/modules/usage/configuration-options.md @@ -156,9 +156,9 @@ The core configuration options are defined in the `[core]` section of the `confi - Description: Path to rewrite the workspace mount path to. You can usually ignore this, it refers to special cases of running inside another container. **Miscellaneous** -- `run_as_openhands` - - Type: `bool` - - Default: `true` +- `run_as_user` + - Type: `str` + - Default: `"openhands"` - Description: Run as OpenHands - `runtime` diff --git a/evaluation/benchmarks/EDA/run_infer.py b/evaluation/benchmarks/EDA/run_infer.py index b5a021a0b853..6072af74de00 100644 --- a/evaluation/benchmarks/EDA/run_infer.py +++ b/evaluation/benchmarks/EDA/run_infer.py @@ -62,7 +62,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/agent_bench/run_infer.py b/evaluation/benchmarks/agent_bench/run_infer.py index 554ddc66488f..f2b14b9c2ff9 100644 --- a/evaluation/benchmarks/agent_bench/run_infer.py +++ b/evaluation/benchmarks/agent_bench/run_infer.py @@ -42,7 +42,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/aider_bench/run_infer.py b/evaluation/benchmarks/aider_bench/run_infer.py index 51179724e239..30fdd27eac85 100644 --- a/evaluation/benchmarks/aider_bench/run_infer.py +++ b/evaluation/benchmarks/aider_bench/run_infer.py @@ -49,7 +49,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/biocoder/run_infer.py b/evaluation/benchmarks/biocoder/run_infer.py index dfc0eaf1f912..37546bc468c4 100644 --- a/evaluation/benchmarks/biocoder/run_infer.py +++ b/evaluation/benchmarks/biocoder/run_infer.py @@ -60,7 +60,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/bird/run_infer.py b/evaluation/benchmarks/bird/run_infer.py index 3254e4a2c791..1747bbf7ad62 100644 --- a/evaluation/benchmarks/bird/run_infer.py +++ b/evaluation/benchmarks/bird/run_infer.py @@ -73,7 +73,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/browsing_delegation/run_infer.py b/evaluation/benchmarks/browsing_delegation/run_infer.py index 3313c9ff4c3d..d834df7c08f7 100644 --- a/evaluation/benchmarks/browsing_delegation/run_infer.py +++ b/evaluation/benchmarks/browsing_delegation/run_infer.py @@ -38,7 +38,7 @@ def get_config( ), 'max_iterations must be 1 for browsing delegation evaluation.' config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/commit0_bench/run_infer.py b/evaluation/benchmarks/commit0_bench/run_infer.py index d8f1f64b1a6b..64d63cc5e247 100644 --- a/evaluation/benchmarks/commit0_bench/run_infer.py +++ b/evaluation/benchmarks/commit0_bench/run_infer.py @@ -122,7 +122,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', max_iterations=metadata.max_iterations, runtime=os.environ.get('RUNTIME', 'docker'), sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/discoverybench/run_infer.py b/evaluation/benchmarks/discoverybench/run_infer.py index 05ff44003517..de036ca9bdf3 100644 --- a/evaluation/benchmarks/discoverybench/run_infer.py +++ b/evaluation/benchmarks/discoverybench/run_infer.py @@ -64,7 +64,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/gaia/run_infer.py b/evaluation/benchmarks/gaia/run_infer.py index 7974a092903c..7954f4490f74 100644 --- a/evaluation/benchmarks/gaia/run_infer.py +++ b/evaluation/benchmarks/gaia/run_infer.py @@ -49,7 +49,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/gorilla/run_infer.py b/evaluation/benchmarks/gorilla/run_infer.py index 740a3c3ada8f..86112f45d5ad 100644 --- a/evaluation/benchmarks/gorilla/run_infer.py +++ b/evaluation/benchmarks/gorilla/run_infer.py @@ -42,7 +42,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/gpqa/run_infer.py b/evaluation/benchmarks/gpqa/run_infer.py index eb1c808ec8a4..1adca9ad63f0 100644 --- a/evaluation/benchmarks/gpqa/run_infer.py +++ b/evaluation/benchmarks/gpqa/run_infer.py @@ -63,7 +63,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/humanevalfix/run_infer.py b/evaluation/benchmarks/humanevalfix/run_infer.py index ba802ddf9dfa..297ff0a83585 100644 --- a/evaluation/benchmarks/humanevalfix/run_infer.py +++ b/evaluation/benchmarks/humanevalfix/run_infer.py @@ -84,7 +84,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/logic_reasoning/run_infer.py b/evaluation/benchmarks/logic_reasoning/run_infer.py index ee48f5ea76c8..50b69801d1d6 100644 --- a/evaluation/benchmarks/logic_reasoning/run_infer.py +++ b/evaluation/benchmarks/logic_reasoning/run_infer.py @@ -47,7 +47,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/miniwob/run_infer.py b/evaluation/benchmarks/miniwob/run_infer.py index acc1431c81f1..6036e9b00fe1 100644 --- a/evaluation/benchmarks/miniwob/run_infer.py +++ b/evaluation/benchmarks/miniwob/run_infer.py @@ -57,7 +57,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/mint/run_infer.py b/evaluation/benchmarks/mint/run_infer.py index 61223572ae83..2e0528d03b7c 100644 --- a/evaluation/benchmarks/mint/run_infer.py +++ b/evaluation/benchmarks/mint/run_infer.py @@ -105,7 +105,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/ml_bench/run_infer.py b/evaluation/benchmarks/ml_bench/run_infer.py index 4e396b3c3fe1..becdcd602213 100644 --- a/evaluation/benchmarks/ml_bench/run_infer.py +++ b/evaluation/benchmarks/ml_bench/run_infer.py @@ -79,7 +79,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/scienceagentbench/run_infer.py b/evaluation/benchmarks/scienceagentbench/run_infer.py index 2ca5c2b40323..d5bfbdc026bb 100644 --- a/evaluation/benchmarks/scienceagentbench/run_infer.py +++ b/evaluation/benchmarks/scienceagentbench/run_infer.py @@ -61,7 +61,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime=os.environ.get('RUNTIME', 'docker'), max_budget_per_task=4, max_iterations=metadata.max_iterations, diff --git a/evaluation/benchmarks/swe_bench/eval_infer.py b/evaluation/benchmarks/swe_bench/eval_infer.py index 7beacf344408..afdad1a3bc87 100644 --- a/evaluation/benchmarks/swe_bench/eval_infer.py +++ b/evaluation/benchmarks/swe_bench/eval_infer.py @@ -75,7 +75,7 @@ def get_config(instance: pd.Series) -> AppConfig: f'Submit an issue on https://github.com/All-Hands-AI/OpenHands if you run into any issues.' ) config = AppConfig( - run_as_openhands=False, + run_as_user='root', runtime=os.environ.get('RUNTIME', 'docker'), sandbox=SandboxConfig( base_container_image=base_container_image, diff --git a/evaluation/benchmarks/swe_bench/run_infer.py b/evaluation/benchmarks/swe_bench/run_infer.py index bf065ada9734..643d303d33af 100644 --- a/evaluation/benchmarks/swe_bench/run_infer.py +++ b/evaluation/benchmarks/swe_bench/run_infer.py @@ -120,7 +120,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', max_iterations=metadata.max_iterations, runtime=os.environ.get('RUNTIME', 'docker'), sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/the_agent_company/run_infer.py b/evaluation/benchmarks/the_agent_company/run_infer.py index 03561913087c..5258662b5795 100644 --- a/evaluation/benchmarks/the_agent_company/run_infer.py +++ b/evaluation/benchmarks/the_agent_company/run_infer.py @@ -36,7 +36,7 @@ def get_config( llm_config: LLMConfig, ) -> AppConfig: config = AppConfig( - run_as_openhands=False, + run_as_user='root', max_budget_per_task=4, max_iterations=100, trajectories_path=os.path.join( diff --git a/evaluation/benchmarks/toolqa/run_infer.py b/evaluation/benchmarks/toolqa/run_infer.py index 8586f9a7bb7c..6b6fa67122a6 100644 --- a/evaluation/benchmarks/toolqa/run_infer.py +++ b/evaluation/benchmarks/toolqa/run_infer.py @@ -43,7 +43,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/webarena/run_infer.py b/evaluation/benchmarks/webarena/run_infer.py index c35c79ba2cce..07d18dcb4425 100644 --- a/evaluation/benchmarks/webarena/run_infer.py +++ b/evaluation/benchmarks/webarena/run_infer.py @@ -52,7 +52,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/integration_tests/run_infer.py b/evaluation/integration_tests/run_infer.py index fe85d23bf585..aad7e69534c5 100644 --- a/evaluation/integration_tests/run_infer.py +++ b/evaluation/integration_tests/run_infer.py @@ -41,7 +41,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/openhands/core/config/app_config.py b/openhands/core/config/app_config.py index 2dbb4aeaa8c4..0bfda6775e6f 100644 --- a/openhands/core/config/app_config.py +++ b/openhands/core/config/app_config.py @@ -33,7 +33,8 @@ class AppConfig: workspace_mount_path_in_sandbox: Path to mount the workspace in sandbox. Defaults to `/workspace`. workspace_mount_rewrite: Path to rewrite the workspace mount path. cache_dir: Path to cache directory. Defaults to `/tmp/cache`. - run_as_openhands: Whether to run as openhands. + run_as_user: User to run as. Defaults to `openhands`, other options are `root`. + Only local runtime has official support for running as the current user. max_iterations: Maximum number of iterations allowed. max_budget_per_task: Maximum budget per task, agent stops if exceeded. e2b_api_key: E2B API key. @@ -60,7 +61,7 @@ class AppConfig: workspace_mount_path_in_sandbox: str = '/workspace' workspace_mount_rewrite: str | None = None cache_dir: str = '/tmp/cache' - run_as_openhands: bool = True + run_as_user: str = 'openhands' max_iterations: int = OH_MAX_ITERATIONS max_budget_per_task: float | None = None e2b_api_key: str = '' diff --git a/openhands/core/config/utils.py b/openhands/core/config/utils.py index 7719ce0d59b1..f7e6d56ae790 100644 --- a/openhands/core/config/utils.py +++ b/openhands/core/config/utils.py @@ -5,7 +5,7 @@ import sys from dataclasses import is_dataclass from types import UnionType -from typing import Any, MutableMapping, get_args, get_origin +from typing import Any, Callable, MutableMapping, get_args, get_origin from uuid import uuid4 import toml @@ -36,6 +36,13 @@ def load_from_env(cfg: AppConfig, env_or_toml_dict: dict | MutableMapping[str, s cfg: The AppConfig object to set attributes on. env_or_toml_dict: The environment variables or a config.toml dict. """ + COMPATIBILITY_MAP: dict[str, tuple[str, Callable[[str], str]]] = { + # new env var name -> (old env var name, old to new conversion function) + 'RUN_AS_USER': ( + 'RUN_AS_OPENHANDS', + lambda old_value: 'openhands' if old_value else 'root', + ) + } def get_optional_type(union_type: UnionType) -> Any: """Returns the non-None type from a Union.""" @@ -58,6 +65,14 @@ def set_attr_from_env(sub_config: Any, prefix=''): # convert the env var to the correct type and set it value = env_or_toml_dict[env_var_name] + if env_var_name in COMPATIBILITY_MAP: + old_env_var_name, conversion_func = COMPATIBILITY_MAP[env_var_name] + if old_env_var_name in env_or_toml_dict: + value = conversion_func(env_or_toml_dict[old_env_var_name]) + logger.openhands_logger.warning( + f'Compatibility warning: {old_env_var_name} is deprecated, please use {env_var_name} instead.' + ) + # skip empty config values (fall back to default) if not value: continue diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 8c11ec641949..3e29c1f83000 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -6,7 +6,6 @@ import subprocess import tempfile import threading -from pathlib import Path from typing import Callable, Optional import requests @@ -14,17 +13,10 @@ from openhands.core.config import AppConfig from openhands.core.exceptions import AgentRuntimeDisconnectedError +from openhands.core.logger import DEBUG_RUNTIME from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream -from openhands.events.action import ( - Action, - BrowseInteractiveAction, - BrowseURLAction, - CmdRunAction, - FileReadAction, - FileWriteAction, - IPythonRunCellAction, -) +from openhands.events.action import Action from openhands.events.observation import ( ErrorObservation, Observation, @@ -70,15 +62,6 @@ def __init__( headless_mode: bool = True, ): self.config = config - if self.config.run_as_openhands: - raise RuntimeError( - 'Local runtime does not support running as openhands. It only supports running as root.' - ) - if self.config.sandbox.user_id != 0: - logger.warning( - 'Local runtime does not support running as a non-root user. Setting user ID to 0.' - ) - self.config.sandbox.user_id = 0 self._temp_workspace: str | None = None if self.config.workspace_base is not None: @@ -110,6 +93,9 @@ def __init__( ) self.config.workspace_mount_path_in_sandbox = self._temp_workspace + # self.user_id = os.getuid() + # self.username = os.getenv('USER') + # Initialize the action_execution_server super().__init__( config, @@ -126,7 +112,8 @@ def __init__( 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' 'are running in a controlled environment.\n' - f'Temp workspace: {self._temp_workspace}' + f'Temp workspace: {self._temp_workspace}\n' + f'NOTE:' ) def _get_action_execution_server_host(self): @@ -170,8 +157,9 @@ def log_output(): break self.log('debug', f'Server: {line.strip()}') - log_thread = threading.Thread(target=log_output, daemon=True) - log_thread.start() + if DEBUG_RUNTIME: + log_thread = threading.Thread(target=log_output, daemon=True) + log_thread.start() self.log('info', f'Waiting for server to become ready at {self.api_url}...') self.send_status_message('STATUS$WAITING_FOR_CLIENT') diff --git a/openhands/runtime/utils/command.py b/openhands/runtime/utils/command.py index 415f9fcb7f78..731b521446f5 100644 --- a/openhands/runtime/utils/command.py +++ b/openhands/runtime/utils/command.py @@ -31,7 +31,7 @@ def get_action_execution_server_startup_command( '--browsergym-eval-env' ] + sandbox_config.browsergym_eval_env.split(' ') - is_root = not app_config.run_as_openhands + is_root = app_config.run_as_user == 'root' base_cmd = [ *python_prefix, @@ -44,7 +44,7 @@ def get_action_execution_server_startup_command( app_config.workspace_mount_path_in_sandbox, *plugin_args, '--username', - 'openhands' if app_config.run_as_openhands else 'root', + app_config.run_as_user, '--user-id', str(sandbox_config.user_id), *browsergym_args, diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index a77b5163b890..441e7f9b8879 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -142,15 +142,16 @@ def get_runtime_classes() -> list[type[Runtime]]: raise ValueError(f'Invalid runtime: {runtime}') -def get_run_as_openhands() -> list[bool]: +def get_run_as_user() -> list[bool]: print( '\n\n########################################################################' ) - print('USER: ' + 'openhands' if RUN_AS_OPENHANDS else 'root') + user = 'openhands' if RUN_AS_OPENHANDS else 'root' + print('USER: ' + user) print( '########################################################################\n\n' ) - return [RUN_AS_OPENHANDS] + return [user] @pytest.fixture(scope='module') # for xdist @@ -177,8 +178,8 @@ def runtime_cls(request): # TODO: We will change this to `run_as_user` when `ServerRuntime` is deprecated. # since `DockerRuntime` supports running as an arbitrary user. -@pytest.fixture(scope='module', params=get_run_as_openhands()) -def run_as_openhands(request): +@pytest.fixture(scope='module', params=get_run_as_user()) +def run_as_user(request): time.sleep(1) return request.param @@ -211,7 +212,7 @@ def base_container_image(request): def _load_runtime( temp_dir, runtime_cls, - run_as_openhands: bool = True, + run_as_user: bool = True, enable_auto_lint: bool = False, base_container_image: str | None = None, browsergym_eval_env: str | None = None, @@ -227,7 +228,7 @@ def _load_runtime( plugins = [AgentSkillsRequirement(), JupyterRequirement()] config = load_app_config() - config.run_as_openhands = run_as_openhands + config.run_as_user = run_as_user config.sandbox.force_rebuild_runtime = force_rebuild_runtime config.sandbox.keep_runtime_alive = False config.sandbox.docker_runtime_kwargs = docker_runtime_kwargs diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index bebcf498012d..b1c7ada80d5c 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -29,8 +29,8 @@ def _run_cmd_action(runtime, custom_command: str): return obs -def test_bash_command_env(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_bash_command_env(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: obs = runtime.run_action(CmdRunAction(command='env')) assert isinstance( @@ -41,8 +41,8 @@ def test_bash_command_env(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_bash_server(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_bash_server(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: action = CmdRunAction(command='python3 -m http.server 8080') action.timeout = 1 @@ -108,7 +108,7 @@ def test_multiline_commands(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_openhands): +def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_user): cmds = [ 'ls -l', 'echo -e "hello\nworld"', @@ -122,7 +122,7 @@ def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_openhands): ] joined_cmds = '\n'.join(cmds) - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # First test that running multiple commands at once fails obs = _run_cmd_action(runtime, joined_cmds) @@ -167,9 +167,9 @@ def test_complex_commands(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_no_ps2_in_output(temp_dir, runtime_cls, run_as_openhands): +def test_no_ps2_in_output(temp_dir, runtime_cls, run_as_user): """Test that the PS2 sign is not added to the output of a multiline command.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: obs = _run_cmd_action(runtime, 'echo -e "hello\nworld"') assert obs.exit_code == 0, 'The exit code should be 0.' @@ -207,8 +207,8 @@ def test_multiline_command_loop(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_cmd_run(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: obs = _run_cmd_action(runtime, 'ls -l /openhands/workspace') assert obs.exit_code == 0 @@ -222,7 +222,7 @@ def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): obs = _run_cmd_action(runtime, 'ls -l') assert obs.exit_code == 0 - if run_as_openhands: + if run_as_user: assert 'openhands' in obs.content else: assert 'root' in obs.content @@ -244,12 +244,12 @@ def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: obs = _run_cmd_action(runtime, 'cd ~ && pwd') assert obs.exit_code == 0 - if run_as_openhands: + if run_as_user: assert '/home/openhands' in obs.content else: assert '/root' in obs.content @@ -449,7 +449,7 @@ def test_git_operation(runtime_cls): use_workspace=False, runtime_cls=runtime_cls, # Need to use non-root user to expose issues - run_as_openhands=True, + run_as_user=True, ) # this will happen if permission of runtime is not properly configured # fatal: detected dubious ownership in repository at '/workspace' @@ -498,8 +498,8 @@ def test_git_operation(runtime_cls): _close_test_runtime(runtime) -def test_python_version(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_python_version(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: obs = runtime.run_action(CmdRunAction(command='python --version')) @@ -512,8 +512,8 @@ def test_python_version(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_pwd_property(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_pwd_property(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Create a subdirectory and verify pwd updates obs = _run_cmd_action(runtime, 'mkdir -p random_dir') @@ -526,8 +526,8 @@ def test_pwd_property(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_basic_command(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_basic_command(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Test simple command obs = _run_cmd_action(runtime, "echo 'hello world'") @@ -554,8 +554,8 @@ def test_basic_command(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_interactive_command(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_interactive_command(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Test interactive command action = CmdRunAction('read -p "Enter name: " name && echo "Hello $name"') @@ -585,8 +585,8 @@ def test_interactive_command(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_long_output(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_long_output(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Generate a long output action = CmdRunAction('for i in $(seq 1 5000); do echo "Line $i"; done') @@ -599,8 +599,8 @@ def test_long_output(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Generate a long output action = CmdRunAction('for i in $(seq 1 50000); do echo "Line $i"; done') @@ -615,8 +615,8 @@ def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_openhand _close_test_runtime(runtime) -def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Create nested directories with many files setup_cmd = 'mkdir -p /tmp/test_dir && cd /tmp/test_dir && for i in $(seq 1 100); do mkdir -p "folder_$i"; for j in $(seq 1 100); do touch "folder_$i/file_$j.txt"; done; done' @@ -640,8 +640,8 @@ def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_openh _close_test_runtime(runtime) -def test_command_backslash(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_command_backslash(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Create a file with the content "implemented_function" action = CmdRunAction( @@ -667,8 +667,8 @@ def test_command_backslash(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_command_output_continuation(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_command_output_continuation(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Start a command that produces output slowly action = CmdRunAction('for i in {1..5}; do echo $i; sleep 3; done') @@ -706,10 +706,8 @@ def test_command_output_continuation(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_long_running_command_follow_by_execute( - temp_dir, runtime_cls, run_as_openhands -): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_long_running_command_follow_by_execute(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Test command that produces output slowly action = CmdRunAction('for i in {1..3}; do echo $i; sleep 3; done') @@ -746,8 +744,8 @@ def test_long_running_command_follow_by_execute( _close_test_runtime(runtime) -def test_empty_command_errors(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_empty_command_errors(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Test empty command without previous command obs = runtime.run_action(CmdRunAction('')) @@ -757,8 +755,8 @@ def test_empty_command_errors(temp_dir, runtime_cls, run_as_openhands): _close_test_runtime(runtime) -def test_python_interactive_input(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_python_interactive_input(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: # Test Python program that asks for input - properly escaped for bash python_script = """name = input('Enter your name: '); age = input('Enter your age: '); print(f'Hello {name}, you are {age} years old')""" diff --git a/tests/runtime/test_browsergym_envs.py b/tests/runtime/test_browsergym_envs.py index 426ecacaf54c..9f06e76f1149 100644 --- a/tests/runtime/test_browsergym_envs.py +++ b/tests/runtime/test_browsergym_envs.py @@ -32,7 +32,7 @@ def test_browsergym_eval_env(runtime_cls, temp_dir): runtime = _load_runtime( temp_dir, runtime_cls=runtime_cls, - run_as_openhands=False, # need root permission to access file + run_as_user='root', # need root permission to access file base_container_image='xingyaoww/od-eval-miniwob:v1.0', browsergym_eval_env='browsergym/miniwob.choose-list', force_rebuild_runtime=True, diff --git a/tests/runtime/test_browsing.py b/tests/runtime/test_browsing.py index 0dee3750953f..ef701a03dcc2 100644 --- a/tests/runtime/test_browsing.py +++ b/tests/runtime/test_browsing.py @@ -20,8 +20,8 @@ PY3_FOR_TESTING = '/openhands/micromamba/bin/micromamba run -n openhands python3' -def test_simple_browse(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_simple_browse(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) # Test browse action_cmd = CmdRunAction( diff --git a/tests/runtime/test_edit.py b/tests/runtime/test_edit.py index c507166a840d..cfd5a919b014 100644 --- a/tests/runtime/test_edit.py +++ b/tests/runtime/test_edit.py @@ -27,8 +27,8 @@ def index(): TEST_IN_CI != 'True', reason='This test requires LLM to run.', ) -def test_edit_from_scratch(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_edit_from_scratch(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: action = FileEditAction( content=ORGINAL, @@ -67,8 +67,8 @@ def index(): TEST_IN_CI != 'True', reason='This test requires LLM to run.', ) -def test_edit(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_edit(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: action = FileEditAction( content=ORGINAL, @@ -126,8 +126,8 @@ def test_edit(temp_dir, runtime_cls, run_as_openhands): TEST_IN_CI != 'True', reason='This test requires LLM to run.', ) -def test_edit_long_file(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_edit_long_file(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) try: action = FileEditAction( content=ORIGINAL_LONG, diff --git a/tests/runtime/test_env_vars.py b/tests/runtime/test_env_vars.py index 898003ff66c7..2dd31abfd63f 100644 --- a/tests/runtime/test_env_vars.py +++ b/tests/runtime/test_env_vars.py @@ -13,9 +13,9 @@ # ============================================================================================================================ -def test_env_vars_os_environ(temp_dir, runtime_cls, run_as_openhands): +def test_env_vars_os_environ(temp_dir, runtime_cls, run_as_user): with patch.dict(os.environ, {'SANDBOX_ENV_FOOBAR': 'BAZ'}): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) obs: CmdOutputObservation = runtime.run_action(CmdRunAction(command='env')) print(obs) diff --git a/tests/runtime/test_ipython.py b/tests/runtime/test_ipython.py index 922dcd404aac..bbd46e643cb2 100644 --- a/tests/runtime/test_ipython.py +++ b/tests/runtime/test_ipython.py @@ -29,8 +29,8 @@ # ============================================================================================================================ -def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) # Test run command action_cmd = CmdRunAction(command='ls -l') @@ -101,8 +101,8 @@ def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_openhands): TEST_IN_CI != 'True', reason='This test is not working in WSL (file ownership)', ) -def test_ipython_multi_user(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) +def test_ipython_multi_user(temp_dir, runtime_cls, run_as_user): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) # Test run ipython # get username @@ -113,7 +113,7 @@ def test_ipython_multi_user(temp_dir, runtime_cls, run_as_openhands): assert isinstance(obs, IPythonRunCellObservation) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) - if run_as_openhands: + if run_as_user: assert 'openhands' in obs.content else: assert 'root' in obs.content @@ -156,7 +156,7 @@ def test_ipython_multi_user(temp_dir, runtime_cls, run_as_openhands): obs = runtime.run_action(action) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) assert obs.exit_code == 0 - if run_as_openhands: + if run_as_user: # -rw-r--r-- 1 openhands root 13 Jul 28 03:53 test.txt assert 'openhands' in obs.content.split('\r\n')[0] else: @@ -196,9 +196,9 @@ def test_ipython_simple(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_ipython_package_install(temp_dir, runtime_cls, run_as_openhands): +def test_ipython_package_install(temp_dir, runtime_cls, run_as_user): """Make sure that cd in bash also update the current working directory in ipython.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) # It should error out since pymsgbox is not installed action = IPythonRunCellAction(code='import pymsgbox') @@ -233,7 +233,7 @@ def test_ipython_package_install(temp_dir, runtime_cls, run_as_openhands): def test_ipython_file_editor_permissions_as_openhands(temp_dir, runtime_cls): """Test file editor permission behavior when running as different users.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands=True) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_user=True) # Create a file owned by root with restricted permissions action = CmdRunAction( @@ -312,8 +312,8 @@ def test_ipython_file_editor_permissions_as_openhands(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_file_read_and_edit_via_oh_aci(runtime_cls, run_as_openhands): - runtime = _load_runtime(None, runtime_cls, run_as_openhands) +def test_file_read_and_edit_via_oh_aci(runtime_cls, run_as_user): + runtime = _load_runtime(None, runtime_cls, run_as_user) sandbox_dir = '/openhands/workspace' actions = [ diff --git a/tests/runtime/test_stress_remote_runtime.py b/tests/runtime/test_stress_remote_runtime.py index 6f2f011ee2a3..e742ee618f66 100644 --- a/tests/runtime/test_stress_remote_runtime.py +++ b/tests/runtime/test_stress_remote_runtime.py @@ -53,7 +53,7 @@ def get_config( ), 'ALLHANDS_API_KEY must be set.' config = AppConfig( default_agent=metadata.agent_class, - run_as_openhands=False, + run_as_user='root', max_iterations=metadata.max_iterations, runtime='remote', sandbox=SandboxConfig( From 7cb48e9642b437499313f5de60096bdb76824f6a Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 15:42:33 -0500 Subject: [PATCH 32/90] Revert "replace run_as_openhands with more general run_as_user" This reverts commit efe96221e1434ba18e650fda084a3e9fd11342e3. --- .github/workflows/ghcr-build.yml | 6 +- config.template.toml | 2 +- containers/app/Dockerfile | 2 +- containers/app/entrypoint.sh | 2 +- .../current/usage/configuration-options.md | 6 +- .../current/usage/custom_sandbox_guide.md | 4 +- .../current/usage/configuration-options.md | 6 +- .../current/usage/custom_sandbox_guide.md | 4 +- docs/modules/usage/configuration-options.md | 6 +- evaluation/benchmarks/EDA/run_infer.py | 2 +- .../benchmarks/agent_bench/run_infer.py | 2 +- .../benchmarks/aider_bench/run_infer.py | 2 +- evaluation/benchmarks/biocoder/run_infer.py | 2 +- evaluation/benchmarks/bird/run_infer.py | 2 +- .../browsing_delegation/run_infer.py | 2 +- .../benchmarks/commit0_bench/run_infer.py | 2 +- .../benchmarks/discoverybench/run_infer.py | 2 +- evaluation/benchmarks/gaia/run_infer.py | 2 +- evaluation/benchmarks/gorilla/run_infer.py | 2 +- evaluation/benchmarks/gpqa/run_infer.py | 2 +- .../benchmarks/humanevalfix/run_infer.py | 2 +- .../benchmarks/logic_reasoning/run_infer.py | 2 +- evaluation/benchmarks/miniwob/run_infer.py | 2 +- evaluation/benchmarks/mint/run_infer.py | 2 +- evaluation/benchmarks/ml_bench/run_infer.py | 2 +- .../benchmarks/scienceagentbench/run_infer.py | 2 +- evaluation/benchmarks/swe_bench/eval_infer.py | 2 +- evaluation/benchmarks/swe_bench/run_infer.py | 2 +- .../benchmarks/the_agent_company/run_infer.py | 2 +- evaluation/benchmarks/toolqa/run_infer.py | 2 +- evaluation/benchmarks/webarena/run_infer.py | 2 +- evaluation/integration_tests/run_infer.py | 2 +- openhands/core/config/app_config.py | 5 +- openhands/core/config/utils.py | 17 +--- openhands/runtime/impl/local/local_runtime.py | 32 +++++--- openhands/runtime/utils/command.py | 4 +- tests/runtime/conftest.py | 15 ++-- tests/runtime/test_bash.py | 80 ++++++++++--------- tests/runtime/test_browsergym_envs.py | 2 +- tests/runtime/test_browsing.py | 4 +- tests/runtime/test_edit.py | 12 +-- tests/runtime/test_env_vars.py | 4 +- tests/runtime/test_ipython.py | 22 ++--- tests/runtime/test_stress_remote_runtime.py | 2 +- 44 files changed, 140 insertions(+), 143 deletions(-) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index 2fc087c2ad1e..625ade07a770 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -279,7 +279,7 @@ jobs: run: | TEST_RUNTIME=local \ TEST_IN_CI=true \ - RUN_AS_USER=root \ + RUN_AS_OPENHANDS=false \ poetry run pytest -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Run docker runtime tests run: | @@ -296,7 +296,7 @@ jobs: SANDBOX_USER_ID=$(id -u) \ SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \ TEST_IN_CI=true \ - RUN_AS_USER=root \ + RUN_AS_OPENHANDS=false \ poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 @@ -373,7 +373,7 @@ jobs: SANDBOX_USER_ID=$(id -u) \ SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \ TEST_IN_CI=true \ - RUN_AS_USER=openhands \ + RUN_AS_OPENHANDS=true \ poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 diff --git a/config.template.toml b/config.template.toml index 270aeadc7b6d..de0ebf3a578f 100644 --- a/config.template.toml +++ b/config.template.toml @@ -64,7 +64,7 @@ workspace_base = "./workspace" #workspace_mount_rewrite = "" # Run as openhands -#run_as_user = "openhands" +#run_as_openhands = true # Runtime environment #runtime = "eventstream" diff --git a/containers/app/Dockerfile b/containers/app/Dockerfile index 0e1e4515e1c4..adcda79f338e 100644 --- a/containers/app/Dockerfile +++ b/containers/app/Dockerfile @@ -34,7 +34,7 @@ WORKDIR /app ARG OPENHANDS_BUILD_VERSION #re-declare for this section -ENV RUN_AS_USER=openhands +ENV RUN_AS_OPENHANDS=true # A random number--we need this to be different from the user's UID on the host machine ENV OPENHANDS_USER_ID=42420 ENV SANDBOX_LOCAL_RUNTIME_URL=http://host.docker.internal diff --git a/containers/app/entrypoint.sh b/containers/app/entrypoint.sh index 84ee6c6ede37..380490449a99 100644 --- a/containers/app/entrypoint.sh +++ b/containers/app/entrypoint.sh @@ -25,7 +25,7 @@ fi if [[ "$SANDBOX_USER_ID" -eq 0 ]]; then echo "Running OpenHands as root" - export RUN_AS_USER=root + export RUN_AS_OPENHANDS=false mkdir -p /root/.cache/ms-playwright/ if [ -d "/home/openhands/.cache/ms-playwright/" ]; then mv /home/openhands/.cache/ms-playwright/ /root/.cache/ diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md index 72ebeb131f1d..36024b2f3c15 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/configuration-options.md @@ -158,9 +158,9 @@ Les options de configuration de base sont définies dans la section `[core]` du - Description : Chemin pour réécrire le chemin de montage de l'espace de travail. Vous pouvez généralement ignorer cela, cela fait référence à des cas spéciaux d'exécution à l'intérieur d'un autre conteneur. **Divers** -- `run_as_user` - - Type : `str` - - Valeur par défaut : `openhands` +- `run_as_openhands` + - Type : `bool` + - Valeur par défaut : `true` - Description : Exécuter en tant qu'OpenHands - `runtime` diff --git a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md index c97bf14f9114..bf4a0b328c67 100644 --- a/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md +++ b/docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md @@ -41,7 +41,7 @@ Créez un fichier ```config.toml``` dans le répertoire OpenHands et entrez ces ```toml [core] workspace_base="./workspace" -run_as_user="openhands" +run_as_openhands=true sandbox_base_container_image="image_personnalisée" ``` @@ -87,7 +87,7 @@ Si vous voyez cette erreur dans la sortie de la console, il s'agit du fait que O ```toml [core] workspace_base="./workspace" -run_as_user="openhands" +run_as_openhands=true sandbox_base_container_image="image_personnalisée" sandbox_user_id="1001" ``` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md index 19a92d300faa..0cff6a5f933d 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/configuration-options.md @@ -155,9 +155,9 @@ - 描述: 重写工作区挂载路径的路径。通常可以忽略这个,它指的是在另一个容器内运行的特殊情况。 **其他** -- `run_as_user` - - 类型: `str` - - 默认值: `openhands` +- `run_as_openhands` + - 类型: `bool` + - 默认值: `true` - 描述: 以 OpenHands 身份运行 - `runtime` diff --git a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md index 3c62700b4677..04e05f915d94 100644 --- a/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md +++ b/docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/custom_sandbox_guide.md @@ -57,7 +57,7 @@ docker build -t custom_image . ``` [core] workspace_base="./workspace" -run_as_user="openhands" +run_as_openhands=true sandbox_base_container_image="custom_image" ``` @@ -88,7 +88,7 @@ sandbox_base_container_image="custom_image" ``` [core] workspace_base="./workspace" -run_as_user="openhands" +run_as_openhands=true sandbox_base_container_image="custom_image" sandbox_user_id="1001" ``` diff --git a/docs/modules/usage/configuration-options.md b/docs/modules/usage/configuration-options.md index 15be982291fb..29c9b6759507 100644 --- a/docs/modules/usage/configuration-options.md +++ b/docs/modules/usage/configuration-options.md @@ -156,9 +156,9 @@ The core configuration options are defined in the `[core]` section of the `confi - Description: Path to rewrite the workspace mount path to. You can usually ignore this, it refers to special cases of running inside another container. **Miscellaneous** -- `run_as_user` - - Type: `str` - - Default: `"openhands"` +- `run_as_openhands` + - Type: `bool` + - Default: `true` - Description: Run as OpenHands - `runtime` diff --git a/evaluation/benchmarks/EDA/run_infer.py b/evaluation/benchmarks/EDA/run_infer.py index 6072af74de00..b5a021a0b853 100644 --- a/evaluation/benchmarks/EDA/run_infer.py +++ b/evaluation/benchmarks/EDA/run_infer.py @@ -62,7 +62,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/agent_bench/run_infer.py b/evaluation/benchmarks/agent_bench/run_infer.py index f2b14b9c2ff9..554ddc66488f 100644 --- a/evaluation/benchmarks/agent_bench/run_infer.py +++ b/evaluation/benchmarks/agent_bench/run_infer.py @@ -42,7 +42,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/aider_bench/run_infer.py b/evaluation/benchmarks/aider_bench/run_infer.py index 30fdd27eac85..51179724e239 100644 --- a/evaluation/benchmarks/aider_bench/run_infer.py +++ b/evaluation/benchmarks/aider_bench/run_infer.py @@ -49,7 +49,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/biocoder/run_infer.py b/evaluation/benchmarks/biocoder/run_infer.py index 37546bc468c4..dfc0eaf1f912 100644 --- a/evaluation/benchmarks/biocoder/run_infer.py +++ b/evaluation/benchmarks/biocoder/run_infer.py @@ -60,7 +60,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/bird/run_infer.py b/evaluation/benchmarks/bird/run_infer.py index 1747bbf7ad62..3254e4a2c791 100644 --- a/evaluation/benchmarks/bird/run_infer.py +++ b/evaluation/benchmarks/bird/run_infer.py @@ -73,7 +73,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/browsing_delegation/run_infer.py b/evaluation/benchmarks/browsing_delegation/run_infer.py index d834df7c08f7..3313c9ff4c3d 100644 --- a/evaluation/benchmarks/browsing_delegation/run_infer.py +++ b/evaluation/benchmarks/browsing_delegation/run_infer.py @@ -38,7 +38,7 @@ def get_config( ), 'max_iterations must be 1 for browsing delegation evaluation.' config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/commit0_bench/run_infer.py b/evaluation/benchmarks/commit0_bench/run_infer.py index 64d63cc5e247..d8f1f64b1a6b 100644 --- a/evaluation/benchmarks/commit0_bench/run_infer.py +++ b/evaluation/benchmarks/commit0_bench/run_infer.py @@ -122,7 +122,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, max_iterations=metadata.max_iterations, runtime=os.environ.get('RUNTIME', 'docker'), sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/discoverybench/run_infer.py b/evaluation/benchmarks/discoverybench/run_infer.py index de036ca9bdf3..05ff44003517 100644 --- a/evaluation/benchmarks/discoverybench/run_infer.py +++ b/evaluation/benchmarks/discoverybench/run_infer.py @@ -64,7 +64,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/gaia/run_infer.py b/evaluation/benchmarks/gaia/run_infer.py index 7954f4490f74..7974a092903c 100644 --- a/evaluation/benchmarks/gaia/run_infer.py +++ b/evaluation/benchmarks/gaia/run_infer.py @@ -49,7 +49,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/gorilla/run_infer.py b/evaluation/benchmarks/gorilla/run_infer.py index 86112f45d5ad..740a3c3ada8f 100644 --- a/evaluation/benchmarks/gorilla/run_infer.py +++ b/evaluation/benchmarks/gorilla/run_infer.py @@ -42,7 +42,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/gpqa/run_infer.py b/evaluation/benchmarks/gpqa/run_infer.py index 1adca9ad63f0..eb1c808ec8a4 100644 --- a/evaluation/benchmarks/gpqa/run_infer.py +++ b/evaluation/benchmarks/gpqa/run_infer.py @@ -63,7 +63,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/humanevalfix/run_infer.py b/evaluation/benchmarks/humanevalfix/run_infer.py index 297ff0a83585..ba802ddf9dfa 100644 --- a/evaluation/benchmarks/humanevalfix/run_infer.py +++ b/evaluation/benchmarks/humanevalfix/run_infer.py @@ -84,7 +84,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/logic_reasoning/run_infer.py b/evaluation/benchmarks/logic_reasoning/run_infer.py index 50b69801d1d6..ee48f5ea76c8 100644 --- a/evaluation/benchmarks/logic_reasoning/run_infer.py +++ b/evaluation/benchmarks/logic_reasoning/run_infer.py @@ -47,7 +47,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/miniwob/run_infer.py b/evaluation/benchmarks/miniwob/run_infer.py index 6036e9b00fe1..acc1431c81f1 100644 --- a/evaluation/benchmarks/miniwob/run_infer.py +++ b/evaluation/benchmarks/miniwob/run_infer.py @@ -57,7 +57,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/mint/run_infer.py b/evaluation/benchmarks/mint/run_infer.py index 2e0528d03b7c..61223572ae83 100644 --- a/evaluation/benchmarks/mint/run_infer.py +++ b/evaluation/benchmarks/mint/run_infer.py @@ -105,7 +105,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/ml_bench/run_infer.py b/evaluation/benchmarks/ml_bench/run_infer.py index becdcd602213..4e396b3c3fe1 100644 --- a/evaluation/benchmarks/ml_bench/run_infer.py +++ b/evaluation/benchmarks/ml_bench/run_infer.py @@ -79,7 +79,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/scienceagentbench/run_infer.py b/evaluation/benchmarks/scienceagentbench/run_infer.py index d5bfbdc026bb..2ca5c2b40323 100644 --- a/evaluation/benchmarks/scienceagentbench/run_infer.py +++ b/evaluation/benchmarks/scienceagentbench/run_infer.py @@ -61,7 +61,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime=os.environ.get('RUNTIME', 'docker'), max_budget_per_task=4, max_iterations=metadata.max_iterations, diff --git a/evaluation/benchmarks/swe_bench/eval_infer.py b/evaluation/benchmarks/swe_bench/eval_infer.py index afdad1a3bc87..7beacf344408 100644 --- a/evaluation/benchmarks/swe_bench/eval_infer.py +++ b/evaluation/benchmarks/swe_bench/eval_infer.py @@ -75,7 +75,7 @@ def get_config(instance: pd.Series) -> AppConfig: f'Submit an issue on https://github.com/All-Hands-AI/OpenHands if you run into any issues.' ) config = AppConfig( - run_as_user='root', + run_as_openhands=False, runtime=os.environ.get('RUNTIME', 'docker'), sandbox=SandboxConfig( base_container_image=base_container_image, diff --git a/evaluation/benchmarks/swe_bench/run_infer.py b/evaluation/benchmarks/swe_bench/run_infer.py index 643d303d33af..bf065ada9734 100644 --- a/evaluation/benchmarks/swe_bench/run_infer.py +++ b/evaluation/benchmarks/swe_bench/run_infer.py @@ -120,7 +120,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, max_iterations=metadata.max_iterations, runtime=os.environ.get('RUNTIME', 'docker'), sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/the_agent_company/run_infer.py b/evaluation/benchmarks/the_agent_company/run_infer.py index 5258662b5795..03561913087c 100644 --- a/evaluation/benchmarks/the_agent_company/run_infer.py +++ b/evaluation/benchmarks/the_agent_company/run_infer.py @@ -36,7 +36,7 @@ def get_config( llm_config: LLMConfig, ) -> AppConfig: config = AppConfig( - run_as_user='root', + run_as_openhands=False, max_budget_per_task=4, max_iterations=100, trajectories_path=os.path.join( diff --git a/evaluation/benchmarks/toolqa/run_infer.py b/evaluation/benchmarks/toolqa/run_infer.py index 6b6fa67122a6..8586f9a7bb7c 100644 --- a/evaluation/benchmarks/toolqa/run_infer.py +++ b/evaluation/benchmarks/toolqa/run_infer.py @@ -43,7 +43,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/benchmarks/webarena/run_infer.py b/evaluation/benchmarks/webarena/run_infer.py index 07d18dcb4425..c35c79ba2cce 100644 --- a/evaluation/benchmarks/webarena/run_infer.py +++ b/evaluation/benchmarks/webarena/run_infer.py @@ -52,7 +52,7 @@ def get_config( config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime='docker', max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/evaluation/integration_tests/run_infer.py b/evaluation/integration_tests/run_infer.py index aad7e69534c5..fe85d23bf585 100644 --- a/evaluation/integration_tests/run_infer.py +++ b/evaluation/integration_tests/run_infer.py @@ -41,7 +41,7 @@ def get_config( ) -> AppConfig: config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, runtime=os.environ.get('RUNTIME', 'docker'), max_iterations=metadata.max_iterations, sandbox=SandboxConfig( diff --git a/openhands/core/config/app_config.py b/openhands/core/config/app_config.py index 0bfda6775e6f..2dbb4aeaa8c4 100644 --- a/openhands/core/config/app_config.py +++ b/openhands/core/config/app_config.py @@ -33,8 +33,7 @@ class AppConfig: workspace_mount_path_in_sandbox: Path to mount the workspace in sandbox. Defaults to `/workspace`. workspace_mount_rewrite: Path to rewrite the workspace mount path. cache_dir: Path to cache directory. Defaults to `/tmp/cache`. - run_as_user: User to run as. Defaults to `openhands`, other options are `root`. - Only local runtime has official support for running as the current user. + run_as_openhands: Whether to run as openhands. max_iterations: Maximum number of iterations allowed. max_budget_per_task: Maximum budget per task, agent stops if exceeded. e2b_api_key: E2B API key. @@ -61,7 +60,7 @@ class AppConfig: workspace_mount_path_in_sandbox: str = '/workspace' workspace_mount_rewrite: str | None = None cache_dir: str = '/tmp/cache' - run_as_user: str = 'openhands' + run_as_openhands: bool = True max_iterations: int = OH_MAX_ITERATIONS max_budget_per_task: float | None = None e2b_api_key: str = '' diff --git a/openhands/core/config/utils.py b/openhands/core/config/utils.py index f7e6d56ae790..7719ce0d59b1 100644 --- a/openhands/core/config/utils.py +++ b/openhands/core/config/utils.py @@ -5,7 +5,7 @@ import sys from dataclasses import is_dataclass from types import UnionType -from typing import Any, Callable, MutableMapping, get_args, get_origin +from typing import Any, MutableMapping, get_args, get_origin from uuid import uuid4 import toml @@ -36,13 +36,6 @@ def load_from_env(cfg: AppConfig, env_or_toml_dict: dict | MutableMapping[str, s cfg: The AppConfig object to set attributes on. env_or_toml_dict: The environment variables or a config.toml dict. """ - COMPATIBILITY_MAP: dict[str, tuple[str, Callable[[str], str]]] = { - # new env var name -> (old env var name, old to new conversion function) - 'RUN_AS_USER': ( - 'RUN_AS_OPENHANDS', - lambda old_value: 'openhands' if old_value else 'root', - ) - } def get_optional_type(union_type: UnionType) -> Any: """Returns the non-None type from a Union.""" @@ -65,14 +58,6 @@ def set_attr_from_env(sub_config: Any, prefix=''): # convert the env var to the correct type and set it value = env_or_toml_dict[env_var_name] - if env_var_name in COMPATIBILITY_MAP: - old_env_var_name, conversion_func = COMPATIBILITY_MAP[env_var_name] - if old_env_var_name in env_or_toml_dict: - value = conversion_func(env_or_toml_dict[old_env_var_name]) - logger.openhands_logger.warning( - f'Compatibility warning: {old_env_var_name} is deprecated, please use {env_var_name} instead.' - ) - # skip empty config values (fall back to default) if not value: continue diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 3e29c1f83000..8c11ec641949 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -6,6 +6,7 @@ import subprocess import tempfile import threading +from pathlib import Path from typing import Callable, Optional import requests @@ -13,10 +14,17 @@ from openhands.core.config import AppConfig from openhands.core.exceptions import AgentRuntimeDisconnectedError -from openhands.core.logger import DEBUG_RUNTIME from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream -from openhands.events.action import Action +from openhands.events.action import ( + Action, + BrowseInteractiveAction, + BrowseURLAction, + CmdRunAction, + FileReadAction, + FileWriteAction, + IPythonRunCellAction, +) from openhands.events.observation import ( ErrorObservation, Observation, @@ -62,6 +70,15 @@ def __init__( headless_mode: bool = True, ): self.config = config + if self.config.run_as_openhands: + raise RuntimeError( + 'Local runtime does not support running as openhands. It only supports running as root.' + ) + if self.config.sandbox.user_id != 0: + logger.warning( + 'Local runtime does not support running as a non-root user. Setting user ID to 0.' + ) + self.config.sandbox.user_id = 0 self._temp_workspace: str | None = None if self.config.workspace_base is not None: @@ -93,9 +110,6 @@ def __init__( ) self.config.workspace_mount_path_in_sandbox = self._temp_workspace - # self.user_id = os.getuid() - # self.username = os.getenv('USER') - # Initialize the action_execution_server super().__init__( config, @@ -112,8 +126,7 @@ def __init__( 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' 'are running in a controlled environment.\n' - f'Temp workspace: {self._temp_workspace}\n' - f'NOTE:' + f'Temp workspace: {self._temp_workspace}' ) def _get_action_execution_server_host(self): @@ -157,9 +170,8 @@ def log_output(): break self.log('debug', f'Server: {line.strip()}') - if DEBUG_RUNTIME: - log_thread = threading.Thread(target=log_output, daemon=True) - log_thread.start() + log_thread = threading.Thread(target=log_output, daemon=True) + log_thread.start() self.log('info', f'Waiting for server to become ready at {self.api_url}...') self.send_status_message('STATUS$WAITING_FOR_CLIENT') diff --git a/openhands/runtime/utils/command.py b/openhands/runtime/utils/command.py index 731b521446f5..415f9fcb7f78 100644 --- a/openhands/runtime/utils/command.py +++ b/openhands/runtime/utils/command.py @@ -31,7 +31,7 @@ def get_action_execution_server_startup_command( '--browsergym-eval-env' ] + sandbox_config.browsergym_eval_env.split(' ') - is_root = app_config.run_as_user == 'root' + is_root = not app_config.run_as_openhands base_cmd = [ *python_prefix, @@ -44,7 +44,7 @@ def get_action_execution_server_startup_command( app_config.workspace_mount_path_in_sandbox, *plugin_args, '--username', - app_config.run_as_user, + 'openhands' if app_config.run_as_openhands else 'root', '--user-id', str(sandbox_config.user_id), *browsergym_args, diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 441e7f9b8879..a77b5163b890 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -142,16 +142,15 @@ def get_runtime_classes() -> list[type[Runtime]]: raise ValueError(f'Invalid runtime: {runtime}') -def get_run_as_user() -> list[bool]: +def get_run_as_openhands() -> list[bool]: print( '\n\n########################################################################' ) - user = 'openhands' if RUN_AS_OPENHANDS else 'root' - print('USER: ' + user) + print('USER: ' + 'openhands' if RUN_AS_OPENHANDS else 'root') print( '########################################################################\n\n' ) - return [user] + return [RUN_AS_OPENHANDS] @pytest.fixture(scope='module') # for xdist @@ -178,8 +177,8 @@ def runtime_cls(request): # TODO: We will change this to `run_as_user` when `ServerRuntime` is deprecated. # since `DockerRuntime` supports running as an arbitrary user. -@pytest.fixture(scope='module', params=get_run_as_user()) -def run_as_user(request): +@pytest.fixture(scope='module', params=get_run_as_openhands()) +def run_as_openhands(request): time.sleep(1) return request.param @@ -212,7 +211,7 @@ def base_container_image(request): def _load_runtime( temp_dir, runtime_cls, - run_as_user: bool = True, + run_as_openhands: bool = True, enable_auto_lint: bool = False, base_container_image: str | None = None, browsergym_eval_env: str | None = None, @@ -228,7 +227,7 @@ def _load_runtime( plugins = [AgentSkillsRequirement(), JupyterRequirement()] config = load_app_config() - config.run_as_user = run_as_user + config.run_as_openhands = run_as_openhands config.sandbox.force_rebuild_runtime = force_rebuild_runtime config.sandbox.keep_runtime_alive = False config.sandbox.docker_runtime_kwargs = docker_runtime_kwargs diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index b1c7ada80d5c..bebcf498012d 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -29,8 +29,8 @@ def _run_cmd_action(runtime, custom_command: str): return obs -def test_bash_command_env(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_bash_command_env(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = runtime.run_action(CmdRunAction(command='env')) assert isinstance( @@ -41,8 +41,8 @@ def test_bash_command_env(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_bash_server(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_bash_server(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = CmdRunAction(command='python3 -m http.server 8080') action.timeout = 1 @@ -108,7 +108,7 @@ def test_multiline_commands(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_user): +def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_openhands): cmds = [ 'ls -l', 'echo -e "hello\nworld"', @@ -122,7 +122,7 @@ def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_user): ] joined_cmds = '\n'.join(cmds) - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # First test that running multiple commands at once fails obs = _run_cmd_action(runtime, joined_cmds) @@ -167,9 +167,9 @@ def test_complex_commands(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_no_ps2_in_output(temp_dir, runtime_cls, run_as_user): +def test_no_ps2_in_output(temp_dir, runtime_cls, run_as_openhands): """Test that the PS2 sign is not added to the output of a multiline command.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = _run_cmd_action(runtime, 'echo -e "hello\nworld"') assert obs.exit_code == 0, 'The exit code should be 0.' @@ -207,8 +207,8 @@ def test_multiline_command_loop(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_cmd_run(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = _run_cmd_action(runtime, 'ls -l /openhands/workspace') assert obs.exit_code == 0 @@ -222,7 +222,7 @@ def test_cmd_run(temp_dir, runtime_cls, run_as_user): obs = _run_cmd_action(runtime, 'ls -l') assert obs.exit_code == 0 - if run_as_user: + if run_as_openhands: assert 'openhands' in obs.content else: assert 'root' in obs.content @@ -244,12 +244,12 @@ def test_cmd_run(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = _run_cmd_action(runtime, 'cd ~ && pwd') assert obs.exit_code == 0 - if run_as_user: + if run_as_openhands: assert '/home/openhands' in obs.content else: assert '/root' in obs.content @@ -449,7 +449,7 @@ def test_git_operation(runtime_cls): use_workspace=False, runtime_cls=runtime_cls, # Need to use non-root user to expose issues - run_as_user=True, + run_as_openhands=True, ) # this will happen if permission of runtime is not properly configured # fatal: detected dubious ownership in repository at '/workspace' @@ -498,8 +498,8 @@ def test_git_operation(runtime_cls): _close_test_runtime(runtime) -def test_python_version(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_python_version(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = runtime.run_action(CmdRunAction(command='python --version')) @@ -512,8 +512,8 @@ def test_python_version(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_pwd_property(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_pwd_property(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Create a subdirectory and verify pwd updates obs = _run_cmd_action(runtime, 'mkdir -p random_dir') @@ -526,8 +526,8 @@ def test_pwd_property(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_basic_command(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_basic_command(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test simple command obs = _run_cmd_action(runtime, "echo 'hello world'") @@ -554,8 +554,8 @@ def test_basic_command(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_interactive_command(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_interactive_command(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test interactive command action = CmdRunAction('read -p "Enter name: " name && echo "Hello $name"') @@ -585,8 +585,8 @@ def test_interactive_command(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_long_output(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_long_output(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Generate a long output action = CmdRunAction('for i in $(seq 1 5000); do echo "Line $i"; done') @@ -599,8 +599,8 @@ def test_long_output(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Generate a long output action = CmdRunAction('for i in $(seq 1 50000); do echo "Line $i"; done') @@ -615,8 +615,8 @@ def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Create nested directories with many files setup_cmd = 'mkdir -p /tmp/test_dir && cd /tmp/test_dir && for i in $(seq 1 100); do mkdir -p "folder_$i"; for j in $(seq 1 100); do touch "folder_$i/file_$j.txt"; done; done' @@ -640,8 +640,8 @@ def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_user) _close_test_runtime(runtime) -def test_command_backslash(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_command_backslash(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Create a file with the content "implemented_function" action = CmdRunAction( @@ -667,8 +667,8 @@ def test_command_backslash(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_command_output_continuation(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_command_output_continuation(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Start a command that produces output slowly action = CmdRunAction('for i in {1..5}; do echo $i; sleep 3; done') @@ -706,8 +706,10 @@ def test_command_output_continuation(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_long_running_command_follow_by_execute(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_long_running_command_follow_by_execute( + temp_dir, runtime_cls, run_as_openhands +): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test command that produces output slowly action = CmdRunAction('for i in {1..3}; do echo $i; sleep 3; done') @@ -744,8 +746,8 @@ def test_long_running_command_follow_by_execute(temp_dir, runtime_cls, run_as_us _close_test_runtime(runtime) -def test_empty_command_errors(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_empty_command_errors(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test empty command without previous command obs = runtime.run_action(CmdRunAction('')) @@ -755,8 +757,8 @@ def test_empty_command_errors(temp_dir, runtime_cls, run_as_user): _close_test_runtime(runtime) -def test_python_interactive_input(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_python_interactive_input(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test Python program that asks for input - properly escaped for bash python_script = """name = input('Enter your name: '); age = input('Enter your age: '); print(f'Hello {name}, you are {age} years old')""" diff --git a/tests/runtime/test_browsergym_envs.py b/tests/runtime/test_browsergym_envs.py index 9f06e76f1149..426ecacaf54c 100644 --- a/tests/runtime/test_browsergym_envs.py +++ b/tests/runtime/test_browsergym_envs.py @@ -32,7 +32,7 @@ def test_browsergym_eval_env(runtime_cls, temp_dir): runtime = _load_runtime( temp_dir, runtime_cls=runtime_cls, - run_as_user='root', # need root permission to access file + run_as_openhands=False, # need root permission to access file base_container_image='xingyaoww/od-eval-miniwob:v1.0', browsergym_eval_env='browsergym/miniwob.choose-list', force_rebuild_runtime=True, diff --git a/tests/runtime/test_browsing.py b/tests/runtime/test_browsing.py index ef701a03dcc2..0dee3750953f 100644 --- a/tests/runtime/test_browsing.py +++ b/tests/runtime/test_browsing.py @@ -20,8 +20,8 @@ PY3_FOR_TESTING = '/openhands/micromamba/bin/micromamba run -n openhands python3' -def test_simple_browse(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_simple_browse(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test browse action_cmd = CmdRunAction( diff --git a/tests/runtime/test_edit.py b/tests/runtime/test_edit.py index cfd5a919b014..c507166a840d 100644 --- a/tests/runtime/test_edit.py +++ b/tests/runtime/test_edit.py @@ -27,8 +27,8 @@ def index(): TEST_IN_CI != 'True', reason='This test requires LLM to run.', ) -def test_edit_from_scratch(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_edit_from_scratch(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = FileEditAction( content=ORGINAL, @@ -67,8 +67,8 @@ def index(): TEST_IN_CI != 'True', reason='This test requires LLM to run.', ) -def test_edit(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_edit(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = FileEditAction( content=ORGINAL, @@ -126,8 +126,8 @@ def test_edit(temp_dir, runtime_cls, run_as_user): TEST_IN_CI != 'True', reason='This test requires LLM to run.', ) -def test_edit_long_file(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_edit_long_file(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = FileEditAction( content=ORIGINAL_LONG, diff --git a/tests/runtime/test_env_vars.py b/tests/runtime/test_env_vars.py index 2dd31abfd63f..898003ff66c7 100644 --- a/tests/runtime/test_env_vars.py +++ b/tests/runtime/test_env_vars.py @@ -13,9 +13,9 @@ # ============================================================================================================================ -def test_env_vars_os_environ(temp_dir, runtime_cls, run_as_user): +def test_env_vars_os_environ(temp_dir, runtime_cls, run_as_openhands): with patch.dict(os.environ, {'SANDBOX_ENV_FOOBAR': 'BAZ'}): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) obs: CmdOutputObservation = runtime.run_action(CmdRunAction(command='env')) print(obs) diff --git a/tests/runtime/test_ipython.py b/tests/runtime/test_ipython.py index bbd46e643cb2..922dcd404aac 100644 --- a/tests/runtime/test_ipython.py +++ b/tests/runtime/test_ipython.py @@ -29,8 +29,8 @@ # ============================================================================================================================ -def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test run command action_cmd = CmdRunAction(command='ls -l') @@ -101,8 +101,8 @@ def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_user): TEST_IN_CI != 'True', reason='This test is not working in WSL (file ownership)', ) -def test_ipython_multi_user(temp_dir, runtime_cls, run_as_user): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) +def test_ipython_multi_user(temp_dir, runtime_cls, run_as_openhands): + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test run ipython # get username @@ -113,7 +113,7 @@ def test_ipython_multi_user(temp_dir, runtime_cls, run_as_user): assert isinstance(obs, IPythonRunCellObservation) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) - if run_as_user: + if run_as_openhands: assert 'openhands' in obs.content else: assert 'root' in obs.content @@ -156,7 +156,7 @@ def test_ipython_multi_user(temp_dir, runtime_cls, run_as_user): obs = runtime.run_action(action) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) assert obs.exit_code == 0 - if run_as_user: + if run_as_openhands: # -rw-r--r-- 1 openhands root 13 Jul 28 03:53 test.txt assert 'openhands' in obs.content.split('\r\n')[0] else: @@ -196,9 +196,9 @@ def test_ipython_simple(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_ipython_package_install(temp_dir, runtime_cls, run_as_user): +def test_ipython_package_install(temp_dir, runtime_cls, run_as_openhands): """Make sure that cd in bash also update the current working directory in ipython.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # It should error out since pymsgbox is not installed action = IPythonRunCellAction(code='import pymsgbox') @@ -233,7 +233,7 @@ def test_ipython_package_install(temp_dir, runtime_cls, run_as_user): def test_ipython_file_editor_permissions_as_openhands(temp_dir, runtime_cls): """Test file editor permission behavior when running as different users.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_user=True) + runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands=True) # Create a file owned by root with restricted permissions action = CmdRunAction( @@ -312,8 +312,8 @@ def test_ipython_file_editor_permissions_as_openhands(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_file_read_and_edit_via_oh_aci(runtime_cls, run_as_user): - runtime = _load_runtime(None, runtime_cls, run_as_user) +def test_file_read_and_edit_via_oh_aci(runtime_cls, run_as_openhands): + runtime = _load_runtime(None, runtime_cls, run_as_openhands) sandbox_dir = '/openhands/workspace' actions = [ diff --git a/tests/runtime/test_stress_remote_runtime.py b/tests/runtime/test_stress_remote_runtime.py index e742ee618f66..6f2f011ee2a3 100644 --- a/tests/runtime/test_stress_remote_runtime.py +++ b/tests/runtime/test_stress_remote_runtime.py @@ -53,7 +53,7 @@ def get_config( ), 'ALLHANDS_API_KEY must be set.' config = AppConfig( default_agent=metadata.agent_class, - run_as_user='root', + run_as_openhands=False, max_iterations=metadata.max_iterations, runtime='remote', sandbox=SandboxConfig( From 28bf3f832248b4dbe392c24da0a6e821d50e734e Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 15:45:54 -0500 Subject: [PATCH 33/90] add the ability to override user name --- openhands/runtime/impl/local/local_runtime.py | 20 ++++++++++--------- openhands/runtime/utils/command.py | 14 ++++++++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 8c11ec641949..205858148d66 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -2,11 +2,11 @@ This runtime runs the action_execution_server directly on the local machine without Docker. """ +import os import shutil import subprocess import tempfile import threading -from pathlib import Path from typing import Callable, Optional import requests @@ -18,12 +18,6 @@ from openhands.events import EventStream from openhands.events.action import ( Action, - BrowseInteractiveAction, - BrowseURLAction, - CmdRunAction, - FileReadAction, - FileWriteAction, - IPythonRunCellAction, ) from openhands.events.observation import ( ErrorObservation, @@ -110,6 +104,9 @@ def __init__( ) self.config.workspace_mount_path_in_sandbox = self._temp_workspace + self._user_id = os.getuid() + self._username = os.getenv('USER') + # Initialize the action_execution_server super().__init__( config, @@ -124,9 +121,12 @@ def __init__( logger.warning( 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' + '`run_as_openhands` will be ignored since the current user will be used to launch the server. ' 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' 'are running in a controlled environment.\n' - f'Temp workspace: {self._temp_workspace}' + f'Temp workspace: {self._temp_workspace}. ' + f'User ID: {self._user_id}. ' + f'Username: {self._username}.' ) def _get_action_execution_server_host(self): @@ -149,7 +149,9 @@ async def connect(self): server_port=self._host_port, plugins=self.plugins, app_config=self.config, - python_prefix=[], # empty + python_prefix=[], + override_user_id=self._user_id, + override_username=self._username, ) self.log('debug', f'Starting server with command: {cmd}') diff --git a/openhands/runtime/utils/command.py b/openhands/runtime/utils/command.py index 76722daca476..17458c1f3ee0 100644 --- a/openhands/runtime/utils/command.py +++ b/openhands/runtime/utils/command.py @@ -17,6 +17,8 @@ def get_action_execution_server_startup_command( app_config: AppConfig, python_prefix: list[str] = DEFAULT_PYTHON_PREFIX, use_nice_for_root: bool = True, + override_user_id: int | None = None, + override_username: str | None = None, ): sandbox_config = app_config.sandbox @@ -32,7 +34,13 @@ def get_action_execution_server_startup_command( '--browsergym-eval-env' ] + sandbox_config.browsergym_eval_env.split(' ') - is_root = not app_config.run_as_openhands + username = override_username or ( + 'openhands' if app_config.run_as_openhands else 'root' + ) + user_id = override_user_id or ( + sandbox_config.user_id if app_config.run_as_openhands else 0 + ) + is_root = bool(username == 'root') base_cmd = [ *python_prefix, @@ -45,9 +53,9 @@ def get_action_execution_server_startup_command( app_config.workspace_mount_path_in_sandbox, *plugin_args, '--username', - 'openhands' if app_config.run_as_openhands else 'root', + username, '--user-id', - str(sandbox_config.user_id), + str(user_id), *browsergym_args, ] From 67612da848481ad0c789642ac25aa0dfad902c16 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 15:50:40 -0500 Subject: [PATCH 34/90] tweak --- openhands/runtime/impl/local/local_runtime.py | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 205858148d66..c9951b810418 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -64,17 +64,26 @@ def __init__( headless_mode: bool = True, ): self.config = config - if self.config.run_as_openhands: - raise RuntimeError( - 'Local runtime does not support running as openhands. It only supports running as root.' - ) - if self.config.sandbox.user_id != 0: - logger.warning( - 'Local runtime does not support running as a non-root user. Setting user ID to 0.' - ) - self.config.sandbox.user_id = 0 + self._user_id = os.getuid() + self._username = os.getenv('USER') + + # A temporary directory is created for the agent to run in + # This is used for the local runtime only + self._temp_workspace = tempfile.mkdtemp( + prefix=f'openhands_workspace_{sid}', + ) + self.config.workspace_mount_path_in_sandbox = self._temp_workspace + + logger.warning( + 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' + '`run_as_openhands` will be ignored since the current user will be used to launch the server. ' + 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' + 'are running in a controlled environment.\n' + f'Temp workspace: {self._temp_workspace}. ' + f'User ID: {self._user_id}. ' + f'Username: {self._username}.' + ) - self._temp_workspace: str | None = None if self.config.workspace_base is not None: logger.warning( f'Workspace base path is set to {self.config.workspace_base}. It will be used as the path for the agent to run in.' @@ -97,16 +106,6 @@ def __init__( self.server_process: Optional[subprocess.Popen[str]] = None self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time - # A temporary directory is created for the agent to run in - # This is used for the local runtime only - self._temp_workspace = tempfile.mkdtemp( - prefix=f'openhands_workspace_{sid}', - ) - self.config.workspace_mount_path_in_sandbox = self._temp_workspace - - self._user_id = os.getuid() - self._username = os.getenv('USER') - # Initialize the action_execution_server super().__init__( config, @@ -119,16 +118,6 @@ def __init__( headless_mode, ) - logger.warning( - 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' - '`run_as_openhands` will be ignored since the current user will be used to launch the server. ' - 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' - 'are running in a controlled environment.\n' - f'Temp workspace: {self._temp_workspace}. ' - f'User ID: {self._user_id}. ' - f'Username: {self._username}.' - ) - def _get_action_execution_server_host(self): return self.api_url @@ -156,7 +145,7 @@ async def connect(self): self.log('debug', f'Starting server with command: {cmd}') self.server_process = subprocess.Popen( - cmd.split(), + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, @@ -200,7 +189,7 @@ def _find_available_port(self, port_range, max_attempts=5): @tenacity.retry( wait=tenacity.wait_exponential(multiplier=0.1, min=0.1, max=1), - stop=stop_if_should_exit, + stop=stop_if_should_exit(), before_sleep=lambda retry_state: logger.debug( f'Waiting for server to be ready... (attempt {retry_state.attempt_number})' ), From 2d35f1afc3250267b4609fd1113c8fa45d3ecfd6 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 16:26:07 -0500 Subject: [PATCH 35/90] got it somewhat working! --- openhands/runtime/action_execution_server.py | 3 +- openhands/runtime/impl/local/local_runtime.py | 45 +++++++++++++++---- openhands/runtime/plugins/jupyter/__init__.py | 44 ++++++++++++++---- openhands/runtime/plugins/vscode/__init__.py | 9 ++++ openhands/runtime/utils/bash.py | 3 +- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/openhands/runtime/action_execution_server.py b/openhands/runtime/action_execution_server.py index 63d2d1cbdf3e..734578d26ef8 100644 --- a/openhands/runtime/action_execution_server.py +++ b/openhands/runtime/action_execution_server.py @@ -67,7 +67,7 @@ class ActionRequest(BaseModel): ROOT_GID = 0 INIT_COMMANDS = [ - 'git config --global user.name "openhands" && git config --global user.email "openhands@all-hands.dev" && alias git="git --no-pager"', + 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && export GIT_CONFIG_FILE=$(pwd)/.git_config', ] SESSION_API_KEY = os.environ.get('SESSION_API_KEY') @@ -171,7 +171,6 @@ async def _init_bash_commands(self): f'Init command outputs (exit code: {obs.exit_code}): {obs.content}' ) assert obs.exit_code == 0 - logger.debug('Bash init commands completed') async def run_action(self, action) -> Observation: diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index c9951b810418..66a46f3082ca 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -12,6 +12,7 @@ import requests import tenacity +import openhands from openhands.core.config import AppConfig from openhands.core.exceptions import AgentRuntimeDisconnectedError from openhands.core.logger import openhands_logger as logger @@ -144,25 +145,50 @@ async def connect(self): ) self.log('debug', f'Starting server with command: {cmd}') + env = os.environ.copy() + # Get the code repo path + code_repo_path = os.path.dirname(os.path.dirname(openhands.__file__)) + env['PYTHONPATH'] = f'{code_repo_path}:$PYTHONPATH' + env['OPENHANDS_REPO_PATH'] = code_repo_path + # run poetry show -v | head -n 1 | awk '{print $2}' + poetry_venvs_path = ( + subprocess.check_output( + ['poetry', 'show', '-v'], + env=env, + cwd=code_repo_path, + text=True, + shell=False, + ) + .splitlines()[0] + .split(':')[1] + .strip() + ) + env['POETRY_VIRTUALENVS_PATH'] = poetry_venvs_path + logger.debug(f'POETRY_VIRTUALENVS_PATH: {poetry_venvs_path}') + self.server_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1, + env=env, ) # Start a thread to read and log server output def log_output(): - if self.server_process and self.server_process.stdout: - while True: - line = self.server_process.stdout.readline() - if not line: - break - self.log('debug', f'Server: {line.strip()}') - - log_thread = threading.Thread(target=log_output, daemon=True) - log_thread.start() + while ( + self.server_process + and self.server_process.poll() + and self.server_process.stdout + ): + line = self.server_process.stdout.readline() + if not line: + break + self.log('debug', f'Server: {line.strip()}') + + self._log_thread = threading.Thread(target=log_output, daemon=True) + self._log_thread.start() self.log('info', f'Waiting for server to become ready at {self.api_url}...') self.send_status_message('STATUS$WAITING_FOR_CLIENT') @@ -238,6 +264,7 @@ def close(self): except subprocess.TimeoutExpired: self.server_process.kill() self.server_process = None + self._log_thread.join() if self._temp_workspace: shutil.rmtree(self._temp_workspace) diff --git a/openhands/runtime/plugins/jupyter/__init__.py b/openhands/runtime/plugins/jupyter/__init__.py index 23128b60a8f9..dd4962b372f5 100644 --- a/openhands/runtime/plugins/jupyter/__init__.py +++ b/openhands/runtime/plugins/jupyter/__init__.py @@ -1,3 +1,4 @@ +import os import subprocess import time from dataclasses import dataclass @@ -22,19 +23,46 @@ class JupyterPlugin(Plugin): async def initialize(self, username: str, kernel_id: str = 'openhands-default'): self.kernel_gateway_port = find_available_tcp_port(40000, 49999) self.kernel_id = kernel_id - self.gateway_process = subprocess.Popen( - ( - f"su - {username} -s /bin/bash << 'EOF'\n" + if username in ['root', 'openhands']: + # Non-LocalRuntime + prefix = f'su - {username} -s ' + # cd to code repo, setup all env vars and run micromamba + poetry_prefix = ( 'cd /openhands/code\n' 'export POETRY_VIRTUALENVS_PATH=/openhands/poetry;\n' 'export PYTHONPATH=/openhands/code:$PYTHONPATH;\n' 'export MAMBA_ROOT_PREFIX=/openhands/micromamba;\n' '/openhands/micromamba/bin/micromamba run -n openhands ' - 'poetry run jupyter kernelgateway ' - '--KernelGatewayApp.ip=0.0.0.0 ' - f'--KernelGatewayApp.port={self.kernel_gateway_port}\n' - 'EOF' - ), + ) + else: + # LocalRuntime + prefix = '' + code_repo_path = os.environ.get('OPENHANDS_REPO_PATH') + if not code_repo_path: + raise ValueError( + 'OPENHANDS_REPO_PATH environment variable is not set. ' + 'This is required for the jupyter plugin to work with LocalRuntime.' + ) + # assert POETRY_VIRTUALENVS_PATH is set + poetry_venvs_path = os.environ.get('POETRY_VIRTUALENVS_PATH') + if not poetry_venvs_path: + raise ValueError( + 'POETRY_VIRTUALENVS_PATH environment variable is not set. ' + 'This is required for the jupyter plugin to work with LocalRuntime.' + ) + poetry_prefix = f'cd {code_repo_path}\n' + jupyter_launch_command = ( + f"{prefix}/bin/bash << 'EOF'\n" + f'{poetry_prefix}' + 'poetry run jupyter kernelgateway ' + '--KernelGatewayApp.ip=0.0.0.0 ' + f'--KernelGatewayApp.port={self.kernel_gateway_port}\n' + 'EOF' + ) + logger.debug(f'Jupyter launch command: {jupyter_launch_command}') + + self.gateway_process = subprocess.Popen( + jupyter_launch_command, stderr=subprocess.STDOUT, shell=True, ) diff --git a/openhands/runtime/plugins/vscode/__init__.py b/openhands/runtime/plugins/vscode/__init__.py index cb1052dd45b8..6765432545a4 100644 --- a/openhands/runtime/plugins/vscode/__init__.py +++ b/openhands/runtime/plugins/vscode/__init__.py @@ -19,6 +19,15 @@ class VSCodePlugin(Plugin): name: str = 'vscode' async def initialize(self, username: str): + if username not in ['root', 'openhands']: + self.vscode_port = None + self.vscode_connection_token = None + logger.warning( + 'VSCodePlugin is only supported for root or openhands user. ' + 'It is not yet supported for other users (i.e., when running LocalRuntime).' + ) + return + self.vscode_port = int(os.environ['VSCODE_PORT']) self.vscode_connection_token = str(uuid.uuid4()) assert check_port_available(self.vscode_port) diff --git a/openhands/runtime/utils/bash.py b/openhands/runtime/utils/bash.py index 351d990dcda6..c46c6f798cb3 100644 --- a/openhands/runtime/utils/bash.py +++ b/openhands/runtime/utils/bash.py @@ -184,9 +184,10 @@ def __init__( def initialize(self): self.server = libtmux.Server() window_command = '/bin/bash' - if self.username: + if self.username in ['root', 'openhands']: # This starts a non-login (new) shell for the given user window_command = f'su {self.username} -' + # otherwise, we are running as the CURRENT USER (e.g., when running LocalRuntime) session_name = f'openhands-{self.username}-{uuid.uuid4()}' self.session = self.server.new_session( From 77ea88cfc074d648a0b400aaa6214a35d8360a1f Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 16:29:15 -0500 Subject: [PATCH 36/90] remove duplicate line --- openhands/runtime/impl/local/local_runtime.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 66a46f3082ca..90d9220a4b5b 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -271,10 +271,6 @@ def close(self): super().close() - # Cleanup the temp workspace - if self._temp_workspace: - shutil.rmtree(self._temp_workspace) - @property def vscode_url(self) -> str | None: token = super().get_vscode_token() From 9d4300da95c09d282e87449d2edcf68ca43776b5 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 17:10:25 -0500 Subject: [PATCH 37/90] try fix command --- .github/workflows/ghcr-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index 625ade07a770..bac18530febd 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -280,7 +280,7 @@ jobs: TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ - poetry run pytest -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py + poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Run docker runtime tests run: | # We install pytest-xdist in order to run tests across CPUs From 16c6e52797d35dd1eb1bf95c4696da6da5828667 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 10 Jan 2025 17:10:49 -0500 Subject: [PATCH 38/90] fix import --- tests/runtime/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index a77b5163b890..79d8e338f3d2 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -13,7 +13,7 @@ from openhands.events import EventStream from openhands.runtime.base import Runtime from openhands.runtime.impl.docker.docker_runtime import DockerRuntime -from openhands.runtime.impl.local import LocalRuntime +from openhands.runtime.impl.local.local_runtime import LocalRuntime from openhands.runtime.impl.remote.remote_runtime import RemoteRuntime from openhands.runtime.impl.runloop.runloop_runtime import RunloopRuntime from openhands.runtime.plugins import AgentSkillsRequirement, JupyterRequirement From 37751adc6a02281b2f5f7159addadfde7cbf6e3e Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 20:46:43 -0500 Subject: [PATCH 39/90] support using provided workspace base1 --- openhands/runtime/impl/local/local_runtime.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 90d9220a4b5b..c82cabd0649b 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -68,12 +68,20 @@ def __init__( self._user_id = os.getuid() self._username = os.getenv('USER') - # A temporary directory is created for the agent to run in - # This is used for the local runtime only - self._temp_workspace = tempfile.mkdtemp( - prefix=f'openhands_workspace_{sid}', - ) - self.config.workspace_mount_path_in_sandbox = self._temp_workspace + if self.config.workspace_base is not None: + logger.warning( + f'Workspace base path is set to {self.config.workspace_base}. ' + 'It will be used as the path for the agent to run in. ' + 'Be careful, the agent can EDIT files in this directory!' + ) + self.config.workspace_mount_path_in_sandbox = self.config.workspace_base + else: + # A temporary directory is created for the agent to run in + # This is used for the local runtime only + self._temp_workspace = tempfile.mkdtemp( + prefix=f'openhands_workspace_{sid}', + ) + self.config.workspace_mount_path_in_sandbox = self._temp_workspace logger.warning( 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' From 187325088b44e257ffd2fa3a98fdff2c29760ffc Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 21:06:00 -0500 Subject: [PATCH 40/90] add dependency check; increase timeout for wait; --- openhands/runtime/impl/local/local_runtime.py | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index c82cabd0649b..edef91d1557c 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -41,6 +41,49 @@ from openhands.utils.tenacity_stop import stop_if_should_exit +def check_dependencies(code_repo_path: str, poetry_venvs_path: str): + ERROR_MESSAGE = 'Please follow the instructions in https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md to install OpenHands.' + if not os.path.exists(code_repo_path): + raise ValueError( + f'Code repo path {code_repo_path} does not exist. ' + ERROR_MESSAGE + ) + if not os.path.exists(poetry_venvs_path): + raise ValueError( + f'Poetry venvs path {poetry_venvs_path} does not exist. ' + ERROR_MESSAGE + ) + # Check jupyter is installed + logger.debug('Checking dependencies: Jupyter') + output = subprocess.check_output( + 'poetry run jupyter --version', + shell=True, + text=True, + cwd=code_repo_path, + ) + logger.debug(f'Jupyter output: {output}') + if 'jupyter' not in output.lower(): + raise ValueError('Jupyter is not properly installed. ' + ERROR_MESSAGE) + + # Check libtmux is installed + logger.debug('Checking dependencies: libtmux') + import libtmux + + server = libtmux.Server() + session = server.new_session(session_name='test-session') + pane = session.attached_pane + pane.send_keys('echo "test"') + pane_output = '\n'.join(pane.cmd('capture-pane', '-p').stdout) + session.kill_session() + if 'test' not in pane_output: + raise ValueError('libtmux is not properly installed. ' + ERROR_MESSAGE) + + # Check browser works + logger.debug('Checking dependencies: browser') + from openhands.runtime.browser.browser_env import BrowserEnv + + browser = BrowserEnv() + browser.close() + + class LocalRuntime(ActionExecutionClient): """This runtime will run the action_execution_server directly on the local machine. When receiving an event, it will send the event to the server via HTTP. @@ -75,6 +118,7 @@ def __init__( 'Be careful, the agent can EDIT files in this directory!' ) self.config.workspace_mount_path_in_sandbox = self.config.workspace_base + self._temp_workspace = None else: # A temporary directory is created for the agent to run in # This is used for the local runtime only @@ -174,6 +218,7 @@ async def connect(self): env['POETRY_VIRTUALENVS_PATH'] = poetry_venvs_path logger.debug(f'POETRY_VIRTUALENVS_PATH: {poetry_venvs_path}') + check_dependencies(code_repo_path, poetry_venvs_path) self.server_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, @@ -222,7 +267,7 @@ def _find_available_port(self, port_range, max_attempts=5): return port @tenacity.retry( - wait=tenacity.wait_exponential(multiplier=0.1, min=0.1, max=1), + wait=tenacity.wait_exponential(min=1, max=10), stop=stop_if_should_exit(), before_sleep=lambda retry_state: logger.debug( f'Waiting for server to be ready... (attempt {retry_state.attempt_number})' From 424fcb78c05a2453c43e5ada363644a336b71065 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 21:13:15 -0500 Subject: [PATCH 41/90] rm local runtime test from ghcr-build --- .github/workflows/ghcr-build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index bac18530febd..a6e1755a8ef7 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -275,12 +275,6 @@ jobs: run: pipx install poetry - name: Install Python dependencies using Poetry run: make install-python-dependencies - - name: Run local runtime tests - run: | - TEST_RUNTIME=local \ - TEST_IN_CI=true \ - RUN_AS_OPENHANDS=false \ - poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - name: Run docker runtime tests run: | # We install pytest-xdist in order to run tests across CPUs From 316b88a4140c487c7882b4e96756429a01a2af80 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 21:15:33 -0500 Subject: [PATCH 42/90] add seprate ci for localruntime --- .github/workflows/ghcr-build.yml | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index a6e1755a8ef7..d7e041a56f98 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -219,6 +219,58 @@ jobs: exit 1 fi + # Run unit tests with the EventStream runtime Docker images as root + test_runtime_local: + name: RT Unit Tests (LocalRuntime) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + base_image: ['nikolaik'] + steps: + - uses: actions/checkout@v4 + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + # this might remove tools that are actually needed, + # if set to "true" but frees about 6 GB + tool-cache: true + # all of these default to true, but feel free to set to + # "false" if necessary for your workflow + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: false + swap-storage: true + - name: Cache Poetry dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cache/pypoetry + ~/.virtualenvs + key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} + restore-keys: | + ${{ runner.os }}-poetry- + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install poetry via pipx + run: pipx install poetry + - name: Install Python dependencies using Poetry + run: make install-python-dependencies + - name: Run local runtime tests + run: | + TEST_RUNTIME=local \ + TEST_IN_CI=true \ + RUN_AS_OPENHANDS=false \ + poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # Run unit tests with the EventStream runtime Docker images as root test_runtime_root: name: RT Unit Tests (Root) From b8770ad64c533ba7af31a18d4be767568ef10b8c Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 21:16:34 -0500 Subject: [PATCH 43/90] tweak export git and save it to bashrc --- openhands/runtime/action_execution_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openhands/runtime/action_execution_server.py b/openhands/runtime/action_execution_server.py index 734578d26ef8..cde8d0494130 100644 --- a/openhands/runtime/action_execution_server.py +++ b/openhands/runtime/action_execution_server.py @@ -66,9 +66,6 @@ class ActionRequest(BaseModel): ROOT_GID = 0 -INIT_COMMANDS = [ - 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && export GIT_CONFIG_FILE=$(pwd)/.git_config', -] SESSION_API_KEY = os.environ.get('SESSION_API_KEY') api_key_header = APIKeyHeader(name='X-Session-API-Key', auto_error=False) @@ -160,6 +157,9 @@ async def _init_plugin(self, plugin: Plugin): ) async def _init_bash_commands(self): + INIT_COMMANDS = [ + 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && echo "export GIT_CONFIG_FILE=$(pwd)/.git_config" >> ~/.bashrc && source ~/.bashrc', + ] logger.debug(f'Initializing by running {len(INIT_COMMANDS)} bash commands...') for command in INIT_COMMANDS: action = CmdRunAction(command=command) From 41301ac075cdc4cb9f7b50a0aa91a60fdc795b0d Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 21:23:20 -0500 Subject: [PATCH 44/90] move localruntime test to unit test ci --- .github/workflows/ghcr-build.yml | 51 ----------------------------- .github/workflows/py-unit-tests.yml | 37 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 51 deletions(-) diff --git a/.github/workflows/ghcr-build.yml b/.github/workflows/ghcr-build.yml index d7e041a56f98..402db1a24736 100644 --- a/.github/workflows/ghcr-build.yml +++ b/.github/workflows/ghcr-build.yml @@ -219,57 +219,6 @@ jobs: exit 1 fi - # Run unit tests with the EventStream runtime Docker images as root - test_runtime_local: - name: RT Unit Tests (LocalRuntime) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - base_image: ['nikolaik'] - steps: - - uses: actions/checkout@v4 - - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@main - with: - # this might remove tools that are actually needed, - # if set to "true" but frees about 6 GB - tool-cache: true - # all of these default to true, but feel free to set to - # "false" if necessary for your workflow - android: true - dotnet: true - haskell: true - large-packages: true - docker-images: false - swap-storage: true - - name: Cache Poetry dependencies - uses: actions/cache@v4 - with: - path: | - ~/.cache/pypoetry - ~/.virtualenvs - key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} - restore-keys: | - ${{ runner.os }}-poetry- - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install poetry via pipx - run: pipx install poetry - - name: Install Python dependencies using Poetry - run: make install-python-dependencies - - name: Run local runtime tests - run: | - TEST_RUNTIME=local \ - TEST_IN_CI=true \ - RUN_AS_OPENHANDS=false \ - poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # Run unit tests with the EventStream runtime Docker images as root test_runtime_root: diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 1e42ccc2d82c..9a38f6bc5c59 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -49,3 +49,40 @@ jobs: uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # Run python unit tests on Linux + test-on-linux-local-runtime: + name: Local Runtime Tests on Linux + runs-on: ubuntu-latest + env: + INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation + strategy: + matrix: + python-version: ['3.12'] + steps: + - uses: actions/checkout@v4 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + - name: Install tmux + run: sudo apt-get update && sudo apt-get install -y tmux + - name: Install poetry via pipx + run: pipx install poetry + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'poetry' + - name: Install Python dependencies using Poetry + run: poetry install --without evaluation,llama-index + - name: Build Environment + run: make build + - name: Run Local Runtime Tests + run: | + TEST_RUNTIME=local \ + TEST_IN_CI=true \ + RUN_AS_OPENHANDS=false \ + poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 7e37989c69c2c18e0893d368cdcda02d7064ee20 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 12 Jan 2025 21:25:53 -0500 Subject: [PATCH 45/90] fix ci --- .github/workflows/py-unit-tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 9a38f6bc5c59..e9b48cc4dffd 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -78,6 +78,12 @@ jobs: run: make build - name: Run Local Runtime Tests run: | + # We install pytest-xdist in order to run tests across CPUs + poetry run pip install pytest-xdist + + # Install to be able to retry on failures for flaky tests + poetry run pip install pytest-rerunfailures + TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ From 7146d3bc0f3314773113fb915dfe1ea4c3314100 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 13 Jan 2025 14:00:39 -0500 Subject: [PATCH 46/90] fix gitconfig --- openhands/runtime/action_execution_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/action_execution_server.py b/openhands/runtime/action_execution_server.py index cde8d0494130..1b967e2e8bb0 100644 --- a/openhands/runtime/action_execution_server.py +++ b/openhands/runtime/action_execution_server.py @@ -158,7 +158,7 @@ async def _init_plugin(self, plugin: Plugin): async def _init_bash_commands(self): INIT_COMMANDS = [ - 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && echo "export GIT_CONFIG_FILE=$(pwd)/.git_config" >> ~/.bashrc && source ~/.bashrc', + 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && echo "export GIT_CONFIG=$(pwd)/.git_config" >> ~/.bashrc && source ~/.bashrc', ] logger.debug(f'Initializing by running {len(INIT_COMMANDS)} bash commands...') for command in INIT_COMMANDS: From d970bf308407b37b58ab65e65de7616eb46f1e22 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 13 Jan 2025 14:23:48 -0500 Subject: [PATCH 47/90] fix action execution server for non localruntime --- openhands/runtime/action_execution_server.py | 4 +++- openhands/runtime/impl/local/local_runtime.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/openhands/runtime/action_execution_server.py b/openhands/runtime/action_execution_server.py index 1b967e2e8bb0..f6da02454593 100644 --- a/openhands/runtime/action_execution_server.py +++ b/openhands/runtime/action_execution_server.py @@ -158,7 +158,9 @@ async def _init_plugin(self, plugin: Plugin): async def _init_bash_commands(self): INIT_COMMANDS = [ - 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && echo "export GIT_CONFIG=$(pwd)/.git_config" >> ~/.bashrc && source ~/.bashrc', + 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev" && alias git="git --no-pager" && export GIT_CONFIG=$(pwd)/.git_config' + if os.environ.get('LOCAL_RUNTIME_MODE') == '1' + else 'git config --global user.name "openhands" && git config --global user.email "openhands@all-hands.dev" && alias git="git --no-pager"' ] logger.debug(f'Initializing by running {len(INIT_COMMANDS)} bash commands...') for command in INIT_COMMANDS: diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index edef91d1557c..9ca4747ba47b 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -202,6 +202,7 @@ async def connect(self): code_repo_path = os.path.dirname(os.path.dirname(openhands.__file__)) env['PYTHONPATH'] = f'{code_repo_path}:$PYTHONPATH' env['OPENHANDS_REPO_PATH'] = code_repo_path + env['LOCAL_RUNTIME_MODE'] = '1' # run poetry show -v | head -n 1 | awk '{print $2}' poetry_venvs_path = ( subprocess.check_output( From baf0adb023822a2b6198d21bfd9c210f70819501 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 13 Jan 2025 15:24:25 -0500 Subject: [PATCH 48/90] manually install playwright in ci --- .github/workflows/py-unit-tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index e9b48cc4dffd..1cdf0b505627 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -75,7 +75,9 @@ jobs: - name: Install Python dependencies using Poetry run: poetry install --without evaluation,llama-index - name: Build Environment - run: make build + run: | + make build + poetry run playwright install --with-deps chromium - name: Run Local Runtime Tests run: | # We install pytest-xdist in order to run tests across CPUs From 12e810eab89618a814524693c6e38a9eb07ffe08 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 13 Jan 2025 23:46:09 -0500 Subject: [PATCH 49/90] tweak for local runtime --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 1cdf0b505627..f1ac3c8710da 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -64,7 +64,7 @@ jobs: id: buildx uses: docker/setup-buildx-action@v3 - name: Install tmux - run: sudo apt-get update && sudo apt-get install -y tmux + run: sudo apt-get update && sudo apt-get install -y tmux libasound2-plugins - name: Install poetry via pipx run: pipx install poetry - name: Set up Python From c1be6cb3eb8b42a06dc0ae7a51df933d597dec80 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 13 Jan 2025 23:52:25 -0500 Subject: [PATCH 50/90] try a different one --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index f1ac3c8710da..30da58f017d5 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -64,7 +64,7 @@ jobs: id: buildx uses: docker/setup-buildx-action@v3 - name: Install tmux - run: sudo apt-get update && sudo apt-get install -y tmux libasound2-plugins + run: sudo apt-get update && sudo apt-get install -y tmux libasound2t64 - name: Install poetry via pipx run: pipx install poetry - name: Set up Python From 894afc5be57bccd413fdf3867236e83b9e47627c Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Mon, 13 Jan 2025 23:55:31 -0500 Subject: [PATCH 51/90] another one --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 30da58f017d5..8108904639a2 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -64,7 +64,7 @@ jobs: id: buildx uses: docker/setup-buildx-action@v3 - name: Install tmux - run: sudo apt-get update && sudo apt-get install -y tmux libasound2t64 + run: sudo apt-get update && sudo apt-get install -y tmux libasound2-dev - name: Install poetry via pipx run: pipx install poetry - name: Set up Python From e91fd2b80bb047cf96d2403491123dafb610662b Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 13:24:02 -0500 Subject: [PATCH 52/90] use ubuntu 2204 --- .github/workflows/py-unit-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 8108904639a2..7bcf0b5db892 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -52,7 +52,7 @@ jobs: # Run python unit tests on Linux test-on-linux-local-runtime: name: Local Runtime Tests on Linux - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation strategy: @@ -64,7 +64,7 @@ jobs: id: buildx uses: docker/setup-buildx-action@v3 - name: Install tmux - run: sudo apt-get update && sudo apt-get install -y tmux libasound2-dev + run: sudo apt-get update && sudo apt-get install -y tmux - name: Install poetry via pipx run: pipx install poetry - name: Set up Python From d575e256a796af9aa385dc186f110ae8a1063da4 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 15:59:28 -0500 Subject: [PATCH 53/90] setup node --- .github/workflows/py-unit-tests.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 7bcf0b5db892..f4924eb38308 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -30,6 +30,10 @@ jobs: - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v3 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' - name: Install tmux run: sudo apt-get update && sudo apt-get install -y tmux - name: Install poetry via pipx @@ -63,6 +67,10 @@ jobs: - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v3 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' - name: Install tmux run: sudo apt-get update && sudo apt-get install -y tmux - name: Install poetry via pipx From bf39cb8f468edbb486950bfa312770111c6ab0bf Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 16:10:12 -0500 Subject: [PATCH 54/90] remove node --- .github/workflows/py-unit-tests.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index f4924eb38308..a08dac4b62fd 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -30,10 +30,6 @@ jobs: - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v3 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - name: Install tmux run: sudo apt-get update && sudo apt-get install -y tmux - name: Install poetry via pipx From 4d6199f58c40e236c7023dad858989e00412b9ed Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 16:15:52 -0500 Subject: [PATCH 55/90] tweak ver --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index a08dac4b62fd..62dbd00865f8 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -52,7 +52,7 @@ jobs: # Run python unit tests on Linux test-on-linux-local-runtime: name: Local Runtime Tests on Linux - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation strategy: From b017f2ecd6b37792a6b9036e03339cc12c75eaa9 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 16:24:59 -0500 Subject: [PATCH 56/90] revert to 2204 --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 62dbd00865f8..a08dac4b62fd 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -52,7 +52,7 @@ jobs: # Run python unit tests on Linux test-on-linux-local-runtime: name: Local Runtime Tests on Linux - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 env: INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation strategy: From 6f3735fb7ef74df4d2cf96289c178c64d7d06900 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 16:29:44 -0500 Subject: [PATCH 57/90] attempt to fix pclcs bug --- .github/workflows/py-unit-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index a08dac4b62fd..0588143f3c98 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -82,6 +82,9 @@ jobs: run: | make build poetry run playwright install --with-deps chromium + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt-get update -y + sudo apt-get install --only-upgrade libstdc++6 -y - name: Run Local Runtime Tests run: | # We install pytest-xdist in order to run tests across CPUs From b79c63e2358c2911a5470a76a376ad3ccbcc6e8b Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 16 Jan 2025 16:34:50 -0500 Subject: [PATCH 58/90] move it up --- .github/workflows/py-unit-tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 0588143f3c98..82c30928d659 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -69,6 +69,11 @@ jobs: node-version: '22' - name: Install tmux run: sudo apt-get update && sudo apt-get install -y tmux + - name: Dependency fix + run: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt-get update -y + sudo apt-get install --only-upgrade libstdc++6 -y - name: Install poetry via pipx run: pipx install poetry - name: Set up Python @@ -82,9 +87,6 @@ jobs: run: | make build poetry run playwright install --with-deps chromium - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update -y - sudo apt-get install --only-upgrade libstdc++6 -y - name: Run Local Runtime Tests run: | # We install pytest-xdist in order to run tests across CPUs From 45787cecfb75c0a47faaed9d48c1a608d6f13a3e Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 17 Jan 2025 10:51:00 -0500 Subject: [PATCH 59/90] update ignored tests for localruntime; use config.workspace_mount_path_in_sandbox for bash tests --- .github/workflows/py-unit-tests.yml | 2 +- tests/runtime/conftest.py | 6 +- tests/runtime/test_bash.py | 77 ++++++++++----------- tests/runtime/test_browsergym_envs.py | 2 +- tests/runtime/test_browsing.py | 2 +- tests/runtime/test_edit.py | 6 +- tests/runtime/test_env_vars.py | 6 +- tests/runtime/test_images.py | 6 +- tests/runtime/test_ipython.py | 12 ++-- tests/runtime/test_microagent.py | 6 +- tests/runtime/test_stress_docker_runtime.py | 2 +- 11 files changed, 63 insertions(+), 64 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 82c30928d659..7a4740b581f8 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -98,7 +98,7 @@ jobs: TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ - poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py + poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --ignore=tests/runtime/test_stress_docker_runtime.py --ignore=tests/runtime/test_images.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 2a2c8515f276..3bec5d58b60f 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -8,7 +8,7 @@ import pytest from pytest import TempPathFactory -from openhands.core.config import load_app_config +from openhands.core.config import AppConfig, load_app_config from openhands.core.logger import openhands_logger as logger from openhands.events import EventStream from openhands.runtime.base import Runtime @@ -219,7 +219,7 @@ def _load_runtime( force_rebuild_runtime: bool = False, runtime_startup_env_vars: dict[str, str] | None = None, docker_runtime_kwargs: dict[str, str] | None = None, -) -> Runtime: +) -> tuple[Runtime, AppConfig]: sid = 'rt_' + str(random.randint(100000, 999999)) # AgentSkills need to be initialized **before** Jupyter @@ -272,7 +272,7 @@ def _load_runtime( ) call_async_from_sync(runtime.connect) time.sleep(2) - return runtime + return runtime, config # Export necessary function diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index 5cf14d71ee2c..f1faa6af9b47 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -14,7 +14,6 @@ from openhands.core.logger import openhands_logger as logger from openhands.events.action import CmdRunAction from openhands.events.observation import CmdOutputObservation, ErrorObservation -from openhands.runtime.base import Runtime # ============================================================================================================================ # Bash-specific tests @@ -31,7 +30,7 @@ def _run_cmd_action(runtime, custom_command: str): def test_bash_command_env(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = runtime.run_action(CmdRunAction(command='env')) assert isinstance( @@ -43,7 +42,7 @@ def test_bash_command_env(temp_dir, runtime_cls, run_as_openhands): def test_bash_server(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = CmdRunAction(command='python3 -m http.server 8080') action.set_hard_timeout(1) @@ -64,7 +63,7 @@ def test_bash_server(temp_dir, runtime_cls, run_as_openhands): assert isinstance(obs, CmdOutputObservation) assert obs.exit_code == 0 assert 'Keyboard interrupt received, exiting.' in obs.content - assert '/workspace' in obs.metadata.working_dir + assert config.workspace_mount_path_in_sandbox in obs.metadata.working_dir action = CmdRunAction(command='ls') action.set_hard_timeout(1) @@ -73,7 +72,7 @@ def test_bash_server(temp_dir, runtime_cls, run_as_openhands): assert isinstance(obs, CmdOutputObservation) assert obs.exit_code == 0 assert 'Keyboard interrupt received, exiting.' not in obs.content - assert '/workspace' in obs.metadata.working_dir + assert config.workspace_mount_path_in_sandbox in obs.metadata.working_dir # run it again! action = CmdRunAction(command='python3 -m http.server 8080') @@ -89,7 +88,7 @@ def test_bash_server(temp_dir, runtime_cls, run_as_openhands): def test_multiline_commands(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: # single multiline command obs = _run_cmd_action(runtime, 'echo \\\n -e "foo"') @@ -123,7 +122,7 @@ def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_openhands): ] joined_cmds = '\n'.join(cmds) - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # First test that running multiple commands at once fails obs = _run_cmd_action(runtime, joined_cmds) @@ -157,7 +156,7 @@ def test_multiple_multiline_commands(temp_dir, runtime_cls, run_as_openhands): def test_complex_commands(temp_dir, runtime_cls): cmd = """count=0; tries=0; while [ $count -lt 3 ]; do result=$(echo "Heads"); tries=$((tries+1)); echo "Flip $tries: $result"; if [ "$result" = "Heads" ]; then count=$((count+1)); else count=0; fi; done; echo "Got 3 heads in a row after $tries flips!";""" - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: obs = _run_cmd_action(runtime, cmd) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) @@ -170,7 +169,7 @@ def test_complex_commands(temp_dir, runtime_cls): def test_no_ps2_in_output(temp_dir, runtime_cls, run_as_openhands): """Test that the PS2 sign is not added to the output of a multiline command.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = _run_cmd_action(runtime, 'echo -e "hello\nworld"') assert obs.exit_code == 0, 'The exit code should be 0.' @@ -195,7 +194,7 @@ def test_multiline_command_loop(temp_dir, runtime_cls): mv "$file" "$new_date" done && echo "success" """ - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: obs = _run_cmd_action(runtime, init_cmd) assert obs.exit_code == 0, 'The exit code should be 0.' @@ -209,7 +208,7 @@ def test_multiline_command_loop(temp_dir, runtime_cls): def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = _run_cmd_action(runtime, 'ls -l /workspace') assert obs.exit_code == 0 @@ -246,7 +245,7 @@ def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = _run_cmd_action(runtime, 'cd ~ && pwd') assert obs.exit_code == 0 @@ -259,18 +258,18 @@ def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_openhands): def test_multi_cmd_run_in_single_line(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: obs = _run_cmd_action(runtime, 'pwd && ls -l') assert obs.exit_code == 0 - assert '/workspace' in obs.content + assert config.workspace_mount_path_in_sandbox in obs.content assert 'total 0' in obs.content finally: _close_test_runtime(runtime) def test_stateful_cmd(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: obs = _run_cmd_action(runtime, 'mkdir -p test') assert obs.exit_code == 0, 'The exit code should be 0.' @@ -286,7 +285,7 @@ def test_stateful_cmd(temp_dir, runtime_cls): def test_failed_cmd(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: obs = _run_cmd_action(runtime, 'non_existing_command') assert obs.exit_code != 0, 'The exit code should not be 0 for a failed command.' @@ -301,7 +300,7 @@ def _create_test_file(host_temp_dir): def test_copy_single_file(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: sandbox_dir = _get_sandbox_folder(runtime) sandbox_file = os.path.join(sandbox_dir, 'test_file.txt') @@ -331,7 +330,7 @@ def _create_host_test_dir_with_files(test_dir): def test_copy_directory_recursively(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) sandbox_dir = _get_sandbox_folder(runtime) try: @@ -360,7 +359,7 @@ def test_copy_directory_recursively(temp_dir, runtime_cls): def test_copy_to_non_existent_directory(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: sandbox_dir = _get_sandbox_folder(runtime) _create_test_file(temp_dir) @@ -376,9 +375,9 @@ def test_copy_to_non_existent_directory(temp_dir, runtime_cls): def test_overwrite_existing_file(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: - sandbox_dir = '/workspace' + sandbox_dir = config.workspace_mount_path_in_sandbox obs = _run_cmd_action(runtime, f'ls -alh {sandbox_dir}') assert obs.exit_code == 0 @@ -404,7 +403,7 @@ def test_overwrite_existing_file(temp_dir, runtime_cls): def test_copy_non_existent_file(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) try: sandbox_dir = _get_sandbox_folder(runtime) with pytest.raises(FileNotFoundError): @@ -420,8 +419,8 @@ def test_copy_non_existent_file(temp_dir, runtime_cls): def test_copy_from_directory(temp_dir, runtime_cls): - runtime: Runtime = _load_runtime(temp_dir, runtime_cls) - sandbox_dir = _get_sandbox_folder(runtime) + runtime, config = _load_runtime(temp_dir, runtime_cls) + sandbox_dir = config.workspace_mount_path_in_sandbox try: temp_dir_copy = os.path.join(temp_dir, 'test_dir') # We need a separate directory, since temp_dir is mounted to /workspace @@ -445,7 +444,7 @@ def test_git_operation(runtime_cls): # do not mount workspace, since workspace mount by tests will be owned by root # while the user_id we get via os.getuid() is different from root # which causes permission issues - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir=None, use_workspace=False, runtime_cls=runtime_cls, @@ -453,7 +452,7 @@ def test_git_operation(runtime_cls): run_as_openhands=True, ) # this will happen if permission of runtime is not properly configured - # fatal: detected dubious ownership in repository at '/workspace' + # fatal: detected dubious ownership in repository at config.workspace_mount_path_in_sandbox try: obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') assert obs.exit_code == 0 @@ -500,7 +499,7 @@ def test_git_operation(runtime_cls): def test_python_version(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: obs = runtime.run_action(CmdRunAction(command='python --version')) @@ -514,7 +513,7 @@ def test_python_version(temp_dir, runtime_cls, run_as_openhands): def test_pwd_property(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Create a subdirectory and verify pwd updates obs = _run_cmd_action(runtime, 'mkdir -p random_dir') @@ -528,7 +527,7 @@ def test_pwd_property(temp_dir, runtime_cls, run_as_openhands): def test_basic_command(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test simple command obs = _run_cmd_action(runtime, "echo 'hello world'") @@ -556,7 +555,7 @@ def test_basic_command(temp_dir, runtime_cls, run_as_openhands): def test_interactive_command(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, run_as_openhands, @@ -592,7 +591,7 @@ def test_interactive_command(temp_dir, runtime_cls, run_as_openhands): def test_long_output(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Generate a long output action = CmdRunAction('for i in $(seq 1 5000); do echo "Line $i"; done') @@ -606,7 +605,7 @@ def test_long_output(temp_dir, runtime_cls, run_as_openhands): def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Generate a long output action = CmdRunAction('for i in $(seq 1 50000); do echo "Line $i"; done') @@ -622,7 +621,7 @@ def test_long_output_exceed_history_limit(temp_dir, runtime_cls, run_as_openhand def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Create nested directories with many files setup_cmd = 'mkdir -p /tmp/test_dir && cd /tmp/test_dir && for i in $(seq 1 100); do mkdir -p "folder_$i"; for j in $(seq 1 100); do touch "folder_$i/file_$j.txt"; done; done' @@ -647,7 +646,7 @@ def test_long_output_from_nested_directories(temp_dir, runtime_cls, run_as_openh def test_command_backslash(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Create a file with the content "implemented_function" action = CmdRunAction( @@ -674,7 +673,7 @@ def test_command_backslash(temp_dir, runtime_cls, run_as_openhands): def test_command_output_continuation(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Start a command that produces output slowly action = CmdRunAction('for i in {1..5}; do echo $i; sleep 3; done') @@ -714,7 +713,7 @@ def test_command_output_continuation(temp_dir, runtime_cls, run_as_openhands): def test_long_running_command_follow_by_execute( temp_dir, runtime_cls, run_as_openhands ): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test command that produces output slowly action = CmdRunAction('for i in {1..3}; do echo $i; sleep 3; done') @@ -758,7 +757,7 @@ def test_long_running_command_follow_by_execute( def test_empty_command_errors(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test empty command without previous command obs = runtime.run_action(CmdRunAction('')) @@ -769,7 +768,7 @@ def test_empty_command_errors(temp_dir, runtime_cls, run_as_openhands): def test_python_interactive_input(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test Python program that asks for input - properly escaped for bash python_script = """name = input('Enter your name: '); age = input('Enter your age: '); print(f'Hello {name}, you are {age} years old')""" @@ -799,7 +798,7 @@ def test_python_interactive_input(temp_dir, runtime_cls, run_as_openhands): def test_stress_long_output_with_soft_and_hard_timeout( temp_dir, runtime_cls, run_as_openhands ): - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, run_as_openhands, diff --git a/tests/runtime/test_browsergym_envs.py b/tests/runtime/test_browsergym_envs.py index 426ecacaf54c..c3806e484ecc 100644 --- a/tests/runtime/test_browsergym_envs.py +++ b/tests/runtime/test_browsergym_envs.py @@ -29,7 +29,7 @@ def has_miniwob(): reason='Requires browsergym-miniwob package to be installed', ) def test_browsergym_eval_env(runtime_cls, temp_dir): - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls=runtime_cls, run_as_openhands=False, # need root permission to access file diff --git a/tests/runtime/test_browsing.py b/tests/runtime/test_browsing.py index 0dee3750953f..e11c2d0c6a16 100644 --- a/tests/runtime/test_browsing.py +++ b/tests/runtime/test_browsing.py @@ -21,7 +21,7 @@ def test_simple_browse(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test browse action_cmd = CmdRunAction( diff --git a/tests/runtime/test_edit.py b/tests/runtime/test_edit.py index c507166a840d..7039259596c8 100644 --- a/tests/runtime/test_edit.py +++ b/tests/runtime/test_edit.py @@ -28,7 +28,7 @@ def index(): reason='This test requires LLM to run.', ) def test_edit_from_scratch(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = FileEditAction( content=ORGINAL, @@ -68,7 +68,7 @@ def index(): reason='This test requires LLM to run.', ) def test_edit(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = FileEditAction( content=ORGINAL, @@ -127,7 +127,7 @@ def test_edit(temp_dir, runtime_cls, run_as_openhands): reason='This test requires LLM to run.', ) def test_edit_long_file(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: action = FileEditAction( content=ORIGINAL_LONG, diff --git a/tests/runtime/test_env_vars.py b/tests/runtime/test_env_vars.py index 898003ff66c7..3d33af67b1a1 100644 --- a/tests/runtime/test_env_vars.py +++ b/tests/runtime/test_env_vars.py @@ -15,7 +15,7 @@ def test_env_vars_os_environ(temp_dir, runtime_cls, run_as_openhands): with patch.dict(os.environ, {'SANDBOX_ENV_FOOBAR': 'BAZ'}): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) obs: CmdOutputObservation = runtime.run_action(CmdRunAction(command='env')) print(obs) @@ -33,7 +33,7 @@ def test_env_vars_os_environ(temp_dir, runtime_cls, run_as_openhands): def test_env_vars_runtime_operations(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) # Test adding single env var runtime.add_env_vars({'QUUX': 'abc"def'}) @@ -68,7 +68,7 @@ def test_env_vars_runtime_operations(temp_dir, runtime_cls): def test_env_vars_added_by_config(temp_dir, runtime_cls): - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, runtime_startup_env_vars={'ADDED_ENV_VAR': 'added_value'}, diff --git a/tests/runtime/test_images.py b/tests/runtime/test_images.py index b7ab82b54b3c..130ea1159336 100644 --- a/tests/runtime/test_images.py +++ b/tests/runtime/test_images.py @@ -18,7 +18,7 @@ def test_bash_python_version(temp_dir, runtime_cls, base_container_image): ]: pytest.skip('This test is only for python-related images') - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, base_container_image=base_container_image ) @@ -52,7 +52,7 @@ def test_nodejs_22_version(temp_dir, runtime_cls, base_container_image): ]: pytest.skip('This test is only for nodejs-related images') - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, base_container_image=base_container_image ) @@ -73,7 +73,7 @@ def test_go_version(temp_dir, runtime_cls, base_container_image): ]: pytest.skip('This test is only for go-related images') - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, base_container_image=base_container_image ) diff --git a/tests/runtime/test_ipython.py b/tests/runtime/test_ipython.py index 84cfdc1bb5b2..ea0db4ac88b5 100644 --- a/tests/runtime/test_ipython.py +++ b/tests/runtime/test_ipython.py @@ -30,7 +30,7 @@ def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test run command action_cmd = CmdRunAction(command='ls -l') @@ -102,7 +102,7 @@ def test_simple_cmd_ipython_and_fileop(temp_dir, runtime_cls, run_as_openhands): reason='This test is not working in WSL (file ownership)', ) def test_ipython_multi_user(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test run ipython # get username @@ -174,7 +174,7 @@ def test_ipython_multi_user(temp_dir, runtime_cls, run_as_openhands): def test_ipython_simple(temp_dir, runtime_cls): - runtime = _load_runtime(temp_dir, runtime_cls) + runtime, config = _load_runtime(temp_dir, runtime_cls) # Test run ipython # get username @@ -198,7 +198,7 @@ def test_ipython_simple(temp_dir, runtime_cls): def test_ipython_package_install(temp_dir, runtime_cls, run_as_openhands): """Make sure that cd in bash also update the current working directory in ipython.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # It should error out since pymsgbox is not installed action = IPythonRunCellAction(code='import pymsgbox') @@ -233,7 +233,7 @@ def test_ipython_package_install(temp_dir, runtime_cls, run_as_openhands): def test_ipython_file_editor_permissions_as_openhands(temp_dir, runtime_cls): """Test file editor permission behavior when running as different users.""" - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands=True) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands=True) # Create a file owned by root with restricted permissions action = CmdRunAction( @@ -313,7 +313,7 @@ def test_ipython_file_editor_permissions_as_openhands(temp_dir, runtime_cls): def test_file_read_and_edit_via_oh_aci(runtime_cls, run_as_openhands): - runtime = _load_runtime(None, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(None, runtime_cls, run_as_openhands) sandbox_dir = '/workspace' actions = [ diff --git a/tests/runtime/test_microagent.py b/tests/runtime/test_microagent.py index 6f0305f6fb96..0f14c2e9b870 100644 --- a/tests/runtime/test_microagent.py +++ b/tests/runtime/test_microagent.py @@ -78,7 +78,7 @@ def test_load_microagents_with_trailing_slashes( """Test loading microagents when directory paths have trailing slashes.""" # Create test files _create_test_microagents(temp_dir) - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Load microagents loaded_agents = runtime.get_microagents_from_selected_repo(None) @@ -119,7 +119,7 @@ def test_load_microagents_with_selected_repo(temp_dir, runtime_cls, run_as_openh repo_dir.mkdir(parents=True) _create_test_microagents(str(repo_dir)) - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Load microagents with selected repository loaded_agents = runtime.get_microagents_from_selected_repo( @@ -174,7 +174,7 @@ def test_load_microagents_with_missing_files(temp_dir, runtime_cls, run_as_openh """ (microagents_dir / 'repo.md').write_text(repo_agent) - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Load microagents loaded_agents = runtime.get_microagents_from_selected_repo(None) diff --git a/tests/runtime/test_stress_docker_runtime.py b/tests/runtime/test_stress_docker_runtime.py index 6e8a9d5957e8..b679a0836253 100644 --- a/tests/runtime/test_stress_docker_runtime.py +++ b/tests/runtime/test_stress_docker_runtime.py @@ -7,7 +7,7 @@ def test_stress_docker_runtime(temp_dir, runtime_cls, repeat=1): - runtime = _load_runtime( + runtime, config = _load_runtime( temp_dir, runtime_cls, docker_runtime_kwargs={ From 0957aad2b2b8ac65be72d18f7108dfda66e665ef Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 17 Jan 2025 12:41:01 -0500 Subject: [PATCH 60/90] make localruntime tests not parallel --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 7a4740b581f8..1f74c35dd0a5 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -98,7 +98,7 @@ jobs: TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ - poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --ignore=tests/runtime/test_stress_docker_runtime.py --ignore=tests/runtime/test_images.py + poetry run pytest -n 1 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --ignore=tests/runtime/test_stress_docker_runtime.py --ignore=tests/runtime/test_images.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: From 2cddda5aa6bd7cff743c04eee27d3ea776bdc99d Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 17 Jan 2025 16:11:17 -0500 Subject: [PATCH 61/90] fix sandbox dir --- tests/runtime/conftest.py | 9 --------- tests/runtime/test_bash.py | 9 ++++----- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/tests/runtime/conftest.py b/tests/runtime/conftest.py index 3bec5d58b60f..73b18680a11e 100644 --- a/tests/runtime/conftest.py +++ b/tests/runtime/conftest.py @@ -3,7 +3,6 @@ import shutil import stat import time -from pathlib import Path import pytest from pytest import TempPathFactory @@ -39,13 +38,6 @@ def _get_host_folder(runtime: Runtime) -> str: return runtime.config.workspace_mount_path -def _get_sandbox_folder(runtime: Runtime) -> Path | None: - sid = _get_runtime_sid(runtime) - if sid: - return Path(os.path.join(sandbox_test_folder, sid)) - return None - - def _remove_folder(folder: str) -> bool: success = False if folder and os.path.isdir(folder): @@ -279,6 +271,5 @@ def _load_runtime( __all__ = [ '_load_runtime', '_get_host_folder', - '_get_sandbox_folder', '_remove_folder', ] diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index f1faa6af9b47..41e76366879f 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -7,7 +7,6 @@ import pytest from conftest import ( _close_test_runtime, - _get_sandbox_folder, _load_runtime, ) @@ -302,7 +301,7 @@ def _create_test_file(host_temp_dir): def test_copy_single_file(temp_dir, runtime_cls): runtime, config = _load_runtime(temp_dir, runtime_cls) try: - sandbox_dir = _get_sandbox_folder(runtime) + sandbox_dir = config.workspace_mount_path_in_sandbox sandbox_file = os.path.join(sandbox_dir, 'test_file.txt') _create_test_file(temp_dir) runtime.copy_to(os.path.join(temp_dir, 'test_file.txt'), sandbox_dir) @@ -332,7 +331,7 @@ def _create_host_test_dir_with_files(test_dir): def test_copy_directory_recursively(temp_dir, runtime_cls): runtime, config = _load_runtime(temp_dir, runtime_cls) - sandbox_dir = _get_sandbox_folder(runtime) + sandbox_dir = config.workspace_mount_path_in_sandbox try: temp_dir_copy = os.path.join(temp_dir, 'test_dir') # We need a separate directory, since temp_dir is mounted to /workspace @@ -361,7 +360,7 @@ def test_copy_directory_recursively(temp_dir, runtime_cls): def test_copy_to_non_existent_directory(temp_dir, runtime_cls): runtime, config = _load_runtime(temp_dir, runtime_cls) try: - sandbox_dir = _get_sandbox_folder(runtime) + sandbox_dir = config.workspace_mount_path_in_sandbox _create_test_file(temp_dir) runtime.copy_to( os.path.join(temp_dir, 'test_file.txt'), f'{sandbox_dir}/new_dir' @@ -405,7 +404,7 @@ def test_overwrite_existing_file(temp_dir, runtime_cls): def test_copy_non_existent_file(temp_dir, runtime_cls): runtime, config = _load_runtime(temp_dir, runtime_cls) try: - sandbox_dir = _get_sandbox_folder(runtime) + sandbox_dir = config.workspace_mount_path_in_sandbox with pytest.raises(FileNotFoundError): runtime.copy_to( os.path.join(sandbox_dir, 'non_existent_file.txt'), From 8cb627676404496446c431174c9f491a880df1a4 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 17 Jan 2025 16:11:54 -0500 Subject: [PATCH 62/90] add timeout for retry --- openhands/runtime/impl/local/local_runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 9ca4747ba47b..997ca1df3b97 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -269,7 +269,7 @@ def _find_available_port(self, port_range, max_attempts=5): @tenacity.retry( wait=tenacity.wait_exponential(min=1, max=10), - stop=stop_if_should_exit(), + stop=tenacity.stop_after_attempt(10) | stop_if_should_exit(), before_sleep=lambda retry_state: logger.debug( f'Waiting for server to be ready... (attempt {retry_state.attempt_number})' ), From 627549e18540b64ae8f6b425aca225e1bd0c5c80 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 17 Jan 2025 21:45:17 -0500 Subject: [PATCH 63/90] fix linter --- openhands/runtime/impl/local/local_runtime.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 997ca1df3b97..949526deba6d 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -154,7 +154,6 @@ def __init__( self._app_ports: list[int] = [] self.api_url = f'{self.config.sandbox.local_runtime_url}:{self._host_port}' - self.session = requests.Session() self.status_callback = status_callback self.server_process: Optional[subprocess.Popen[str]] = None self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time From 6e009df8290d2384940e5e5209ea858bcf018aa5 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 17 Jan 2025 21:46:18 -0500 Subject: [PATCH 64/90] fix test --- tests/runtime/test_bash.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index a2c5552e974e..48f1adcfbc93 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -796,7 +796,7 @@ def test_python_interactive_input(temp_dir, runtime_cls, run_as_openhands): def test_python_interactive_input_without_set_input( temp_dir, runtime_cls, run_as_openhands ): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # Test Python program that asks for input - properly escaped for bash python_script = """name = input('Enter your name: '); age = input('Enter your age: '); print(f'Hello {name}, you are {age} years old')""" @@ -923,7 +923,7 @@ def test_stress_long_output_with_soft_and_hard_timeout( def test_bash_remove_prefix(temp_dir, runtime_cls, run_as_openhands): - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: # create a git repo action = CmdRunAction( From 3560a8d3b6dcfe8cb10bb2a01950b0265f3b4a2d Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sat, 18 Jan 2025 12:25:12 -0500 Subject: [PATCH 65/90] fix test browsing for localruntime --- tests/runtime/test_browsing.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/runtime/test_browsing.py b/tests/runtime/test_browsing.py index e11c2d0c6a16..0214e20b2796 100644 --- a/tests/runtime/test_browsing.py +++ b/tests/runtime/test_browsing.py @@ -1,5 +1,7 @@ """Browsing-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" +import os + from conftest import _close_test_runtime, _load_runtime from openhands.core.logger import openhands_logger as logger @@ -17,7 +19,11 @@ # For eval environments, tests need to run with poetry install # ============================================================================================================================ -PY3_FOR_TESTING = '/openhands/micromamba/bin/micromamba run -n openhands python3' +if os.environ.get('POETRY_VIRTUALENVS_PATH') != '': + # For Local Runtime + PY3_FOR_TESTING = 'poetry run python3' +else: + PY3_FOR_TESTING = '/openhands/micromamba/bin/micromamba run -n openhands python3' def test_simple_browse(temp_dir, runtime_cls, run_as_openhands): From c051d71213ff2dfb549a0633894f29fd9c37e5dc Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sun, 19 Jan 2025 21:51:44 -0500 Subject: [PATCH 66/90] tweak local runtime --- tests/runtime/test_browsing.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/tests/runtime/test_browsing.py b/tests/runtime/test_browsing.py index 0214e20b2796..32b7950796d6 100644 --- a/tests/runtime/test_browsing.py +++ b/tests/runtime/test_browsing.py @@ -1,7 +1,5 @@ """Browsing-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" -import os - from conftest import _close_test_runtime, _load_runtime from openhands.core.logger import openhands_logger as logger @@ -19,20 +17,12 @@ # For eval environments, tests need to run with poetry install # ============================================================================================================================ -if os.environ.get('POETRY_VIRTUALENVS_PATH') != '': - # For Local Runtime - PY3_FOR_TESTING = 'poetry run python3' -else: - PY3_FOR_TESTING = '/openhands/micromamba/bin/micromamba run -n openhands python3' - def test_simple_browse(temp_dir, runtime_cls, run_as_openhands): runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) # Test browse - action_cmd = CmdRunAction( - command=f'{PY3_FOR_TESTING} -m http.server 8000 > server.log 2>&1 &' - ) + action_cmd = CmdRunAction(command='python3 -m http.server 8000 > server.log 2>&1 &') logger.info(action_cmd, extra={'msg_type': 'ACTION'}) obs = runtime.run_action(action_cmd) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) From 039f0619ffabacbac62fee9b14eb1c067b4acc39 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Tue, 21 Jan 2025 12:35:22 -0500 Subject: [PATCH 67/90] now try to parallize localruntime tests --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 1f74c35dd0a5..7a4740b581f8 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -98,7 +98,7 @@ jobs: TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ - poetry run pytest -n 1 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --ignore=tests/runtime/test_stress_docker_runtime.py --ignore=tests/runtime/test_images.py + poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --ignore=tests/runtime/test_stress_docker_runtime.py --ignore=tests/runtime/test_images.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: From 83bccd47841e11779810967411097a2f82cc636c Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Tue, 21 Jan 2025 15:09:14 -0500 Subject: [PATCH 68/90] try test bash first --- .github/workflows/py-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 7a4740b581f8..0a80b21deea2 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -98,7 +98,7 @@ jobs: TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ - poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py --ignore=tests/runtime/test_stress_docker_runtime.py --ignore=tests/runtime/test_images.py + poetry run pytest -n 1 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime/test_bash.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: From 89e4d6de23d1303debdbdebaff89622cca92633f Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Tue, 28 Jan 2025 11:56:04 -0500 Subject: [PATCH 69/90] fix some tests for localruntime --- openhands/runtime/impl/local/local_runtime.py | 4 ++++ tests/runtime/test_bash.py | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index 949526deba6d..ef8271c73fae 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -158,6 +158,10 @@ def __init__( self.server_process: Optional[subprocess.Popen[str]] = None self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time + # Update env vars + if self.config.sandbox.runtime_startup_env_vars: + os.environ.update(self.config.sandbox.runtime_startup_env_vars) + # Initialize the action_execution_server super().__init__( config, diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index 48f1adcfbc93..a0cf3a79a634 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -13,6 +13,7 @@ from openhands.core.logger import openhands_logger as logger from openhands.events.action import CmdRunAction from openhands.events.observation import CmdOutputObservation, ErrorObservation +from openhands.runtime.impl.local.local_runtime import LocalRuntime # ============================================================================================================================ # Bash-specific tests @@ -209,7 +210,9 @@ def test_multiline_command_loop(temp_dir, runtime_cls): def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) try: - obs = _run_cmd_action(runtime, 'ls -l /workspace') + obs = _run_cmd_action( + runtime, f'ls -l {config.workspace_mount_path_in_sandbox}' + ) assert obs.exit_code == 0 obs = _run_cmd_action(runtime, 'ls -l') @@ -248,7 +251,9 @@ def test_run_as_user_correct_home_dir(temp_dir, runtime_cls, run_as_openhands): try: obs = _run_cmd_action(runtime, 'cd ~ && pwd') assert obs.exit_code == 0 - if run_as_openhands: + if runtime_cls == LocalRuntime: + assert os.getenv('HOME') in obs.content + elif run_as_openhands: assert '/home/openhands' in obs.content else: assert '/root' in obs.content @@ -278,7 +283,7 @@ def test_stateful_cmd(temp_dir, runtime_cls): obs = _run_cmd_action(runtime, 'pwd') assert obs.exit_code == 0, 'The exit code should be 0.' - assert '/workspace/test' in obs.content + assert f'{config.workspace_mount_path_in_sandbox}/test' in obs.content finally: _close_test_runtime(runtime) @@ -453,8 +458,9 @@ def test_git_operation(runtime_cls): # this will happen if permission of runtime is not properly configured # fatal: detected dubious ownership in repository at config.workspace_mount_path_in_sandbox try: - obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') - assert obs.exit_code == 0 + if runtime_cls != LocalRuntime: + obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') + assert obs.exit_code == 0 # check the ownership of the current directory obs = _run_cmd_action(runtime, 'ls -alh .') @@ -462,6 +468,9 @@ def test_git_operation(runtime_cls): # drwx--S--- 2 openhands root 64 Aug 7 23:32 . # drwxr-xr-x 1 root root 4.0K Aug 7 23:33 .. for line in obs.content.split('\n'): + if runtime_cls == LocalRuntime: + continue # skip these checks + if ' ..' in line: # parent directory should be owned by root assert 'root' in line From aeb0ee5947c791e0667e4891faf488e289889bc3 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sat, 1 Feb 2025 00:32:28 -0500 Subject: [PATCH 70/90] try fix --- tests/runtime/test_bash.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index a0cf3a79a634..e62a9291ace2 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -489,6 +489,14 @@ def test_git_operation(runtime_cls): obs = _run_cmd_action(runtime, 'echo "hello" > test_file.txt') assert obs.exit_code == 0 + if runtime_cls != LocalRuntime: + obs = _run_cmd_action( + runtime, + 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev"', + ) + assert obs.exit_code == 0 + + # git add # git add obs = _run_cmd_action(runtime, 'git add test_file.txt') assert obs.exit_code == 0 From 2056c9b8f1c6398ddbed380d49ec40a5874cbbfb Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sat, 1 Feb 2025 23:06:58 +0100 Subject: [PATCH 71/90] attempt to fix git user/email --- tests/runtime/test_bash.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index e62a9291ace2..39bf4a05b23d 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -6,6 +6,7 @@ import pytest from conftest import ( + TEST_IN_CI, _close_test_runtime, _load_runtime, ) @@ -489,7 +490,7 @@ def test_git_operation(runtime_cls): obs = _run_cmd_action(runtime, 'echo "hello" > test_file.txt') assert obs.exit_code == 0 - if runtime_cls != LocalRuntime: + if runtime_cls != LocalRuntime or TEST_IN_CI: obs = _run_cmd_action( runtime, 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev"', From 9bb155ccbbccc167955ed39d2735d660b7c8b80e Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sat, 1 Feb 2025 23:25:41 +0100 Subject: [PATCH 72/90] fix replay --- tests/runtime/test_replay.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/runtime/test_replay.py b/tests/runtime/test_replay.py index b66ef30a7a7e..f847677efb15 100644 --- a/tests/runtime/test_replay.py +++ b/tests/runtime/test_replay.py @@ -29,9 +29,8 @@ def test_simple_replay(temp_dir, runtime_cls, run_as_openhands): A simple replay test that involves simple terminal operations and edits (creating a simple 2048 game), using the default agent """ - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) - - config = _get_config('basic') + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + config.replay_trajectory_path = './tests/runtime/trajs/basic.json' state: State | None = asyncio.run( run_controller( @@ -55,9 +54,8 @@ def test_replay_wrong_initial_state(temp_dir, runtime_cls, run_as_openhands): look like: the following events would still be replayed even though they are meaningless. """ - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) - - config = _get_config('wrong_initial_state') + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + config.replay_trajectory_path = './tests/runtime/trajs/wrong_initial_state.json' state: State | None = asyncio.run( run_controller( From 363406290baaf87615d8ea31b9a22c12510ae68b Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sun, 2 Feb 2025 00:58:14 +0100 Subject: [PATCH 73/90] fix user assert --- tests/runtime/test_bash.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index 39bf4a05b23d..6356e3928942 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -227,6 +227,8 @@ def test_cmd_run(temp_dir, runtime_cls, run_as_openhands): assert obs.exit_code == 0 if run_as_openhands: assert 'openhands' in obs.content + elif runtime_cls == LocalRuntime: + assert 'root' not in obs.content and 'openhands' not in obs.content else: assert 'root' in obs.content assert 'test' in obs.content @@ -491,6 +493,8 @@ def test_git_operation(runtime_cls): assert obs.exit_code == 0 if runtime_cls != LocalRuntime or TEST_IN_CI: + # set git config author in CI only, not on local machine + logger.info('Setting git config author') obs = _run_cmd_action( runtime, 'git config --file ./.git_config user.name "openhands" && git config --file ./.git_config user.email "openhands@all-hands.dev"', From 7c70d4ba4cea24f35c442ba750b5536c06356193 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sun, 2 Feb 2025 01:01:56 +0100 Subject: [PATCH 74/90] add deps --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 0987711d41d0..e47ad93b5ce5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,8 @@ openhands-aci = "^0.2.0" python-socketio = "^5.11.4" redis = "^5.2.0" sse-starlette = "^2.1.3" +ipywidgets = "^8.1.5" +qtconsole = "^5.6.1" [tool.poetry.group.llama-index.dependencies] llama-index = "*" From b2b8f1542eeecc4417c6026c68e21dcef91f6091 Mon Sep 17 00:00:00 2001 From: Engel Nyst Date: Sun, 2 Feb 2025 01:04:39 +0100 Subject: [PATCH 75/90] poetry lock --- poetry.lock | 543 +++++++++------------------------------------------- 1 file changed, 90 insertions(+), 453 deletions(-) diff --git a/poetry.lock b/poetry.lock index a90442ccd7e2..64b2771cb6c1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -6,7 +6,6 @@ version = "2.4.4" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, @@ -18,7 +17,6 @@ version = "3.11.11" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8"}, {file = "aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5"}, @@ -116,7 +114,6 @@ version = "1.2.1" description = "asyncio rate limiter, a leaky bucket implementation" optional = false python-versions = "<4.0,>=3.8" -groups = ["evaluation", "llama-index"] files = [ {file = "aiolimiter-1.2.1-py3-none-any.whl", hash = "sha256:d3f249e9059a20badcb56b61601a83556133655c11d1eb3dd3e04ff069e5f3c7"}, {file = "aiolimiter-1.2.1.tar.gz", hash = "sha256:e02a37ea1a855d9e832252a105420ad4d15011505512a1a1d814647451b5cca9"}, @@ -128,7 +125,6 @@ version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -143,7 +139,6 @@ version = "5.5.0" description = "Vega-Altair: A declarative statistical visualization library for Python." optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c"}, {file = "altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d"}, @@ -168,7 +163,6 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -180,7 +174,6 @@ version = "0.45.2" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "anthropic-0.45.2-py3-none-any.whl", hash = "sha256:ecd746f7274451dfcb7e1180571ead624c7e1195d1d46cb7c70143d2aedb4d35"}, {file = "anthropic-0.45.2.tar.gz", hash = "sha256:32a18b9ecd12c91b2be4cae6ca2ab46a06937b5aa01b21308d97a6d29794fb5e"}, @@ -206,7 +199,6 @@ version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, @@ -228,8 +220,6 @@ version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = ">=3.6" -groups = ["runtime"] -markers = "platform_system == \"Darwin\"" files = [ {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, @@ -241,7 +231,6 @@ version = "23.1.0" description = "Argon2 for Python" optional = false python-versions = ">=3.7" -groups = ["main", "runtime"] files = [ {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, @@ -262,7 +251,6 @@ version = "21.2.0" description = "Low-level CFFI bindings for Argon2" optional = false python-versions = ">=3.6" -groups = ["main", "runtime"] files = [ {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, @@ -300,7 +288,6 @@ version = "1.3.0" description = "Better dates & times for Python" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, @@ -320,7 +307,6 @@ version = "3.8.1" description = "ASGI specs, helper code, and adapters" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, @@ -335,7 +321,6 @@ version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, @@ -351,7 +336,6 @@ version = "2.0.4" description = "Simple LRU cache for asyncio" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627"}, {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, @@ -363,7 +347,6 @@ version = "24.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, @@ -383,7 +366,6 @@ version = "1.32.0" description = "Microsoft Azure Core Library for Python" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "azure_core-1.32.0-py3-none-any.whl", hash = "sha256:eac191a0efb23bfa83fddf321b27b122b4ec847befa3091fa736a5c32c50d7b4"}, {file = "azure_core-1.32.0.tar.gz", hash = "sha256:22b3c35d6b2dae14990f6c1be2912bf23ffe50b220e708a28ab1bb92b1c730e5"}, @@ -403,7 +385,6 @@ version = "1.19.0" description = "Microsoft Azure Identity Library for Python" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "azure_identity-1.19.0-py3-none-any.whl", hash = "sha256:e3f6558c181692d7509f09de10cca527c7dce426776454fb97df512a46527e81"}, {file = "azure_identity-1.19.0.tar.gz", hash = "sha256:500144dc18197d7019b81501165d4fa92225f03778f17d7ca8a2a180129a9c83"}, @@ -422,7 +403,6 @@ version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, @@ -437,7 +417,6 @@ version = "2.2.1" description = "Function decoration for backoff and retry" optional = false python-versions = ">=3.7,<4.0" -groups = ["llama-index"] files = [ {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, @@ -449,7 +428,6 @@ version = "0.18" description = "Python parser for bash" optional = false python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4" -groups = ["main"] files = [ {file = "bashlex-0.18-py2.py3-none-any.whl", hash = "sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa"}, {file = "bashlex-0.18.tar.gz", hash = "sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee"}, @@ -461,7 +439,6 @@ version = "4.2.1" description = "Modern password hashing for your software and your servers" optional = false python-versions = ">=3.7" -groups = ["llama-index"] files = [ {file = "bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17"}, {file = "bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f"}, @@ -500,7 +477,6 @@ version = "0.12.0" description = "Unbearably fast runtime type checking in pure Python." optional = false python-versions = ">=3.7.0" -groups = ["evaluation"] files = [ {file = "beartype-0.12.0-py3-none-any.whl", hash = "sha256:3d9d5bec198bcf965c000d7b5120bebdd19a444ef6e39e97d0e93eb8832e10c8"}, {file = "beartype-0.12.0.tar.gz", hash = "sha256:3b7545b3f333a6b07042b68b102141554c9add2e979dab7b0f8ed6378f7af7d7"}, @@ -519,7 +495,6 @@ version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, @@ -541,7 +516,6 @@ version = "0.23.1" description = "The bidirectional mapping library for Python." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"}, {file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"}, @@ -553,7 +527,6 @@ version = "6.2.0" description = "An easy safelist-based HTML-sanitizing tool." optional = false python-versions = ">=3.9" -groups = ["runtime"] files = [ {file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"}, {file = "bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"}, @@ -572,7 +545,6 @@ version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, @@ -584,7 +556,6 @@ version = "1.36.9" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "boto3-1.36.9-py3-none-any.whl", hash = "sha256:440d0b70990efb732f63b40fa16c663c86fee80347eb4bf3bcc08b593e8ac77f"}, {file = "boto3-1.36.9.tar.gz", hash = "sha256:035ed3868ff3b9afe05a49d0bde35582315bc438e60b5e76727a00b107567bfb"}, @@ -604,7 +575,6 @@ version = "1.36.9" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "botocore-1.36.9-py3-none-any.whl", hash = "sha256:e31d206c7708300c541d0799df73b576bbe7d8bed011687d96323ed48763ffd2"}, {file = "botocore-1.36.9.tar.gz", hash = "sha256:cb3baefdb8326fdfae0750015e5868330e18d3a088a31da658df2cc8cba7ac73"}, @@ -624,7 +594,6 @@ version = "0.10.2" description = "BrowserGym: a gym environment for web task automation in the Chromium browser" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym-0.10.2-py3-none-any.whl", hash = "sha256:9581d1d1f1fcd1cf35266cf30c881d60c147a0d374b3491eeaebb07d9690f868"}, {file = "browsergym-0.10.2.tar.gz", hash = "sha256:3cdd7520cca857421aa7ec0a965968df4bcef721299a424397f86d7cad078ab0"}, @@ -645,7 +614,6 @@ version = "0.10.2" description = "AssistantBench benchmark for BrowserGym" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym_assistantbench-0.10.2-py3-none-any.whl", hash = "sha256:af0d3a3e23686066b070feca38f8740262bed6d65ccf9098f393334a005987c0"}, {file = "browsergym_assistantbench-0.10.2.tar.gz", hash = "sha256:de18eb7c010403d5d467b927b4713b56f6e97a59493bee4c42599d4d7cb54dce"}, @@ -663,7 +631,6 @@ version = "0.10.2" description = "BrowserGym: a gym environment for web task automation in the Chromium browser" optional = false python-versions = ">3.9" -groups = ["main", "evaluation"] files = [ {file = "browsergym_core-0.10.2-py3-none-any.whl", hash = "sha256:0686a8e2ee7244e33c97326193f54df0ad08d99aad9a4ed9ac28baba5ca26d18"}, {file = "browsergym_core-0.10.2.tar.gz", hash = "sha256:7e93bad5cc3990badee77e9481413d625d2fce2ec8f7f9e195dbc194b6cfb4e9"}, @@ -684,7 +651,6 @@ version = "0.10.2" description = "Experimentation tools for BrowserGym" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym_experiments-0.10.2-py3-none-any.whl", hash = "sha256:60a626b3159ef63b5ff72a6c8156c8f3cf82a9278dfc5a9d3ece39c2b1913595"}, {file = "browsergym_experiments-0.10.2.tar.gz", hash = "sha256:b49bc27f315ad12014ff21580c7c7aca6489ca4106e7ab46502f716674efa236"}, @@ -701,7 +667,6 @@ version = "0.10.2" description = "MiniWoB++ benchmark for BrowserGym" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym_miniwob-0.10.2-py3-none-any.whl", hash = "sha256:b11b04378868a8f5dee34f721134baed4780fd55ccaebf9db4de6fcac48f3190"}, {file = "browsergym_miniwob-0.10.2.tar.gz", hash = "sha256:9109b8122a61b27e227d923861055f220c6ddd60f34f877c3a30444c6f8a7b05"}, @@ -716,7 +681,6 @@ version = "0.10.2" description = "VisualWebArena benchmark for BrowserGym" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym_visualwebarena-0.10.2-py3-none-any.whl", hash = "sha256:87c913ccd4d12a79c625b5c4d9ead7e0bc50b298d19e413204bb586a67736d83"}, {file = "browsergym_visualwebarena-0.10.2.tar.gz", hash = "sha256:5f84a4f33a21106c9b650cecb0362b78af2546d9927255828c273fe800d776a1"}, @@ -733,7 +697,6 @@ version = "0.10.2" description = "WebArena benchmark for BrowserGym" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym_webarena-0.10.2-py3-none-any.whl", hash = "sha256:e9ca6d0ad263412ebb229fe1b66e1ab7f5841a3f838abedf3bf01b800a7c6597"}, {file = "browsergym_webarena-0.10.2.tar.gz", hash = "sha256:b4b9a38f144b6aaa56bbbbce9dd2c5565a39a1b55e3647d61e02458ca3f5fd24"}, @@ -749,7 +712,6 @@ version = "0.4.1" description = "WorkArena benchmark for BrowserGym" optional = false python-versions = ">3.7" -groups = ["evaluation"] files = [ {file = "browsergym_workarena-0.4.1-py3-none-any.whl", hash = "sha256:b8f04b2e3801fd32962b7d99f0685c507b258841e2b4bfdb46d041091d2f1b89"}, {file = "browsergym_workarena-0.4.1.tar.gz", hash = "sha256:ba2958d804b80836c7f81360d66b99c6c655c5070eddc5fae9c1c88306a23403"}, @@ -770,7 +732,6 @@ version = "1.2.2.post1" description = "A simple, correct Python build frontend" optional = false python-versions = ">=3.8" -groups = ["dev", "llama-index"] files = [ {file = "build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5"}, {file = "build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7"}, @@ -794,7 +755,6 @@ version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, @@ -806,7 +766,6 @@ version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, @@ -818,7 +777,6 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" -groups = ["main", "llama-index", "runtime"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -888,7 +846,6 @@ files = [ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] -markers = {llama-index = "platform_python_implementation != \"PyPy\""} [package.dependencies] pycparser = "*" @@ -899,7 +856,6 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" -groups = ["dev", "evaluation"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -911,7 +867,6 @@ version = "5.2.0" description = "Universal encoding detector for Python 3" optional = false python-versions = ">=3.7" -groups = ["evaluation", "test"] files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, @@ -923,7 +878,6 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -1025,7 +979,6 @@ version = "0.7.6" description = "Chromas fork of hnswlib" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "chroma_hnswlib-0.7.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f35192fbbeadc8c0633f0a69c3d3e9f1a4eab3a46b65458bbcbcabdd9e895c36"}, {file = "chroma_hnswlib-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f007b608c96362b8f0c8b6b2ac94f67f83fcbabd857c378ae82007ec92f4d82"}, @@ -1067,7 +1020,6 @@ version = "0.6.3" description = "Chroma." optional = false python-versions = ">=3.9" -groups = ["llama-index"] files = [ {file = "chromadb-0.6.3-py3-none-any.whl", hash = "sha256:4851258489a3612b558488d98d09ae0fe0a28d5cad6bd1ba64b96fdc419dc0e5"}, {file = "chromadb-0.6.3.tar.gz", hash = "sha256:c8f34c0b704b9108b04491480a36d42e894a960429f87c6516027b5481d59ed3"}, @@ -1109,7 +1061,6 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -1124,7 +1075,6 @@ version = "3.1.0" description = "Pickler class to extend the standard pickle.Pickler functionality" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "cloudpickle-3.1.0-py3-none-any.whl", hash = "sha256:fe11acda67f61aaaec473e3afe030feb131d78a43461b718185363384f1ba12e"}, {file = "cloudpickle-3.1.0.tar.gz", hash = "sha256:81a929b6e3c7335c863c771d673d105f02efdb89dfaba0c90495d1c64796601b"}, @@ -1136,12 +1086,10 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\"", dev = "os_name == \"nt\"", evaluation = "platform_system == \"Windows\" or sys_platform == \"win32\"", llama-index = "platform_system == \"Windows\" or os_name == \"nt\" or sys_platform == \"win32\"", runtime = "sys_platform == \"win32\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "coloredlogs" @@ -1149,7 +1097,6 @@ version = "15.0.1" description = "Colored terminal output for Python's logging module" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["llama-index"] files = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, @@ -1167,7 +1114,6 @@ version = "0.2.2" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, @@ -1185,7 +1131,6 @@ version = "0.1.8" description = "A development and evaluation framework for using language models to generate libraries." optional = false python-versions = ">=3.10" -groups = ["evaluation"] files = [ {file = "commit0-0.1.8-py3-none-any.whl", hash = "sha256:b5369d7c814d2f2b82f39da60849f7b8519f277a05c5ed91a591b800c4b4d431"}, {file = "commit0-0.1.8.tar.gz", hash = "sha256:fd6402381df7daf29f3d5d423c1e1d7b26e8e9e9b506d5dfa7a3f0e724ec87fd"}, @@ -1213,7 +1158,6 @@ version = "1.3.1" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.10" -groups = ["main", "evaluation"] files = [ {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, @@ -1287,7 +1231,6 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" -groups = ["test"] files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -1362,7 +1305,6 @@ version = "44.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" -groups = ["main", "llama-index"] files = [ {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"}, @@ -1370,6 +1312,7 @@ files = [ {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, @@ -1380,6 +1323,7 @@ files = [ {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, @@ -1412,7 +1356,6 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -1428,7 +1371,6 @@ version = "0.6.7" description = "Easily serialize dataclasses to and from JSON." optional = false python-versions = "<4.0,>=3.7" -groups = ["evaluation", "llama-index"] files = [ {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, @@ -1444,7 +1386,6 @@ version = "3.0.1" description = "HuggingFace community-driven open-source library of datasets" optional = false python-versions = ">=3.8.0" -groups = ["main", "evaluation"] files = [ {file = "datasets-3.0.1-py3-none-any.whl", hash = "sha256:db080aab41c8cc68645117a0f172e5c6789cbc672f066de0aa5a08fc3eebc686"}, {file = "datasets-3.0.1.tar.gz", hash = "sha256:40d63b09e76a3066c32e746d6fdc36fd3f29ed2acd49bf5b1a2100da32936511"}, @@ -1487,7 +1428,6 @@ version = "1.8.11" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd"}, {file = "debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f"}, @@ -1523,7 +1463,6 @@ version = "5.1.1" description = "Decorators for Humans" optional = false python-versions = ">=3.5" -groups = ["evaluation", "runtime"] files = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, @@ -1535,7 +1474,6 @@ version = "0.7.1" description = "XML bomb protection for Python stdlib modules" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["runtime"] files = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, @@ -1547,7 +1485,6 @@ version = "1.2.15" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["main", "llama-index"] files = [ {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, @@ -1565,7 +1502,6 @@ version = "0.3.8" description = "serialize all of Python" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, @@ -1581,7 +1517,6 @@ version = "0.5.0" description = "Python module and CLI for hashing of file system directories." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "dirhash-0.5.0-py3-none-any.whl", hash = "sha256:523dfd6b058c64f45b31604376926c6e2bd2ea301d0df23095d4055674e38b09"}, {file = "dirhash-0.5.0.tar.gz", hash = "sha256:e60760f0ab2e935d8cb088923ea2c6492398dca42cec785df778985fd4cd5386"}, @@ -1596,7 +1531,6 @@ version = "1.0.8" description = "JSON decoder for Python that can extract data from the muck" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53"}, {file = "dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd"}, @@ -1608,7 +1542,6 @@ version = "5.6.3" description = "Disk Cache -- Disk and file backed persistent cache." optional = false python-versions = ">=3" -groups = ["main"] files = [ {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, @@ -1620,7 +1553,6 @@ version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" -groups = ["dev", "evaluation"] files = [ {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, @@ -1632,7 +1564,6 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -1644,7 +1575,6 @@ version = "7.1.0" description = "A Python library for the Docker Engine API." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, @@ -1667,7 +1597,6 @@ version = "0.16" description = "Parse Python docstrings in reST, Google and Numpydoc format" optional = false python-versions = ">=3.6,<4.0" -groups = ["main"] files = [ {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, @@ -1679,7 +1608,6 @@ version = "0.9" description = "Module for converting between datetime.timedelta and Go's Duration strings." optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38"}, {file = "durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a"}, @@ -1691,7 +1619,6 @@ version = "1.0.6" description = "E2B SDK that give agents cloud environments" optional = false python-versions = "<4.0,>=3.8" -groups = ["main"] files = [ {file = "e2b-1.0.6-py3-none-any.whl", hash = "sha256:4ae6e00d46e6b0b9ab05388c408f9155488ee9f022c5a6fd47939f492ccf3b58"}, {file = "e2b-1.0.6.tar.gz", hash = "sha256:e35d47f5581565060a5c18e4cb839cf61de310d275fa0a6589d8fc8bf65957a7"}, @@ -1712,7 +1639,6 @@ version = "2.0.1" description = "Generate sets of english words by combining different word lists" optional = false python-versions = "*" -groups = ["evaluation"] files = [ {file = "english-words-2.0.1.tar.gz", hash = "sha256:a4105c57493bb757a3d8973fcf8e1dc05e7ca09c836dff467c3fb445f84bc43d"}, ] @@ -1723,7 +1649,6 @@ version = "0.4.3" description = "HuggingFace community-driven open-source library of evaluation" optional = false python-versions = ">=3.8.0" -groups = ["evaluation"] files = [ {file = "evaluate-0.4.3-py3-none-any.whl", hash = "sha256:47d8770bdea76e2c2ed0d40189273027d1a41ccea861bcc7ba12d30ec5d1e517"}, {file = "evaluate-0.4.3.tar.gz", hash = "sha256:3a5700cf83aabee9549264e1e5666f116367c61dbd4d38352015e859a5e2098d"}, @@ -1759,7 +1684,6 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" -groups = ["test"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -1774,7 +1698,6 @@ version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, @@ -1789,7 +1712,6 @@ version = "33.1.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "Faker-33.1.0-py3-none-any.whl", hash = "sha256:d30c5f0e2796b8970de68978365247657486eb0311c5abe88d0b895b68dff05d"}, {file = "faker-33.1.0.tar.gz", hash = "sha256:1c925fc0e86a51fc46648b504078c88d0cd48da1da2595c4e712841cab43a1e4"}, @@ -1805,7 +1727,6 @@ version = "0.0.4" description = "Notifications for all Farama Foundation maintained libraries." optional = false python-versions = "*" -groups = ["main", "evaluation"] files = [ {file = "Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18"}, {file = "Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae"}, @@ -1817,7 +1738,6 @@ version = "0.115.8" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"}, {file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"}, @@ -1838,7 +1758,6 @@ version = "1.7.28" description = "Python supercharged for fastai development" optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "fastcore-1.7.28-py3-none-any.whl", hash = "sha256:ffa1ab1b34518795a4342b85ebb9cd2b30588210c21df028a11e420678a59e20"}, {file = "fastcore-1.7.28.tar.gz", hash = "sha256:606e4507eb4b8892e4c83ddf5462fbcf32f4bde4fa6caf56ca67ee5e2dbe2b1e"}, @@ -1856,7 +1775,6 @@ version = "2.21.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" -groups = ["runtime"] files = [ {file = "fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"}, {file = "fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4"}, @@ -1871,7 +1789,6 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" -groups = ["main", "dev", "evaluation", "llama-index"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -1888,7 +1805,6 @@ version = "1.2.0" description = "Infer file type and MIME type of any file/buffer. No external dependencies." optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, @@ -1900,7 +1816,6 @@ version = "7.1.1" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" -groups = ["main", "runtime", "test"] files = [ {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, @@ -1917,7 +1832,6 @@ version = "3.1.0" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, @@ -1940,7 +1854,6 @@ version = "24.12.23" description = "The FlatBuffers serialization format for Python" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "flatbuffers-24.12.23-py2.py3-none-any.whl", hash = "sha256:c418e0d48890f4142b92fd3e343e73a48f194e1f80075ddcc5793779b3585444"}, {file = "flatbuffers-24.12.23.tar.gz", hash = "sha256:2910b0bc6ae9b6db78dd2b18d0b7a0709ba240fb5585f286a3a2b30785c22dac"}, @@ -1952,7 +1865,6 @@ version = "4.55.3" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0"}, {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f"}, @@ -2026,7 +1938,6 @@ version = "1.5.1" description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" optional = false python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" -groups = ["runtime"] files = [ {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, @@ -2038,7 +1949,6 @@ version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -2140,7 +2050,6 @@ version = "2024.6.1" description = "File-system specification" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, @@ -2183,7 +2092,6 @@ version = "4.3.5" description = "Python module which allows you to specify timeouts when calling any existing function. Also provides support for stoppable-threads" optional = false python-versions = "*" -groups = ["evaluation"] files = [ {file = "func_timeout-4.3.5.tar.gz", hash = "sha256:74cd3c428ec94f4edfba81f9b2f14904846d5ffccc27c92433b8b5939b5575dd"}, ] @@ -2194,7 +2102,6 @@ version = "5.2.0" description = "Google Drive Public File/Folder Downloader" optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "gdown-5.2.0-py3-none-any.whl", hash = "sha256:33083832d82b1101bdd0e9df3edd0fbc0e1c5f14c9d8c38d2a35bf1683b526d6"}, {file = "gdown-5.2.0.tar.gz", hash = "sha256:2145165062d85520a3cd98b356c9ed522c5e7984d408535409fd46f94defc787"}, @@ -2215,7 +2122,6 @@ version = "24.2.1" description = "Coroutine-based network library" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07"}, {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3"}, @@ -2279,7 +2185,6 @@ version = "1.0.6" description = "A python client for the GitHub API" optional = false python-versions = ">=3.7" -groups = ["evaluation"] files = [ {file = "ghapi-1.0.6-py3-none-any.whl", hash = "sha256:b3d96bf18fcaa2cb7131bad9de2948e2a1c2bb226377a25826f6c80950c57854"}, {file = "ghapi-1.0.6.tar.gz", hash = "sha256:64fdd9f06d8e3373065c42c2a03e067e2bbb9ca18b583cd6e38a28aaad0224f6"}, @@ -2298,7 +2203,6 @@ version = "4.0.12" description = "Git Object Database" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation"] files = [ {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"}, {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"}, @@ -2313,7 +2217,6 @@ version = "3.1.44" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation"] files = [ {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, {file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"}, @@ -2332,7 +2235,6 @@ version = "0.6.15" description = "Google Ai Generativelanguage API client library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c"}, {file = "google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3"}, @@ -2353,7 +2255,6 @@ version = "2.24.0" description = "Google API client core library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9"}, {file = "google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf"}, @@ -2383,7 +2284,6 @@ version = "2.160.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_api_python_client-2.160.0-py2.py3-none-any.whl", hash = "sha256:63d61fb3e4cf3fb31a70a87f45567c22f6dfe87bbfa27252317e3e2c42900db4"}, {file = "google_api_python_client-2.160.0.tar.gz", hash = "sha256:a8ccafaecfa42d15d5b5c3134ced8de08380019717fc9fb1ed510ca58eca3b7e"}, @@ -2402,7 +2302,6 @@ version = "2.37.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" -groups = ["main", "llama-index"] files = [ {file = "google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0"}, {file = "google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00"}, @@ -2427,7 +2326,6 @@ version = "0.2.0" description = "Google Authentication Library: httplib2 transport" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"}, {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"}, @@ -2443,7 +2341,6 @@ version = "1.2.1" description = "Google Authentication Library" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "google_auth_oauthlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:2d58a27262d55aa1b87678c3ba7142a080098cbc2024f903c62355deb235d91f"}, {file = "google_auth_oauthlib-1.2.1.tar.gz", hash = "sha256:afd0cad092a2eaa53cd8e8298557d6de1034c6cb4a740500b5357b648af97263"}, @@ -2462,7 +2359,6 @@ version = "1.79.0" description = "Vertex AI API client library" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "google_cloud_aiplatform-1.79.0-py2.py3-none-any.whl", hash = "sha256:e52d518c386ce2b4ce57f1b73b46c57531d9a6ccd70c21a37b349f428bfc1c3f"}, {file = "google_cloud_aiplatform-1.79.0.tar.gz", hash = "sha256:362bfd16716dcfb6c131736f25246790002b29c99a246fcf4c08a7c71bd2301f"}, @@ -2511,7 +2407,6 @@ version = "3.27.0" description = "Google BigQuery API client library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_cloud_bigquery-3.27.0-py2.py3-none-any.whl", hash = "sha256:b53b0431e5ba362976a4cd8acce72194b4116cdf8115030c7b339b884603fcc3"}, {file = "google_cloud_bigquery-3.27.0.tar.gz", hash = "sha256:379c524054d7b090fa56d0c22662cc6e6458a6229b6754c0e7177e3a73421d2c"}, @@ -2543,7 +2438,6 @@ version = "2.4.1" description = "Google Cloud API client core library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073"}, {file = "google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61"}, @@ -2562,7 +2456,6 @@ version = "1.14.0" description = "Google Cloud Resource Manager API client library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_cloud_resource_manager-1.14.0-py2.py3-none-any.whl", hash = "sha256:4860c3ea9ace760b317ea90d4e27f1b32e54ededdcc340a7cb70c8ef238d8f7c"}, {file = "google_cloud_resource_manager-1.14.0.tar.gz", hash = "sha256:daa70a3a4704759d31f812ed221e3b6f7b660af30c7862e4a0060ea91291db30"}, @@ -2584,7 +2477,6 @@ version = "2.19.0" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba"}, {file = "google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2"}, @@ -2608,7 +2500,6 @@ version = "1.6.0" description = "A python wrapper of the C library 'Google CRC32C'" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa"}, {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9"}, @@ -2648,7 +2539,6 @@ version = "0.8.4" description = "Google Generative AI High level API client library and tools." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "google_generativeai-0.8.4-py3-none-any.whl", hash = "sha256:e987b33ea6decde1e69191ddcaec6ef974458864d243de7191db50c21a7c5b82"}, ] @@ -2672,7 +2562,6 @@ version = "2.7.2" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa"}, {file = "google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0"}, @@ -2691,7 +2580,6 @@ version = "1.66.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" -groups = ["main", "llama-index"] files = [ {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, @@ -2710,7 +2598,6 @@ version = "3.0.0" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"}, {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"}, @@ -2775,7 +2662,6 @@ files = [ {file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"}, {file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"}, ] -markers = {llama-index = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} [package.extras] docs = ["Sphinx"] @@ -2787,7 +2673,6 @@ version = "0.3.3" description = "A tool to grep through the AST of a source file" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "grep_ast-0.3.3-py3-none-any.whl", hash = "sha256:515cb889bffefefa26c4ab1377b9a75b3fc678aa5fa02bf9aa4f8f20999a83ad"}, {file = "grep_ast-0.3.3.tar.gz", hash = "sha256:42b8887d57301dc55634368f8d549e9c49c913dafb4d19c9b54c3ddb604fccf4"}, @@ -2803,7 +2688,6 @@ version = "0.14.0" description = "IAM API client library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4"}, {file = "grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99"}, @@ -2820,7 +2704,6 @@ version = "1.69.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"}, {file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"}, @@ -2888,7 +2771,6 @@ version = "1.62.3" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "grpcio-status-1.62.3.tar.gz", hash = "sha256:289bdd7b2459794a12cf95dc0cb727bd4a1742c37bd823f760236c937e53a485"}, {file = "grpcio_status-1.62.3-py3-none-any.whl", hash = "sha256:f9049b762ba8de6b1086789d8315846e094edac2c50beaf462338b301a8fd4b8"}, @@ -2905,7 +2787,6 @@ version = "0.4.7" description = "Pure-Python gRPC implementation for asyncio" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation"] files = [ {file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"}, ] @@ -2923,7 +2804,6 @@ version = "1.0.0" description = "A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "gymnasium-1.0.0-py3-none-any.whl", hash = "sha256:b6f40e1e24c5bd419361e1a5b86a9117d2499baecc3a660d44dfff4c465393ad"}, {file = "gymnasium-1.0.0.tar.gz", hash = "sha256:9d2b66f30c1b34fe3c2ce7fae65ecf365d0e9982d2b3d860235e773328a3b403"}, @@ -2954,7 +2834,6 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -2966,7 +2845,6 @@ version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" optional = false python-versions = ">=3.6.1" -groups = ["main", "evaluation"] files = [ {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, @@ -2982,7 +2860,6 @@ version = "4.0.0" description = "Pure-Python HPACK header compression" optional = false python-versions = ">=3.6.1" -groups = ["main", "evaluation"] files = [ {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, @@ -2994,7 +2871,6 @@ version = "2024.2.26" description = "Turn HTML into equivalent Markdown-structured text." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "html2text-2024.2.26.tar.gz", hash = "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32"}, ] @@ -3005,7 +2881,6 @@ version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, @@ -3027,7 +2902,6 @@ version = "0.22.0" description = "A comprehensive HTTP client library." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] files = [ {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, @@ -3042,7 +2916,6 @@ version = "0.6.4" description = "A collection of framework independent HTTP protocol utils." optional = false python-versions = ">=3.8.0" -groups = ["llama-index"] files = [ {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, @@ -3098,7 +2971,6 @@ version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, @@ -3124,7 +2996,6 @@ version = "0.27.1" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "huggingface_hub-0.27.1-py3-none-any.whl", hash = "sha256:1c5155ca7d60b60c2e2fc38cbb3ffb7f7c3adf48f824015b219af9061771daec"}, {file = "huggingface_hub-0.27.1.tar.gz", hash = "sha256:c004463ca870283909d715d20f066ebd6968c2207dae9393fdffb3c1d4d8f98b"}, @@ -3160,7 +3031,6 @@ version = "10.0" description = "Human friendly output for text interfaces using Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["llama-index"] files = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, @@ -3175,7 +3045,6 @@ version = "6.0.1" description = "HTTP/2 framing layer for Python" optional = false python-versions = ">=3.6.1" -groups = ["main", "evaluation"] files = [ {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, @@ -3187,7 +3056,6 @@ version = "2.6.5" description = "File identification library for Python" optional = false python-versions = ">=3.9" -groups = ["dev", "evaluation"] files = [ {file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"}, {file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"}, @@ -3202,7 +3070,6 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -3217,7 +3084,6 @@ version = "2.36.1" description = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "imageio-2.36.1-py3-none-any.whl", hash = "sha256:20abd2cae58e55ca1af8a8dcf43293336a59adf0391f1917bf8518633cfc2cdf"}, {file = "imageio-2.36.1.tar.gz", hash = "sha256:e4e1d231f47f9a9e16100b0f7ce1a86e8856fb4d1c0fa2c4365a316f1746be62"}, @@ -3251,7 +3117,6 @@ version = "7.1.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, @@ -3271,7 +3136,6 @@ version = "6.5.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.9" -groups = ["llama-index"] files = [ {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, @@ -3291,7 +3155,6 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" -groups = ["evaluation", "test"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -3303,7 +3166,6 @@ version = "6.29.5" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5"}, {file = "ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"}, @@ -3337,7 +3199,6 @@ version = "8.31.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" -groups = ["runtime"] files = [ {file = "ipython-8.31.0-py3-none-any.whl", hash = "sha256:46ec58f8d3d076a61d128fe517a51eb730e3aaf0c184ea8c17d16e366660c6a6"}, {file = "ipython-8.31.0.tar.gz", hash = "sha256:b6a2274606bec6166405ff05e54932ed6e5cfecaca1fc05f2cacde7bb074d70b"}, @@ -3368,13 +3229,33 @@ qtconsole = ["qtconsole"] test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] +[[package]] +name = "ipywidgets" +version = "8.1.5" +description = "Jupyter interactive widgets" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ipywidgets-8.1.5-py3-none-any.whl", hash = "sha256:3290f526f87ae6e77655555baba4f36681c555b8bdbbff430b70e52c34c86245"}, + {file = "ipywidgets-8.1.5.tar.gz", hash = "sha256:870e43b1a35656a80c18c9503bbf2d16802db1cb487eec6fab27d683381dde17"}, +] + +[package.dependencies] +comm = ">=0.1.3" +ipython = ">=6.1.0" +jupyterlab-widgets = ">=3.0.12,<3.1.0" +traitlets = ">=4.3.1" +widgetsnbextension = ">=4.0.12,<4.1.0" + +[package.extras] +test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] + [[package]] name = "isoduration" version = "20.11.0" description = "Operations with ISO 8601 durations" optional = false python-versions = ">=3.7" -groups = ["runtime"] files = [ {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, @@ -3389,7 +3270,6 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -3401,7 +3281,6 @@ version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" -groups = ["runtime"] files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, @@ -3421,7 +3300,6 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -3439,7 +3317,6 @@ version = "0.8.2" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, @@ -3525,7 +3402,6 @@ version = "1.0.1" description = "JSON Matching Expressions" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, @@ -3537,7 +3413,6 @@ version = "1.4.2" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, @@ -3549,7 +3424,6 @@ version = "0.35.0" description = "A package to repair broken json strings" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "json_repair-0.35.0-py3-none-any.whl", hash = "sha256:1d429407158474d28a996e745b8f8f7dc78957cb2cfbc92120b9f580b5230a9e"}, {file = "json_repair-0.35.0.tar.gz", hash = "sha256:e70f834865a4ae5fe64352c23c1c16d3b70c5dd62dc544a169d8b0932bdbdcaa"}, @@ -3561,7 +3435,6 @@ version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8.0" -groups = ["runtime"] files = [ {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, @@ -3576,7 +3449,6 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false python-versions = ">=3.7" -groups = ["runtime"] files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -3588,7 +3460,6 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "runtime"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -3618,7 +3489,6 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "runtime"] files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -3633,7 +3503,6 @@ version = "8.6.3" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, @@ -3656,7 +3525,6 @@ version = "5.7.2" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, @@ -3677,7 +3545,6 @@ version = "0.11.0" description = "Jupyter Event System library" optional = false python-versions = ">=3.9" -groups = ["runtime"] files = [ {file = "jupyter_events-0.11.0-py3-none-any.whl", hash = "sha256:36399b41ce1ca45fe8b8271067d6a140ffa54cec4028e95491c93b78a855cacf"}, {file = "jupyter_events-0.11.0.tar.gz", hash = "sha256:c0bc56a37aac29c1fbc3bcfbddb8c8c49533f9cf11f1c4e6adadba936574ab90"}, @@ -3703,7 +3570,6 @@ version = "3.0.1" description = "A web server for spawning and communicating with Jupyter kernels" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyter_kernel_gateway-3.0.1-py3-none-any.whl", hash = "sha256:9f74a2f4ff9f03737bcab79f44ae0f6473ee32deb30fce00b8f05adcdd023f03"}, {file = "jupyter_kernel_gateway-3.0.1.tar.gz", hash = "sha256:900690c4c0e796867355468d685f7fa1cf3c7775d08e871c157f77d65fbd6d7f"}, @@ -3727,7 +3593,6 @@ version = "2.2.5" description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyter-lsp-2.2.5.tar.gz", hash = "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001"}, {file = "jupyter_lsp-2.2.5-py3-none-any.whl", hash = "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da"}, @@ -3742,7 +3607,6 @@ version = "2.15.0" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." optional = false python-versions = ">=3.9" -groups = ["runtime"] files = [ {file = "jupyter_server-2.15.0-py3-none-any.whl", hash = "sha256:872d989becf83517012ee669f09604aa4a28097c0bd90b2f424310156c2cdae3"}, {file = "jupyter_server-2.15.0.tar.gz", hash = "sha256:9d446b8697b4f7337a1b7cdcac40778babdd93ba614b6d68ab1c0c918f1c4084"}, @@ -3779,7 +3643,6 @@ version = "0.5.3" description = "A Jupyter Server Extension Providing Terminals." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa"}, {file = "jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269"}, @@ -3799,7 +3662,6 @@ version = "4.3.5" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyterlab-4.3.5-py3-none-any.whl", hash = "sha256:571bbdee20e4c5321ab5195bc41cf92a75a5cff886be5e57ce78dfa37a5e9fdb"}, {file = "jupyterlab-4.3.5.tar.gz", hash = "sha256:c779bf72ced007d7d29d5bcef128e7fdda96ea69299e19b04a43635a7d641f9d"}, @@ -3833,7 +3695,6 @@ version = "0.3.0" description = "Pygments theme using JupyterLab CSS variables" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, @@ -3845,7 +3706,6 @@ version = "2.27.3" description = "A set of server components for JupyterLab and JupyterLab like applications." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "jupyterlab_server-2.27.3-py3-none-any.whl", hash = "sha256:e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4"}, {file = "jupyterlab_server-2.27.3.tar.gz", hash = "sha256:eb36caca59e74471988f0ae25c77945610b887f777255aa21f8065def9e51ed4"}, @@ -3865,13 +3725,23 @@ docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pyd openapi = ["openapi-core (>=0.18.0,<0.19.0)", "ruamel-yaml"] test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0,<8)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] +[[package]] +name = "jupyterlab-widgets" +version = "3.0.13" +description = "Jupyter interactive widgets for JupyterLab" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54"}, + {file = "jupyterlab_widgets-3.0.13.tar.gz", hash = "sha256:a2966d385328c1942b683a8cd96b89b8dd82c8b8f81dda902bb2bc06d46f5bed"}, +] + [[package]] name = "kiwisolver" version = "1.4.8" description = "A fast implementation of the Cassowary constraint solver" optional = false python-versions = ">=3.10" -groups = ["main", "evaluation"] files = [ {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, @@ -3961,7 +3831,6 @@ version = "31.0.0" description = "Kubernetes python client" optional = false python-versions = ">=3.6" -groups = ["llama-index"] files = [ {file = "kubernetes-31.0.0-py2.py3-none-any.whl", hash = "sha256:bf141e2d380c8520eada8b351f4e319ffee9636328c137aa432bc486ca1200e1"}, {file = "kubernetes-31.0.0.tar.gz", hash = "sha256:28945de906c8c259c1ebe62703b56a03b714049372196f854105afe4e6d014c0"}, @@ -3989,7 +3858,6 @@ version = "0.4" description = "Makes it easy to load subpackages and functions on demand." optional = false python-versions = ">=3.7" -groups = ["evaluation"] files = [ {file = "lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc"}, {file = "lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1"}, @@ -4009,7 +3877,6 @@ version = "0.39.0" description = "Typed library that provides an ORM wrapper for tmux, a terminal multiplexer." optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] files = [ {file = "libtmux-0.39.0-py3-none-any.whl", hash = "sha256:6b6e338be2727f67aa6b7eb67fa134368fa3c3eac5df27565396467692891c1e"}, {file = "libtmux-0.39.0.tar.gz", hash = "sha256:59346aeef3c0d6017f3bc5e23248d43cdf50f32b775b9cb5d9ff5e2e5f3059f4"}, @@ -4021,7 +3888,6 @@ version = "0.0.14" description = "This is an unofficial, use-at-your-own risks port of the visualwebarena benchmark, for use as a standalone library package." optional = false python-versions = "<4,>=3.7" -groups = ["evaluation"] files = [ {file = "libvisualwebarena-0.0.14-py3-none-any.whl", hash = "sha256:636b06ca1d52f1a363503b5b563492e83f2482efaf85bb26b69744565a499f0f"}, {file = "libvisualwebarena-0.0.14.tar.gz", hash = "sha256:7e660179f60f1df8d884204f2b742a2117e7fe050823d839ca5744ea1c0709a7"}, @@ -4049,7 +3915,6 @@ version = "0.0.3" description = "This is an unofficial, use-at-your-own risks port of the webarena benchmark, for use as a standalone library package." optional = false python-versions = "<4,>=3.7" -groups = ["evaluation"] files = [ {file = "libwebarena-0.0.3-py3-none-any.whl", hash = "sha256:aa0a0879486e5c90b2b2ec1c3bf309b0c7f13ee2bf7c8945447ac15f7027d248"}, {file = "libwebarena-0.0.3.tar.gz", hash = "sha256:3d05fae6749931aaf26e6c80fd665725dfeab41ac4848f168c407dbe3de89baf"}, @@ -4076,7 +3941,6 @@ version = "1.59.9" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" -groups = ["main"] files = [ {file = "litellm-1.59.9-py3-none-any.whl", hash = "sha256:f2012e98d61d7aeb1d103a70215ddc713eb62973c58da0cd71942e771cc5f511"}, {file = "litellm-1.59.9.tar.gz", hash = "sha256:51d6801529042a613bc3ef8d6f9bb2dbaf265ebcbeb44735cbb2503293fc6375"}, @@ -4105,7 +3969,6 @@ version = "0.1.7" description = "" optional = false python-versions = "<4,>=3.8" -groups = ["llama-index"] files = [ {file = "llama_cloud-0.1.7-py3-none-any.whl", hash = "sha256:266db22939c537a2b802eea6a9af2701beff98d5ba46513248011a4f1c17afc6"}, {file = "llama_cloud-0.1.7.tar.gz", hash = "sha256:7c1767cb209905400e894566661a91230bcff83cd4d9c08e782fd2143ca6a646"}, @@ -4122,7 +3985,6 @@ version = "0.12.14" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index-0.12.14-py3-none-any.whl", hash = "sha256:cafbac9f08f1f7293169bfd3c75545db3b761742ea829ba6940c3f2c3b1c2d26"}, {file = "llama_index-0.12.14.tar.gz", hash = "sha256:aa74315b32e93a77e285519459d77b98be7db9ae4c5aa64aac2c54cc919c838f"}, @@ -4148,7 +4010,6 @@ version = "0.4.1" description = "llama-index agent openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_agent_openai-0.4.1-py3-none-any.whl", hash = "sha256:162507543082f739a8c806911344c8d7f2434d0ee91124cfdd7b0ba5f76d0e57"}, {file = "llama_index_agent_openai-0.4.1.tar.gz", hash = "sha256:3a89137b228a6e9c2b3f46e367a27b75fb31b458e21777bba819de654707d59e"}, @@ -4165,7 +4026,6 @@ version = "0.4.0" description = "llama-index cli" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_cli-0.4.0-py3-none-any.whl", hash = "sha256:60d12f89e6b85e80a0cc3a8b531f05a911b5eebaebc37314411476d1ba685904"}, {file = "llama_index_cli-0.4.0.tar.gz", hash = "sha256:d6ab201359962a8a34368aeda3a49bbbe67e9e009c59bd925c4fb2be4ace3906"}, @@ -4182,7 +4042,6 @@ version = "0.12.14" description = "Interface between LLMs and your data" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_core-0.12.14-py3-none-any.whl", hash = "sha256:6fdb30e3fadf98e7df75f9db5d06f6a7f8503ca545a71e048d786ff88012bd50"}, {file = "llama_index_core-0.12.14.tar.gz", hash = "sha256:378bbf5bf4d1a8c692d3a980c1a6ed3be7a9afb676a4960429dea15f62d06cd3"}, @@ -4218,7 +4077,6 @@ version = "0.3.0" description = "llama-index embeddings azure openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_embeddings_azure_openai-0.3.0-py3-none-any.whl", hash = "sha256:2ca61d6b75468d1230cfc1151a878d892b237130b8af09b4434f8c0466d44dfe"}, {file = "llama_index_embeddings_azure_openai-0.3.0.tar.gz", hash = "sha256:80b0cf977d8b967a08536d65b8e2d0c6c966eeaf1b8fff084e97f3081fd70c34"}, @@ -4235,7 +4093,6 @@ version = "0.5.1" description = "llama-index embeddings huggingface integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_embeddings_huggingface-0.5.1-py3-none-any.whl", hash = "sha256:cff600538e9616829d379ced09f08fc6d237e5599975d781ca52b599a419394e"}, {file = "llama_index_embeddings_huggingface-0.5.1.tar.gz", hash = "sha256:def1639bab8511e3ac0284520104b0c6dce9bc053b4dce38c127bd62bc28f7fc"}, @@ -4252,7 +4109,6 @@ version = "0.5.0" description = "llama-index embeddings ollama integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_embeddings_ollama-0.5.0-py3-none-any.whl", hash = "sha256:843ecccfbe2db548a39e71a85e8ebbfe3cf2659db9533c080dcb291e4975af3b"}, {file = "llama_index_embeddings_ollama-0.5.0.tar.gz", hash = "sha256:fec8fa249ed2fb13912e1511decb21c025a53294728a21f25bd2d5f30f435a94"}, @@ -4268,7 +4124,6 @@ version = "0.3.1" description = "llama-index embeddings openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_embeddings_openai-0.3.1-py3-none-any.whl", hash = "sha256:f15a3d13da9b6b21b8bd51d337197879a453d1605e625a1c6d45e741756c0290"}, {file = "llama_index_embeddings_openai-0.3.1.tar.gz", hash = "sha256:1368aad3ce24cbaed23d5ad251343cef1eb7b4a06d6563d6606d59cb347fef20"}, @@ -4284,7 +4139,6 @@ version = "0.3.1" description = "llama-index embeddings voyageai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_embeddings_voyageai-0.3.1-py3-none-any.whl", hash = "sha256:f0e0b327ab21669a2b0501f207a6862f7a0b0a115bff15b6ceac712273a6fa03"}, {file = "llama_index_embeddings_voyageai-0.3.1.tar.gz", hash = "sha256:cfbc0a0697bda39c18398418628596c6ae8c668a0306d504a4fc16100fcd7d57"}, @@ -4300,7 +4154,6 @@ version = "0.6.3" description = "llama-index indices llama-cloud integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_indices_managed_llama_cloud-0.6.3-py3-none-any.whl", hash = "sha256:7f125602f624a2d321b6a4130cd98df35eb8c15818a159390755b2c13068f4ce"}, {file = "llama_index_indices_managed_llama_cloud-0.6.3.tar.gz", hash = "sha256:f09e4182cbc2a2bd75ae85cebb1681075247f0d91b931b094cac4315386ce87a"}, @@ -4316,7 +4169,6 @@ version = "0.3.0" description = "llama-index llms azure openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_llms_azure_openai-0.3.0-py3-none-any.whl", hash = "sha256:24091aedf7ba24a7b217d17c4358e62b5d6b43a4d3ca44750d442b02a440d26e"}, {file = "llama_index_llms_azure_openai-0.3.0.tar.gz", hash = "sha256:0feea9319d832c8b5e8e0f397c905e45df54c529b6a778825adcd0d254bd7d63"}, @@ -4334,7 +4186,6 @@ version = "0.3.12" description = "llama-index llms openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_llms_openai-0.3.12-py3-none-any.whl", hash = "sha256:08be76b9e649f6085e93292504074728a6531eb7f8930eaf40a2fce70a9f59df"}, {file = "llama_index_llms_openai-0.3.12.tar.gz", hash = "sha256:1880273a7e409c05f1dbccdbac5ce3c214771901cd3696aeb556a29dfed8477a"}, @@ -4350,7 +4201,6 @@ version = "0.4.2" description = "llama-index multi-modal-llms openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_multi_modal_llms_openai-0.4.2-py3-none-any.whl", hash = "sha256:093f60f59fc423abab110810f8f129b96b0212b9737d74480f0e3e1b715e975b"}, {file = "llama_index_multi_modal_llms_openai-0.4.2.tar.gz", hash = "sha256:3437a08cec85cebbc212aa73da5c9b8b054b4dc628338568435a7df88489476f"}, @@ -4366,7 +4216,6 @@ version = "0.3.1" description = "llama-index program openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_program_openai-0.3.1-py3-none-any.whl", hash = "sha256:93646937395dc5318fd095153d2f91bd632b25215d013d14a87c088887d205f9"}, {file = "llama_index_program_openai-0.3.1.tar.gz", hash = "sha256:6039a6cdbff62c6388c07e82a157fe2edd3bbef0c5adf292ad8546bf4ec75b82"}, @@ -4383,7 +4232,6 @@ version = "0.3.0" description = "llama-index question_gen openai integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_question_gen_openai-0.3.0-py3-none-any.whl", hash = "sha256:9b60ec114273a63b50349948666e5744a8f58acb645824e07c979041e8fec598"}, {file = "llama_index_question_gen_openai-0.3.0.tar.gz", hash = "sha256:efd3b468232808e9d3474670aaeab00e41b90f75f52d0c9bfbf11207e0963d62"}, @@ -4400,7 +4248,6 @@ version = "0.4.2" description = "llama-index readers file integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_readers_file-0.4.2-py3-none-any.whl", hash = "sha256:9341ff375aae3ab58256af4fc7c6619e08b04a1e78bc5c9d3d1763df3b9223a6"}, {file = "llama_index_readers_file-0.4.2.tar.gz", hash = "sha256:d677a2eef0695d00b487ac4ea14c82e6a4eaade3a09c540f8f81626d852e3491"}, @@ -4422,7 +4269,6 @@ version = "0.4.0" description = "llama-index readers llama-parse integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_readers_llama_parse-0.4.0-py3-none-any.whl", hash = "sha256:574e48386f28d2c86c3f961ca4a4906910312f3400dd0c53014465bfbc6b32bf"}, {file = "llama_index_readers_llama_parse-0.4.0.tar.gz", hash = "sha256:e99ec56f4f8546d7fda1a7c1ae26162fb9acb7ebcac343b5abdb4234b4644e0f"}, @@ -4438,7 +4284,6 @@ version = "0.4.1" description = "llama-index vector_stores chroma integration" optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_index_vector_stores_chroma-0.4.1-py3-none-any.whl", hash = "sha256:42200af4fa5c8df820b865825d1b506cecb922c8fbd7421eda8a5609b390c1d5"}, {file = "llama_index_vector_stores_chroma-0.4.1.tar.gz", hash = "sha256:70ee74ccf304adda04171d014e483759c68a1c92f679ea2ca2e6b6f45b6fef08"}, @@ -4454,7 +4299,6 @@ version = "0.5.19" description = "Parse files into RAG-Optimized formats." optional = false python-versions = "<4.0,>=3.9" -groups = ["llama-index"] files = [ {file = "llama_parse-0.5.19-py3-none-any.whl", hash = "sha256:715cc895d183531b4299359d4f4004089b2e522f5f137f316084e7aa04035b62"}, {file = "llama_parse-0.5.19.tar.gz", hash = "sha256:db69da70e199a2664705eb983a70fa92b7cee19dd6cff175af7692a0b8a4dd53"}, @@ -4471,7 +4315,6 @@ version = "5.3.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" -groups = ["main", "evaluation"] files = [ {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, @@ -4626,7 +4469,6 @@ version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, @@ -4651,7 +4493,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -4722,7 +4563,6 @@ version = "3.24.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" -groups = ["evaluation", "llama-index"] files = [ {file = "marshmallow-3.24.1-py3-none-any.whl", hash = "sha256:ddb5c9987017d37be351c184e4e867e7bf55f7331f4da730dedad6b7af662cdd"}, {file = "marshmallow-3.24.1.tar.gz", hash = "sha256:efdcb656ac8788f0e3d1d938f8dc0f237bf1a99aff8f6dfbffa594981641cea0"}, @@ -4742,7 +4582,6 @@ version = "3.10.0" description = "Python plotting package" optional = false python-versions = ">=3.10" -groups = ["main", "evaluation"] files = [ {file = "matplotlib-3.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6"}, {file = "matplotlib-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e"}, @@ -4800,7 +4639,6 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, @@ -4815,7 +4653,6 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" -groups = ["main", "runtime", "test"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -4827,7 +4664,6 @@ version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, @@ -4839,7 +4675,6 @@ version = "7.2.15" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "minio-7.2.15-py3-none-any.whl", hash = "sha256:c06ef7a43e5d67107067f77b6c07ebdd68733e5aa7eed03076472410ca19d876"}, {file = "minio-7.2.15.tar.gz", hash = "sha256:5247df5d4dca7bfa4c9b20093acd5ad43e82d8710ceb059d79c6eea970f49f79"}, @@ -4858,7 +4693,6 @@ version = "3.1.0" description = "A sane and fast Markdown parser with useful plugins and renderers" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "mistune-3.1.0-py3-none-any.whl", hash = "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1"}, {file = "mistune-3.1.0.tar.gz", hash = "sha256:dbcac2f78292b9dc066cd03b7a3a26b62d85f8159f2ea5fd28e55df79908d667"}, @@ -4870,7 +4704,6 @@ version = "5.0.1" description = "Python extension for MurmurHash (MurmurHash3), a set of fast and robust hash functions." optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "mmh3-5.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f0a4b4bf05778ed77d820d6e7d0e9bd6beb0c01af10e1ce9233f5d2f814fcafa"}, {file = "mmh3-5.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac7a391039aeab95810c2d020b69a94eb6b4b37d4e2374831e92db3a0cdf71c6"}, @@ -4984,7 +4817,6 @@ version = "0.73.2" description = "Python client library for Modal" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation"] files = [ {file = "modal-0.73.2-py3-none-any.whl", hash = "sha256:244db6dec3b538e702bcb15ea4fbc3f507d54a600c4767080e568507b8c4fc46"}, ] @@ -5011,7 +4843,6 @@ version = "1.6" description = "An implementation of time.monotonic() for Python 2 & < 3.3" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, @@ -5023,7 +4854,6 @@ version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" -groups = ["evaluation", "llama-index"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -5041,7 +4871,6 @@ version = "1.31.1" description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." optional = false python-versions = ">=3.7" -groups = ["llama-index"] files = [ {file = "msal-1.31.1-py3-none-any.whl", hash = "sha256:29d9882de247e96db01386496d59f29035e5e841bcac892e6d7bf4390bf6bd17"}, {file = "msal-1.31.1.tar.gz", hash = "sha256:11b5e6a3f802ffd3a72107203e20c4eac6ef53401961b880af2835b723d80578"}, @@ -5061,7 +4890,6 @@ version = "1.2.0" description = "Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism." optional = false python-versions = ">=3.7" -groups = ["llama-index"] files = [ {file = "msal_extensions-1.2.0-py3-none-any.whl", hash = "sha256:cf5ba83a2113fa6dc011a254a72f1c223c88d7dfad74cc30617c4679a417704d"}, {file = "msal_extensions-1.2.0.tar.gz", hash = "sha256:6f41b320bfd2933d631a215c91ca0dd3e67d84bd1a2f50ce917d5874ec646bef"}, @@ -5077,7 +4905,6 @@ version = "6.1.0" description = "multidict implementation" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -5179,7 +5006,6 @@ version = "0.70.16" description = "better multiprocessing and multithreading in Python" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"}, {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"}, @@ -5204,7 +5030,6 @@ version = "1.14.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, @@ -5263,7 +5088,6 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" -groups = ["dev", "evaluation", "llama-index"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -5275,7 +5099,6 @@ version = "1.21.1" description = "Extremely lightweight compatibility layer between dataframe libraries" optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "narwhals-1.21.1-py3-none-any.whl", hash = "sha256:f5f2cd33a6fa636de74067f4050d6dd9a9343b39a5a911dc97810d55d8f24cdd"}, {file = "narwhals-1.21.1.tar.gz", hash = "sha256:44082c6273fd0125a2bde5baae6ddb7465e185c24fd6e1c5e71cab1d746c89cc"}, @@ -5302,7 +5125,6 @@ version = "0.10.2" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.9.0" -groups = ["runtime"] files = [ {file = "nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d"}, {file = "nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193"}, @@ -5325,7 +5147,6 @@ version = "7.16.5" description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "nbconvert-7.16.5-py3-none-any.whl", hash = "sha256:e12eac052d6fd03040af4166c563d76e7aeead2e9aadf5356db552a1784bd547"}, {file = "nbconvert-7.16.5.tar.gz", hash = "sha256:c83467bb5777fdfaac5ebbb8e864f300b277f68692ecc04d6dab72f2d8442344"}, @@ -5362,7 +5183,6 @@ version = "5.10.4" description = "The Jupyter Notebook format" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, @@ -5384,7 +5204,6 @@ version = "1.6.0" description = "Patch asyncio to allow nested event loops" optional = false python-versions = ">=3.5" -groups = ["llama-index", "runtime"] files = [ {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, @@ -5396,7 +5215,6 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -5416,7 +5234,6 @@ version = "3.9.1" description = "Natural Language Toolkit" optional = false python-versions = ">=3.8" -groups = ["evaluation", "llama-index"] files = [ {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"}, {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"}, @@ -5442,7 +5259,6 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev", "evaluation"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -5454,7 +5270,6 @@ version = "7.3.2" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "notebook-7.3.2-py3-none-any.whl", hash = "sha256:e5f85fc59b69d3618d73cf27544418193ff8e8058d5bf61d315ce4f473556288"}, {file = "notebook-7.3.2.tar.gz", hash = "sha256:705e83a1785f45b383bf3ee13cb76680b92d24f56fb0c7d2136fe1d850cd3ca8"}, @@ -5478,7 +5293,6 @@ version = "0.2.4" description = "A shim layer for notebook traits and config" optional = false python-versions = ">=3.7" -groups = ["runtime"] files = [ {file = "notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef"}, {file = "notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"}, @@ -5496,7 +5310,6 @@ version = "2.2.2" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"}, {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"}, @@ -5561,8 +5374,6 @@ version = "12.4.5.8" description = "CUBLAS native runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3"}, {file = "nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b"}, @@ -5575,8 +5386,6 @@ version = "12.4.127" description = "CUDA profiling tools runtime libs." optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a"}, {file = "nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb"}, @@ -5589,8 +5398,6 @@ version = "12.4.127" description = "NVRTC native runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198"}, {file = "nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338"}, @@ -5603,8 +5410,6 @@ version = "12.4.127" description = "CUDA Runtime native Libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3"}, {file = "nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5"}, @@ -5617,8 +5422,6 @@ version = "9.1.0.70" description = "cuDNN runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f"}, {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a"}, @@ -5633,8 +5436,6 @@ version = "11.2.1.3" description = "CUFFT native runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399"}, {file = "nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9"}, @@ -5650,8 +5451,6 @@ version = "10.3.5.147" description = "CURAND native runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9"}, {file = "nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b"}, @@ -5664,8 +5463,6 @@ version = "11.6.1.9" description = "CUDA solver native runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e"}, {file = "nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260"}, @@ -5683,8 +5480,6 @@ version = "12.3.1.170" description = "CUSPARSE native runtime libraries" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3"}, {file = "nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1"}, @@ -5700,8 +5495,6 @@ version = "2.21.5" description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0"}, ] @@ -5712,8 +5505,6 @@ version = "12.4.127" description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83"}, {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, @@ -5726,8 +5517,6 @@ version = "12.4.127" description = "NVIDIA Tools Extension" optional = false python-versions = ">=3" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3"}, {file = "nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a"}, @@ -5740,7 +5529,6 @@ version = "3.2.2" description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" optional = false python-versions = ">=3.6" -groups = ["main", "llama-index"] files = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, @@ -5757,7 +5545,6 @@ version = "0.4.5" description = "The official Python client for Ollama." optional = false python-versions = "<4.0,>=3.8" -groups = ["llama-index"] files = [ {file = "ollama-0.4.5-py3-none-any.whl", hash = "sha256:74936de89a41c87c9745f09f2e1db964b4783002188ac21241bfab747f46d925"}, {file = "ollama-0.4.5.tar.gz", hash = "sha256:e7fb71a99147046d028ab8b75e51e09437099aea6f8f9a0d91a71f787e97439e"}, @@ -5773,7 +5560,6 @@ version = "1.20.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "onnxruntime-1.20.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:e50ba5ff7fed4f7d9253a6baf801ca2883cc08491f9d32d78a80da57256a5439"}, {file = "onnxruntime-1.20.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b2908b50101a19e99c4d4e97ebb9905561daf61829403061c1adc1b588bc0de"}, @@ -5812,7 +5598,6 @@ version = "1.60.2" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "openai-1.60.2-py3-none-any.whl", hash = "sha256:993bd11b96900b9098179c728026f016b4982ded7ee30dfcf4555eab1171fff9"}, {file = "openai-1.60.2.tar.gz", hash = "sha256:a8f843e10f2855713007f491d96afb2694b11b5e02cb97c7d01a0be60bc5bb51"}, @@ -5838,7 +5623,6 @@ version = "0.2.0" description = "An Agent-Computer Interface (ACI) designed for software development agents OpenHands." optional = false python-versions = "<4.0,>=3.12" -groups = ["main"] files = [ {file = "openhands_aci-0.2.0-py3-none-any.whl", hash = "sha256:5ca0df7ab6dab1034e70d3982b401db9888dd6deb8149d30e47193bf8588ed65"}, {file = "openhands_aci-0.2.0.tar.gz", hash = "sha256:6c54defd07a7b2e861ff5c8f683777c2c2503a0f417eeb570382be682e7038d6"}, @@ -5863,7 +5647,6 @@ version = "1.25.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "opentelemetry_api-1.25.0-py3-none-any.whl", hash = "sha256:757fa1aa020a0f8fa139f8959e53dec2051cc26b832e76fa839a6d76ecefd737"}, {file = "opentelemetry_api-1.25.0.tar.gz", hash = "sha256:77c4985f62f2614e42ce77ee4c9da5fa5f0bc1e1821085e9a47533a9323ae869"}, @@ -5879,7 +5662,6 @@ version = "1.25.0" description = "OpenTelemetry Protobuf encoding" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "opentelemetry_exporter_otlp_proto_common-1.25.0-py3-none-any.whl", hash = "sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693"}, {file = "opentelemetry_exporter_otlp_proto_common-1.25.0.tar.gz", hash = "sha256:c93f4e30da4eee02bacd1e004eb82ce4da143a2f8e15b987a9f603e0a85407d3"}, @@ -5894,7 +5676,6 @@ version = "1.25.0" description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0-py3-none-any.whl", hash = "sha256:3131028f0c0a155a64c430ca600fd658e8e37043cb13209f0109db5c1a3e4eb4"}, {file = "opentelemetry_exporter_otlp_proto_grpc-1.25.0.tar.gz", hash = "sha256:c0b1661415acec5af87625587efa1ccab68b873745ca0ee96b69bb1042087eac"}, @@ -5915,7 +5696,6 @@ version = "0.46b0" description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "opentelemetry_instrumentation-0.46b0-py3-none-any.whl", hash = "sha256:89cd721b9c18c014ca848ccd11181e6b3fd3f6c7669e35d59c48dc527408c18b"}, {file = "opentelemetry_instrumentation-0.46b0.tar.gz", hash = "sha256:974e0888fb2a1e01c38fbacc9483d024bb1132aad92d6d24e2e5543887a7adda"}, @@ -5932,7 +5712,6 @@ version = "0.46b0" description = "ASGI instrumentation for OpenTelemetry" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "opentelemetry_instrumentation_asgi-0.46b0-py3-none-any.whl", hash = "sha256:f13c55c852689573057837a9500aeeffc010c4ba59933c322e8f866573374759"}, {file = "opentelemetry_instrumentation_asgi-0.46b0.tar.gz", hash = "sha256:02559f30cf4b7e2a737ab17eb52aa0779bcf4cc06573064f3e2cb4dcc7d3040a"}, @@ -5954,7 +5733,6 @@ version = "0.46b0" description = "OpenTelemetry FastAPI Instrumentation" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "opentelemetry_instrumentation_fastapi-0.46b0-py3-none-any.whl", hash = "sha256:e0f5d150c6c36833dd011f0e6ef5ede6d7406c1aed0c7c98b2d3b38a018d1b33"}, {file = "opentelemetry_instrumentation_fastapi-0.46b0.tar.gz", hash = "sha256:928a883a36fc89f9702f15edce43d1a7104da93d740281e32d50ffd03dbb4365"}, @@ -5976,7 +5754,6 @@ version = "1.25.0" description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "opentelemetry_proto-1.25.0-py3-none-any.whl", hash = "sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f"}, {file = "opentelemetry_proto-1.25.0.tar.gz", hash = "sha256:35b6ef9dc4a9f7853ecc5006738ad40443701e52c26099e197895cbda8b815a3"}, @@ -5991,7 +5768,6 @@ version = "1.25.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "opentelemetry_sdk-1.25.0-py3-none-any.whl", hash = "sha256:d97ff7ec4b351692e9d5a15af570c693b8715ad78b8aafbec5c7100fe966b4c9"}, {file = "opentelemetry_sdk-1.25.0.tar.gz", hash = "sha256:ce7fc319c57707ef5bf8b74fb9f8ebdb8bfafbe11898410e0d2a761d08a98ec7"}, @@ -6008,7 +5784,6 @@ version = "0.46b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "opentelemetry_semantic_conventions-0.46b0-py3-none-any.whl", hash = "sha256:6daef4ef9fa51d51855d9f8e0ccd3a1bd59e0e545abe99ac6203804e36ab3e07"}, {file = "opentelemetry_semantic_conventions-0.46b0.tar.gz", hash = "sha256:fbc982ecbb6a6e90869b15c1673be90bd18c8a56ff1cffc0864e38e2edffaefa"}, @@ -6023,7 +5798,6 @@ version = "0.46b0" description = "Web util for OpenTelemetry" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "opentelemetry_util_http-0.46b0-py3-none-any.whl", hash = "sha256:8dc1949ce63caef08db84ae977fdc1848fe6dc38e6bbaad0ae3e6ecd0d451629"}, {file = "opentelemetry_util_http-0.46b0.tar.gz", hash = "sha256:03b6e222642f9c7eae58d9132343e045b50aca9761fcb53709bd2b663571fdf6"}, @@ -6035,7 +5809,6 @@ version = "3.10.13" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "orjson-3.10.13-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1232c5e873a4d1638ef957c5564b4b0d6f2a6ab9e207a9b3de9de05a09d1d920"}, {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d26a0eca3035619fa366cbaf49af704c7cb1d4a0e6c79eced9f6a3f2437964b6"}, @@ -6120,7 +5893,6 @@ version = "7.7.0" description = "A decorator to automatically detect mismatch when overriding a method." optional = false python-versions = ">=3.6" -groups = ["llama-index", "runtime"] files = [ {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, @@ -6132,7 +5904,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -6144,7 +5915,6 @@ version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -6227,7 +5997,6 @@ version = "1.5.1" description = "Utilities for writing pandoc filters in python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["runtime"] files = [ {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, @@ -6239,7 +6008,6 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" -groups = ["runtime"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, @@ -6255,7 +6023,6 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -6267,7 +6034,6 @@ version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" -groups = ["main", "runtime"] files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -6282,7 +6048,6 @@ version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, @@ -6371,7 +6136,6 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" -groups = ["dev", "evaluation", "runtime"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -6388,7 +6152,6 @@ version = "1.39.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "playwright-1.39.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:384e195a6d09343f319031cf552e9cd601ede78fe9c082b9fa197537c5cbfe7a"}, {file = "playwright-1.39.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d2c3634411828d9273196ed6f69f2fa7645c89732b3c982dcf09ab03ed4c5d2b"}, @@ -6409,7 +6172,6 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["evaluation", "test"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -6425,7 +6187,6 @@ version = "2.10.1" description = "Wraps the portalocker recipe for easy usage" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf"}, {file = "portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f"}, @@ -6445,7 +6206,6 @@ version = "3.7.5" description = "Integrate PostHog into any python application." optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "posthog-3.7.5-py2.py3-none-any.whl", hash = "sha256:022132c17069dde03c5c5904e2ae1b9bd68d5059cbc5a8dffc5c1537a1b71cb5"}, {file = "posthog-3.7.5.tar.gz", hash = "sha256:8ba40ab623da35db72715fc87fe7dccb7fc272ced92581fe31db2d4dbe7ad761"}, @@ -6469,7 +6229,6 @@ version = "4.1.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" -groups = ["dev", "evaluation"] files = [ {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"}, {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"}, @@ -6488,7 +6247,6 @@ version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, @@ -6503,7 +6261,6 @@ version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" -groups = ["runtime"] files = [ {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, @@ -6518,7 +6275,6 @@ version = "0.2.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, @@ -6610,7 +6366,6 @@ version = "1.25.0" description = "Beautiful, Pythonic protocol buffers." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961"}, {file = "proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91"}, @@ -6628,7 +6383,6 @@ version = "4.25.6" description = "" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "protobuf-4.25.6-cp310-abi3-win32.whl", hash = "sha256:61df6b5786e2b49fc0055f636c1e8f0aff263808bb724b95b164685ac1bcc13a"}, {file = "protobuf-4.25.6-cp310-abi3-win_amd64.whl", hash = "sha256:b8f837bfb77513fe0e2f263250f423217a173b6d85135be4d81e96a4653bcd3c"}, @@ -6649,7 +6403,6 @@ version = "6.1.1" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["runtime"] files = [ {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, @@ -6680,7 +6433,6 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" -groups = ["main", "runtime"] files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -6692,7 +6444,6 @@ version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" -groups = ["runtime"] files = [ {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, @@ -6707,7 +6458,6 @@ version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["evaluation", "test"] files = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -6719,7 +6469,6 @@ version = "19.0.0" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation"] files = [ {file = "pyarrow-19.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c318eda14f6627966997a7d8c374a87d084a94e4e38e9abbe97395c215830e0c"}, {file = "pyarrow-19.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:62ef8360ff256e960f57ce0299090fb86423afed5e46f18f1225f960e05aae3d"}, @@ -6774,7 +6523,6 @@ version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -6786,7 +6534,6 @@ version = "0.4.1" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, @@ -6801,7 +6548,6 @@ version = "2.13.6" description = "Seamless operability between C++11 and Python" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "pybind11-2.13.6-py3-none-any.whl", hash = "sha256:237c41e29157b962835d356b370ededd57594a26d5894a795960f0047cb5caf5"}, {file = "pybind11-2.13.6.tar.gz", hash = "sha256:ba6af10348c12b24e92fa086b39cfba0eff619b61ac77c406167d813b096d39a"}, @@ -6816,7 +6562,6 @@ version = "2.12.1" description = "Python style guide checker" optional = false python-versions = ">=3.8" -groups = ["main", "runtime", "test"] files = [ {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, @@ -6828,12 +6573,10 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main", "llama-index", "runtime"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -markers = {llama-index = "platform_python_implementation != \"PyPy\""} [[package]] name = "pycryptodome" @@ -6841,7 +6584,6 @@ version = "3.21.0" description = "Cryptographic library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["main"] files = [ {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, @@ -6883,7 +6625,6 @@ version = "2.10.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, @@ -6904,7 +6645,6 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -7017,7 +6757,6 @@ version = "0.9.1" description = "Widget for deck.gl maps" optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038"}, {file = "pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605"}, @@ -7037,7 +6776,6 @@ version = "11.0.1" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "pyee-11.0.1-py3-none-any.whl", hash = "sha256:9bcc9647822234f42c228d88de63d0f9ffa881e87a87f9d36ddf5211f6ac977d"}, {file = "pyee-11.0.1.tar.gz", hash = "sha256:a642c51e3885a33ead087286e35212783a4e9b8d6514a10a5db4e57ac57b2b29"}, @@ -7055,7 +6793,6 @@ version = "3.2.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" -groups = ["main", "runtime", "test"] files = [ {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, @@ -7067,7 +6804,6 @@ version = "2.5.0" description = "Use the full Github API v3" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "PyGithub-2.5.0-py3-none-any.whl", hash = "sha256:b0b635999a658ab8e08720bdd3318893ff20e2275f6446fcf35bf3f44f2c0fd2"}, {file = "pygithub-2.5.0.tar.gz", hash = "sha256:e1613ac508a9be710920d26eb18b1905ebd9926aa49398e88151c1b526aad3cf"}, @@ -7087,7 +6823,6 @@ version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -7102,7 +6837,6 @@ version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" -groups = ["main", "llama-index"] files = [ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, @@ -7123,7 +6857,6 @@ version = "2.10" description = "Simple LaTeX parser providing latex-to-unicode and unicode-to-latex conversion" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pylatexenc-2.10.tar.gz", hash = "sha256:3dd8fd84eb46dc30bee1e23eaab8d8fb5a7f507347b23e5f38ad9675c84f40d3"}, ] @@ -7134,7 +6867,6 @@ version = "0.1.1" description = "super fast cpp implementation of longest common subsequence" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "pylcs-0.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:7b8adea6b41dff27332c967533ec3c42a5e94171be778d6f01f0c5cee82e7604"}, {file = "pylcs-0.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ff06e037c54056cb67d6ef5ad946c0360afeff7d43be67ce09e55201ecc15cc"}, @@ -7155,7 +6887,6 @@ version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, @@ -7182,7 +6913,6 @@ version = "3.2.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation"] files = [ {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, @@ -7197,7 +6927,6 @@ version = "5.1.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "pypdf-5.1.0-py3-none-any.whl", hash = "sha256:3bd4f503f4ebc58bae40d81e81a9176c400cbbac2ba2d877367595fb524dfdfc"}, {file = "pypdf-5.1.0.tar.gz", hash = "sha256:425a129abb1614183fd1aca6982f650b47f8026867c0ce7c4b9f281c443d2740"}, @@ -7217,7 +6946,6 @@ version = "3.0.1" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440"}, {file = "pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928"}, @@ -7236,7 +6964,6 @@ version = "0.48.9" description = "A SQL query builder API for Python" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "PyPika-0.48.9.tar.gz", hash = "sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378"}, ] @@ -7247,7 +6974,6 @@ version = "1.2.0" description = "Wrappers to call pyproject.toml-based build backend hooks." optional = false python-versions = ">=3.7" -groups = ["dev", "llama-index"] files = [ {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"}, {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"}, @@ -7259,8 +6985,6 @@ version = "3.5.4" description = "A python implementation of GNU readline." optional = false python-versions = ">=3.8" -groups = ["llama-index"] -markers = "sys_platform == \"win32\"" files = [ {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, @@ -7275,7 +6999,6 @@ version = "1.7.1" description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["evaluation"] files = [ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, @@ -7288,7 +7011,6 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["evaluation", "test"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -7309,7 +7031,6 @@ version = "0.25.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.9" -groups = ["test"] files = [ {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, @@ -7328,7 +7049,6 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" -groups = ["test"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -7347,7 +7067,6 @@ version = "1.6.0" description = "run tests in isolated forked subprocesses" optional = false python-versions = ">=3.7" -groups = ["test"] files = [ {file = "pytest-forked-1.6.0.tar.gz", hash = "sha256:4dafd46a9a600f65d822b8f605133ecf5b3e1941ebb3588e943b4e3eb71a5a3f"}, {file = "pytest_forked-1.6.0-py3-none-any.whl", hash = "sha256:810958f66a91afb1a1e2ae83089d8dc1cd2437ac96b12963042fbb9fb4d16af0"}, @@ -7357,28 +7076,12 @@ files = [ py = "*" pytest = ">=3.10" -[[package]] -name = "pytest-rerunfailures" -version = "15.0" -description = "pytest plugin to re-run tests to eliminate flaky failures" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest-rerunfailures-15.0.tar.gz", hash = "sha256:2d9ac7baf59f4c13ac730b47f6fa80e755d1ba0581da45ce30b72fb3542b4474"}, - {file = "pytest_rerunfailures-15.0-py3-none-any.whl", hash = "sha256:dd150c4795c229ef44320adc9a0c0532c51b78bb7a6843a8c53556b9a611df1a"}, -] - -[package.dependencies] -packaging = ">=17.1" -pytest = ">=7.4,<8.2.2 || >8.2.2" - [[package]] name = "pytest-xdist" version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" -groups = ["test"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -7399,7 +7102,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -7414,7 +7116,6 @@ version = "1.1.2" description = "Create, read, and update Microsoft Word .docx files." optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "python_docx-1.1.2-py3-none-any.whl", hash = "sha256:08c20d6058916fb19853fcf080f7f42b6270d89eac9fa5f8c15f691c0017fabe"}, {file = "python_docx-1.1.2.tar.gz", hash = "sha256:0cf1f22e95b9002addca7948e16f2cd7acdfd498047f1941ca5d293db7762efd"}, @@ -7430,7 +7131,6 @@ version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -7445,7 +7145,6 @@ version = "4.11.2" description = "Engine.IO server and client for Python" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "python_engineio-4.11.2-py3-none-any.whl", hash = "sha256:f0971ac4c65accc489154fe12efd88f53ca8caf04754c46a66e85f5102ef22ad"}, {file = "python_engineio-4.11.2.tar.gz", hash = "sha256:145bb0daceb904b4bb2d3eb2d93f7dbb7bb87a6a0c4f20a94cc8654dec977129"}, @@ -7465,7 +7164,6 @@ version = "1.1.0" description = "Parse and manage posts with YAML (or other) frontmatter" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "python-frontmatter-1.1.0.tar.gz", hash = "sha256:7118d2bd56af9149625745c58c9b51fb67e8d1294a0c76796dafdc72c36e5f6d"}, {file = "python_frontmatter-1.1.0-py3-none-any.whl", hash = "sha256:335465556358d9d0e6c98bbeb69b1c969f2a4a21360587b9873bfc3b213407c1"}, @@ -7484,7 +7182,6 @@ version = "3.2.1" description = "JSON Log Formatter for the Python Logging Package" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "python_json_logger-3.2.1-py3-none-any.whl", hash = "sha256:cdc17047eb5374bd311e748b42f99d71223f3b0e186f4206cc5d52aefe85b090"}, {file = "python_json_logger-3.2.1.tar.gz", hash = "sha256:8eb0554ea17cb75b05d2848bc14fb02fbdbd9d6972120781b974380bfa162008"}, @@ -7499,7 +7196,6 @@ version = "0.0.20" description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, @@ -7511,7 +7207,6 @@ version = "1.0.2" description = "Create, read, and update PowerPoint 2007+ (.pptx) files." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba"}, {file = "python_pptx-1.0.2.tar.gz", hash = "sha256:479a8af0eaf0f0d76b6f00b0887732874ad2e3188230315290cd1f9dd9cc7095"}, @@ -7529,7 +7224,6 @@ version = "5.12.1" description = "Socket.IO server and client for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "python_socketio-5.12.1-py3-none-any.whl", hash = "sha256:24a0ea7cfff0e021eb28c68edbf7914ee4111bdf030b95e4d250c4dc9af7a386"}, {file = "python_socketio-5.12.1.tar.gz", hash = "sha256:0299ff1f470b676c09c1bfab1dead25405077d227b2c13cf217a34dadc68ba9c"}, @@ -7550,7 +7244,6 @@ version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, @@ -7562,7 +7255,6 @@ version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, @@ -7583,7 +7275,6 @@ files = [ {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, ] -markers = {main = "sys_platform == \"win32\"", evaluation = "sys_platform == \"win32\"", llama-index = "platform_system == \"Windows\"", runtime = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} [[package]] name = "pywinpty" @@ -7591,8 +7282,6 @@ version = "2.0.14" description = "Pseudo terminal support for Windows from Python." optional = false python-versions = ">=3.8" -groups = ["runtime"] -markers = "os_name == \"nt\"" files = [ {file = "pywinpty-2.0.14-cp310-none-win_amd64.whl", hash = "sha256:0b149c2918c7974f575ba79f5a4aad58bd859a52fa9eb1296cc22aa412aa411f"}, {file = "pywinpty-2.0.14-cp311-none-win_amd64.whl", hash = "sha256:cf2a43ac7065b3e0dc8510f8c1f13a75fb8fde805efa3b8cff7599a1ef497bc7"}, @@ -7608,7 +7297,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "evaluation", "llama-index", "runtime"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -7671,7 +7359,6 @@ version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" -groups = ["runtime"] files = [ {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, @@ -7787,13 +7474,53 @@ files = [ [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +[[package]] +name = "qtconsole" +version = "5.6.1" +description = "Jupyter Qt console" +optional = false +python-versions = ">=3.8" +files = [ + {file = "qtconsole-5.6.1-py3-none-any.whl", hash = "sha256:3d22490d9589bace566ad4f3455b61fa2209156f40e87e19e2c3cb64e9264950"}, + {file = "qtconsole-5.6.1.tar.gz", hash = "sha256:5cad1c7e6c75d3ef8143857fd2ed28062b4b92b933c2cc328252d18a9cfd0be5"}, +] + +[package.dependencies] +ipykernel = ">=4.1" +jupyter-client = ">=4.1" +jupyter-core = "*" +packaging = "*" +pygments = "*" +qtpy = ">=2.4.0" +traitlets = "<5.2.1 || >5.2.1,<5.2.2 || >5.2.2" + +[package.extras] +doc = ["Sphinx (>=1.3)"] +test = ["flaky", "pytest", "pytest-qt"] + +[[package]] +name = "qtpy" +version = "2.4.2" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." +optional = false +python-versions = ">=3.7" +files = [ + {file = "QtPy-2.4.2-py3-none-any.whl", hash = "sha256:5a696b1dd7a354cb330657da1d17c20c2190c72d4888ba923f8461da67aa1a1c"}, + {file = "qtpy-2.4.2.tar.gz", hash = "sha256:9d6ec91a587cc1495eaebd23130f7619afa5cdd34a277acb87735b4ad7c65156"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] + [[package]] name = "redis" version = "5.2.1" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, @@ -7809,7 +7536,6 @@ version = "0.35.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "runtime"] files = [ {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, @@ -7825,7 +7551,6 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -7929,7 +7654,6 @@ version = "4.2.5" description = "The Reportlab Toolkit" optional = false python-versions = "<4,>=3.7" -groups = ["test"] files = [ {file = "reportlab-4.2.5-py3-none-any.whl", hash = "sha256:eb2745525a982d9880babb991619e97ac3f661fae30571b7d50387026ca765ee"}, {file = "reportlab-4.2.5.tar.gz", hash = "sha256:5cf35b8fd609b68080ac7bbb0ae1e376104f7d5f7b2d3914c7adc63f2593941f"}, @@ -7950,7 +7674,6 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -7973,7 +7696,6 @@ version = "2.0.0" description = "OAuthlib authentication support for Requests." optional = false python-versions = ">=3.4" -groups = ["main", "llama-index"] files = [ {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, @@ -7992,7 +7714,6 @@ version = "0.9.2" description = "Easy to use retry decorator." optional = false python-versions = "*" -groups = ["evaluation"] files = [ {file = "retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606"}, {file = "retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4"}, @@ -8008,7 +7729,6 @@ version = "0.1.4" description = "A pure python RFC3339 validator" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["runtime"] files = [ {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, @@ -8023,7 +7743,6 @@ version = "0.1.1" description = "Pure python rfc3986 validator" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["runtime"] files = [ {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, @@ -8035,7 +7754,6 @@ version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, @@ -8054,7 +7772,6 @@ version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "runtime"] files = [ {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, @@ -8167,7 +7884,6 @@ version = "4.9" description = "Pure-Python RSA implementation" optional = false python-versions = ">=3.6,<4" -groups = ["main", "llama-index"] files = [ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, @@ -8182,7 +7898,6 @@ version = "0.9.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -groups = ["dev", "evaluation"] files = [ {file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"}, {file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"}, @@ -8210,7 +7925,6 @@ version = "0.15.0" description = "The official Python library for the runloop API" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "runloop_api_client-0.15.0-py3-none-any.whl", hash = "sha256:a62bfa06ca4a2652f0a9618dbe2108d05365a9034d0bbe27218dcc358413b10f"}, {file = "runloop_api_client-0.15.0.tar.gz", hash = "sha256:53dfa7ceab2cd38f08399f011a2e3e4352c6a6ae6cb1ef9a51a23b95b8dac494"}, @@ -8230,7 +7944,6 @@ version = "0.11.1" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "s3transfer-0.11.1-py3-none-any.whl", hash = "sha256:8fa0aa48177be1f3425176dfe1ab85dcd3d962df603c3dbfc585e6bf857ef0ff"}, {file = "s3transfer-0.11.1.tar.gz", hash = "sha256:3f25c900a367c8b7f7d8f9c34edc87e300bde424f779dc9f0a8ae4f9df9264f6"}, @@ -8248,7 +7961,6 @@ version = "0.5.1" description = "" optional = false python-versions = ">=3.7" -groups = ["evaluation", "llama-index"] files = [ {file = "safetensors-0.5.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:5480b078590dd37ee1c27f153e1ee9a274b62b30871ee16c412d11341215f305"}, {file = "safetensors-0.5.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:547e9fe8f3c9c50caf07cfcb6d2392f511853f7041821812ba73a05a915e91dd"}, @@ -8286,7 +7998,6 @@ version = "0.0.4" description = "Flexible recursive directory iterator: scandir meets glob(\"**\", recursive=True)" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "scantree-0.0.4-py3-none-any.whl", hash = "sha256:7616ab65aa6b7f16fcf8e6fa1d9afaa99a27ab72bba05c61b691853b96763174"}, {file = "scantree-0.0.4.tar.gz", hash = "sha256:15bd5cb24483b04db2c70653604e8ea3522e98087db7e38ab8482f053984c0ac"}, @@ -8302,7 +8013,6 @@ version = "0.24.0" description = "Image processing in Python" optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "scikit_image-0.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb3bc0264b6ab30b43c4179ee6156bc18b4861e78bb329dd8d16537b7bbf827a"}, {file = "scikit_image-0.24.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:9c7a52e20cdd760738da38564ba1fed7942b623c0317489af1a598a8dedf088b"}, @@ -8351,7 +8061,6 @@ version = "1.6.0" description = "A set of python modules for machine learning and data mining" optional = false python-versions = ">=3.9" -groups = ["llama-index"] files = [ {file = "scikit_learn-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:366fb3fa47dce90afed3d6106183f4978d6f24cfd595c2373424171b915ee718"}, {file = "scikit_learn-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:59cd96a8d9f8dfd546f5d6e9787e1b989e981388d7803abbc9efdcde61e47460"}, @@ -8406,7 +8115,6 @@ version = "1.15.0" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "scipy-1.15.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:aeac60d3562a7bf2f35549bdfdb6b1751c50590f55ce7322b4b2fc821dc27fca"}, {file = "scipy-1.15.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5abbdc6ede5c5fed7910cf406a948e2c0869231c0db091593a6b2fa78be77e5d"}, @@ -8464,7 +8172,6 @@ version = "0.13.2" description = "Statistical data visualization" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"}, {file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"}, @@ -8486,7 +8193,6 @@ version = "1.8.3" description = "Send file to trash natively under Mac OS X, Windows and Linux" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["runtime"] files = [ {file = "Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9"}, {file = "Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf"}, @@ -8503,7 +8209,6 @@ version = "3.3.1" description = "State-of-the-Art Text Embeddings" optional = false python-versions = ">=3.9" -groups = ["llama-index"] files = [ {file = "sentence_transformers-3.3.1-py3-none-any.whl", hash = "sha256:abffcc79dab37b7d18d21a26d5914223dd42239cfe18cb5e111c66c54b658ae7"}, {file = "sentence_transformers-3.3.1.tar.gz", hash = "sha256:9635dbfb11c6b01d036b9cfcee29f7716ab64cf2407ad9f403a2e607da2ac48b"}, @@ -8531,7 +8236,6 @@ version = "75.7.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" -groups = ["main", "llama-index", "runtime"] files = [ {file = "setuptools-75.7.0-py3-none-any.whl", hash = "sha256:84fb203f278ebcf5cd08f97d3fb96d3fbed4b629d500b29ad60d11e00769b183"}, {file = "setuptools-75.7.0.tar.gz", hash = "sha256:886ff7b16cd342f1d1defc16fc98c9ce3fde69e087a4e1983d7ab634e5f41f4f"}, @@ -8552,7 +8256,6 @@ version = "2.0.6" description = "Manipulation and analysis of geometric objects" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "shapely-2.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29a34e068da2d321e926b5073539fd2a1d4429a2c656bd63f0bd4c8f5b236d0b"}, {file = "shapely-2.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c84c3f53144febf6af909d6b581bc05e8785d57e27f35ebaa5c1ab9baba13b"}, @@ -8611,7 +8314,6 @@ version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, @@ -8623,7 +8325,6 @@ version = "4.0.1" description = "Utilities for working with inspect.Signature objects." optional = false python-versions = ">=3.6" -groups = ["main", "evaluation"] files = [ {file = "sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c"}, {file = "sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c"}, @@ -8642,7 +8343,6 @@ version = "1.1.0" description = "Simple WebSocket server and client for Python" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c"}, {file = "simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"}, @@ -8661,7 +8361,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -8673,7 +8372,6 @@ version = "5.0.2" description = "A pure Python implementation of a sliding window memory map manager" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation"] files = [ {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, @@ -8685,7 +8383,6 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -8697,7 +8394,6 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -8709,7 +8405,6 @@ version = "2.0.36" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -groups = ["llama-index"] files = [ {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72"}, {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908"}, @@ -8771,10 +8466,7 @@ files = [ ] [package.dependencies] -greenlet = [ - {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}, - {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""}, -] +greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""} typing-extensions = ">=4.6.0" [package.extras] @@ -8808,7 +8500,6 @@ version = "2.2.1" description = "SSE plugin for Starlette" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99"}, {file = "sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419"}, @@ -8828,7 +8519,6 @@ version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" -groups = ["runtime"] files = [ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, @@ -8848,7 +8538,6 @@ version = "0.41.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, @@ -8866,7 +8555,6 @@ version = "1.41.1" description = "A faster way to build and share data apps" optional = false python-versions = "!=3.9.7,>=3.9" -groups = ["evaluation"] files = [ {file = "streamlit-1.41.1-py2.py3-none-any.whl", hash = "sha256:0def00822480071d642e6df36cd63c089f991da3a69fd9eb4ab8f65ce27de4e0"}, {file = "streamlit-1.41.1.tar.gz", hash = "sha256:6626d32b098ba1458b71eebdd634c62af2dd876380e59c4b6a1e828a39d62d69"}, @@ -8902,7 +8590,6 @@ version = "0.4.15" description = "An Enum that inherits from str." optional = false python-versions = "*" -groups = ["evaluation"] files = [ {file = "StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659"}, {file = "StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff"}, @@ -8919,7 +8606,6 @@ version = "0.0.26" description = "A simple library to convert rtf to text" optional = false python-versions = "*" -groups = ["llama-index"] files = [ {file = "striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb"}, {file = "striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa"}, @@ -8931,7 +8617,6 @@ version = "2.0.13" description = "The official SWE-bench package - a benchmark for evaluating LMs on software engineering" optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [] develop = false @@ -8964,7 +8649,6 @@ version = "1.13.1" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.8" -groups = ["evaluation", "llama-index"] files = [ {file = "sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8"}, {file = "sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f"}, @@ -8982,7 +8666,6 @@ version = "0.9.10" description = "Export blocking and async library versions from a single async implementation" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "synchronicity-0.9.10-py3-none-any.whl", hash = "sha256:5073064753961d930366520a4fb04d3b9acd98d6d6eba946374421a9e18ff042"}, {file = "synchronicity-0.9.10.tar.gz", hash = "sha256:7460a471190ba53c1ae3e15f9e51ed28d8bcc80df867194686ed28eaad5f19f2"}, @@ -8998,7 +8681,6 @@ version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" -groups = ["evaluation"] files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -9013,7 +8695,6 @@ version = "9.0.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, @@ -9029,7 +8710,6 @@ version = "2.5.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, @@ -9044,7 +8724,6 @@ version = "0.18.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"}, @@ -9066,7 +8745,6 @@ version = "0.7.0" description = "Hugging Face Text Generation Python Client" optional = false python-versions = "<4.0,>=3.7" -groups = ["evaluation"] files = [ {file = "text_generation-0.7.0-py3-none-any.whl", hash = "sha256:02ab337a0ee0e7c70e04a607b311c261caae74bde46a7d837c6fdd150108f4d8"}, {file = "text_generation-0.7.0.tar.gz", hash = "sha256:689200cd1f0d4141562af2515393c2c21cdbd9fac21c8398bf3043cdcc14184e"}, @@ -9083,7 +8761,6 @@ version = "3.5.0" description = "threadpoolctl" optional = false python-versions = ">=3.8" -groups = ["llama-index"] files = [ {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, @@ -9095,7 +8772,6 @@ version = "2024.9.20" description = "Read and write TIFF files" optional = false python-versions = ">=3.10" -groups = ["evaluation"] files = [ {file = "tifffile-2024.9.20-py3-none-any.whl", hash = "sha256:c54dc85bc1065d972cb8a6ffb3181389d597876aa80177933459733e4ed243dd"}, {file = "tifffile-2024.9.20.tar.gz", hash = "sha256:3fbf3be2f995a7051a8ae05a4be70c96fc0789f22ed6f1c4104c973cf68a640b"}, @@ -9118,7 +8794,6 @@ version = "0.8.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "tiktoken-0.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b07e33283463089c81ef1467180e3e00ab00d46c2c4bbcef0acab5f771d6695e"}, {file = "tiktoken-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9269348cb650726f44dd3bbb3f9110ac19a8dcc8f54949ad3ef652ca22a38e21"}, @@ -9166,7 +8841,6 @@ version = "1.4.0" description = "A tiny CSS parser" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289"}, {file = "tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7"}, @@ -9185,7 +8859,6 @@ version = "0.21.0" description = "" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, @@ -9218,7 +8891,6 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -groups = ["main", "evaluation"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -9230,7 +8902,6 @@ version = "2.5.1" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" optional = false python-versions = ">=3.8.0" -groups = ["llama-index"] files = [ {file = "torch-2.5.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:71328e1bbe39d213b8721678f9dcac30dfc452a46d586f1d514a6aa0a99d4744"}, {file = "torch-2.5.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:34bfa1a852e5714cbfa17f27c49d8ce35e1b7af5608c4bc6e81392c352dbc601"}, @@ -9283,7 +8954,6 @@ version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" -groups = ["main", "evaluation", "runtime"] files = [ {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, @@ -9304,7 +8974,6 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -9326,7 +8995,6 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, @@ -9342,7 +9010,6 @@ version = "4.47.1" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = false python-versions = ">=3.9.0" -groups = ["evaluation", "llama-index"] files = [ {file = "transformers-4.47.1-py3-none-any.whl", hash = "sha256:d2f5d19bb6283cd66c893ec7e6d931d6370bbf1cc93633326ff1f41a40046c9c"}, {file = "transformers-4.47.1.tar.gz", hash = "sha256:6c29c05a5f595e278481166539202bf8641281536df1c42357ee58a45d0a564a"}, @@ -9412,7 +9079,6 @@ version = "0.21.3" description = "Python bindings for the Tree-Sitter parsing library" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "tree-sitter-0.21.3.tar.gz", hash = "sha256:b5de3028921522365aa864d95b3c41926e0ba6a85ee5bd000e10dc49b0766988"}, {file = "tree_sitter-0.21.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:351f302b6615230c9dac9829f0ba20a94362cd658206ca9a7b2d58d73373dfb0"}, @@ -9458,7 +9124,6 @@ version = "1.10.2" description = "Binary Python wheels for all tree sitter languages." optional = false python-versions = "*" -groups = ["main"] files = [ {file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5580348f0b20233b1d5431fa178ccd3d07423ca4a3275df02a44608fd72344b9"}, {file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:103c7466644486b1e9e03850df46fc6aa12f13ca636c74f173270276220ac80b"}, @@ -9530,8 +9195,6 @@ version = "3.1.0" description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "*" -groups = ["llama-index"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\"" files = [ {file = "triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8"}, {file = "triton-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f34f6e7885d1bf0eaaf7ba875a5f0ce6f3c13ba98f9503651c1e6dc6757ed5c"}, @@ -9554,7 +9217,6 @@ version = "0.15.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, @@ -9572,7 +9234,6 @@ version = "2021.10.8.3" description = "Typing stubs for certifi" optional = false python-versions = "*" -groups = ["main", "evaluation"] files = [ {file = "types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f"}, {file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"}, @@ -9584,7 +9245,6 @@ version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" -groups = ["runtime"] files = [ {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, @@ -9596,7 +9256,6 @@ version = "2.32.0.20241016" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, @@ -9611,7 +9270,6 @@ version = "0.10.8.20240310" description = "Typing stubs for toml" optional = false python-versions = ">=3.8" -groups = ["main", "evaluation"] files = [ {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, @@ -9623,7 +9281,6 @@ version = "4.67.0.20241221" description = "Typing stubs for tqdm" optional = false python-versions = ">=3.8" -groups = ["evaluation"] files = [ {file = "types_tqdm-4.67.0.20241221-py3-none-any.whl", hash = "sha256:a1f1c9cda5c2d8482d2c73957a5398bfdedda10f6bc7b3b4e812d5c910486d29"}, {file = "types_tqdm-4.67.0.20241221.tar.gz", hash = "sha256:e56046631056922385abe89aeb18af5611f471eadd7918a0ad7f34d84cd4c8cc"}, @@ -9638,12 +9295,10 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "dev", "evaluation", "llama-index", "runtime", "test"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {runtime = "python_version < \"3.13\""} [[package]] name = "typing-inspect" @@ -9651,7 +9306,6 @@ version = "0.9.0" description = "Runtime inspection utilities for typing module." optional = false python-versions = "*" -groups = ["evaluation", "llama-index"] files = [ {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, @@ -9667,7 +9321,6 @@ version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" -groups = ["main", "evaluation", "llama-index", "test"] files = [ {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, @@ -9679,7 +9332,6 @@ version = "0.7.5" description = "Unified diff parsing/metadata extraction library." optional = false python-versions = "*" -groups = ["evaluation"] files = [ {file = "unidiff-0.7.5-py2.py3-none-any.whl", hash = "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8"}, {file = "unidiff-0.7.5.tar.gz", hash = "sha256:2e5f0162052248946b9f0970a40e9e124236bf86c82b70821143a6fc1dea2574"}, @@ -9691,7 +9343,6 @@ version = "1.3.0" description = "RFC 6570 URI Template Processor" optional = false python-versions = ">=3.7" -groups = ["runtime"] files = [ {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, @@ -9706,7 +9357,6 @@ version = "4.1.1" description = "Implementation of RFC 6570 URI Templates" optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, @@ -9718,7 +9368,6 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index", "runtime"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -9736,7 +9385,6 @@ version = "0.34.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" -groups = ["main", "llama-index"] files = [ {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, @@ -9762,8 +9410,6 @@ version = "0.21.0" description = "Fast implementation of asyncio event loop on top of libuv" optional = false python-versions = ">=3.8.0" -groups = ["llama-index"] -markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\"" files = [ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, @@ -9815,7 +9461,6 @@ version = "20.28.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" -groups = ["dev", "evaluation"] files = [ {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, @@ -9836,7 +9481,6 @@ version = "0.2.4" description = "" optional = false python-versions = "<4.0.0,>=3.7.1" -groups = ["llama-index"] files = [ {file = "voyageai-0.2.4-py3-none-any.whl", hash = "sha256:e3070e5c78dec89adae43231334b4637aa88933dad99b1c33d3219fdfc94dfa4"}, {file = "voyageai-0.2.4.tar.gz", hash = "sha256:b9911d8629e8a4e363291c133482fead49a3536afdf1e735f3ab3aaccd8d250d"}, @@ -9855,8 +9499,6 @@ version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" -groups = ["evaluation"] -markers = "platform_system != \"Darwin\"" files = [ {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, @@ -9899,7 +9541,6 @@ version = "1.0.3" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "watchfiles-1.0.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42"}, {file = "watchfiles-1.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3"}, @@ -9983,7 +9624,6 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" -groups = ["runtime"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -9995,7 +9635,6 @@ version = "24.11.1" description = "A library for working with the color formats defined by HTML and CSS." optional = false python-versions = ">=3.9" -groups = ["runtime"] files = [ {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, @@ -10007,7 +9646,6 @@ version = "0.5.1" description = "Character encoding aliases for legacy web content" optional = false python-versions = "*" -groups = ["runtime"] files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, @@ -10019,7 +9657,6 @@ version = "1.8.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" -groups = ["llama-index", "runtime"] files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -10036,7 +9673,6 @@ version = "14.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" -groups = ["llama-index"] files = [ {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, @@ -10115,7 +9751,6 @@ version = "3.1.3" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" -groups = ["evaluation"] files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -10133,19 +9768,28 @@ version = "1.0.7" description = "A patch parsing and application library." optional = false python-versions = ">=3.9" -groups = ["main", "evaluation"] files = [ {file = "whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed"}, {file = "whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf"}, ] +[[package]] +name = "widgetsnbextension" +version = "4.0.13" +description = "Jupyter interactive widgets for Jupyter Notebook" +optional = false +python-versions = ">=3.7" +files = [ + {file = "widgetsnbextension-4.0.13-py3-none-any.whl", hash = "sha256:74b2692e8500525cc38c2b877236ba51d34541e6385eeed5aec15a70f88a6c71"}, + {file = "widgetsnbextension-4.0.13.tar.gz", hash = "sha256:ffcb67bc9febd10234a362795f643927f4e0c05d9342c727b65d2384f8feacb6"}, +] + [[package]] name = "wrapt" version = "1.17.0" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" -groups = ["main", "llama-index"] files = [ {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, @@ -10220,7 +9864,6 @@ version = "1.2.0" description = "WebSockets state-machine based protocol implementation" optional = false python-versions = ">=3.7.0" -groups = ["main"] files = [ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, @@ -10235,7 +9878,6 @@ version = "3.2.0" description = "A Python module for creating Excel XLSX files." optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "XlsxWriter-3.2.0-py3-none-any.whl", hash = "sha256:ecfd5405b3e0e228219bcaf24c2ca0915e012ca9464a14048021d21a995d490e"}, {file = "XlsxWriter-3.2.0.tar.gz", hash = "sha256:9977d0c661a72866a61f9f7a809e25ebbb0fb7036baa3b9fe74afcfca6b3cb8c"}, @@ -10247,7 +9889,6 @@ version = "3.5.0" description = "Python binding for xxHash" optional = false python-versions = ">=3.7" -groups = ["main", "evaluation"] files = [ {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"}, {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"}, @@ -10380,7 +10021,6 @@ version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" -groups = ["main", "evaluation", "llama-index"] files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -10477,7 +10117,6 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["main", "llama-index"] files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, @@ -10497,7 +10136,6 @@ version = "5.0" description = "Very basic event publishing system" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, @@ -10516,7 +10154,6 @@ version = "7.2" description = "Interfaces for Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -10566,6 +10203,6 @@ test = ["coverage[toml]", "zope.event", "zope.testing"] testing = ["coverage[toml]", "zope.event", "zope.testing"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = "^3.12" -content-hash = "109bcb06fade3936184b0c8c1f80b9b8a5bced97d021d22ca35b473c0d05eda0" +content-hash = "5289eb4f65267d3efe7cc1f722b6f4b5820793bc82e2e8be8b85694b2df91f58" From c459dafdc8281ea58a4c0951b01beffb1072dbf3 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Wed, 5 Feb 2025 16:19:57 -0500 Subject: [PATCH 76/90] fix test bash --- tests/runtime/test_bash.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index 6356e3928942..971f92a8a131 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -6,7 +6,6 @@ import pytest from conftest import ( - TEST_IN_CI, _close_test_runtime, _load_runtime, ) @@ -464,6 +463,14 @@ def test_git_operation(runtime_cls): if runtime_cls != LocalRuntime: obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') assert obs.exit_code == 0 + else: + # cd into a temp directory + obs = _run_cmd_action( + runtime, + 'mkdir -p /tmp/openhands-test-dir && cd /tmp/openhands-test-dir', + ) + logger.info(obs, extra={'msg_type': 'OBSERVATION'}) + assert obs.exit_code == 0 # check the ownership of the current directory obs = _run_cmd_action(runtime, 'ls -alh .') @@ -492,7 +499,7 @@ def test_git_operation(runtime_cls): obs = _run_cmd_action(runtime, 'echo "hello" > test_file.txt') assert obs.exit_code == 0 - if runtime_cls != LocalRuntime or TEST_IN_CI: + if runtime_cls == LocalRuntime: # set git config author in CI only, not on local machine logger.info('Setting git config author') obs = _run_cmd_action( From 2f666e2ca857e624dcf7d3d33e14f234e6ab1a0b Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Wed, 5 Feb 2025 16:22:55 -0500 Subject: [PATCH 77/90] use temp dir for git operation test --- tests/runtime/test_bash.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index 971f92a8a131..c9f6950a55c9 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -1,6 +1,8 @@ """Bash-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import os +import shutil +import tempfile import time from pathlib import Path @@ -463,11 +465,13 @@ def test_git_operation(runtime_cls): if runtime_cls != LocalRuntime: obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') assert obs.exit_code == 0 + tmp_dir = None else: # cd into a temp directory + tmp_dir = tempfile.mkdtemp() obs = _run_cmd_action( runtime, - 'mkdir -p /tmp/openhands-test-dir && cd /tmp/openhands-test-dir', + f'cd {tmp_dir}', ) logger.info(obs, extra={'msg_type': 'OBSERVATION'}) assert obs.exit_code == 0 @@ -523,6 +527,8 @@ def test_git_operation(runtime_cls): obs = _run_cmd_action(runtime, 'git commit -m "test commit"') assert obs.exit_code == 0 finally: + if tmp_dir: + shutil.rmtree(tmp_dir) _close_test_runtime(runtime) From 035ed530b3a9b86b8a0b3749cb9303d2e7f61ff7 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Wed, 5 Feb 2025 16:24:33 -0500 Subject: [PATCH 78/90] set git config --- tests/runtime/test_bash.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index c9f6950a55c9..c2bdb7a16ef3 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -512,7 +512,10 @@ def test_git_operation(runtime_cls): ) assert obs.exit_code == 0 - # git add + # Set up git config + obs = _run_cmd_action(runtime, 'git config --file ./.git_config') + assert obs.exit_code == 0 + # git add obs = _run_cmd_action(runtime, 'git add test_file.txt') assert obs.exit_code == 0 From 92aa08e7fc20a933c170b5ee131af1b9f7dc1ea0 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Wed, 5 Feb 2025 16:28:51 -0500 Subject: [PATCH 79/90] run in temp dir for git operation --- tests/runtime/test_bash.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index c2bdb7a16ef3..d4f3048c67fa 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -2,7 +2,6 @@ import os import shutil -import tempfile import time from pathlib import Path @@ -448,12 +447,12 @@ def test_copy_from_directory(temp_dir, runtime_cls): _close_test_runtime(runtime) -def test_git_operation(runtime_cls): +def test_git_operation(temp_dir, runtime_cls): # do not mount workspace, since workspace mount by tests will be owned by root # while the user_id we get via os.getuid() is different from root # which causes permission issues runtime, config = _load_runtime( - temp_dir=None, + temp_dir=temp_dir, use_workspace=False, runtime_cls=runtime_cls, # Need to use non-root user to expose issues @@ -466,15 +465,6 @@ def test_git_operation(runtime_cls): obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') assert obs.exit_code == 0 tmp_dir = None - else: - # cd into a temp directory - tmp_dir = tempfile.mkdtemp() - obs = _run_cmd_action( - runtime, - f'cd {tmp_dir}', - ) - logger.info(obs, extra={'msg_type': 'OBSERVATION'}) - assert obs.exit_code == 0 # check the ownership of the current directory obs = _run_cmd_action(runtime, 'ls -alh .') From 46008f7f7e8f53b35bbb4faf1993e51de1f09700 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 6 Feb 2025 00:34:12 -0500 Subject: [PATCH 80/90] fix runtime tests --- tests/runtime/test_env_vars.py | 2 +- tests/runtime/test_replay.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/runtime/test_env_vars.py b/tests/runtime/test_env_vars.py index 277ff52bfdd3..36fb5fb48716 100644 --- a/tests/runtime/test_env_vars.py +++ b/tests/runtime/test_env_vars.py @@ -86,7 +86,7 @@ def test_env_vars_added_by_config(temp_dir, runtime_cls): def test_docker_runtime_env_vars_persist_after_restart(temp_dir): from openhands.runtime.impl.docker.docker_runtime import DockerRuntime - runtime = _load_runtime(temp_dir, DockerRuntime) + runtime, config = _load_runtime(temp_dir, DockerRuntime) # Add a test environment variable runtime.add_env_vars({'GITHUB_TOKEN': 'test_token'}) diff --git a/tests/runtime/test_replay.py b/tests/runtime/test_replay.py index 9d91a12f23d6..ca4aeed9d76d 100644 --- a/tests/runtime/test_replay.py +++ b/tests/runtime/test_replay.py @@ -58,7 +58,7 @@ def test_simple_gui_replay(temp_dir, runtime_cls, run_as_openhands): 2. In GUI mode, agents typically don't finish; rather, they wait for the next task from the user, so this exported trajectory ends with awaiting_user_input """ - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) config = _get_config('basic_gui_mode') @@ -118,7 +118,7 @@ def test_replay_basic_interactions(temp_dir, runtime_cls, run_as_openhands): interference (no asking for user input). 2) The user messages in the trajectory should appear in the history. """ - runtime = _load_runtime(temp_dir, runtime_cls, run_as_openhands) + runtime, config = _load_runtime(temp_dir, runtime_cls, run_as_openhands) config = _get_config('basic_interactions') From 494bee28df356a63090013c71365eb98522abd0d Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 6 Feb 2025 00:34:48 -0500 Subject: [PATCH 81/90] simplify test --- .github/workflows/py-unit-tests.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 0a80b21deea2..a8f96665965a 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -89,16 +89,13 @@ jobs: poetry run playwright install --with-deps chromium - name: Run Local Runtime Tests run: | - # We install pytest-xdist in order to run tests across CPUs - poetry run pip install pytest-xdist - # Install to be able to retry on failures for flaky tests poetry run pip install pytest-rerunfailures TEST_RUNTIME=local \ TEST_IN_CI=true \ RUN_AS_OPENHANDS=false \ - poetry run pytest -n 1 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime/test_bash.py + poetry run pytest -raRs --cov=openhands --cov-report=xml -s ./tests/runtime/test_bash.py - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 env: From 6bb65ebd714a2fd9c101238689583ea568749ab5 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 6 Feb 2025 12:24:41 -0500 Subject: [PATCH 82/90] remove manual clean up of tmp_dir --- tests/runtime/test_bash.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/runtime/test_bash.py b/tests/runtime/test_bash.py index d4f3048c67fa..48dae5c422c9 100644 --- a/tests/runtime/test_bash.py +++ b/tests/runtime/test_bash.py @@ -1,7 +1,6 @@ """Bash-related tests for the DockerRuntime, which connects to the ActionExecutor running in the sandbox.""" import os -import shutil import time from pathlib import Path @@ -464,7 +463,6 @@ def test_git_operation(temp_dir, runtime_cls): if runtime_cls != LocalRuntime: obs = _run_cmd_action(runtime, 'sudo chown -R openhands:root .') assert obs.exit_code == 0 - tmp_dir = None # check the ownership of the current directory obs = _run_cmd_action(runtime, 'ls -alh .') @@ -520,8 +518,6 @@ def test_git_operation(temp_dir, runtime_cls): obs = _run_cmd_action(runtime, 'git commit -m "test commit"') assert obs.exit_code == 0 finally: - if tmp_dir: - shutil.rmtree(tmp_dir) _close_test_runtime(runtime) From 0246f33daa83275f4bc3350138432d19ea7d84ca Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 6 Feb 2025 12:25:01 -0500 Subject: [PATCH 83/90] remove local runtime tests --- .github/workflows/py-unit-tests.yml | 51 ----------------------------- 1 file changed, 51 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index a8f96665965a..1e42ccc2d82c 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -49,54 +49,3 @@ jobs: uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - # Run python unit tests on Linux - test-on-linux-local-runtime: - name: Local Runtime Tests on Linux - runs-on: ubuntu-22.04 - env: - INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation - strategy: - matrix: - python-version: ['3.12'] - steps: - - uses: actions/checkout@v4 - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - - name: Install tmux - run: sudo apt-get update && sudo apt-get install -y tmux - - name: Dependency fix - run: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update -y - sudo apt-get install --only-upgrade libstdc++6 -y - - name: Install poetry via pipx - run: pipx install poetry - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'poetry' - - name: Install Python dependencies using Poetry - run: poetry install --without evaluation,llama-index - - name: Build Environment - run: | - make build - poetry run playwright install --with-deps chromium - - name: Run Local Runtime Tests - run: | - # Install to be able to retry on failures for flaky tests - poetry run pip install pytest-rerunfailures - - TEST_RUNTIME=local \ - TEST_IN_CI=true \ - RUN_AS_OPENHANDS=false \ - poetry run pytest -raRs --cov=openhands --cov-report=xml -s ./tests/runtime/test_bash.py - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 050c06264c5c6b1fa37d0de4368b9a1c75050d55 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 6 Feb 2025 12:26:38 -0500 Subject: [PATCH 84/90] remove extra log buffer --- openhands/runtime/utils/log_buffer.py | 64 --------------------------- 1 file changed, 64 deletions(-) delete mode 100644 openhands/runtime/utils/log_buffer.py diff --git a/openhands/runtime/utils/log_buffer.py b/openhands/runtime/utils/log_buffer.py deleted file mode 100644 index 141d91dde220..000000000000 --- a/openhands/runtime/utils/log_buffer.py +++ /dev/null @@ -1,64 +0,0 @@ -import threading -from typing import Callable - - -class LogBuffer: - """Synchronous buffer for Docker container logs. - - This class provides a thread-safe way to collect, store, and retrieve logs - from a Docker container. It uses a list to store log lines and provides methods - for appending, retrieving, and clearing logs. - """ - - def __init__(self, container, logFn: Callable): - self.init_msg = 'Runtime client initialized.' - - self.buffer: list[str] = [] - self.lock = threading.Lock() - self._stop_event = threading.Event() - self.log_generator = container.logs(stream=True, follow=True) - self.log_stream_thread = threading.Thread(target=self.stream_logs) - self.log_stream_thread.daemon = True - self.log_stream_thread.start() - self.log = logFn - - def append(self, log_line: str): - with self.lock: - self.buffer.append(log_line) - - def get_and_clear(self) -> list[str]: - with self.lock: - logs = list(self.buffer) - self.buffer.clear() - return logs - - def stream_logs(self): - """Stream logs from the Docker container in a separate thread. - - This method runs in its own thread to handle the blocking - operation of reading log lines from the Docker SDK's synchronous generator. - """ - try: - for log_line in self.log_generator: - if self._stop_event.is_set(): - break - if log_line: - decoded_line = log_line.decode('utf-8').rstrip() - self.append(decoded_line) - except Exception as e: - self.log('error', f'Error streaming docker logs: {e}') - - def __del__(self): - if self.log_stream_thread.is_alive(): - self.log( - 'warn', - "LogBuffer was not properly closed. Use 'log_buffer.close()' for clean shutdown.", - ) - self.close(timeout=5) - - def close(self, timeout: float = 5.0): - self._stop_event.set() - self.log_stream_thread.join(timeout) - # Close the log generator to release the file descriptor - if hasattr(self.log_generator, 'close'): - self.log_generator.close() From ac562b6eee9fbc9c2995ec3ba624073af279cebb Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Thu, 6 Feb 2025 12:30:02 -0500 Subject: [PATCH 85/90] add unit test with parallism again --- .github/workflows/py-unit-tests.yml | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 1e42ccc2d82c..2f7cd1106ab6 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -49,3 +49,57 @@ jobs: uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # Run python unit tests on Linux + test-on-linux-local-runtime: + name: Local Runtime Tests on Linux + runs-on: ubuntu-22.04 + env: + INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation + strategy: + matrix: + python-version: ['3.12'] + steps: + - uses: actions/checkout@v4 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Install tmux + run: sudo apt-get update && sudo apt-get install -y tmux + - name: Dependency fix + run: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt-get update -y + sudo apt-get install --only-upgrade libstdc++6 -y + - name: Install poetry via pipx + run: pipx install poetry + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'poetry' + - name: Install Python dependencies using Poetry + run: poetry install --without evaluation,llama-index + - name: Build Environment + run: | + make build + poetry run playwright install --with-deps chromium + - name: Run Local Runtime Tests + run: | + # We install pytest-xdist in order to run tests across CPUs + poetry run pip install pytest-xdist + + # Install to be able to retry on failures for flaky tests + poetry run pip install pytest-rerunfailures + + TEST_RUNTIME=local \ + TEST_IN_CI=true \ + RUN_AS_OPENHANDS=false \ + poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime/test_bash.py + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 218ef2a91ca8b885c207ca568fa8930bce9e4635 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 7 Feb 2025 11:10:01 -0500 Subject: [PATCH 86/90] Revert "add unit test with parallism again" This reverts commit ac562b6eee9fbc9c2995ec3ba624073af279cebb. --- .github/workflows/py-unit-tests.yml | 54 ----------------------------- 1 file changed, 54 deletions(-) diff --git a/.github/workflows/py-unit-tests.yml b/.github/workflows/py-unit-tests.yml index 2f7cd1106ab6..1e42ccc2d82c 100644 --- a/.github/workflows/py-unit-tests.yml +++ b/.github/workflows/py-unit-tests.yml @@ -49,57 +49,3 @@ jobs: uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - # Run python unit tests on Linux - test-on-linux-local-runtime: - name: Local Runtime Tests on Linux - runs-on: ubuntu-22.04 - env: - INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation - strategy: - matrix: - python-version: ['3.12'] - steps: - - uses: actions/checkout@v4 - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - - name: Install tmux - run: sudo apt-get update && sudo apt-get install -y tmux - - name: Dependency fix - run: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update -y - sudo apt-get install --only-upgrade libstdc++6 -y - - name: Install poetry via pipx - run: pipx install poetry - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'poetry' - - name: Install Python dependencies using Poetry - run: poetry install --without evaluation,llama-index - - name: Build Environment - run: | - make build - poetry run playwright install --with-deps chromium - - name: Run Local Runtime Tests - run: | - # We install pytest-xdist in order to run tests across CPUs - poetry run pip install pytest-xdist - - # Install to be able to retry on failures for flaky tests - poetry run pip install pytest-rerunfailures - - TEST_RUNTIME=local \ - TEST_IN_CI=true \ - RUN_AS_OPENHANDS=false \ - poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime/test_bash.py - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 4c6a421945ca8d1825e6a5cebf1698c18bb2b25c Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sat, 8 Feb 2025 00:10:24 +0800 Subject: [PATCH 87/90] Update openhands/runtime/README.md Co-authored-by: Engel Nyst --- openhands/runtime/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/README.md b/openhands/runtime/README.md index 6bdba4a40af2..5a4c1bd0f4fa 100644 --- a/openhands/runtime/README.md +++ b/openhands/runtime/README.md @@ -115,7 +115,7 @@ This is the default runtime used within OpenHands. ### Local Runtime -The Local Runtime is designed for direct execution on the local machine. Currently only supports running as `root`: +The Local Runtime is designed for direct execution on the local machine. Currently only supports running as the local user: - Runs the action_execution_server directly on the host - No Docker container overhead From f25ce1c10740bc78e735aac24dd3a1177f81fde0 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Sat, 8 Feb 2025 00:10:37 +0800 Subject: [PATCH 88/90] Update openhands/runtime/impl/local/local_runtime.py Co-authored-by: Engel Nyst --- openhands/runtime/impl/local/local_runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index ef8271c73fae..fa40c985c87d 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -293,7 +293,7 @@ def _wait_until_alive(self): async def execute_action(self, action: Action) -> Observation: """Execute an action by sending it to the server.""" if not self._runtime_initialized: - return ErrorObservation('Runtime not initialized') + raise AgentRuntimeDisconnectedError('Runtime not initialized') if self.server_process is None or self.server_process.poll() is not None: return ErrorObservation('Server process died') From f33f6917ae5950f1e2df7121e87a0933557541b2 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 7 Feb 2025 11:12:15 -0500 Subject: [PATCH 89/90] address engel comment --- openhands/runtime/impl/local/local_runtime.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index fa40c985c87d..bd599460fd98 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -21,7 +21,6 @@ Action, ) from openhands.events.observation import ( - ErrorObservation, Observation, ) from openhands.events.serialization import event_to_dict, observation_from_dict @@ -296,7 +295,7 @@ async def execute_action(self, action: Action) -> Observation: raise AgentRuntimeDisconnectedError('Runtime not initialized') if self.server_process is None or self.server_process.poll() is not None: - return ErrorObservation('Server process died') + raise AgentRuntimeDisconnectedError('Server process died') with self.action_semaphore: try: @@ -309,8 +308,6 @@ async def execute_action(self, action: Action) -> Observation: return observation_from_dict(response.json()) except requests.exceptions.ConnectionError: raise AgentRuntimeDisconnectedError('Server connection lost') - except requests.exceptions.RequestException as e: - return ErrorObservation(f'Failed to execute action: {e}') def close(self): """Stop the server process.""" From 350e1bb530ad0ccad2ff366750cd1b901c4e9b23 Mon Sep 17 00:00:00 2001 From: Xingyao Wang Date: Fri, 7 Feb 2025 11:16:44 -0500 Subject: [PATCH 90/90] add experimental feature text --- openhands/runtime/impl/local/local_runtime.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openhands/runtime/impl/local/local_runtime.py b/openhands/runtime/impl/local/local_runtime.py index bd599460fd98..547f2981d515 100644 --- a/openhands/runtime/impl/local/local_runtime.py +++ b/openhands/runtime/impl/local/local_runtime.py @@ -128,6 +128,7 @@ def __init__( logger.warning( 'Initializing LocalRuntime. WARNING: NO SANDBOX IS USED. ' + 'This is an experimental feature, please report issues to https://github.com/All-Hands-AI/OpenHands/issues. ' '`run_as_openhands` will be ignored since the current user will be used to launch the server. ' 'We highly recommend using a sandbox (eg. DockerRuntime) unless you ' 'are running in a controlled environment.\n'