mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1da841fd49 |
+1
-1
@@ -5,7 +5,7 @@
|
||||
/frontend/ @rbren @amanape
|
||||
|
||||
# Evaluation code owners
|
||||
/evaluation/ @xingyaoww @neubig
|
||||
/evaluation/ @xingyaoww @neubig
|
||||
|
||||
# Documentation code owners
|
||||
/docs/ @mamoodi
|
||||
|
||||
+1
-14
@@ -161,6 +161,7 @@ cython_debug/
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
.cursorignore
|
||||
|
||||
# VS Code: Ignore all but certain files that specify repo-specific settings.
|
||||
# https://stackoverflow.com/questions/32964920/should-i-commit-the-vscode-folder-to-source-control
|
||||
@@ -170,20 +171,6 @@ cython_debug/
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
|
||||
# VS Code extensions/forks:
|
||||
.cursorignore
|
||||
.rooignore
|
||||
.clineignore
|
||||
.windsurfignore
|
||||
.cursorrules
|
||||
.roorules
|
||||
.clinerules
|
||||
.windsurfrules
|
||||
.cursor/rules
|
||||
.roo/rules
|
||||
.cline/rules
|
||||
.windsurf/rules
|
||||
|
||||
# evaluation
|
||||
evaluation/evaluation_outputs
|
||||
evaluation/outputs
|
||||
|
||||
@@ -26,6 +26,7 @@ Backend:
|
||||
- Located in the `openhands` directory
|
||||
- Testing:
|
||||
- All tests are in `tests/unit/test_*.py`
|
||||
- To run all the unit tests, run `poetry run pytest --forked -n auto -svv ./tests/unit`
|
||||
- To test new code, run `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate file for the current functionality
|
||||
- Write all tests with pytest
|
||||
|
||||
|
||||
@@ -178,4 +178,4 @@ interface OpenHandsEvent {
|
||||
### Event Handling Issues
|
||||
|
||||
- Check that you're correctly parsing the event data
|
||||
- Verify that your event handlers are properly registered
|
||||
- Verify that your event handlers are properly registered
|
||||
@@ -1,6 +1,6 @@
|
||||
# Azure
|
||||
|
||||
OpenHands uses LiteLLM to make calls to Azure's chat models. You can find their documentation on using Azure as a
|
||||
OpenHands uses LiteLLM to make calls to Azure's chat models. You can find their documentation on using Azure as a
|
||||
provider [here](https://docs.litellm.ai/docs/providers/azure).
|
||||
|
||||
## Azure OpenAI Configuration
|
||||
|
||||
@@ -10,7 +10,7 @@ OpenHands uses LiteLLM to make calls to Google's chat models. You can find their
|
||||
When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings under the `LLM` tab:
|
||||
- `LLM Provider` to `Gemini`
|
||||
- `LLM Model` to the model you will be using.
|
||||
If the model is not in the list, enable `Advanced` options, and enter it in `Custom Model`
|
||||
If the model is not in the list, enable `Advanced` options, and enter it in `Custom Model`
|
||||
(e.g. gemini/<model-name> like `gemini/gemini-2.0-flash`).
|
||||
- `API Key` to your Gemini API key
|
||||
|
||||
@@ -28,5 +28,5 @@ VERTEXAI_LOCATION="<your-gcp-location>"
|
||||
Then set the following in the OpenHands UI through the Settings under the `LLM` tab:
|
||||
- `LLM Provider` to `VertexAI`
|
||||
- `LLM Model` to the model you will be using.
|
||||
If the model is not in the list, enable `Advanced` options, and enter it in `Custom Model`
|
||||
If the model is not in the list, enable `Advanced` options, and enter it in `Custom Model`
|
||||
(e.g. vertex_ai/<model-name>).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Groq
|
||||
|
||||
OpenHands uses LiteLLM to make calls to chat models on Groq. You can find their documentation on using Groq as a
|
||||
OpenHands uses LiteLLM to make calls to chat models on Groq. You can find their documentation on using Groq as a
|
||||
provider [here](https://docs.litellm.ai/docs/providers/groq).
|
||||
|
||||
## Configuration
|
||||
@@ -8,7 +8,7 @@ provider [here](https://docs.litellm.ai/docs/providers/groq).
|
||||
When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings under the `LLM` tab:
|
||||
- `LLM Provider` to `Groq`
|
||||
- `LLM Model` to the model you will be using. [Visit here to see the list of
|
||||
models that Groq hosts](https://console.groq.com/docs/models). If the model is not in the list,
|
||||
models that Groq hosts](https://console.groq.com/docs/models). If the model is not in the list,
|
||||
enable `Advanced` options, and enter it in `Custom Model` (e.g. groq/<model-name> like `groq/llama3-70b-8192`).
|
||||
- `API key` to your Groq API key. To find or create your Groq API Key, [see here](https://console.groq.com/keys).
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ To use LiteLLM proxy with OpenHands, you need to:
|
||||
|
||||
## Supported Models
|
||||
|
||||
The supported models depend on your LiteLLM proxy configuration. OpenHands supports any model that your LiteLLM proxy
|
||||
The supported models depend on your LiteLLM proxy configuration. OpenHands supports any model that your LiteLLM proxy
|
||||
is configured to handle.
|
||||
|
||||
Refer to your LiteLLM proxy configuration for the list of available models and their names.
|
||||
|
||||
@@ -25,7 +25,7 @@ OpenHands will issue many prompts to the LLM you configure. Most of these LLMs c
|
||||
limits and monitor usage.
|
||||
:::
|
||||
|
||||
If you have successfully run OpenHands with specific providers, we encourage you to open a PR to share your setup process
|
||||
If you have successfully run OpenHands with specific providers, we encourage you to open a PR to share your setup process
|
||||
to help others using the same provider!
|
||||
|
||||
For a full list of the providers and models available, please consult the
|
||||
|
||||
@@ -25,7 +25,7 @@ We recommend using [LMStudio](https://lmstudio.ai/) for serving these models loc
|
||||
- Option 2: Download a LLM in GGUF format. For example, to download [Devstral Small 2505 GGUF](https://huggingface.co/mistralai/Devstral-Small-2505_gguf), using `huggingface-cli download mistralai/Devstral-Small-2505_gguf --local-dir mistralai/Devstral-Small-2505_gguf`. Then in bash terminal, run `lms import {model_name}` in the directory where you've downloaded the model checkpoint (e.g. run `lms import devstralQ4_K_M.gguf` in `mistralai/Devstral-Small-2505_gguf`)
|
||||
|
||||
3. Open LM Studio application, you should first switch to `power user` mode, and then open the developer tab:
|
||||
|
||||
|
||||

|
||||
|
||||
4. Then click `Select a model to load` on top of the application:
|
||||
@@ -154,7 +154,7 @@ Start OpenHands using `make run`.
|
||||
|
||||
### Configure OpenHands
|
||||
|
||||
Once OpenHands is running, you'll need to set the following in the OpenHands UI through the Settings under the `LLM` tab:
|
||||
Once OpenHands is running, you'll need to set the following in the OpenHands UI through the Settings under the `LLM` tab:
|
||||
1. Enable `Advanced` options.
|
||||
2. Set the following:
|
||||
- `Custom Model` to `openai/<served-model-name>` (e.g. `openai/openhands-lm-32b-v0.1`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OpenAI
|
||||
|
||||
OpenHands uses LiteLLM to make calls to OpenAI's chat models. You can find their documentation on using OpenAI as a
|
||||
OpenHands uses LiteLLM to make calls to OpenAI's chat models. You can find their documentation on using OpenAI as a
|
||||
provider [here](https://docs.litellm.ai/docs/providers/openai).
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OpenRouter
|
||||
|
||||
OpenHands uses LiteLLM to make calls to chat models on OpenRouter. You can find their documentation on using
|
||||
OpenHands uses LiteLLM to make calls to chat models on OpenRouter. You can find their documentation on using
|
||||
OpenRouter as a provider [here](https://docs.litellm.ai/docs/providers/openrouter).
|
||||
|
||||
## Configuration
|
||||
@@ -9,6 +9,6 @@ When running OpenHands, you'll need to set the following in the OpenHands UI thr
|
||||
* `LLM Provider` to `OpenRouter`
|
||||
* `LLM Model` to the model you will be using.
|
||||
[Visit here to see a full list of OpenRouter models](https://openrouter.ai/models).
|
||||
If the model is not in the list, enable `Advanced` options, and enter it in
|
||||
If the model is not in the list, enable `Advanced` options, and enter it in
|
||||
`Custom Model` (e.g. openrouter/<model-name> like `openrouter/anthropic/claude-3.5-sonnet`).
|
||||
* `API Key` to your OpenRouter API key.
|
||||
|
||||
@@ -6,7 +6,7 @@ Organizations and users can define microagents that apply to all repositories be
|
||||
|
||||
## Usage
|
||||
|
||||
These microagents can be [any type of microagent](./microagents-overview#microagent-types) and will be loaded
|
||||
These microagents can be [any type of microagent](./microagents-overview#microagent-types) and will be loaded
|
||||
accordingly. However, they are applied to all repositories belonging to the organization or user.
|
||||
|
||||
Add a `.openhands` repository under the organization or user and create a `microagents` directory and place the
|
||||
|
||||
@@ -15,7 +15,7 @@ Before using the Local Runtime, ensure that:
|
||||
1. You can run OpenHands using the [Development workflow](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
|
||||
2. For Linux and Mac, tmux is available on your system.
|
||||
3. For Windows, PowerShell is available on your system.
|
||||
- Only [CLI mode](../how-to/cli-mode) and [headless mode](../how-to/headless-mode) are supported in Windows with Local Runtime.
|
||||
- Only [CLI mode](../how-to/cli-mode) and [headless mode](../how-to/headless-mode) are supported in Windows with Local Runtime.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
Generated
+1906
-1686
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -17,10 +17,10 @@
|
||||
},
|
||||
"// Note": "The OpenAPI spec is stored in docs/static/openapi.json so it's accessible at /openapi.json in the deployed site",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.8.0",
|
||||
"@docusaurus/plugin-content-pages": "^3.8.0",
|
||||
"@docusaurus/preset-classic": "^3.8.0",
|
||||
"@docusaurus/theme-mermaid": "^3.8.0",
|
||||
"@docusaurus/core": "^3.7.0",
|
||||
"@docusaurus/plugin-content-pages": "^3.7.0",
|
||||
"@docusaurus/preset-classic": "^3.7.0",
|
||||
"@docusaurus/theme-mermaid": "^3.7.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@node-rs/jieba": "^2.0.1",
|
||||
"clsx": "^2.0.0",
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.5.1",
|
||||
"@docusaurus/tsconfig": "^3.8.0",
|
||||
"@docusaurus/tsconfig": "^3.7.0",
|
||||
"@docusaurus/types": "^3.5.1",
|
||||
"swagger-cli": "^4.0.4",
|
||||
"swagger-ui-dist": "^5.22.0",
|
||||
|
||||
@@ -17,7 +17,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -59,10 +59,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -25,7 +25,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -39,11 +39,11 @@ from openhands.utils.async_utils import call_async_from_sync
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-slim'
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
|
||||
@@ -24,7 +24,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
load_from_toml,
|
||||
parse_arguments,
|
||||
@@ -46,10 +46,10 @@ SKIP_NUM = (
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.11-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
|
||||
@@ -22,7 +22,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -55,12 +55,12 @@ FILE_EXT_MAP = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
BIOCODER_BENCH_CONTAINER_IMAGE = 'public.ecr.aws/i5g0m1f6/eval_biocoder:v1.0'
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = BIOCODER_BENCH_CONTAINER_IMAGE
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -25,7 +25,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -70,11 +70,11 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -18,7 +18,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -33,13 +33,13 @@ SUPPORTED_AGENT_CLS = {'CodeActAgent'}
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
assert metadata.max_iterations == 1, (
|
||||
'max_iterations must be 1 for browsing delegation evaluation.'
|
||||
)
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -25,7 +25,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -101,7 +101,7 @@ def get_instance_docker_image(repo_name: str) -> str:
|
||||
def get_config(
|
||||
instance: pd.Series,
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
repo_name = instance['repo'].split('/')[1]
|
||||
base_container_image = get_instance_docker_image(repo_name)
|
||||
logger.info(
|
||||
@@ -113,7 +113,7 @@ def get_config(
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = base_container_image
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
|
||||
@@ -25,7 +25,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -61,10 +61,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -21,7 +21,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -47,10 +47,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -19,7 +19,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -39,10 +39,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -37,7 +37,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -60,10 +60,10 @@ ACTION_FORMAT = """
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -30,7 +30,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -81,10 +81,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
@@ -201,7 +201,7 @@ def process_instance(
|
||||
) -> EvalOutput:
|
||||
config = get_config(metadata)
|
||||
# use a session id for concurrent evaluation
|
||||
conversation_id = _get_instance_id(instance)
|
||||
sid = _get_instance_id(instance)
|
||||
|
||||
# Setup the logger properly, so you can run multi-processing to parallelize the evaluation
|
||||
if reset_logger:
|
||||
@@ -218,7 +218,7 @@ def process_instance(
|
||||
|
||||
# Prepare instruction
|
||||
instruction = (
|
||||
f'Please fix the function in {conversation_id}.py such that all test cases pass.\n'
|
||||
f'Please fix the function in {sid}.py such that all test cases pass.\n'
|
||||
'Environment has been set up for you to start working. You may assume all necessary tools are installed.\n\n'
|
||||
'# Problem Statement\n'
|
||||
f'{problem_statement}\n\n'
|
||||
|
||||
@@ -19,10 +19,10 @@ from evaluation.utils.shared import (
|
||||
make_metadata,
|
||||
)
|
||||
from openhands.core.config import (
|
||||
AppConfig,
|
||||
LLMConfig,
|
||||
OpenHandsConfig,
|
||||
get_parser,
|
||||
load_openhands_config,
|
||||
load_app_config,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime
|
||||
@@ -34,10 +34,10 @@ from openhands.utils.async_utils import call_async_from_sync
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
@@ -53,7 +53,7 @@ def get_config(
|
||||
return config
|
||||
|
||||
|
||||
config = load_openhands_config()
|
||||
config = load_app_config()
|
||||
|
||||
|
||||
def load_bench_config():
|
||||
|
||||
@@ -29,10 +29,10 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
load_openhands_config,
|
||||
load_app_config,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime, run_controller
|
||||
@@ -44,10 +44,10 @@ from openhands.utils.async_utils import call_async_from_sync
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
@@ -63,7 +63,7 @@ def get_config(
|
||||
return config
|
||||
|
||||
|
||||
config = load_openhands_config()
|
||||
config = load_app_config()
|
||||
|
||||
|
||||
def load_bench_config():
|
||||
|
||||
@@ -17,7 +17,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -44,14 +44,14 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'xingyaoww/od-eval-logic-reasoning:v1.0'
|
||||
sandbox_config.runtime_extra_deps = (
|
||||
'$OH_INTERPRETER_PATH -m pip install scitools-pyke'
|
||||
)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -21,7 +21,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -54,10 +54,10 @@ AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
env_id: str,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'xingyaoww/od-eval-miniwob:v1.0'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
|
||||
@@ -22,7 +22,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -102,14 +102,14 @@ def load_incontext_example(task_name: str, with_tool: bool = True):
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'xingyaoww/od-eval-mint:v1.0'
|
||||
sandbox_config.runtime_extra_deps = (
|
||||
f'$OH_INTERPRETER_PATH -m pip install {" ".join(MINT_DEPENDENCIES)}'
|
||||
)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -4,11 +4,11 @@ import pprint
|
||||
|
||||
import tqdm
|
||||
|
||||
from openhands.core.config import get_llm_config_arg, get_parser, load_openhands_config
|
||||
from openhands.core.config import get_llm_config_arg, get_parser, load_app_config
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.llm.llm import LLM
|
||||
|
||||
config = load_openhands_config()
|
||||
config = load_app_config()
|
||||
|
||||
|
||||
def extract_test_results(res_file_path: str) -> tuple[list[str], list[str]]:
|
||||
|
||||
@@ -33,10 +33,10 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
load_openhands_config,
|
||||
load_app_config,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime, run_controller
|
||||
@@ -45,7 +45,7 @@ from openhands.events.observation import CmdOutputObservation
|
||||
from openhands.runtime.base import Runtime
|
||||
from openhands.utils.async_utils import call_async_from_sync
|
||||
|
||||
config = load_openhands_config()
|
||||
config = load_app_config()
|
||||
|
||||
AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
|
||||
'CodeActAgent': codeact_user_response,
|
||||
@@ -76,10 +76,10 @@ ID2CONDA = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'public.ecr.aws/i5g0m1f6/ml-bench'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -28,8 +28,8 @@ from evaluation.utils.shared import (
|
||||
run_evaluation,
|
||||
)
|
||||
from openhands.core.config import (
|
||||
AppConfig,
|
||||
LLMConfig,
|
||||
OpenHandsConfig,
|
||||
get_parser,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
@@ -73,7 +73,7 @@ def process_git_patch(patch):
|
||||
return patch
|
||||
|
||||
|
||||
def get_config(metadata: EvalMetadata, instance: pd.Series) -> OpenHandsConfig:
|
||||
def get_config(metadata: EvalMetadata, instance: pd.Series) -> AppConfig:
|
||||
# We use a different instance image for the each instance of swe-bench eval
|
||||
base_container_image = get_instance_docker_image(instance['instance_id'])
|
||||
logger.info(
|
||||
@@ -87,7 +87,7 @@ def get_config(metadata: EvalMetadata, instance: pd.Series) -> OpenHandsConfig:
|
||||
dataset_name=metadata.dataset,
|
||||
instance_id=instance['instance_id'],
|
||||
)
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
|
||||
@@ -30,7 +30,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -314,7 +314,7 @@ def get_instance_docker_image(instance: pd.Series):
|
||||
def get_config(
|
||||
instance: pd.Series,
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
SWE_BENCH_CONTAINER_IMAGE = 'ghcr.io/opendevin/eval-swe-bench:full-v1.2.1'
|
||||
if USE_INSTANCE_IMAGE:
|
||||
# We use a different instance image for the each instance of swe-bench eval
|
||||
@@ -340,7 +340,7 @@ def get_config(
|
||||
instance_id=instance['instance_id'],
|
||||
)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
|
||||
@@ -20,7 +20,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -58,12 +58,12 @@ def format_task_dict(example, use_knowledge):
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
instance_id: str,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = (
|
||||
'docker.io/xingyaoww/openhands-eval-scienceagentbench'
|
||||
)
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
|
||||
@@ -24,8 +24,8 @@ from evaluation.utils.shared import (
|
||||
run_evaluation,
|
||||
)
|
||||
from openhands.core.config import (
|
||||
AppConfig,
|
||||
LLMConfig,
|
||||
OpenHandsConfig,
|
||||
get_parser,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
@@ -69,7 +69,7 @@ def process_git_patch(patch):
|
||||
return patch
|
||||
|
||||
|
||||
def get_config(metadata: EvalMetadata, instance: pd.Series) -> OpenHandsConfig:
|
||||
def get_config(metadata: EvalMetadata, instance: pd.Series) -> AppConfig:
|
||||
# We use a different instance image for the each instance of swe-bench eval
|
||||
base_container_image = get_instance_docker_image(instance['instance_id'])
|
||||
logger.info(
|
||||
@@ -83,7 +83,7 @@ def get_config(metadata: EvalMetadata, instance: pd.Series) -> OpenHandsConfig:
|
||||
dataset_name=metadata.dataset,
|
||||
instance_id=instance['instance_id'],
|
||||
)
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
sandbox=sandbox_config,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
TASK_INSTRUECTION = """
|
||||
TASK_INSTRUECTION="""
|
||||
Given the following GitHub problem description, your objective is to localize the specific files, classes or functions, and lines of code that need modification or contain key information to resolve the issue.
|
||||
|
||||
Follow these steps to localize the issue:
|
||||
@@ -66,4 +66,4 @@ FAKE_USER_MSG_FOR_LOC = (
|
||||
'Verify that you have carefully analyzed the impact of the found locations on the repository, especially their dependencies. '
|
||||
'If you think you have solved the task, please send your final answer (including the former answer and reranking) to user through message and then call `finish` to finish.\n'
|
||||
'IMPORTANT: YOU SHOULD NEVER ASK FOR HUMAN HELP.\n'
|
||||
)
|
||||
)
|
||||
@@ -40,12 +40,12 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
from openhands.core.config.condenser_config import NoOpCondenserConfig
|
||||
from openhands.core.config.utils import get_condenser_config_arg
|
||||
from openhands.core.config.condenser_config import NoOpCondenserConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime, run_controller
|
||||
from openhands.critic import AgentFinishedCritic
|
||||
@@ -220,7 +220,7 @@ def get_instance_docker_image(
|
||||
def get_config(
|
||||
instance: pd.Series,
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
# We use a different instance image for the each instance of swe-bench eval
|
||||
use_swebench_official_image = 'swe-gym' not in metadata.dataset.lower()
|
||||
base_container_image = get_instance_docker_image(
|
||||
@@ -244,7 +244,7 @@ def get_config(
|
||||
instance_id=instance['instance_id'],
|
||||
)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
@@ -721,16 +721,15 @@ def filter_dataset(dataset: pd.DataFrame, filter_column: str) -> pd.DataFrame:
|
||||
# repos for the swe-bench instances:
|
||||
# ['astropy/astropy', 'django/django', 'matplotlib/matplotlib', 'mwaskom/seaborn', 'pallets/flask', 'psf/requests', 'pydata/xarray', 'pylint-dev/pylint', 'pytest-dev/pytest', 'scikit-learn/scikit-learn', 'sphinx-doc/sphinx', 'sympy/sympy']
|
||||
selected_repos = data['selected_repos']
|
||||
if isinstance(selected_repos, str):
|
||||
selected_repos = [selected_repos]
|
||||
if isinstance(selected_repos, str): selected_repos = [selected_repos]
|
||||
assert isinstance(selected_repos, list)
|
||||
logger.info(
|
||||
f'Filtering {selected_repos} tasks from "selected_repos"...'
|
||||
)
|
||||
subset = dataset[dataset['repo'].isin(selected_repos)]
|
||||
subset = dataset[dataset["repo"].isin(selected_repos)]
|
||||
logger.info(f'Retained {subset.shape[0]} tasks after filtering')
|
||||
return subset
|
||||
|
||||
|
||||
skip_ids = os.environ.get('SKIP_IDS', '').split(',')
|
||||
if len(skip_ids) > 0:
|
||||
logger.info(f'Filtering {len(skip_ids)} tasks from "SKIP_IDS"...')
|
||||
@@ -807,9 +806,7 @@ if __name__ == '__main__':
|
||||
else:
|
||||
# If no specific condenser config is provided via env var, default to NoOpCondenser
|
||||
condenser_config = NoOpCondenserConfig()
|
||||
logger.debug(
|
||||
'No Condenser config provided via EVAL_CONDENSER, using NoOpCondenser.'
|
||||
)
|
||||
logger.debug('No Condenser config provided via EVAL_CONDENSER, using NoOpCondenser.')
|
||||
|
||||
details = {'mode': args.mode}
|
||||
_agent_cls = openhands.agenthub.Agent.get_cls(args.agent_cls)
|
||||
|
||||
@@ -30,7 +30,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -58,7 +58,7 @@ def _get_swebench_workspace_dir_name(instance: pd.Series) -> str:
|
||||
|
||||
|
||||
def get_instruction(instance: pd.Series, metadata: EvalMetadata):
|
||||
_get_swebench_workspace_dir_name(instance)
|
||||
workspace_dir_name = _get_swebench_workspace_dir_name(instance)
|
||||
instruction = f"""
|
||||
Consider the following issue description:
|
||||
|
||||
@@ -168,7 +168,7 @@ def get_instance_docker_image(instance_id: str, official_image: bool = False) ->
|
||||
def get_config(
|
||||
instance: pd.Series,
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
# We use a different instance image for the each instance of swe-bench eval
|
||||
use_official_image = bool(
|
||||
'verified' in metadata.dataset.lower() or 'lite' in metadata.dataset.lower()
|
||||
@@ -197,7 +197,7 @@ def get_config(
|
||||
'REPO_PATH': f'/workspace/{workspace_dir_name}/',
|
||||
}
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
@@ -348,13 +348,13 @@ def initialize_runtime(
|
||||
|
||||
# Check if an existing graph index file is available
|
||||
graph_index_file_path = os.path.join(
|
||||
INDEX_BASE_DIR, 'graph_index_v2.3', f'{instance["instance_id"]}.pkl'
|
||||
INDEX_BASE_DIR, 'graph_index_v2.3', f"{instance['instance_id']}.pkl"
|
||||
)
|
||||
if INDEX_BASE_DIR and os.path.exists(graph_index_file_path):
|
||||
logger.info(
|
||||
f'Copying graph index from {graph_index_file_path} to /workspace/{workspace_dir_name}/_index_data/graph_index_v2.3'
|
||||
f"Copying graph index from {graph_index_file_path} to /workspace/{workspace_dir_name}/_index_data/graph_index_v2.3"
|
||||
)
|
||||
|
||||
|
||||
runtime.copy_to(
|
||||
graph_index_file_path,
|
||||
f'/workspace/{workspace_dir_name}/_index_data/graph_index_v2.3',
|
||||
@@ -364,13 +364,9 @@ def initialize_runtime(
|
||||
)
|
||||
obs = runtime.run_action(action)
|
||||
|
||||
bm25_index_dir = os.path.join(
|
||||
INDEX_BASE_DIR, 'BM25_index', instance['instance_id']
|
||||
)
|
||||
bm25_index_dir = os.path.join(INDEX_BASE_DIR, 'BM25_index', instance['instance_id'])
|
||||
runtime.copy_to(
|
||||
bm25_index_dir,
|
||||
f'/workspace/{workspace_dir_name}/_index_data',
|
||||
recursive=True,
|
||||
bm25_index_dir, f'/workspace/{workspace_dir_name}/_index_data', recursive=True
|
||||
)
|
||||
action = CmdRunAction(
|
||||
command=f'mv _index_data/{instance["instance_id"]} _index_data/bm25_index'
|
||||
|
||||
@@ -41,7 +41,7 @@ from evaluation.utils.shared import (
|
||||
reset_logger_for_multiprocessing,
|
||||
run_evaluation,
|
||||
)
|
||||
from openhands.core.config import OpenHandsConfig, SandboxConfig, get_parser
|
||||
from openhands.core.config import AppConfig, SandboxConfig, get_parser
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime
|
||||
from openhands.events.action import CmdRunAction
|
||||
@@ -52,13 +52,13 @@ DOCKER_IMAGE_PREFIX = os.environ.get('EVAL_DOCKER_IMAGE_PREFIX', 'docker.io/kdja
|
||||
logger.info(f'Using docker image prefix: {DOCKER_IMAGE_PREFIX}')
|
||||
|
||||
|
||||
def get_config(instance: pd.Series) -> OpenHandsConfig:
|
||||
def get_config(instance: pd.Series) -> AppConfig:
|
||||
base_container_image = get_instance_docker_image(instance['instance_id_swebench'])
|
||||
assert base_container_image, (
|
||||
f'Invalid container image for instance {instance["instance_id_swebench"]}.'
|
||||
)
|
||||
logger.info(f'Using instance container image: {base_container_image}.')
|
||||
return OpenHandsConfig(
|
||||
return AppConfig(
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'eventstream'),
|
||||
sandbox=SandboxConfig(
|
||||
|
||||
@@ -35,7 +35,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
SandboxConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
@@ -117,7 +117,7 @@ def get_instance_docker_image(instance_id: str) -> str:
|
||||
def get_config(
|
||||
instance: pd.Series,
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
# We use a different instance image for the each instance of TestGenEval
|
||||
base_container_image = get_instance_docker_image(instance['instance_id_swebench'])
|
||||
logger.info(
|
||||
@@ -126,7 +126,7 @@ def get_config(
|
||||
f'Submit an issue on https://github.com/All-Hands-AI/OpenHands if you run into any issues.'
|
||||
)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
|
||||
@@ -15,8 +15,8 @@ from browsing import pre_login
|
||||
from evaluation.utils.shared import get_default_sandbox_config_for_eval
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AppConfig,
|
||||
LLMConfig,
|
||||
OpenHandsConfig,
|
||||
get_agent_config_arg,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
@@ -36,13 +36,13 @@ def get_config(
|
||||
mount_path_on_host: str,
|
||||
llm_config: LLMConfig,
|
||||
agent_config: AgentConfig | None,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = base_container_image
|
||||
sandbox_config.enable_auto_lint = True
|
||||
# If the web services are running on the host machine, this must be set to True
|
||||
sandbox_config.use_host_network = True
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
run_as_openhands=False,
|
||||
max_budget_per_task=4,
|
||||
max_iterations=100,
|
||||
@@ -126,7 +126,7 @@ def codeact_user_response(state: State) -> str:
|
||||
def run_solver(
|
||||
runtime: Runtime,
|
||||
task_name: str,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
dependencies: list[str],
|
||||
save_final_state: bool,
|
||||
state_dir: str,
|
||||
@@ -141,7 +141,7 @@ def run_solver(
|
||||
state: State | None = asyncio.run(
|
||||
run_controller(
|
||||
config=config,
|
||||
conversation_id=task_name,
|
||||
sid=task_name,
|
||||
initial_user_action=MessageAction(content=instruction),
|
||||
runtime=runtime,
|
||||
fake_user_response_fn=codeact_user_response,
|
||||
@@ -274,7 +274,7 @@ if __name__ == '__main__':
|
||||
temp_dir = os.path.abspath(os.getenv('TMPDIR'))
|
||||
else:
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
config: OpenHandsConfig = get_config(
|
||||
config: AppConfig = get_config(
|
||||
args.task_image_name, task_short_name, temp_dir, agent_llm_config, agent_config
|
||||
)
|
||||
runtime: Runtime = create_runtime(config)
|
||||
|
||||
@@ -18,7 +18,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -40,10 +40,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
|
||||
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.base_container_image = 'python:3.12-bookworm'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -30,7 +30,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
)
|
||||
@@ -135,7 +135,7 @@ def get_instance_docker_image(instance_id: str, official_image: bool = False) ->
|
||||
def get_config(
|
||||
instance: pd.Series,
|
||||
metadata: EvalMetadata,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
# We use a different instance image for the each instance of swe-bench eval
|
||||
use_official_image = bool(
|
||||
'verified' in metadata.dataset.lower() or 'lite' in metadata.dataset.lower()
|
||||
@@ -160,7 +160,7 @@ def get_config(
|
||||
instance_id=instance['instance_id'],
|
||||
)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
max_iterations=metadata.max_iterations,
|
||||
|
||||
@@ -20,7 +20,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -48,7 +48,7 @@ AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
env_id: str,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
base_url = os.environ.get('VISUALWEBARENA_BASE_URL', None)
|
||||
openai_api_key = os.environ.get('OPENAI_API_KEY', None)
|
||||
openai_base_url = os.environ.get('OPENAI_BASE_URL', None)
|
||||
@@ -72,7 +72,7 @@ def get_config(
|
||||
'VWA_WIKIPEDIA': f'{base_url}:8888',
|
||||
'VWA_HOMEPAGE': f'{base_url}:4399',
|
||||
}
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -19,7 +19,7 @@ from evaluation.utils.shared import (
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -44,7 +44,7 @@ SUPPORTED_AGENT_CLS = {'BrowsingAgent'}
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
env_id: str,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
base_url = os.environ.get('WEBARENA_BASE_URL', None)
|
||||
openai_api_key = os.environ.get('OPENAI_API_KEY', None)
|
||||
assert base_url is not None, 'WEBARENA_BASE_URL must be set'
|
||||
@@ -64,7 +64,7 @@ def get_config(
|
||||
'MAP': f'{base_url}:3000',
|
||||
'HOMEPAGE': f'{base_url}:4399',
|
||||
}
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime='docker',
|
||||
|
||||
@@ -21,7 +21,7 @@ from evaluation.utils.shared import (
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AgentConfig,
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
@@ -41,10 +41,10 @@ FAKE_RESPONSES = {
|
||||
def get_config(
|
||||
metadata: EvalMetadata,
|
||||
instance_id: str,
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
sandbox_config = get_default_sandbox_config_for_eval()
|
||||
sandbox_config.platform = 'linux/amd64'
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
run_as_openhands=False,
|
||||
runtime=os.environ.get('RUNTIME', 'docker'),
|
||||
|
||||
@@ -2,9 +2,9 @@ import argparse
|
||||
|
||||
import pytest
|
||||
|
||||
from openhands.config import load_openhands_config
|
||||
from openhands.config import load_app_config
|
||||
|
||||
config = load_openhands_config()
|
||||
config = load_app_config()
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Main entry point of the script.
|
||||
|
||||
@@ -16,8 +16,8 @@ vi.mock("react-i18next", async () => {
|
||||
if (i18nKey === "SETTINGS$API_KEYS_DESCRIPTION") {
|
||||
return (
|
||||
<span>
|
||||
API keys allow you to authenticate with the OpenHands API programmatically.
|
||||
Keep your API keys secure; anyone with your API key can access your account.
|
||||
API keys allow you to authenticate with the OpenHands API programmatically.
|
||||
Keep your API keys secure; anyone with your API key can access your account.
|
||||
For more information on how to use the API, see our {components.a}
|
||||
</span>
|
||||
);
|
||||
@@ -48,7 +48,7 @@ describe("ApiKeysManager", () => {
|
||||
|
||||
it("should render the API documentation link", () => {
|
||||
renderComponent();
|
||||
|
||||
|
||||
// Find the link to the API documentation
|
||||
const link = screen.getByRole("link");
|
||||
expect(link).toBeInTheDocument();
|
||||
@@ -56,4 +56,4 @@ describe("ApiKeysManager", () => {
|
||||
expect(link).toHaveAttribute("target", "_blank");
|
||||
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -60,11 +60,11 @@ Object.entries(translationJson).forEach(([key, translations]) => {
|
||||
if (Object.keys(missingTranslations).length > 0) {
|
||||
console.error('\x1b[31m%s\x1b[0m', 'ERROR: Missing translations detected');
|
||||
console.error(`Found ${Object.keys(missingTranslations).length} translation keys with missing languages:`);
|
||||
|
||||
|
||||
Object.entries(missingTranslations).forEach(([key, langs]) => {
|
||||
console.error(`- Key "${key}" is missing translations for: ${langs.join(', ')}`);
|
||||
});
|
||||
|
||||
|
||||
console.error('\nPlease add the missing translations before committing.');
|
||||
}
|
||||
|
||||
@@ -72,11 +72,11 @@ if (Object.keys(missingTranslations).length > 0) {
|
||||
if (Object.keys(extraLanguages).length > 0) {
|
||||
console.error('\x1b[31m%s\x1b[0m', 'ERROR: Extra languages detected');
|
||||
console.error(`Found ${Object.keys(extraLanguages).length} translation keys with extra languages not in AvailableLanguages:`);
|
||||
|
||||
|
||||
Object.entries(extraLanguages).forEach(([key, langs]) => {
|
||||
console.error(`- Key "${key}" has translations for unsupported languages: ${langs.join(', ')}`);
|
||||
});
|
||||
|
||||
|
||||
console.error('\nPlease remove the extra languages before committing.');
|
||||
}
|
||||
|
||||
@@ -85,4 +85,4 @@ if (hasErrors) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\x1b[32m%s\x1b[0m', 'All translation keys have complete language coverage!');
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ export function FeedbackForm({ onClose, polarity }: FeedbackFormProps) {
|
||||
isDisabled={isPending}
|
||||
>
|
||||
{isPending
|
||||
? t(I18nKey.FEEDBACK$SUBMITTING_LABEL)
|
||||
? t(I18nKey.FEEDBACK$SUBMITTING_LABEL) || "Submitting..."
|
||||
: t(I18nKey.FEEDBACK$SHARE_LABEL)}
|
||||
</BrandButton>
|
||||
<BrandButton
|
||||
@@ -144,7 +144,8 @@ export function FeedbackForm({ onClose, polarity }: FeedbackFormProps) {
|
||||
</div>
|
||||
{isPending && (
|
||||
<p className="text-sm text-center text-neutral-400">
|
||||
{t(I18nKey.FEEDBACK$SUBMITTING_MESSAGE)}
|
||||
{t(I18nKey.FEEDBACK$SUBMITTING_MESSAGE) ||
|
||||
"Submitting your feedback, please wait..."}
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
|
||||
@@ -9,6 +9,7 @@ import GitHubLogo from "#/assets/branding/github-logo.svg?react";
|
||||
import GitLabLogo from "#/assets/branding/gitlab-logo.svg?react";
|
||||
import { useAuthUrl } from "#/hooks/use-auth-url";
|
||||
import { GetConfigResponse } from "#/api/open-hands.types";
|
||||
import { LoginMethod, setLoginMethod } from "#/utils/local-storage";
|
||||
|
||||
interface AuthModalProps {
|
||||
githubAuthUrl: string | null;
|
||||
@@ -25,6 +26,10 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
|
||||
const handleGitHubAuth = () => {
|
||||
if (githubAuthUrl) {
|
||||
// Store the login method in local storage (only in SAAS mode)
|
||||
if (appMode === "saas") {
|
||||
setLoginMethod(LoginMethod.GITHUB);
|
||||
}
|
||||
// Always start the OIDC flow, let the backend handle TOS check
|
||||
window.location.href = githubAuthUrl;
|
||||
}
|
||||
@@ -32,6 +37,10 @@ export function AuthModal({ githubAuthUrl, appMode }: AuthModalProps) {
|
||||
|
||||
const handleGitLabAuth = () => {
|
||||
if (gitlabAuthUrl) {
|
||||
// Store the login method in local storage (only in SAAS mode)
|
||||
if (appMode === "saas") {
|
||||
setLoginMethod(LoginMethod.GITLAB);
|
||||
}
|
||||
// Always start the OIDC flow, let the backend handle TOS check
|
||||
window.location.href = gitlabAuthUrl;
|
||||
}
|
||||
|
||||
@@ -166,8 +166,6 @@ export function WsClientProvider({
|
||||
}
|
||||
|
||||
function handleMessage(event: Record<string, unknown>) {
|
||||
handleAssistantMessage(event);
|
||||
|
||||
if (isOpenHandsEvent(event)) {
|
||||
const isStatusUpdateError =
|
||||
isStatusUpdate(event) && event.type === "error";
|
||||
@@ -257,6 +255,8 @@ export function WsClientProvider({
|
||||
if (!Number.isNaN(parseInt(event.id as string, 10))) {
|
||||
lastEventRef.current = event;
|
||||
}
|
||||
|
||||
handleAssistantMessage(event);
|
||||
}
|
||||
|
||||
function handleDisconnect(data: unknown) {
|
||||
@@ -289,14 +289,14 @@ export function WsClientProvider({
|
||||
|
||||
React.useEffect(() => {
|
||||
lastEventRef.current = null;
|
||||
}, [conversationId]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// reset events when conversationId changes
|
||||
setEvents([]);
|
||||
setParsedEvents([]);
|
||||
setStatus(WsClientProviderStatus.DISCONNECTED);
|
||||
}, [conversationId]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!conversationId) {
|
||||
throw new Error("No conversation ID provided");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useNavigate } from "react-router";
|
||||
import posthog from "posthog-js";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { useConfig } from "../query/use-config";
|
||||
@@ -7,6 +8,7 @@ import { clearLoginData } from "#/utils/local-storage";
|
||||
export const useLogout = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { data: config } = useConfig();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: () => OpenHands.logout(config?.APP_MODE ?? "oss"),
|
||||
@@ -22,6 +24,7 @@ export const useLogout = () => {
|
||||
}
|
||||
|
||||
posthog.reset();
|
||||
await navigate("/");
|
||||
|
||||
// Refresh the page after all logout logic is completed
|
||||
window.location.reload();
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { useIsAuthed } from "./query/use-is-authed";
|
||||
import { LoginMethod, setLoginMethod } from "#/utils/local-storage";
|
||||
import { useConfig } from "./query/use-config";
|
||||
|
||||
/**
|
||||
* Hook to handle authentication callback and set login method after successful authentication
|
||||
*/
|
||||
export const useAuthCallback = () => {
|
||||
const location = useLocation();
|
||||
const { data: isAuthed, isLoading: isAuthLoading } = useIsAuthed();
|
||||
const { data: config } = useConfig();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Only run in SAAS mode
|
||||
if (config?.APP_MODE !== "saas") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for auth to load
|
||||
if (isAuthLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only set login method if authentication was successful
|
||||
if (!isAuthed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have a login_method query parameter
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const loginMethod = searchParams.get("login_method");
|
||||
|
||||
// Set the login method if it's valid
|
||||
if (
|
||||
loginMethod === LoginMethod.GITHUB ||
|
||||
loginMethod === LoginMethod.GITLAB
|
||||
) {
|
||||
setLoginMethod(loginMethod as LoginMethod);
|
||||
|
||||
// Clean up the URL by removing the login_method parameter
|
||||
searchParams.delete("login_method");
|
||||
const newUrl = `${location.pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
||||
navigate(newUrl, { replace: true });
|
||||
}
|
||||
}, [isAuthed, isAuthLoading, location.search, config?.APP_MODE]);
|
||||
};
|
||||
@@ -53,12 +53,8 @@ export const useAutoLogin = () => {
|
||||
|
||||
// If we have an auth URL, redirect to it
|
||||
if (authUrl) {
|
||||
// Add the login method as a query parameter
|
||||
const url = new URL(authUrl);
|
||||
url.searchParams.append("login_method", loginMethod);
|
||||
|
||||
// After successful login, the user will be redirected back and can navigate to the last page
|
||||
window.location.href = url.toString();
|
||||
window.location.href = authUrl;
|
||||
}
|
||||
}, [
|
||||
config?.APP_MODE,
|
||||
|
||||
@@ -36,7 +36,6 @@ import { useDocumentTitleFromState } from "#/hooks/use-document-title-from-state
|
||||
import { transformVSCodeUrl } from "#/utils/vscode-url-helper";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { TabContent } from "#/components/layout/tab-content";
|
||||
import { useIsAuthed } from "#/hooks/query/use-is-authed";
|
||||
|
||||
function AppContent() {
|
||||
useConversationConfig();
|
||||
@@ -44,7 +43,6 @@ function AppContent() {
|
||||
const { data: settings } = useSettings();
|
||||
const { conversationId } = useConversationId();
|
||||
const { data: conversation, isFetched } = useActiveConversation();
|
||||
const { data: isAuthed } = useIsAuthed();
|
||||
|
||||
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
||||
const dispatch = useDispatch();
|
||||
@@ -56,13 +54,13 @@ function AppContent() {
|
||||
const [width, setWidth] = React.useState(window.innerWidth);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isFetched && !conversation && isAuthed) {
|
||||
if (isFetched && !conversation) {
|
||||
displayErrorToast(
|
||||
"This conversation does not exist, or you do not have permission to access it.",
|
||||
);
|
||||
navigate("/");
|
||||
}
|
||||
}, [conversation, isFetched, isAuthed]);
|
||||
}, [conversation, isFetched]);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(clearTerminal());
|
||||
|
||||
@@ -23,7 +23,6 @@ import { SetupPaymentModal } from "#/components/features/payment/setup-payment-m
|
||||
import { displaySuccessToast } from "#/utils/custom-toast-handlers";
|
||||
import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page";
|
||||
import { useAutoLogin } from "#/hooks/use-auto-login";
|
||||
import { useAuthCallback } from "#/hooks/use-auth-callback";
|
||||
import { LOCAL_STORAGE_KEYS } from "#/utils/local-storage";
|
||||
|
||||
export function ErrorBoundary() {
|
||||
@@ -89,9 +88,6 @@ export default function MainApp() {
|
||||
// Auto-login if login method is stored in local storage
|
||||
useAutoLogin();
|
||||
|
||||
// Handle authentication callback and set login method after successful authentication
|
||||
useAuthCallback();
|
||||
|
||||
React.useEffect(() => {
|
||||
// Don't change language when on TOS page
|
||||
if (!isOnTosPage && settings?.LANGUAGE) {
|
||||
@@ -135,8 +131,8 @@ export default function MainApp() {
|
||||
}
|
||||
}, [error?.status, pathname, isOnTosPage]);
|
||||
|
||||
// Function to check if login method exists in local storage
|
||||
const checkLoginMethodExists = React.useCallback(() => {
|
||||
// Check if login method exists in local storage
|
||||
const loginMethodExists = React.useMemo(() => {
|
||||
// Only check localStorage if we're in a browser environment
|
||||
if (typeof window !== "undefined" && window.localStorage) {
|
||||
return localStorage.getItem(LOCAL_STORAGE_KEYS.LOGIN_METHOD) !== null;
|
||||
@@ -144,39 +140,6 @@ export default function MainApp() {
|
||||
return false;
|
||||
}, []);
|
||||
|
||||
// State to track if login method exists
|
||||
const [loginMethodExists, setLoginMethodExists] = React.useState(
|
||||
checkLoginMethodExists(),
|
||||
);
|
||||
|
||||
// Listen for storage events to update loginMethodExists when logout happens
|
||||
React.useEffect(() => {
|
||||
const handleStorageChange = (event: StorageEvent) => {
|
||||
if (event.key === LOCAL_STORAGE_KEYS.LOGIN_METHOD) {
|
||||
setLoginMethodExists(checkLoginMethodExists());
|
||||
}
|
||||
};
|
||||
|
||||
// Also check on window focus, as logout might happen in another tab
|
||||
const handleWindowFocus = () => {
|
||||
setLoginMethodExists(checkLoginMethodExists());
|
||||
};
|
||||
|
||||
window.addEventListener("storage", handleStorageChange);
|
||||
window.addEventListener("focus", handleWindowFocus);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("storage", handleStorageChange);
|
||||
window.removeEventListener("focus", handleWindowFocus);
|
||||
};
|
||||
}, [checkLoginMethodExists]);
|
||||
|
||||
// Check login method status when auth status changes
|
||||
React.useEffect(() => {
|
||||
// When auth status changes (especially on logout), recheck login method
|
||||
setLoginMethodExists(checkLoginMethodExists());
|
||||
}, [isAuthed, checkLoginMethodExists]);
|
||||
|
||||
const renderAuthModal =
|
||||
!isAuthed &&
|
||||
!isAuthError &&
|
||||
|
||||
@@ -16,8 +16,5 @@ export const generateAuthUrl = (identityProvider: string, requestUrl: URL) => {
|
||||
authUrl = `auth.${requestUrl.hostname}`;
|
||||
}
|
||||
const scope = "openid email profile"; // OAuth scope - not user-facing
|
||||
const separator = requestUrl.search ? "&" : "?";
|
||||
const cleanHref = requestUrl.href.replace(/\/$/, "");
|
||||
const state = `${cleanHref}${separator}login_method=${identityProvider}`;
|
||||
return `https://${authUrl}/realms/allhands/protocol/openid-connect/auth?client_id=allhands&kc_idp_hint=${identityProvider}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}`;
|
||||
return `https://${authUrl}/realms/allhands/protocol/openid-connect/auth?client_id=allhands&kc_idp_hint=${identityProvider}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(requestUrl.href)}`;
|
||||
};
|
||||
|
||||
@@ -19,10 +19,10 @@ vi.mock("react-i18next", () => ({
|
||||
|
||||
describe("RepositorySelectionForm", () => {
|
||||
const mockOnRepoSelection = vi.fn();
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
|
||||
|
||||
// Mock the hooks with default values
|
||||
(useUserRepositories as any).mockReturnValue({
|
||||
data: [
|
||||
@@ -32,7 +32,7 @@ describe("RepositorySelectionForm", () => {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
});
|
||||
|
||||
|
||||
(useRepositoryBranches as any).mockReturnValue({
|
||||
data: [
|
||||
{ name: "main" },
|
||||
@@ -41,90 +41,90 @@ describe("RepositorySelectionForm", () => {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
});
|
||||
|
||||
|
||||
(useCreateConversation as any).mockReturnValue({
|
||||
mutate: vi.fn(),
|
||||
isPending: false,
|
||||
isSuccess: false,
|
||||
});
|
||||
|
||||
|
||||
(useIsCreatingConversation as any).mockReturnValue(false);
|
||||
});
|
||||
|
||||
|
||||
it("should clear selected branch when input is empty", async () => {
|
||||
render(<RepositorySelectionForm onRepoSelection={mockOnRepoSelection} />);
|
||||
|
||||
|
||||
// First select a repository to enable the branch dropdown
|
||||
const repoDropdown = screen.getByTestId("repository-dropdown");
|
||||
fireEvent.change(repoDropdown, { target: { value: "test/repo1" } });
|
||||
|
||||
|
||||
// Get the branch dropdown and verify it's enabled
|
||||
const branchDropdown = screen.getByTestId("branch-dropdown");
|
||||
expect(branchDropdown).not.toBeDisabled();
|
||||
|
||||
|
||||
// Simulate deleting all text in the branch input
|
||||
fireEvent.change(branchDropdown, { target: { value: "" } });
|
||||
|
||||
|
||||
// Verify the branch input is cleared (no selected branch)
|
||||
expect(branchDropdown).toHaveValue("");
|
||||
});
|
||||
|
||||
|
||||
it("should clear selected branch when input contains only whitespace", async () => {
|
||||
render(<RepositorySelectionForm onRepoSelection={mockOnRepoSelection} />);
|
||||
|
||||
|
||||
// First select a repository to enable the branch dropdown
|
||||
const repoDropdown = screen.getByTestId("repository-dropdown");
|
||||
fireEvent.change(repoDropdown, { target: { value: "test/repo1" } });
|
||||
|
||||
|
||||
// Get the branch dropdown and verify it's enabled
|
||||
const branchDropdown = screen.getByTestId("branch-dropdown");
|
||||
expect(branchDropdown).not.toBeDisabled();
|
||||
|
||||
|
||||
// Simulate entering only whitespace in the branch input
|
||||
fireEvent.change(branchDropdown, { target: { value: " " } });
|
||||
|
||||
|
||||
// Verify the branch input is cleared (no selected branch)
|
||||
expect(branchDropdown).toHaveValue("");
|
||||
});
|
||||
|
||||
it("should keep branch empty after being cleared even with auto-selection", async () => {
|
||||
render(<RepositorySelectionForm onRepoSelection={mockOnRepoSelection} />);
|
||||
|
||||
|
||||
// First select a repository to enable the branch dropdown
|
||||
const repoDropdown = screen.getByTestId("repository-dropdown");
|
||||
fireEvent.change(repoDropdown, { target: { value: "test/repo1" } });
|
||||
|
||||
|
||||
// Get the branch dropdown and verify it's enabled
|
||||
const branchDropdown = screen.getByTestId("branch-dropdown");
|
||||
expect(branchDropdown).not.toBeDisabled();
|
||||
|
||||
|
||||
// The branch should be auto-selected to "main" initially
|
||||
expect(branchDropdown).toHaveValue("main");
|
||||
|
||||
|
||||
// Simulate deleting all text in the branch input
|
||||
fireEvent.change(branchDropdown, { target: { value: "" } });
|
||||
|
||||
|
||||
// Verify the branch input is cleared (no selected branch)
|
||||
expect(branchDropdown).toHaveValue("");
|
||||
|
||||
|
||||
// Trigger a re-render by changing something else
|
||||
fireEvent.change(repoDropdown, { target: { value: "test/repo2" } });
|
||||
fireEvent.change(repoDropdown, { target: { value: "test/repo1" } });
|
||||
|
||||
|
||||
// The branch should be auto-selected to "main" again after repo change
|
||||
expect(branchDropdown).toHaveValue("main");
|
||||
|
||||
|
||||
// Clear it again
|
||||
fireEvent.change(branchDropdown, { target: { value: "" } });
|
||||
|
||||
|
||||
// Verify it stays empty
|
||||
expect(branchDropdown).toHaveValue("");
|
||||
|
||||
|
||||
// Simulate a component update without changing repos
|
||||
// This would normally trigger the useEffect if our fix wasn't working
|
||||
fireEvent.blur(branchDropdown);
|
||||
|
||||
|
||||
// Verify it still stays empty
|
||||
expect(branchDropdown).toHaveValue("");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -266,6 +266,5 @@ class CodeActAgent(Agent):
|
||||
|
||||
def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
|
||||
return codeact_function_calling.response_to_actions(
|
||||
response,
|
||||
mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
response, mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
)
|
||||
|
||||
@@ -5,13 +5,14 @@ This is similar to the functionality of `CodeActResponseParser`.
|
||||
|
||||
import json
|
||||
|
||||
|
||||
from litellm import (
|
||||
ChatCompletionToolParam,
|
||||
ModelResponse,
|
||||
)
|
||||
|
||||
from openhands.agenthub.codeact_agent.function_calling import combine_thought
|
||||
from openhands.agenthub.codeact_agent.tools import FinishTool
|
||||
from openhands.agenthub.codeact_agent.function_calling import combine_thought
|
||||
from openhands.agenthub.loc_agent.tools import (
|
||||
SearchEntityTool,
|
||||
SearchRepoTool,
|
||||
@@ -31,8 +32,7 @@ from openhands.events.tool import ToolCallMetadata
|
||||
|
||||
|
||||
def response_to_actions(
|
||||
response: ModelResponse,
|
||||
mcp_tool_names: list[str] | None = None,
|
||||
response: ModelResponse, mcp_tool_names: list[str] | None = None,
|
||||
) -> list[Action]:
|
||||
actions: list[Action] = []
|
||||
assert len(response.choices) == 1, 'Only one choice is supported for now'
|
||||
@@ -87,7 +87,7 @@ def response_to_actions(
|
||||
raise FunctionCallNotExistsError(
|
||||
f'Tool {tool_call.function.name} is not registered. (arguments: {arguments}). Please check the tool name and retry with an existing tool.'
|
||||
)
|
||||
|
||||
|
||||
# We only add thought to the first action
|
||||
if i == 0:
|
||||
action = combine_thought(action, thought)
|
||||
@@ -106,7 +106,7 @@ def response_to_actions(
|
||||
wait_for_response=True,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Add response id to actions
|
||||
# This will ensure we can match both actions without tool calls (e.g. MessageAction)
|
||||
# and actions with tool calls (e.g. CmdRunAction, IPythonRunCellAction, etc.)
|
||||
@@ -116,7 +116,7 @@ def response_to_actions(
|
||||
|
||||
assert len(actions) >= 1
|
||||
return actions
|
||||
|
||||
|
||||
|
||||
def get_tools() -> list[ChatCompletionToolParam]:
|
||||
tools = [FinishTool]
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import openhands.agenthub.loc_agent.function_calling as locagent_function_calling
|
||||
from openhands.agenthub.codeact_agent import CodeActAgent
|
||||
import openhands.agenthub.loc_agent.function_calling as locagent_function_calling
|
||||
from openhands.core.config import AgentConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.llm.llm import LLM
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
from openhands.events.action import Action
|
||||
from openhands.llm.llm import ModelResponse
|
||||
|
||||
@@ -34,6 +35,5 @@ class LocAgent(CodeActAgent):
|
||||
|
||||
def response_to_actions(self, response: 'ModelResponse') -> list['Action']:
|
||||
return locagent_function_calling.response_to_actions(
|
||||
response,
|
||||
mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
response, mcp_tool_names=list(self.mcp_tools.keys()),
|
||||
)
|
||||
|
||||
+15
-15
@@ -25,7 +25,7 @@ from openhands.cli.utils import (
|
||||
write_to_file,
|
||||
)
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
)
|
||||
from openhands.core.schema import AgentState
|
||||
from openhands.events import EventSource
|
||||
@@ -41,8 +41,8 @@ async def handle_commands(
|
||||
command: str,
|
||||
event_stream: EventStream,
|
||||
usage_metrics: UsageMetrics,
|
||||
conversation_id: str,
|
||||
config: OpenHandsConfig,
|
||||
sid: str,
|
||||
config: AppConfig,
|
||||
current_dir: str,
|
||||
settings_store: FileSettingsStore,
|
||||
) -> tuple[bool, bool, bool]:
|
||||
@@ -54,7 +54,7 @@ async def handle_commands(
|
||||
close_repl = handle_exit_command(
|
||||
event_stream,
|
||||
usage_metrics,
|
||||
conversation_id,
|
||||
sid,
|
||||
)
|
||||
elif command == '/help':
|
||||
handle_help_command()
|
||||
@@ -63,10 +63,10 @@ async def handle_commands(
|
||||
config, event_stream, current_dir
|
||||
)
|
||||
elif command == '/status':
|
||||
handle_status_command(usage_metrics, conversation_id)
|
||||
handle_status_command(usage_metrics, sid)
|
||||
elif command == '/new':
|
||||
close_repl, new_session_requested = handle_new_command(
|
||||
event_stream, usage_metrics, conversation_id
|
||||
event_stream, usage_metrics, sid
|
||||
)
|
||||
elif command == '/settings':
|
||||
await handle_settings_command(config, settings_store)
|
||||
@@ -81,7 +81,7 @@ async def handle_commands(
|
||||
|
||||
|
||||
def handle_exit_command(
|
||||
event_stream: EventStream, usage_metrics: UsageMetrics, conversation_id: str
|
||||
event_stream: EventStream, usage_metrics: UsageMetrics, sid: str
|
||||
) -> bool:
|
||||
close_repl = False
|
||||
|
||||
@@ -94,7 +94,7 @@ def handle_exit_command(
|
||||
ChangeAgentStateAction(AgentState.STOPPED),
|
||||
EventSource.ENVIRONMENT,
|
||||
)
|
||||
display_shutdown_message(usage_metrics, conversation_id)
|
||||
display_shutdown_message(usage_metrics, sid)
|
||||
close_repl = True
|
||||
|
||||
return close_repl
|
||||
@@ -105,7 +105,7 @@ def handle_help_command() -> None:
|
||||
|
||||
|
||||
async def handle_init_command(
|
||||
config: OpenHandsConfig, event_stream: EventStream, current_dir: str
|
||||
config: AppConfig, event_stream: EventStream, current_dir: str
|
||||
) -> tuple[bool, bool]:
|
||||
REPO_MD_CREATE_PROMPT = """
|
||||
Please explore this repository. Create the file .openhands/microagents/repo.md with:
|
||||
@@ -135,12 +135,12 @@ async def handle_init_command(
|
||||
return close_repl, reload_microagents
|
||||
|
||||
|
||||
def handle_status_command(usage_metrics: UsageMetrics, conversation_id: str) -> None:
|
||||
display_status(usage_metrics, conversation_id)
|
||||
def handle_status_command(usage_metrics: UsageMetrics, sid: str) -> None:
|
||||
display_status(usage_metrics, sid)
|
||||
|
||||
|
||||
def handle_new_command(
|
||||
event_stream: EventStream, usage_metrics: UsageMetrics, conversation_id: str
|
||||
event_stream: EventStream, usage_metrics: UsageMetrics, sid: str
|
||||
) -> tuple[bool, bool]:
|
||||
close_repl = False
|
||||
new_session_requested = False
|
||||
@@ -160,13 +160,13 @@ def handle_new_command(
|
||||
ChangeAgentStateAction(AgentState.STOPPED),
|
||||
EventSource.ENVIRONMENT,
|
||||
)
|
||||
display_shutdown_message(usage_metrics, conversation_id)
|
||||
display_shutdown_message(usage_metrics, sid)
|
||||
|
||||
return close_repl, new_session_requested
|
||||
|
||||
|
||||
async def handle_settings_command(
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
settings_store: FileSettingsStore,
|
||||
) -> None:
|
||||
display_settings(config)
|
||||
@@ -264,7 +264,7 @@ async def init_repository(current_dir: str) -> bool:
|
||||
return init_repo
|
||||
|
||||
|
||||
def check_folder_security_agreement(config: OpenHandsConfig, current_dir: str) -> bool:
|
||||
def check_folder_security_agreement(config: AppConfig, current_dir: str) -> bool:
|
||||
# Directories trusted by user for the CLI to use as workspace
|
||||
# Config from ~/.openhands/config.toml overrides the app config
|
||||
|
||||
|
||||
+12
-12
@@ -30,7 +30,7 @@ from openhands.cli.utils import (
|
||||
from openhands.controller import AgentController
|
||||
from openhands.controller.agent import Agent
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
parse_arguments,
|
||||
setup_config_from_args,
|
||||
)
|
||||
@@ -44,7 +44,7 @@ from openhands.core.setup import (
|
||||
create_controller,
|
||||
create_memory,
|
||||
create_runtime,
|
||||
generate_conversation_id,
|
||||
generate_sid,
|
||||
initialize_repository_for_runtime,
|
||||
)
|
||||
from openhands.events import EventSource, EventStreamSubscriber
|
||||
@@ -77,7 +77,7 @@ async def cleanup_session(
|
||||
event_stream = runtime.event_stream
|
||||
end_state = controller.get_state()
|
||||
end_state.save_to_session(
|
||||
event_stream.conversation_id,
|
||||
event_stream.sid,
|
||||
event_stream.file_store,
|
||||
event_stream.user_id,
|
||||
)
|
||||
@@ -103,7 +103,7 @@ async def cleanup_session(
|
||||
|
||||
async def run_session(
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
settings_store: FileSettingsStore,
|
||||
current_dir: str,
|
||||
task_content: str | None = None,
|
||||
@@ -113,7 +113,7 @@ async def run_session(
|
||||
reload_microagents = False
|
||||
new_session_requested = False
|
||||
|
||||
conversation_id = generate_conversation_id(config, session_name)
|
||||
sid = generate_sid(config, session_name)
|
||||
is_loaded = asyncio.Event()
|
||||
is_paused = asyncio.Event() # Event to track agent pause requests
|
||||
always_confirm_mode = False # Flag to enable always confirm mode
|
||||
@@ -129,7 +129,7 @@ async def run_session(
|
||||
agent = create_agent(config)
|
||||
runtime = create_runtime(
|
||||
config,
|
||||
conversation_id=conversation_id,
|
||||
sid=sid,
|
||||
headless_mode=True,
|
||||
agent=agent,
|
||||
)
|
||||
@@ -164,7 +164,7 @@ async def run_session(
|
||||
next_message,
|
||||
event_stream,
|
||||
usage_metrics,
|
||||
conversation_id,
|
||||
sid,
|
||||
config,
|
||||
current_dir,
|
||||
settings_store,
|
||||
@@ -238,7 +238,7 @@ async def run_session(
|
||||
def on_event(event: Event) -> None:
|
||||
loop.create_task(on_event_async(event))
|
||||
|
||||
event_stream.subscribe(EventStreamSubscriber.MAIN, on_event, conversation_id)
|
||||
event_stream.subscribe(EventStreamSubscriber.MAIN, on_event, sid)
|
||||
|
||||
await runtime.connect()
|
||||
|
||||
@@ -254,7 +254,7 @@ async def run_session(
|
||||
memory = create_memory(
|
||||
runtime=runtime,
|
||||
event_stream=event_stream,
|
||||
conversation_id=conversation_id,
|
||||
sid=sid,
|
||||
selected_repository=config.sandbox.selected_repo,
|
||||
repo_directory=repo_directory,
|
||||
conversation_instructions=conversation_instructions,
|
||||
@@ -282,7 +282,7 @@ async def run_session(
|
||||
clear()
|
||||
|
||||
# Show OpenHands banner and session ID
|
||||
display_banner(session_id=conversation_id)
|
||||
display_banner(session_id=sid)
|
||||
|
||||
welcome_message = 'What do you want to build?' # from the application
|
||||
initial_message = '' # from the user
|
||||
@@ -292,7 +292,7 @@ async def run_session(
|
||||
|
||||
# If we loaded a state, we are resuming a previous session
|
||||
if initial_state is not None:
|
||||
logger.info(f'Resuming session: {conversation_id}')
|
||||
logger.info(f'Resuming session: {sid}')
|
||||
|
||||
if initial_state.last_error:
|
||||
# If the last session ended in an error, provide a message.
|
||||
@@ -334,7 +334,7 @@ async def main(loop: asyncio.AbstractEventLoop) -> None:
|
||||
logger.setLevel(logging.WARNING)
|
||||
|
||||
# Load config from toml and override with command line arguments
|
||||
config: OpenHandsConfig = setup_config_from_args(args)
|
||||
config: AppConfig = setup_config_from_args(args)
|
||||
|
||||
# Load settings from Settings Store
|
||||
# TODO: Make this generic?
|
||||
|
||||
@@ -18,7 +18,7 @@ from openhands.cli.utils import (
|
||||
organize_models_and_providers,
|
||||
)
|
||||
from openhands.controller.agent import Agent
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.config import AppConfig
|
||||
from openhands.core.config.condenser_config import NoOpCondenserConfig
|
||||
from openhands.core.config.utils import OH_DEFAULT_AGENT
|
||||
from openhands.memory.condenser.impl.llm_summarizing_condenser import (
|
||||
@@ -29,7 +29,7 @@ from openhands.storage.settings.file_settings_store import FileSettingsStore
|
||||
from openhands.utils.llm import get_supported_llm_models
|
||||
|
||||
|
||||
def display_settings(config: OpenHandsConfig) -> None:
|
||||
def display_settings(config: AppConfig) -> None:
|
||||
llm_config = config.get_llm_config()
|
||||
advanced_llm_settings = True if llm_config.base_url else False
|
||||
|
||||
@@ -145,7 +145,7 @@ def save_settings_confirmation() -> bool:
|
||||
|
||||
|
||||
async def modify_llm_settings_basic(
|
||||
config: OpenHandsConfig, settings_store: FileSettingsStore
|
||||
config: AppConfig, settings_store: FileSettingsStore
|
||||
) -> None:
|
||||
model_list = get_supported_llm_models(config)
|
||||
organized_models = organize_models_and_providers(model_list)
|
||||
@@ -243,7 +243,7 @@ async def modify_llm_settings_basic(
|
||||
|
||||
|
||||
async def modify_llm_settings_advanced(
|
||||
config: OpenHandsConfig, settings_store: FileSettingsStore
|
||||
config: AppConfig, settings_store: FileSettingsStore
|
||||
) -> None:
|
||||
session = PromptSession(key_bindings=kb_cancel())
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from prompt_toolkit.styles import Style
|
||||
from prompt_toolkit.widgets import Frame, TextArea
|
||||
|
||||
from openhands import __version__
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.config import AppConfig
|
||||
from openhands.core.schema import AgentState
|
||||
from openhands.events import EventSource, EventStream
|
||||
from openhands.events.action import (
|
||||
@@ -180,7 +180,7 @@ def display_initial_user_prompt(prompt: str) -> None:
|
||||
|
||||
|
||||
# Prompt output display functions
|
||||
def display_event(event: Event, config: OpenHandsConfig) -> None:
|
||||
def display_event(event: Event, config: AppConfig) -> None:
|
||||
global streaming_output_text_area
|
||||
with print_lock:
|
||||
if isinstance(event, Action):
|
||||
|
||||
@@ -109,7 +109,7 @@ class AgentController:
|
||||
max_budget_per_task: float | None = None,
|
||||
agent_to_llm_config: dict[str, LLMConfig] | None = None,
|
||||
agent_configs: dict[str, AgentConfig] | None = None,
|
||||
conversation_id: str | None = None,
|
||||
sid: str | None = None,
|
||||
confirmation_mode: bool = False,
|
||||
initial_state: State | None = None,
|
||||
is_delegate: bool = False,
|
||||
@@ -128,7 +128,7 @@ class AgentController:
|
||||
we delegate to a different agent.
|
||||
agent_configs: A dictionary mapping agent names to agent configurations in the case that
|
||||
we delegate to a different agent.
|
||||
conversation_id: The session ID of the agent.
|
||||
sid: The session ID of the agent.
|
||||
confirmation_mode: Whether to enable confirmation mode for agent actions.
|
||||
initial_state: The initial state of the controller.
|
||||
is_delegate: Whether this controller is a delegate.
|
||||
@@ -136,7 +136,7 @@ class AgentController:
|
||||
status_callback: Optional callback function to handle status updates.
|
||||
replay_events: A list of logs to replay.
|
||||
"""
|
||||
self.id = conversation_id or event_stream.conversation_id
|
||||
self.id = sid or event_stream.sid
|
||||
self.agent = agent
|
||||
self.headless_mode = headless_mode
|
||||
self.is_delegate = is_delegate
|
||||
@@ -700,7 +700,7 @@ class AgentController:
|
||||
|
||||
# Create the delegate with is_delegate=True so it does NOT subscribe directly
|
||||
self.delegate = AgentController(
|
||||
conversation_id=self.id + '-delegate',
|
||||
sid=self.id + '-delegate',
|
||||
agent=delegate_agent,
|
||||
event_stream=self.event_stream,
|
||||
max_iterations=self.state.max_iterations,
|
||||
|
||||
@@ -105,19 +105,19 @@ class State:
|
||||
last_error: str = ''
|
||||
|
||||
def save_to_session(
|
||||
self, conversation_id: str, file_store: FileStore, user_id: str | None
|
||||
self, sid: str, file_store: FileStore, user_id: str | None
|
||||
) -> None:
|
||||
pickled = pickle.dumps(self)
|
||||
logger.debug(f'Saving state to session {conversation_id}:{self.agent_state}')
|
||||
logger.debug(f'Saving state to session {sid}:{self.agent_state}')
|
||||
encoded = base64.b64encode(pickled).decode('utf-8')
|
||||
try:
|
||||
file_store.write(
|
||||
get_conversation_agent_state_filename(conversation_id, user_id), encoded
|
||||
get_conversation_agent_state_filename(sid, user_id), encoded
|
||||
)
|
||||
|
||||
# see if state is in the old directory on saas/remote use cases and delete it.
|
||||
if user_id:
|
||||
filename = get_conversation_agent_state_filename(conversation_id)
|
||||
filename = get_conversation_agent_state_filename(sid)
|
||||
try:
|
||||
file_store.delete(filename)
|
||||
except Exception:
|
||||
@@ -128,7 +128,7 @@ class State:
|
||||
|
||||
@staticmethod
|
||||
def restore_from_session(
|
||||
conversation_id: str, file_store: FileStore, user_id: str | None = None
|
||||
sid: str, file_store: FileStore, user_id: str | None = None
|
||||
) -> 'State':
|
||||
"""
|
||||
Restores the state from the previously saved session.
|
||||
@@ -137,7 +137,7 @@ class State:
|
||||
state: State
|
||||
try:
|
||||
encoded = file_store.read(
|
||||
get_conversation_agent_state_filename(conversation_id, user_id)
|
||||
get_conversation_agent_state_filename(sid, user_id)
|
||||
)
|
||||
pickled = base64.b64decode(encoded)
|
||||
state = pickle.loads(pickled)
|
||||
@@ -145,13 +145,13 @@ class State:
|
||||
# if user_id is provided, we are in a saas/remote use case
|
||||
# and we need to check if the state is in the old directory.
|
||||
if user_id:
|
||||
filename = get_conversation_agent_state_filename(conversation_id)
|
||||
filename = get_conversation_agent_state_filename(sid)
|
||||
encoded = file_store.read(filename)
|
||||
pickled = base64.b64decode(encoded)
|
||||
state = pickle.loads(pickled)
|
||||
else:
|
||||
raise FileNotFoundError(
|
||||
f'Could not restore state from session file for conversation_id: {conversation_id}'
|
||||
f'Could not restore state from session file for sid: {sid}'
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(f'Could not restore state from session: {e}')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from openhands.core.config.agent_config import AgentConfig
|
||||
from openhands.core.config.app_config import AppConfig
|
||||
from openhands.core.config.config_utils import (
|
||||
OH_DEFAULT_AGENT,
|
||||
OH_MAX_ITERATIONS,
|
||||
@@ -7,7 +8,6 @@ from openhands.core.config.config_utils import (
|
||||
from openhands.core.config.extended_config import ExtendedConfig
|
||||
from openhands.core.config.llm_config import LLMConfig
|
||||
from openhands.core.config.mcp_config import MCPConfig
|
||||
from openhands.core.config.openhands_config import OpenHandsConfig
|
||||
from openhands.core.config.sandbox_config import SandboxConfig
|
||||
from openhands.core.config.security_config import SecurityConfig
|
||||
from openhands.core.config.utils import (
|
||||
@@ -15,9 +15,9 @@ from openhands.core.config.utils import (
|
||||
get_agent_config_arg,
|
||||
get_llm_config_arg,
|
||||
get_parser,
|
||||
load_app_config,
|
||||
load_from_env,
|
||||
load_from_toml,
|
||||
load_openhands_config,
|
||||
parse_arguments,
|
||||
setup_config_from_args,
|
||||
)
|
||||
@@ -26,13 +26,13 @@ __all__ = [
|
||||
'OH_DEFAULT_AGENT',
|
||||
'OH_MAX_ITERATIONS',
|
||||
'AgentConfig',
|
||||
'OpenHandsConfig',
|
||||
'AppConfig',
|
||||
'MCPConfig',
|
||||
'LLMConfig',
|
||||
'SandboxConfig',
|
||||
'SecurityConfig',
|
||||
'ExtendedConfig',
|
||||
'load_openhands_config',
|
||||
'load_app_config',
|
||||
'load_from_env',
|
||||
'load_from_toml',
|
||||
'finalize_config',
|
||||
|
||||
@@ -16,7 +16,7 @@ from openhands.core.config.sandbox_config import SandboxConfig
|
||||
from openhands.core.config.security_config import SecurityConfig
|
||||
|
||||
|
||||
class OpenHandsConfig(BaseModel):
|
||||
class AppConfig(BaseModel):
|
||||
"""Configuration for the app.
|
||||
|
||||
Attributes:
|
||||
@@ -65,10 +65,7 @@ class OpenHandsConfig(BaseModel):
|
||||
save_trajectory_path: str | None = Field(default=None)
|
||||
save_screenshots_in_trajectory: bool = Field(default=False)
|
||||
replay_trajectory_path: str | None = Field(default=None)
|
||||
search_api_key: SecretStr | None = Field(
|
||||
default=None,
|
||||
description='API key for Tavily search engine (https://tavily.com/). Required for search functionality.',
|
||||
)
|
||||
search_api_key: SecretStr | None = Field(default=None, description="API key for Tavily search engine (https://tavily.com/). Required for search functionality.")
|
||||
|
||||
# Deprecated parameters - will be removed in a future version
|
||||
workspace_base: str | None = Field(default=None, deprecated=True)
|
||||
@@ -76,7 +73,7 @@ class OpenHandsConfig(BaseModel):
|
||||
workspace_mount_path_in_sandbox: str = Field(default='/workspace', deprecated=True)
|
||||
workspace_mount_rewrite: str | None = Field(default=None, deprecated=True)
|
||||
# End of deprecated parameters
|
||||
|
||||
|
||||
cache_dir: str = Field(default='/tmp/cache')
|
||||
run_as_openhands: bool = Field(default=True)
|
||||
max_iterations: int = Field(default=OH_MAX_ITERATIONS)
|
||||
@@ -151,5 +148,5 @@ class OpenHandsConfig(BaseModel):
|
||||
"""Post-initialization hook, called when the instance is created with only default values."""
|
||||
super().model_post_init(__context)
|
||||
|
||||
if not OpenHandsConfig.defaults_dict: # Only set defaults_dict if it's empty
|
||||
OpenHandsConfig.defaults_dict = model_defaults_to_dict(self)
|
||||
if not AppConfig.defaults_dict: # Only set defaults_dict if it's empty
|
||||
AppConfig.defaults_dict = model_defaults_to_dict(self)
|
||||
@@ -1,11 +1,10 @@
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from pydantic import BaseModel, Field, ValidationError, model_validator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from openhands.core.config.openhands_config import OpenHandsConfig
|
||||
from openhands.core.config.app_config import AppConfig
|
||||
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.utils.import_utils import get_impl
|
||||
@@ -148,7 +147,7 @@ class MCPConfig(BaseModel):
|
||||
|
||||
class OpenHandsMCPConfig:
|
||||
@staticmethod
|
||||
def add_search_engine(app_config: 'OpenHandsConfig') -> MCPStdioServerConfig | None:
|
||||
def add_search_engine(app_config: "AppConfig") -> MCPStdioServerConfig | None:
|
||||
"""Add search engine to the MCP config"""
|
||||
if (
|
||||
app_config.search_api_key
|
||||
@@ -166,16 +165,17 @@ class OpenHandsMCPConfig:
|
||||
# Do not add search engine to MCP config in SaaS mode since it will be added by the OpenHands server
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def create_default_mcp_server_config(
|
||||
host: str, config: 'OpenHandsConfig', user_id: str | None = None
|
||||
host: str, config: "AppConfig", user_id: str | None = None
|
||||
) -> tuple[MCPSSEServerConfig, list[MCPStdioServerConfig]]:
|
||||
"""
|
||||
Create a default MCP server configuration.
|
||||
|
||||
Args:
|
||||
host: Host string
|
||||
config: OpenHandsConfig
|
||||
config: AppConfig
|
||||
Returns:
|
||||
tuple[MCPSSEServerConfig, list[MCPStdioServerConfig]]: A tuple containing the default SSE server configuration and a list of MCP stdio server configurations
|
||||
"""
|
||||
|
||||
@@ -15,6 +15,7 @@ from pydantic import BaseModel, SecretStr, ValidationError
|
||||
from openhands import __version__
|
||||
from openhands.core import logger
|
||||
from openhands.core.config.agent_config import AgentConfig
|
||||
from openhands.core.config.app_config import AppConfig
|
||||
from openhands.core.config.condenser_config import (
|
||||
CondenserConfig,
|
||||
condenser_config_from_toml_section,
|
||||
@@ -27,7 +28,6 @@ from openhands.core.config.config_utils import (
|
||||
from openhands.core.config.extended_config import ExtendedConfig
|
||||
from openhands.core.config.llm_config import LLMConfig
|
||||
from openhands.core.config.mcp_config import MCPConfig
|
||||
from openhands.core.config.openhands_config import OpenHandsConfig
|
||||
from openhands.core.config.sandbox_config import SandboxConfig
|
||||
from openhands.core.config.security_config import SecurityConfig
|
||||
from openhands.storage import get_file_store
|
||||
@@ -39,7 +39,7 @@ load_dotenv()
|
||||
|
||||
|
||||
def load_from_env(
|
||||
cfg: OpenHandsConfig, env_or_toml_dict: dict | MutableMapping[str, str]
|
||||
cfg: AppConfig, env_or_toml_dict: dict | MutableMapping[str, str]
|
||||
) -> None:
|
||||
"""Sets config attributes from environment variables or TOML dictionary.
|
||||
|
||||
@@ -48,7 +48,7 @@ def load_from_env(
|
||||
(e.g., AGENT_MEMORY_ENABLED), sandbox settings (e.g., SANDBOX_TIMEOUT), and more.
|
||||
|
||||
Args:
|
||||
cfg: The OpenHandsConfig object to set attributes on.
|
||||
cfg: The AppConfig object to set attributes on.
|
||||
env_or_toml_dict: The environment variables or a config.toml dict.
|
||||
"""
|
||||
|
||||
@@ -121,11 +121,11 @@ def load_from_env(
|
||||
set_attr_from_env(default_agent_config, 'AGENT_')
|
||||
|
||||
|
||||
def load_from_toml(cfg: OpenHandsConfig, toml_file: str = 'config.toml') -> None:
|
||||
def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml') -> None:
|
||||
"""Load the config from the toml file. Supports both styles of config vars.
|
||||
|
||||
Args:
|
||||
cfg: The OpenHandsConfig object to update attributes of.
|
||||
cfg: The AppConfig object to update attributes of.
|
||||
toml_file: The path to the toml file. Defaults to 'config.toml'.
|
||||
|
||||
See Also:
|
||||
@@ -302,7 +302,7 @@ def get_or_create_jwt_secret(file_store: FileStore) -> str:
|
||||
return new_secret
|
||||
|
||||
|
||||
def finalize_config(cfg: OpenHandsConfig) -> None:
|
||||
def finalize_config(cfg: AppConfig) -> None:
|
||||
"""More tweaks to the config after it's been loaded."""
|
||||
# Handle the sandbox.volumes parameter
|
||||
if cfg.workspace_base is not None or cfg.workspace_mount_path is not None:
|
||||
@@ -759,7 +759,7 @@ def parse_arguments() -> argparse.Namespace:
|
||||
return args
|
||||
|
||||
|
||||
def register_custom_agents(config: OpenHandsConfig) -> None:
|
||||
def register_custom_agents(config: AppConfig) -> None:
|
||||
"""Register custom agents from configuration.
|
||||
|
||||
This function is called after configuration is loaded to ensure all custom agents
|
||||
@@ -782,16 +782,16 @@ def register_custom_agents(config: OpenHandsConfig) -> None:
|
||||
)
|
||||
|
||||
|
||||
def load_openhands_config(
|
||||
def load_app_config(
|
||||
set_logging_levels: bool = True, config_file: str = 'config.toml'
|
||||
) -> OpenHandsConfig:
|
||||
) -> AppConfig:
|
||||
"""Load the configuration from the specified config file and environment variables.
|
||||
|
||||
Args:
|
||||
set_logging_levels: Whether to set the global variables for logging levels.
|
||||
config_file: Path to the config file. Defaults to 'config.toml' in the current directory.
|
||||
"""
|
||||
config = OpenHandsConfig()
|
||||
config = AppConfig()
|
||||
load_from_toml(config, config_file)
|
||||
load_from_env(config, os.environ)
|
||||
finalize_config(config)
|
||||
@@ -802,13 +802,13 @@ def load_openhands_config(
|
||||
return config
|
||||
|
||||
|
||||
def setup_config_from_args(args: argparse.Namespace) -> OpenHandsConfig:
|
||||
def setup_config_from_args(args: argparse.Namespace) -> AppConfig:
|
||||
"""Load config from toml and override with command line arguments.
|
||||
|
||||
Common setup used by both CLI and main.py entry points.
|
||||
"""
|
||||
# Load base config from toml and env vars
|
||||
config = load_openhands_config(config_file=args.config_file)
|
||||
config = load_app_config(config_file=args.config_file)
|
||||
|
||||
# Override with command line arguments if provided
|
||||
if args.llm_config:
|
||||
|
||||
+15
-17
@@ -9,7 +9,7 @@ from openhands.controller.agent import Agent
|
||||
from openhands.controller.replay import ReplayManager
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
parse_arguments,
|
||||
setup_config_from_args,
|
||||
)
|
||||
@@ -22,7 +22,7 @@ from openhands.core.setup import (
|
||||
create_controller,
|
||||
create_memory,
|
||||
create_runtime,
|
||||
generate_conversation_id,
|
||||
generate_sid,
|
||||
initialize_repository_for_runtime,
|
||||
)
|
||||
from openhands.events import EventSource, EventStreamSubscriber
|
||||
@@ -47,9 +47,9 @@ class FakeUserResponseFunc(Protocol):
|
||||
|
||||
|
||||
async def run_controller(
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
initial_user_action: Action,
|
||||
conversation_id: str | None = None,
|
||||
sid: str | None = None,
|
||||
runtime: Runtime | None = None,
|
||||
agent: Agent | None = None,
|
||||
exit_on_message: bool = False,
|
||||
@@ -65,7 +65,7 @@ async def run_controller(
|
||||
Args:
|
||||
config: The app config.
|
||||
initial_user_action: An Action object containing initial user input
|
||||
conversation_id: (optional) The session id. IMPORTANT: please don't set this unless you know what you're doing.
|
||||
sid: (optional) The session id. IMPORTANT: please don't set this unless you know what you're doing.
|
||||
Set it to incompatible value will cause unexpected behavior on RemoteRuntime.
|
||||
runtime: (optional) A runtime for the agent to run on.
|
||||
agent: (optional) A agent to run.
|
||||
@@ -90,11 +90,11 @@ async def run_controller(
|
||||
config.max_budget_per_task.
|
||||
|
||||
Example:
|
||||
>>> config = load_openhands_config()
|
||||
>>> config = load_app_config()
|
||||
>>> action = MessageAction(content="Write a hello world program")
|
||||
>>> state = await run_controller(config=config, initial_user_action=action)
|
||||
"""
|
||||
conversation_id = conversation_id or generate_conversation_id(config)
|
||||
sid = sid or generate_sid(config)
|
||||
|
||||
if agent is None:
|
||||
agent = create_agent(config)
|
||||
@@ -104,7 +104,7 @@ async def run_controller(
|
||||
if runtime is None:
|
||||
runtime = create_runtime(
|
||||
config,
|
||||
conversation_id=conversation_id,
|
||||
sid=sid,
|
||||
headless_mode=headless_mode,
|
||||
agent=agent,
|
||||
)
|
||||
@@ -125,7 +125,7 @@ async def run_controller(
|
||||
memory = create_memory(
|
||||
runtime=runtime,
|
||||
event_stream=event_stream,
|
||||
conversation_id=conversation_id,
|
||||
sid=sid,
|
||||
selected_repository=config.sandbox.selected_repo,
|
||||
repo_directory=repo_directory,
|
||||
conversation_instructions=conversation_instructions,
|
||||
@@ -194,7 +194,7 @@ async def run_controller(
|
||||
action = MessageAction(content=message)
|
||||
event_stream.add_event(action, EventSource.USER)
|
||||
|
||||
event_stream.subscribe(EventStreamSubscriber.MAIN, on_event, conversation_id)
|
||||
event_stream.subscribe(EventStreamSubscriber.MAIN, on_event, sid)
|
||||
|
||||
end_states = [
|
||||
AgentState.FINISHED,
|
||||
@@ -214,7 +214,7 @@ async def run_controller(
|
||||
end_state = controller.get_state()
|
||||
# NOTE: the saved state does not include delegates events
|
||||
end_state.save_to_session(
|
||||
event_stream.conversation_id, event_stream.file_store, event_stream.user_id
|
||||
event_stream.sid, event_stream.file_store, event_stream.user_id
|
||||
)
|
||||
|
||||
await controller.close(set_stop_state=False)
|
||||
@@ -225,9 +225,7 @@ async def run_controller(
|
||||
if config.save_trajectory_path is not None:
|
||||
# if save_trajectory_path is a folder, use session id as file name
|
||||
if os.path.isdir(config.save_trajectory_path):
|
||||
file_path = os.path.join(
|
||||
config.save_trajectory_path, conversation_id + '.json'
|
||||
)
|
||||
file_path = os.path.join(config.save_trajectory_path, sid + '.json')
|
||||
else:
|
||||
file_path = config.save_trajectory_path
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
@@ -281,7 +279,7 @@ def load_replay_log(trajectory_path: str) -> tuple[list[Event] | None, Action]:
|
||||
if __name__ == '__main__':
|
||||
args = parse_arguments()
|
||||
|
||||
config: OpenHandsConfig = setup_config_from_args(args)
|
||||
config: AppConfig = setup_config_from_args(args)
|
||||
|
||||
# Read task from file, CLI args, or stdin
|
||||
task_str = read_task(args, config.cli_multiline_input)
|
||||
@@ -301,13 +299,13 @@ if __name__ == '__main__':
|
||||
|
||||
# Set session name
|
||||
session_name = args.name
|
||||
conversation_id = generate_conversation_id(config, session_name)
|
||||
sid = generate_sid(config, session_name)
|
||||
|
||||
asyncio.run(
|
||||
run_controller(
|
||||
config=config,
|
||||
initial_user_action=initial_user_action,
|
||||
conversation_id=conversation_id,
|
||||
sid=sid,
|
||||
fake_user_response_fn=None
|
||||
if args.no_auto_continue
|
||||
else auto_continue_response,
|
||||
|
||||
+17
-19
@@ -10,7 +10,7 @@ from openhands.controller import AgentController
|
||||
from openhands.controller.agent import Agent
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
OpenHandsConfig,
|
||||
AppConfig,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events import EventStream
|
||||
@@ -28,8 +28,8 @@ from openhands.utils.async_utils import GENERAL_TIMEOUT, call_async_from_sync
|
||||
|
||||
|
||||
def create_runtime(
|
||||
config: OpenHandsConfig,
|
||||
conversation_id: str | None = None,
|
||||
config: AppConfig,
|
||||
sid: str | None = None,
|
||||
headless_mode: bool = True,
|
||||
agent: Agent | None = None,
|
||||
) -> Runtime:
|
||||
@@ -37,7 +37,7 @@ def create_runtime(
|
||||
|
||||
Args:
|
||||
config: The app config.
|
||||
conversation_id: (optional) The session id. IMPORTANT: please don't set this unless you know what you're doing.
|
||||
sid: (optional) The session id. IMPORTANT: please don't set this unless you know what you're doing.
|
||||
Set it to incompatible value will cause unexpected behavior on RemoteRuntime.
|
||||
headless_mode: Whether the agent is run in headless mode. `create_runtime` is typically called within evaluation scripts,
|
||||
where we don't want to have the VSCode UI open, so it defaults to True.
|
||||
@@ -46,10 +46,10 @@ def create_runtime(
|
||||
Returns:
|
||||
The created Runtime instance (not yet connected or initialized).
|
||||
"""
|
||||
# if conversation_id is provided on the command line, use it as the name of the event stream
|
||||
# if sid is provided on the command line, use it as the name of the event stream
|
||||
# otherwise generate it on the basis of the configured jwt_secret
|
||||
# we can do this better, this is just so that the conversation_id is retrieved when we want to restore the session
|
||||
session_id = conversation_id or generate_conversation_id(config)
|
||||
# we can do this better, this is just so that the sid is retrieved when we want to restore the session
|
||||
session_id = sid or generate_sid(config)
|
||||
|
||||
# set up the event stream
|
||||
file_store = get_file_store(config.file_store, config.file_store_path)
|
||||
@@ -73,7 +73,7 @@ def create_runtime(
|
||||
runtime: Runtime = runtime_cls(
|
||||
config=config,
|
||||
event_stream=event_stream,
|
||||
conversation_id=session_id,
|
||||
sid=session_id,
|
||||
plugins=agent_cls.sandbox_plugins,
|
||||
headless_mode=headless_mode,
|
||||
)
|
||||
@@ -131,7 +131,7 @@ def initialize_repository_for_runtime(
|
||||
def create_memory(
|
||||
runtime: Runtime,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str,
|
||||
sid: str,
|
||||
selected_repository: str | None = None,
|
||||
repo_directory: str | None = None,
|
||||
status_callback: Callable | None = None,
|
||||
@@ -142,7 +142,7 @@ def create_memory(
|
||||
Args:
|
||||
runtime: The runtime to use.
|
||||
event_stream: The event stream it will subscribe to.
|
||||
conversation_id: The session id.
|
||||
sid: The session id.
|
||||
selected_repository: The repository to clone and start with, if any.
|
||||
repo_directory: The repository directory, if any.
|
||||
status_callback: Optional callback function to handle status updates.
|
||||
@@ -150,7 +150,7 @@ def create_memory(
|
||||
"""
|
||||
memory = Memory(
|
||||
event_stream=event_stream,
|
||||
conversation_id=conversation_id,
|
||||
sid=sid,
|
||||
status_callback=status_callback,
|
||||
)
|
||||
|
||||
@@ -172,7 +172,7 @@ def create_memory(
|
||||
return memory
|
||||
|
||||
|
||||
def create_agent(config: OpenHandsConfig) -> Agent:
|
||||
def create_agent(config: AppConfig) -> Agent:
|
||||
agent_cls: type[Agent] = Agent.get_cls(config.default_agent)
|
||||
agent_config = config.get_agent_config(config.default_agent)
|
||||
llm_config = config.get_llm_config_from_agent(config.default_agent)
|
||||
@@ -188,7 +188,7 @@ def create_agent(config: OpenHandsConfig) -> Agent:
|
||||
def create_controller(
|
||||
agent: Agent,
|
||||
runtime: Runtime,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
headless_mode: bool = True,
|
||||
replay_events: list[Event] | None = None,
|
||||
) -> tuple[AgentController, State | None]:
|
||||
@@ -196,10 +196,10 @@ def create_controller(
|
||||
initial_state = None
|
||||
try:
|
||||
logger.debug(
|
||||
f'Trying to restore agent state from session {event_stream.conversation_id} if available'
|
||||
f'Trying to restore agent state from session {event_stream.sid} if available'
|
||||
)
|
||||
initial_state = State.restore_from_session(
|
||||
event_stream.conversation_id, event_stream.file_store
|
||||
event_stream.sid, event_stream.file_store
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(f'Cannot restore agent state: {e}')
|
||||
@@ -218,10 +218,8 @@ def create_controller(
|
||||
return (controller, initial_state)
|
||||
|
||||
|
||||
def generate_conversation_id(
|
||||
config: OpenHandsConfig, session_name: str | None = None
|
||||
) -> str:
|
||||
"""Generate a conversation id based on the session name and the jwt secret."""
|
||||
def generate_sid(config: AppConfig, session_name: str | None = None) -> str:
|
||||
"""Generate a session id based on the session name and the jwt secret."""
|
||||
session_name = session_name or str(uuid.uuid4())
|
||||
jwt_secret = config.jwt_secret
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class EventStore(EventStoreABC):
|
||||
A stored list of events backing a conversation
|
||||
"""
|
||||
|
||||
conversation_id: str
|
||||
sid: str
|
||||
file_store: FileStore
|
||||
user_id: str | None
|
||||
cur_id: int = -1 # We fix this in post init if it is not specified
|
||||
@@ -57,12 +57,10 @@ class EventStore(EventStoreABC):
|
||||
return
|
||||
events = []
|
||||
try:
|
||||
events_dir = get_conversation_events_dir(self.conversation_id, self.user_id)
|
||||
events_dir = get_conversation_events_dir(self.sid, self.user_id)
|
||||
events = self.file_store.list(events_dir)
|
||||
except FileNotFoundError:
|
||||
logger.debug(
|
||||
f'No events found for session {self.conversation_id} at {events_dir}'
|
||||
)
|
||||
logger.debug(f'No events found for session {self.sid} at {events_dir}')
|
||||
|
||||
if not events:
|
||||
self.cur_id = 0
|
||||
@@ -147,10 +145,10 @@ class EventStore(EventStoreABC):
|
||||
yield event
|
||||
|
||||
def _get_filename_for_id(self, id: int, user_id: str | None) -> str:
|
||||
return get_conversation_event_filename(self.conversation_id, id, user_id)
|
||||
return get_conversation_event_filename(self.sid, id, user_id)
|
||||
|
||||
def _get_filename_for_cache(self, start: int, end: int) -> str:
|
||||
return f'{get_conversation_dir(self.conversation_id, self.user_id)}event_cache/{start}-{end}.json'
|
||||
return f'{get_conversation_dir(self.sid, self.user_id)}event_cache/{start}-{end}.json'
|
||||
|
||||
def _load_cache_page(self, start: int, end: int) -> _CachePage:
|
||||
"""Read a page from the cache. Reading individual events is slow when there are a lot of them, so we use pages."""
|
||||
|
||||
@@ -13,7 +13,7 @@ class EventStoreABC:
|
||||
A stored list of events backing a conversation
|
||||
"""
|
||||
|
||||
conversation_id: str
|
||||
sid: str
|
||||
user_id: str | None
|
||||
|
||||
@abstractmethod
|
||||
|
||||
@@ -17,7 +17,7 @@ class NestedEventStore(EventStoreABC):
|
||||
"""
|
||||
|
||||
base_url: str
|
||||
conversation_id: str
|
||||
sid: str
|
||||
user_id: str | None
|
||||
|
||||
def search_events(
|
||||
|
||||
@@ -11,9 +11,7 @@ class MCPObservation(Observation):
|
||||
|
||||
observation: str = ObservationType.MCP
|
||||
name: str = '' # The name of the MCP tool that was called
|
||||
arguments: dict[str, Any] = field(
|
||||
default_factory=dict
|
||||
) # The arguments passed to the MCP tool
|
||||
arguments: dict[str, Any] = field(default_factory=dict) # The arguments passed to the MCP tool
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
|
||||
@@ -32,12 +32,10 @@ class EventStreamSubscriber(str, Enum):
|
||||
|
||||
|
||||
async def session_exists(
|
||||
conversation_id: str, file_store: FileStore, user_id: str | None = None
|
||||
sid: str, file_store: FileStore, user_id: str | None = None
|
||||
) -> bool:
|
||||
try:
|
||||
await call_sync_from_async(
|
||||
file_store.list, get_conversation_dir(conversation_id, user_id)
|
||||
)
|
||||
await call_sync_from_async(file_store.list, get_conversation_dir(sid, user_id))
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
@@ -56,10 +54,8 @@ class EventStream(EventStore):
|
||||
_thread_loops: dict[str, dict[str, asyncio.AbstractEventLoop]]
|
||||
_write_page_cache: list[dict]
|
||||
|
||||
def __init__(
|
||||
self, conversation_id: str, file_store: FileStore, user_id: str | None = None
|
||||
):
|
||||
super().__init__(conversation_id, file_store, user_id)
|
||||
def __init__(self, sid: str, file_store: FileStore, user_id: str | None = None):
|
||||
super().__init__(sid, file_store, user_id)
|
||||
self._stop_flag = threading.Event()
|
||||
self._queue: queue.Queue[Event] = queue.Queue()
|
||||
self._thread_pools = {}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{{ issue_comment }}
|
||||
{{ issue_comment }}
|
||||
@@ -1 +1 @@
|
||||
Please fix issue number #{{ issue_number }} in your repository.
|
||||
Please fix issue number #{{ issue_number }} in your repository.
|
||||
@@ -1 +1 @@
|
||||
{{ pr_comment }}
|
||||
{{ pr_comment }}
|
||||
@@ -4,11 +4,11 @@ from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from openhands.controller.agent import Agent
|
||||
|
||||
from openhands.core.config.app_config import AppConfig
|
||||
from openhands.core.config.mcp_config import (
|
||||
MCPConfig,
|
||||
MCPSSEServerConfig,
|
||||
)
|
||||
from openhands.core.config.openhands_config import OpenHandsConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events.action.mcp import MCPAction
|
||||
from openhands.events.observation.mcp import MCPObservation
|
||||
@@ -162,7 +162,7 @@ async def call_tool_mcp(mcp_clients: list[MCPClient], action: MCPAction) -> Obse
|
||||
|
||||
|
||||
async def add_mcp_tools_to_agent(
|
||||
agent: 'Agent', runtime: Runtime, memory: 'Memory', app_config: OpenHandsConfig
|
||||
agent: 'Agent', runtime: Runtime, memory: 'Memory', app_config: AppConfig
|
||||
):
|
||||
"""
|
||||
Add MCP tools to an agent.
|
||||
|
||||
@@ -40,7 +40,7 @@ class Memory:
|
||||
(a RecallAction) and publishes observations with the content (such as RecallObservation).
|
||||
"""
|
||||
|
||||
conversation_id: str
|
||||
sid: str
|
||||
event_stream: EventStream
|
||||
status_callback: Callable | None
|
||||
loop: asyncio.AbstractEventLoop | None
|
||||
@@ -48,18 +48,18 @@ class Memory:
|
||||
def __init__(
|
||||
self,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str,
|
||||
sid: str,
|
||||
status_callback: Callable | None = None,
|
||||
):
|
||||
self.event_stream = event_stream
|
||||
self.conversation_id = conversation_id if conversation_id else str(uuid.uuid4())
|
||||
self.sid = sid if sid else str(uuid.uuid4())
|
||||
self.status_callback = status_callback
|
||||
self.loop = None
|
||||
|
||||
self.event_stream.subscribe(
|
||||
EventStreamSubscriber.MEMORY,
|
||||
self.on_event,
|
||||
self.conversation_id,
|
||||
self.sid,
|
||||
)
|
||||
|
||||
# Additional placeholders to store user workspace microagents
|
||||
|
||||
@@ -16,7 +16,7 @@ from termcolor import colored
|
||||
|
||||
import openhands
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import AgentConfig, LLMConfig, OpenHandsConfig, SandboxConfig
|
||||
from openhands.core.config import AgentConfig, AppConfig, LLMConfig, SandboxConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime, run_controller
|
||||
from openhands.events.action import CmdRunAction, MessageAction
|
||||
@@ -377,7 +377,7 @@ class IssueResolver:
|
||||
shutil.rmtree(workspace_base)
|
||||
shutil.copytree(os.path.join(self.output_dir, 'repo'), workspace_base)
|
||||
|
||||
config = OpenHandsConfig(
|
||||
config = AppConfig(
|
||||
default_agent='CodeActAgent',
|
||||
runtime='docker',
|
||||
max_budget_per_task=4,
|
||||
|
||||
@@ -4,4 +4,4 @@ You SHOULD INCLUDE PROPER INDENTATION in your edit commands.{% if repo_instructi
|
||||
Some basic information about this repository:
|
||||
{{ repo_instruction }}{% endif %}
|
||||
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
@@ -13,4 +13,4 @@ You SHOULD INCLUDE PROPER INDENTATION in your edit commands.{% if repo_instructi
|
||||
Some basic information about this repository:
|
||||
{{ repo_instruction }}{% endif %}
|
||||
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
When you think you have fixed the issue through code changes, please finish the interaction.
|
||||
@@ -2,4 +2,4 @@ Please fix the following issue for the repository in /workspace.
|
||||
An environment has been set up for you to start working. You may assume all necessary tools are installed.
|
||||
|
||||
# Problem Statement
|
||||
{{ body }}
|
||||
{{ body }}
|
||||
@@ -2,4 +2,4 @@ Please fix the following issue for the repository in /workspace.
|
||||
An environment has been set up for you to start working. You may assume all necessary tools are installed.
|
||||
|
||||
# Problem Statement
|
||||
{{ body }}
|
||||
{{ body }}
|
||||
+11
-13
@@ -15,7 +15,7 @@ from zipfile import ZipFile
|
||||
|
||||
import httpx
|
||||
|
||||
from openhands.core.config import OpenHandsConfig, SandboxConfig
|
||||
from openhands.core.config import AppConfig, SandboxConfig
|
||||
from openhands.core.config.mcp_config import MCPConfig, MCPStdioServerConfig
|
||||
from openhands.core.exceptions import AgentRuntimeDisconnectedError
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
@@ -93,11 +93,11 @@ class Runtime(FileEditRuntimeMixin):
|
||||
"""The runtime is how the agent interacts with the external environment.
|
||||
This includes a bash sandbox, a browser, and filesystem interactions.
|
||||
|
||||
conversation_id is the session id, which is used to identify the current user session.
|
||||
sid is the session id, which is used to identify the current user session.
|
||||
"""
|
||||
|
||||
conversation_id: str
|
||||
config: OpenHandsConfig
|
||||
sid: str
|
||||
config: AppConfig
|
||||
initial_env_vars: dict[str, str]
|
||||
attach_to_existing: bool
|
||||
status_callback: Callable[[str, str, str], None] | None
|
||||
@@ -105,9 +105,9 @@ class Runtime(FileEditRuntimeMixin):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str = 'default',
|
||||
sid: str = 'default',
|
||||
plugins: list[PluginRequirement] | None = None,
|
||||
env_vars: dict[str, str] | None = None,
|
||||
status_callback: Callable[[str, str, str], None] | None = None,
|
||||
@@ -119,11 +119,11 @@ class Runtime(FileEditRuntimeMixin):
|
||||
self.git_handler = GitHandler(
|
||||
execute_shell_fn=self._execute_shell_fn_git_handler
|
||||
)
|
||||
self.conversation_id = conversation_id
|
||||
self.sid = sid
|
||||
self.event_stream = event_stream
|
||||
if event_stream:
|
||||
event_stream.subscribe(
|
||||
EventStreamSubscriber.RUNTIME, self.on_event, self.conversation_id
|
||||
EventStreamSubscriber.RUNTIME, self.on_event, self.sid
|
||||
)
|
||||
self.plugins = (
|
||||
copy.deepcopy(plugins) if plugins is not None and len(plugins) > 0 else []
|
||||
@@ -189,7 +189,7 @@ class Runtime(FileEditRuntimeMixin):
|
||||
pass
|
||||
|
||||
def log(self, level: str, message: str) -> None:
|
||||
message = f'[runtime {self.conversation_id}] {message}'
|
||||
message = f'[runtime {self.sid}] {message}'
|
||||
getattr(logger, level)(message, stacklevel=2)
|
||||
|
||||
def send_status_message(self, message_id: str):
|
||||
@@ -266,9 +266,7 @@ class Runtime(FileEditRuntimeMixin):
|
||||
if not providers_called:
|
||||
return
|
||||
|
||||
logger.info(
|
||||
f'Fetching latest provider tokens for runtime: {self.conversation_id}'
|
||||
)
|
||||
logger.info(f'Fetching latest provider tokens for runtime: {self.sid}')
|
||||
env_vars = await self.provider_handler.get_env_vars(
|
||||
providers=providers_called, expose_secrets=False, get_latest=True
|
||||
)
|
||||
@@ -284,7 +282,7 @@ class Runtime(FileEditRuntimeMixin):
|
||||
self.add_env_vars(self.provider_handler.expose_env_vars(env_vars))
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f'Failed export latest github token to runtime: {self.conversation_id}, {e}'
|
||||
f'Failed export latest github token to runtime: {self.sid}, {e}'
|
||||
)
|
||||
|
||||
async def _handle_action(self, event: Action) -> None:
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import base64
|
||||
import io
|
||||
|
||||
import numpy as np
|
||||
import base64
|
||||
from PIL import Image
|
||||
|
||||
import numpy as np
|
||||
|
||||
def image_to_png_base64_url(
|
||||
image: np.ndarray | Image.Image, add_data_prefix: bool = False
|
||||
@@ -23,7 +21,6 @@ def image_to_png_base64_url(
|
||||
else f'{image_base64}'
|
||||
)
|
||||
|
||||
|
||||
def png_base64_url_to_image(png_base64_url: str) -> Image.Image:
|
||||
"""Convert a base64 encoded png image url to a PIL Image."""
|
||||
splited = png_base64_url.split(',')
|
||||
|
||||
@@ -12,14 +12,13 @@ from browsergym.utils.obs import flatten_dom_to_str, overlay_som
|
||||
|
||||
from openhands.core.exceptions import BrowserInitException
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.runtime.browser.base64 import image_to_png_base64_url
|
||||
from openhands.utils.shutdown_listener import should_continue, should_exit
|
||||
from openhands.utils.tenacity_stop import stop_if_should_exit
|
||||
from openhands.runtime.browser.base64 import image_to_png_base64_url
|
||||
|
||||
BROWSER_EVAL_GET_GOAL_ACTION = 'GET_EVAL_GOAL'
|
||||
BROWSER_EVAL_GET_REWARDS_ACTION = 'GET_EVAL_REWARDS'
|
||||
|
||||
|
||||
class BrowserEnv:
|
||||
def __init__(self, browsergym_eval_env: str | None = None):
|
||||
self.html_text_converter = self.get_html_text_converter()
|
||||
|
||||
@@ -9,7 +9,7 @@ import httpcore
|
||||
import httpx
|
||||
from tenacity import retry, retry_if_exception, stop_after_attempt, wait_exponential
|
||||
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.config import AppConfig
|
||||
from openhands.core.config.mcp_config import (
|
||||
MCPConfig,
|
||||
MCPSSEServerConfig,
|
||||
@@ -65,9 +65,9 @@ class ActionExecutionClient(Runtime):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str = 'default',
|
||||
sid: str = 'default',
|
||||
plugins: list[PluginRequirement] | None = None,
|
||||
env_vars: dict[str, str] | None = None,
|
||||
status_callback: Any | None = None,
|
||||
@@ -84,7 +84,7 @@ class ActionExecutionClient(Runtime):
|
||||
super().__init__(
|
||||
config,
|
||||
event_stream,
|
||||
conversation_id,
|
||||
sid,
|
||||
plugins,
|
||||
env_vars,
|
||||
status_callback,
|
||||
@@ -409,7 +409,7 @@ class ActionExecutionClient(Runtime):
|
||||
'warning',
|
||||
f'Some MCP servers failed to be added: {result["router_error_log"]}',
|
||||
)
|
||||
|
||||
|
||||
# Update our cached list with combined servers after successful update
|
||||
self._last_updated_mcp_stdio_servers = combined_servers.copy()
|
||||
self.log(
|
||||
@@ -448,9 +448,7 @@ class ActionExecutionClient(Runtime):
|
||||
)
|
||||
|
||||
# Create clients for this specific operation
|
||||
mcp_clients = await create_mcp_clients(
|
||||
updated_mcp_config.sse_servers, self.conversation_id
|
||||
)
|
||||
mcp_clients = await create_mcp_clients(updated_mcp_config.sse_servers, self.sid)
|
||||
|
||||
# Call the tool and return the result
|
||||
# No need for try/finally since disconnect() is now just resetting state
|
||||
|
||||
@@ -22,7 +22,7 @@ from openhands_aci.editor.results import ToolResult
|
||||
from openhands_aci.utils.diff import get_diff
|
||||
from pydantic import SecretStr
|
||||
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.config import AppConfig
|
||||
from openhands.core.config.mcp_config import MCPConfig, MCPStdioServerConfig
|
||||
from openhands.core.exceptions import LLMMalformedActionError
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
@@ -57,9 +57,9 @@ class CLIRuntime(Runtime):
|
||||
file operations using Python's standard library. It does not implement browser functionality.
|
||||
|
||||
Args:
|
||||
config (OpenHandsConfig): The application configuration.
|
||||
config (AppConfig): The application configuration.
|
||||
event_stream (EventStream): The event stream to subscribe to.
|
||||
conversation_id (str, optional): The session ID. Defaults to 'default'.
|
||||
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.
|
||||
status_callback (Callable | None, optional): Callback for status updates. Defaults to None.
|
||||
@@ -71,9 +71,9 @@ class CLIRuntime(Runtime):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str = 'default',
|
||||
sid: str = 'default',
|
||||
plugins: list[PluginRequirement] | None = None,
|
||||
env_vars: dict[str, str] | None = None,
|
||||
status_callback: Callable[[str, str, str], None] | None = None,
|
||||
@@ -85,7 +85,7 @@ class CLIRuntime(Runtime):
|
||||
super().__init__(
|
||||
config,
|
||||
event_stream,
|
||||
conversation_id,
|
||||
sid,
|
||||
plugins,
|
||||
env_vars,
|
||||
status_callback,
|
||||
@@ -106,7 +106,7 @@ class CLIRuntime(Runtime):
|
||||
else:
|
||||
# Create a temporary directory for the workspace
|
||||
self._workspace_path = tempfile.mkdtemp(
|
||||
prefix=f'openhands_workspace_{conversation_id}_'
|
||||
prefix=f'openhands_workspace_{sid}_'
|
||||
)
|
||||
logger.info(f'Created temporary workspace at {self._workspace_path}')
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from daytona_sdk import (
|
||||
Workspace,
|
||||
)
|
||||
|
||||
from openhands.core.config.openhands_config import OpenHandsConfig
|
||||
from openhands.core.config.app_config import AppConfig
|
||||
from openhands.events.stream import EventStream
|
||||
from openhands.runtime.impl.action_execution.action_execution_client import (
|
||||
ActionExecutionClient,
|
||||
@@ -33,9 +33,9 @@ class DaytonaRuntime(ActionExecutionClient):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str = 'default',
|
||||
sid: str = 'default',
|
||||
plugins: list[PluginRequirement] | None = None,
|
||||
env_vars: dict[str, str] | None = None,
|
||||
status_callback: Callable | None = None,
|
||||
@@ -45,8 +45,8 @@ class DaytonaRuntime(ActionExecutionClient):
|
||||
assert config.daytona_api_key, 'Daytona API key is required'
|
||||
|
||||
self.config = config
|
||||
self.conversation_id = conversation_id
|
||||
self.workspace_id = WORKSPACE_PREFIX + conversation_id
|
||||
self.sid = sid
|
||||
self.workspace_id = WORKSPACE_PREFIX + sid
|
||||
self.workspace: Workspace | None = None
|
||||
self._vscode_url: str | None = None
|
||||
|
||||
@@ -67,7 +67,7 @@ class DaytonaRuntime(ActionExecutionClient):
|
||||
super().__init__(
|
||||
config,
|
||||
event_stream,
|
||||
conversation_id,
|
||||
sid,
|
||||
plugins,
|
||||
env_vars,
|
||||
status_callback,
|
||||
|
||||
@@ -8,7 +8,7 @@ import httpx
|
||||
import tenacity
|
||||
from docker.models.containers import Container
|
||||
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.config import AppConfig
|
||||
from openhands.core.exceptions import (
|
||||
AgentRuntimeDisconnectedError,
|
||||
AgentRuntimeNotFoundError,
|
||||
@@ -23,10 +23,7 @@ from openhands.runtime.impl.action_execution.action_execution_client import (
|
||||
from openhands.runtime.impl.docker.containers import stop_all_containers
|
||||
from openhands.runtime.plugins import PluginRequirement
|
||||
from openhands.runtime.utils import find_available_tcp_port
|
||||
from openhands.runtime.utils.command import (
|
||||
DEFAULT_MAIN_MODULE,
|
||||
get_action_execution_server_startup_command,
|
||||
)
|
||||
from openhands.runtime.utils.command import DEFAULT_MAIN_MODULE, 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
|
||||
@@ -65,9 +62,9 @@ class DockerRuntime(ActionExecutionClient):
|
||||
When receive an event, it will send the event to runtime-client which run inside the docker environment.
|
||||
|
||||
Args:
|
||||
config (OpenHandsConfig): The application configuration.
|
||||
config (AppConfig): The application configuration.
|
||||
event_stream (EventStream): The event stream to subscribe to.
|
||||
conversation_id (str, optional): The session ID. Defaults to 'default'.
|
||||
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.
|
||||
"""
|
||||
@@ -76,9 +73,9 @@ class DockerRuntime(ActionExecutionClient):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: OpenHandsConfig,
|
||||
config: AppConfig,
|
||||
event_stream: EventStream,
|
||||
conversation_id: str = 'default',
|
||||
sid: str = 'default',
|
||||
plugins: list[PluginRequirement] | None = None,
|
||||
env_vars: dict[str, str] | None = None,
|
||||
status_callback: Callable | None = None,
|
||||
@@ -112,7 +109,7 @@ class DockerRuntime(ActionExecutionClient):
|
||||
|
||||
self.base_container_image = self.config.sandbox.base_container_image
|
||||
self.runtime_container_image = self.config.sandbox.runtime_container_image
|
||||
self.container_name = CONTAINER_NAME_PREFIX + conversation_id
|
||||
self.container_name = CONTAINER_NAME_PREFIX + sid
|
||||
self.container: Container | None = None
|
||||
self.main_module = main_module
|
||||
|
||||
@@ -124,7 +121,7 @@ class DockerRuntime(ActionExecutionClient):
|
||||
super().__init__(
|
||||
config,
|
||||
event_stream,
|
||||
conversation_id,
|
||||
sid,
|
||||
plugins,
|
||||
env_vars,
|
||||
status_callback,
|
||||
@@ -132,7 +129,7 @@ class DockerRuntime(ActionExecutionClient):
|
||||
headless_mode,
|
||||
)
|
||||
|
||||
# Log runtime_extra_deps after base class initialization so self.conversation_id is available
|
||||
# Log runtime_extra_deps after base class initialization so self.sid is available
|
||||
if self.config.sandbox.runtime_extra_deps:
|
||||
self.log(
|
||||
'debug',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user