Rename Conversation to ServerConversation and AppConfig to OpenHandsConfig (#8754)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Robert Brennan 2025-05-28 15:48:34 -04:00 committed by GitHub
parent c76809a766
commit 205f0234e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
111 changed files with 627 additions and 537 deletions

View File

@ -17,7 +17,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
@ -59,10 +59,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -25,7 +25,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-slim'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),

View File

@ -24,7 +24,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
load_from_toml,
parse_arguments,
@ -46,10 +46,10 @@ SKIP_NUM = (
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.11-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),

View File

@ -22,7 +22,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -55,12 +55,12 @@ FILE_EXT_MAP = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -25,7 +25,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -70,11 +70,11 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -18,7 +18,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -33,13 +33,13 @@ SUPPORTED_AGENT_CLS = {'CodeActAgent'}
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -25,7 +25,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
max_iterations=metadata.max_iterations,

View File

@ -25,7 +25,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -61,10 +61,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -21,7 +21,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
@ -47,10 +47,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -19,7 +19,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
@ -39,10 +39,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -37,7 +37,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
@ -60,10 +60,10 @@ ACTION_FORMAT = """
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -30,7 +30,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -81,10 +81,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -19,10 +19,10 @@ from evaluation.utils.shared import (
make_metadata,
)
from openhands.core.config import (
AppConfig,
LLMConfig,
OpenHandsConfig,
get_parser,
load_app_config,
load_openhands_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,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',
@ -53,7 +53,7 @@ def get_config(
return config
config = load_app_config()
config = load_openhands_config()
def load_bench_config():

View File

@ -29,10 +29,10 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
load_app_config,
load_openhands_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,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',
@ -63,7 +63,7 @@ def get_config(
return config
config = load_app_config()
config = load_openhands_config()
def load_bench_config():

View File

@ -17,7 +17,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
@ -44,14 +44,14 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -21,7 +21,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'xingyaoww/od-eval-miniwob:v1.0'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),

View File

@ -22,7 +22,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -4,11 +4,11 @@ import pprint
import tqdm
from openhands.core.config import get_llm_config_arg, get_parser, load_app_config
from openhands.core.config import get_llm_config_arg, get_parser, load_openhands_config
from openhands.core.logger import openhands_logger as logger
from openhands.llm.llm import LLM
config = load_app_config()
config = load_openhands_config()
def extract_test_results(res_file_path: str) -> tuple[list[str], list[str]]:

View File

@ -33,10 +33,10 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
load_app_config,
load_openhands_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_app_config()
config = load_openhands_config()
AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
'CodeActAgent': codeact_user_response,
@ -76,10 +76,10 @@ ID2CONDA = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'public.ecr.aws/i5g0m1f6/ml-bench'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -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) -> AppConfig:
def get_config(metadata: EvalMetadata, instance: pd.Series) -> OpenHandsConfig:
# 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) -> AppConfig:
dataset_name=metadata.dataset,
instance_id=instance['instance_id'],
)
config = AppConfig(
config = OpenHandsConfig(
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),
sandbox=sandbox_config,

View File

@ -30,7 +30,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
max_iterations=metadata.max_iterations,

View File

@ -20,7 +20,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = (
'docker.io/xingyaoww/openhands-eval-scienceagentbench'
)
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),

View File

@ -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) -> AppConfig:
def get_config(metadata: EvalMetadata, instance: pd.Series) -> OpenHandsConfig:
# 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) -> AppConfig:
dataset_name=metadata.dataset,
instance_id=instance['instance_id'],
)
config = AppConfig(
config = OpenHandsConfig(
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),
sandbox=sandbox_config,

View File

@ -40,12 +40,12 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
from openhands.core.config.utils import get_condenser_config_arg
from openhands.core.config.condenser_config import NoOpCondenserConfig
from openhands.core.config.utils import get_condenser_config_arg
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,
) -> AppConfig:
) -> OpenHandsConfig:
# 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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
max_iterations=metadata.max_iterations,
@ -721,15 +721,16 @@ 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"...')
@ -806,7 +807,9 @@ 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)

View File

@ -30,7 +30,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
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):
workspace_dir_name = _get_swebench_workspace_dir_name(instance)
_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,
) -> AppConfig:
) -> OpenHandsConfig:
# 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 = AppConfig(
config = OpenHandsConfig(
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,9 +364,13 @@ 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'

View File

@ -41,7 +41,7 @@ from evaluation.utils.shared import (
reset_logger_for_multiprocessing,
run_evaluation,
)
from openhands.core.config import AppConfig, SandboxConfig, get_parser
from openhands.core.config import OpenHandsConfig, 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) -> AppConfig:
def get_config(instance: pd.Series) -> OpenHandsConfig:
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 AppConfig(
return OpenHandsConfig(
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'eventstream'),
sandbox=SandboxConfig(

View File

@ -35,7 +35,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
# 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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
max_iterations=metadata.max_iterations,

View File

@ -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,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
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: AppConfig,
config: OpenHandsConfig,
dependencies: list[str],
save_final_state: bool,
state_dir: str,
@ -274,7 +274,7 @@ if __name__ == '__main__':
temp_dir = os.path.abspath(os.getenv('TMPDIR'))
else:
temp_dir = tempfile.mkdtemp()
config: AppConfig = get_config(
config: OpenHandsConfig = get_config(
args.task_image_name, task_short_name, temp_dir, agent_llm_config, agent_config
)
runtime: Runtime = create_runtime(config)

View File

@ -18,7 +18,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
get_parser,
)
@ -40,10 +40,10 @@ AGENT_CLS_TO_INST_SUFFIX = {
def get_config(
metadata: EvalMetadata,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.base_container_image = 'python:3.12-bookworm'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -30,7 +30,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
# 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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
max_iterations=metadata.max_iterations,

View File

@ -20,7 +20,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
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,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -19,7 +19,7 @@ from evaluation.utils.shared import (
)
from openhands.controller.state.state import State
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -44,7 +44,7 @@ SUPPORTED_AGENT_CLS = {'BrowsingAgent'}
def get_config(
metadata: EvalMetadata,
env_id: str,
) -> AppConfig:
) -> OpenHandsConfig:
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 = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime='docker',

View File

@ -21,7 +21,7 @@ from evaluation.utils.shared import (
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
OpenHandsConfig,
get_llm_config_arg,
parse_arguments,
)
@ -41,10 +41,10 @@ FAKE_RESPONSES = {
def get_config(
metadata: EvalMetadata,
instance_id: str,
) -> AppConfig:
) -> OpenHandsConfig:
sandbox_config = get_default_sandbox_config_for_eval()
sandbox_config.platform = 'linux/amd64'
config = AppConfig(
config = OpenHandsConfig(
default_agent=metadata.agent_class,
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'docker'),

View File

@ -2,9 +2,9 @@ import argparse
import pytest
from openhands.config import load_app_config
from openhands.config import load_openhands_config
config = load_app_config()
config = load_openhands_config()
if __name__ == '__main__':
"""Main entry point of the script.

View File

@ -25,7 +25,7 @@ from openhands.cli.utils import (
write_to_file,
)
from openhands.core.config import (
AppConfig,
OpenHandsConfig,
)
from openhands.core.schema import AgentState
from openhands.events import EventSource
@ -42,7 +42,7 @@ async def handle_commands(
event_stream: EventStream,
usage_metrics: UsageMetrics,
sid: str,
config: AppConfig,
config: OpenHandsConfig,
current_dir: str,
settings_store: FileSettingsStore,
) -> tuple[bool, bool, bool]:
@ -105,7 +105,7 @@ def handle_help_command() -> None:
async def handle_init_command(
config: AppConfig, event_stream: EventStream, current_dir: str
config: OpenHandsConfig, 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:
@ -166,7 +166,7 @@ def handle_new_command(
async def handle_settings_command(
config: AppConfig,
config: OpenHandsConfig,
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: AppConfig, current_dir: str) -> bool:
def check_folder_security_agreement(config: OpenHandsConfig, current_dir: str) -> bool:
# Directories trusted by user for the CLI to use as workspace
# Config from ~/.openhands/config.toml overrides the app config

View File

@ -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 (
AppConfig,
OpenHandsConfig,
parse_arguments,
setup_config_from_args,
)
@ -103,7 +103,7 @@ async def cleanup_session(
async def run_session(
loop: asyncio.AbstractEventLoop,
config: AppConfig,
config: OpenHandsConfig,
settings_store: FileSettingsStore,
current_dir: str,
task_content: str | None = None,
@ -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: AppConfig = setup_config_from_args(args)
config: OpenHandsConfig = setup_config_from_args(args)
# Load settings from Settings Store
# TODO: Make this generic?

View File

@ -18,7 +18,7 @@ from openhands.cli.utils import (
organize_models_and_providers,
)
from openhands.controller.agent import Agent
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
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: AppConfig) -> None:
def display_settings(config: OpenHandsConfig) -> 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: AppConfig, settings_store: FileSettingsStore
config: OpenHandsConfig, 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: AppConfig, settings_store: FileSettingsStore
config: OpenHandsConfig, settings_store: FileSettingsStore
) -> None:
session = PromptSession(key_bindings=kb_cancel())

View File

@ -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 AppConfig
from openhands.core.config import OpenHandsConfig
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: AppConfig) -> None:
def display_event(event: Event, config: OpenHandsConfig) -> None:
global streaming_output_text_area
with print_lock:
if isinstance(event, Action):

View File

@ -1,5 +1,4 @@
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,
@ -8,6 +7,7 @@ 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',
'AppConfig',
'OpenHandsConfig',
'MCPConfig',
'LLMConfig',
'SandboxConfig',
'SecurityConfig',
'ExtendedConfig',
'load_app_config',
'load_openhands_config',
'load_from_env',
'load_from_toml',
'finalize_config',

View File

@ -1,10 +1,11 @@
import os
from urllib.parse import urlparse
from typing import TYPE_CHECKING
from urllib.parse import urlparse
from pydantic import BaseModel, Field, ValidationError, model_validator
if TYPE_CHECKING:
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.utils.import_utils import get_impl
@ -147,7 +148,7 @@ class MCPConfig(BaseModel):
class OpenHandsMCPConfig:
@staticmethod
def add_search_engine(app_config: "AppConfig") -> MCPStdioServerConfig | None:
def add_search_engine(app_config: 'OpenHandsConfig') -> MCPStdioServerConfig | None:
"""Add search engine to the MCP config"""
if (
app_config.search_api_key
@ -165,17 +166,16 @@ 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: "AppConfig", user_id: str | None = None
host: str, config: 'OpenHandsConfig', user_id: str | None = None
) -> tuple[MCPSSEServerConfig, list[MCPStdioServerConfig]]:
"""
Create a default MCP server configuration.
Args:
host: Host string
config: AppConfig
config: OpenHandsConfig
Returns:
tuple[MCPSSEServerConfig, list[MCPStdioServerConfig]]: A tuple containing the default SSE server configuration and a list of MCP stdio server configurations
"""

View File

@ -16,7 +16,7 @@ from openhands.core.config.sandbox_config import SandboxConfig
from openhands.core.config.security_config import SecurityConfig
class AppConfig(BaseModel):
class OpenHandsConfig(BaseModel):
"""Configuration for the app.
Attributes:
@ -65,7 +65,10 @@ class AppConfig(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)
@ -73,7 +76,7 @@ class AppConfig(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)
@ -148,5 +151,5 @@ class AppConfig(BaseModel):
"""Post-initialization hook, called when the instance is created with only default values."""
super().model_post_init(__context)
if not AppConfig.defaults_dict: # Only set defaults_dict if it's empty
AppConfig.defaults_dict = model_defaults_to_dict(self)
if not OpenHandsConfig.defaults_dict: # Only set defaults_dict if it's empty
OpenHandsConfig.defaults_dict = model_defaults_to_dict(self)

View File

@ -15,7 +15,6 @@ 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,
@ -28,6 +27,7 @@ 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: AppConfig, env_or_toml_dict: dict | MutableMapping[str, str]
cfg: OpenHandsConfig, 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 AppConfig object to set attributes on.
cfg: The OpenHandsConfig 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: AppConfig, toml_file: str = 'config.toml') -> None:
def load_from_toml(cfg: OpenHandsConfig, toml_file: str = 'config.toml') -> None:
"""Load the config from the toml file. Supports both styles of config vars.
Args:
cfg: The AppConfig object to update attributes of.
cfg: The OpenHandsConfig 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: AppConfig) -> None:
def finalize_config(cfg: OpenHandsConfig) -> 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: AppConfig) -> None:
def register_custom_agents(config: OpenHandsConfig) -> 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: AppConfig) -> None:
)
def load_app_config(
def load_openhands_config(
set_logging_levels: bool = True, config_file: str = 'config.toml'
) -> AppConfig:
) -> OpenHandsConfig:
"""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 = AppConfig()
config = OpenHandsConfig()
load_from_toml(config, config_file)
load_from_env(config, os.environ)
finalize_config(config)
@ -802,13 +802,13 @@ def load_app_config(
return config
def setup_config_from_args(args: argparse.Namespace) -> AppConfig:
def setup_config_from_args(args: argparse.Namespace) -> OpenHandsConfig:
"""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_app_config(config_file=args.config_file)
config = load_openhands_config(config_file=args.config_file)
# Override with command line arguments if provided
if args.llm_config:

View File

@ -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 (
AppConfig,
OpenHandsConfig,
parse_arguments,
setup_config_from_args,
)
@ -47,7 +47,7 @@ class FakeUserResponseFunc(Protocol):
async def run_controller(
config: AppConfig,
config: OpenHandsConfig,
initial_user_action: Action,
sid: str | None = None,
runtime: Runtime | None = None,
@ -90,7 +90,7 @@ async def run_controller(
config.max_budget_per_task.
Example:
>>> config = load_app_config()
>>> config = load_openhands_config()
>>> action = MessageAction(content="Write a hello world program")
>>> state = await run_controller(config=config, initial_user_action=action)
"""
@ -279,7 +279,7 @@ def load_replay_log(trajectory_path: str) -> tuple[list[Event] | None, Action]:
if __name__ == '__main__':
args = parse_arguments()
config: AppConfig = setup_config_from_args(args)
config: OpenHandsConfig = setup_config_from_args(args)
# Read task from file, CLI args, or stdin
task_str = read_task(args, config.cli_multiline_input)

View File

@ -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 (
AppConfig,
OpenHandsConfig,
)
from openhands.core.logger import openhands_logger as logger
from openhands.events import EventStream
@ -28,7 +28,7 @@ from openhands.utils.async_utils import GENERAL_TIMEOUT, call_async_from_sync
def create_runtime(
config: AppConfig,
config: OpenHandsConfig,
sid: str | None = None,
headless_mode: bool = True,
agent: Agent | None = None,
@ -172,7 +172,7 @@ def create_memory(
return memory
def create_agent(config: AppConfig) -> Agent:
def create_agent(config: OpenHandsConfig) -> 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: AppConfig) -> Agent:
def create_controller(
agent: Agent,
runtime: Runtime,
config: AppConfig,
config: OpenHandsConfig,
headless_mode: bool = True,
replay_events: list[Event] | None = None,
) -> tuple[AgentController, State | None]:
@ -218,7 +218,7 @@ def create_controller(
return (controller, initial_state)
def generate_sid(config: AppConfig, session_name: str | None = None) -> str:
def generate_sid(config: OpenHandsConfig, 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

View File

@ -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: AppConfig
agent: 'Agent', runtime: Runtime, memory: 'Memory', app_config: OpenHandsConfig
):
"""
Add MCP tools to an agent.

View File

@ -16,7 +16,7 @@ from termcolor import colored
import openhands
from openhands.controller.state.state import State
from openhands.core.config import AgentConfig, AppConfig, LLMConfig, SandboxConfig
from openhands.core.config import AgentConfig, LLMConfig, OpenHandsConfig, 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 = AppConfig(
config = OpenHandsConfig(
default_agent='CodeActAgent',
runtime='docker',
max_budget_per_task=4,

View File

@ -15,7 +15,7 @@ from zipfile import ZipFile
import httpx
from openhands.core.config import AppConfig, SandboxConfig
from openhands.core.config import OpenHandsConfig, 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
@ -97,7 +97,7 @@ class Runtime(FileEditRuntimeMixin):
"""
sid: str
config: AppConfig
config: OpenHandsConfig
initial_env_vars: dict[str, str]
attach_to_existing: bool
status_callback: Callable[[str, str, str], None] | None
@ -105,7 +105,7 @@ class Runtime(FileEditRuntimeMixin):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -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 AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.config.mcp_config import (
MCPConfig,
MCPSSEServerConfig,
@ -65,7 +65,7 @@ class ActionExecutionClient(Runtime):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,
@ -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(

View File

@ -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 AppConfig
from openhands.core.config import OpenHandsConfig
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,7 +57,7 @@ class CLIRuntime(Runtime):
file operations using Python's standard library. It does not implement browser functionality.
Args:
config (AppConfig): The application configuration.
config (OpenHandsConfig): The application configuration.
event_stream (EventStream): The event stream to subscribe to.
sid (str, optional): The session ID. Defaults to 'default'.
plugins (list[PluginRequirement] | None, optional): List of plugin requirements. Defaults to None.
@ -71,7 +71,7 @@ class CLIRuntime(Runtime):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -11,7 +11,7 @@ from daytona_sdk import (
Workspace,
)
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.events.stream import EventStream
from openhands.runtime.impl.action_execution.action_execution_client import (
ActionExecutionClient,
@ -33,7 +33,7 @@ class DaytonaRuntime(ActionExecutionClient):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -8,7 +8,7 @@ import httpx
import tenacity
from docker.models.containers import Container
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.exceptions import (
AgentRuntimeDisconnectedError,
AgentRuntimeNotFoundError,
@ -23,7 +23,10 @@ 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
@ -62,7 +65,7 @@ class DockerRuntime(ActionExecutionClient):
When receive an event, it will send the event to runtime-client which run inside the docker environment.
Args:
config (AppConfig): The application configuration.
config (OpenHandsConfig): The application configuration.
event_stream (EventStream): The event stream to subscribe to.
sid (str, optional): The session ID. Defaults to 'default'.
plugins (list[PluginRequirement] | None, optional): List of plugin requirements. Defaults to None.
@ -73,7 +76,7 @@ class DockerRuntime(ActionExecutionClient):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -1,6 +1,6 @@
from typing import Callable
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events.action import (
FileReadAction,
FileWriteAction,
@ -22,7 +22,7 @@ from openhands.runtime.utils.files import insert_lines, read_lines
class E2BRuntime(Runtime):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -12,7 +12,7 @@ import httpx
import tenacity
import openhands
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.exceptions import AgentRuntimeDisconnectedError
from openhands.core.logger import openhands_logger as logger
from openhands.events import EventStream
@ -107,7 +107,7 @@ class LocalRuntime(ActionExecutionClient):
When receiving an event, it will send the event to the server via HTTP.
Args:
config (AppConfig): The application configuration.
config (OpenHandsConfig): The application configuration.
event_stream (EventStream): The event stream to subscribe to.
sid (str, optional): The session ID. Defaults to 'default'.
plugins (list[PluginRequirement] | None, optional): list of plugin requirements. Defaults to None.
@ -116,7 +116,7 @@ class LocalRuntime(ActionExecutionClient):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -7,7 +7,7 @@ import httpx
import modal
import tenacity
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events import EventStream
from openhands.runtime.impl.action_execution.action_execution_client import (
ActionExecutionClient,
@ -31,7 +31,7 @@ class ModalRuntime(ActionExecutionClient):
When receive an event, it will send the event to runtime-client which run inside the Modal sandbox environment.
Args:
config (AppConfig): The application configuration.
config (OpenHandsConfig): The application configuration.
event_stream (EventStream): The event stream to subscribe to.
sid (str, optional): The session ID. Defaults to 'default'.
plugins (list[PluginRequirement] | None, optional): List of plugin requirements. Defaults to None.
@ -44,7 +44,7 @@ class ModalRuntime(ActionExecutionClient):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -8,7 +8,7 @@ import httpx
import tenacity
from tenacity import RetryCallState
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.exceptions import (
AgentRuntimeDisconnectedError,
AgentRuntimeError,
@ -24,7 +24,10 @@ from openhands.runtime.impl.action_execution.action_execution_client import (
ActionExecutionClient,
)
from openhands.runtime.plugins import PluginRequirement
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.request import send_request
from openhands.runtime.utils.runtime_build import build_runtime_image
from openhands.utils.async_utils import call_sync_from_async
@ -45,7 +48,7 @@ class RemoteRuntime(ActionExecutionClient):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -6,7 +6,7 @@ from runloop_api_client import Runloop
from runloop_api_client.types import DevboxView
from runloop_api_client.types.shared_params import LaunchParameters
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.events import EventStream
from openhands.runtime.impl.action_execution.action_execution_client import (
@ -27,7 +27,7 @@ class RunloopRuntime(ActionExecutionClient):
def __init__(
self,
config: AppConfig,
config: OpenHandsConfig,
event_stream: EventStream,
sid: str = 'default',
plugins: list[PluginRequirement] | None = None,

View File

@ -1,4 +1,4 @@
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.runtime.plugins import PluginRequirement
DEFAULT_PYTHON_PREFIX = [
@ -15,7 +15,7 @@ DEFAULT_MAIN_MODULE = 'openhands.runtime.action_execution_server'
def get_action_execution_server_startup_command(
server_port: int,
plugins: list[PluginRequirement],
app_config: AppConfig,
app_config: OpenHandsConfig,
python_prefix: list[str] = DEFAULT_PYTHON_PREFIX,
override_user_id: int | None = None,
override_username: str | None = None,

View File

@ -6,7 +6,7 @@ from typing import Any
from openhands_aci.utils.diff import get_diff
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.events.action import (
FileEditAction,
@ -83,7 +83,7 @@ def get_new_file_contents(
class FileEditRuntimeInterface(ABC):
config: AppConfig
config: OpenHandsConfig
@abstractmethod
def read(self, action: FileReadAction) -> Observation:

View File

@ -4,14 +4,12 @@ from abc import ABC, abstractmethod
import socketio
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events.action import MessageAction
from openhands.events.event_store import EventStore
from openhands.server.config.server_config import ServerConfig
from openhands.server.data_models.agent_loop_info import AgentLoopInfo
from openhands.server.data_models.conversation_info import ConversationInfo
from openhands.server.monitoring import MonitoringListener
from openhands.server.session.conversation import Conversation
from openhands.server.session.conversation import ServerConversation
from openhands.storage.conversation.conversation_store import ConversationStore
from openhands.storage.data_models.settings import Settings
from openhands.storage.files import FileStore
@ -26,7 +24,7 @@ class ConversationManager(ABC):
"""
sio: socketio.AsyncServer
config: AppConfig
config: OpenHandsConfig
file_store: FileStore
conversation_store: ConversationStore
@ -41,11 +39,11 @@ class ConversationManager(ABC):
@abstractmethod
async def attach_to_conversation(
self, sid: str, user_id: str | None = None
) -> Conversation | None:
) -> ServerConversation | None:
"""Attach to an existing conversation or create a new one."""
@abstractmethod
async def detach_from_conversation(self, conversation: Conversation):
async def detach_from_conversation(self, conversation: ServerConversation):
"""Detach from a conversation."""
@abstractmethod
@ -109,7 +107,7 @@ class ConversationManager(ABC):
def get_instance(
cls,
sio: socketio.AsyncServer,
config: AppConfig,
config: OpenHandsConfig,
file_store: FileStore,
server_config: ServerConfig,
monitoring_listener: MonitoringListener,

View File

@ -15,7 +15,7 @@ from docker.models.containers import Container
from fastapi import status
from openhands.controller.agent import Agent
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.events.action import MessageAction
from openhands.events.nested_event_store import NestedEventStore
@ -30,7 +30,7 @@ from openhands.server.conversation_manager.conversation_manager import (
)
from openhands.server.data_models.agent_loop_info import AgentLoopInfo
from openhands.server.monitoring import MonitoringListener
from openhands.server.session.conversation import Conversation
from openhands.server.session.conversation import ServerConversation
from openhands.server.session.conversation_init_data import ConversationInitData
from openhands.server.session.session import ROOM_KEY, Session
from openhands.storage.conversation.conversation_store import ConversationStore
@ -45,10 +45,10 @@ from openhands.utils.import_utils import get_impl
@dataclass
class DockerNestedConversationManager(ConversationManager):
"""Conversation manager where the agent loops exist inside the docker containers."""
"""ServerConversation manager where the agent loops exist inside the docker containers."""
sio: socketio.AsyncServer
config: AppConfig
config: OpenHandsConfig
server_config: ServerConfig
file_store: FileStore
docker_client: docker.DockerClient = field(default_factory=docker.from_env)
@ -65,11 +65,11 @@ class DockerNestedConversationManager(ConversationManager):
async def attach_to_conversation(
self, sid: str, user_id: str | None = None
) -> Conversation | None:
) -> ServerConversation | None:
# Not supported - clients should connect directly to the nested server!
raise ValueError('unsupported_operation')
async def detach_from_conversation(self, conversation: Conversation):
async def detach_from_conversation(self, conversation: ServerConversation):
# Not supported - clients should connect directly to the nested server!
raise ValueError('unsupported_operation')
@ -309,7 +309,7 @@ class DockerNestedConversationManager(ConversationManager):
def get_instance(
cls,
sio: socketio.AsyncServer,
config: AppConfig,
config: OpenHandsConfig,
file_store: FileStore,
server_config: ServerConfig,
monitoring_listener: MonitoringListener,
@ -433,7 +433,7 @@ class DockerNestedConversationManager(ConversationManager):
volumes = [v.strip() for v in config.sandbox.volumes.split(',')]
conversation_dir = get_conversation_dir(sid, user_id)
volumes.append(
f'{config.file_store_path}/{conversation_dir}:{AppConfig.model_fields["file_store_path"].default}/{conversation_dir}:rw'
f'{config.file_store_path}/{conversation_dir}:{OpenHandsConfig.model_fields["file_store_path"].default}/{conversation_dir}:rw'
)
config.sandbox.volumes = ','.join(volumes)

View File

@ -6,7 +6,7 @@ from typing import Callable, Iterable
import socketio
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.core.exceptions import AgentRuntimeUnavailableError
from openhands.core.logger import openhands_logger as logger
from openhands.core.schema.agent import AgentState
@ -16,7 +16,7 @@ from openhands.server.config.server_config import ServerConfig
from openhands.server.data_models.agent_loop_info import AgentLoopInfo
from openhands.server.monitoring import MonitoringListener
from openhands.server.session.agent_session import WAIT_TIME_BEFORE_CLOSE
from openhands.server.session.conversation import Conversation
from openhands.server.session.conversation import ServerConversation
from openhands.server.session.session import ROOM_KEY, Session
from openhands.storage.conversation.conversation_store import ConversationStore
from openhands.storage.data_models.conversation_metadata import ConversationMetadata
@ -41,17 +41,17 @@ class StandaloneConversationManager(ConversationManager):
"""Manages conversations in standalone mode (single server instance)."""
sio: socketio.AsyncServer
config: AppConfig
config: OpenHandsConfig
file_store: FileStore
server_config: ServerConfig
# Defaulting monitoring_listener for temp backward compatibility.
monitoring_listener: MonitoringListener = MonitoringListener()
_local_agent_loops_by_sid: dict[str, Session] = field(default_factory=dict)
_local_connection_id_to_session_id: dict[str, str] = field(default_factory=dict)
_active_conversations: dict[str, tuple[Conversation, int]] = field(
_active_conversations: dict[str, tuple[ServerConversation, int]] = field(
default_factory=dict
)
_detached_conversations: dict[str, tuple[Conversation, float]] = field(
_detached_conversations: dict[str, tuple[ServerConversation, float]] = field(
default_factory=dict
)
_conversations_lock: asyncio.Lock = field(default_factory=asyncio.Lock)
@ -69,7 +69,7 @@ class StandaloneConversationManager(ConversationManager):
async def attach_to_conversation(
self, sid: str, user_id: str | None = None
) -> Conversation | None:
) -> ServerConversation | None:
start_time = time.time()
if not await session_exists(sid, self.file_store, user_id=user_id):
return None
@ -94,7 +94,7 @@ class StandaloneConversationManager(ConversationManager):
return conversation
# Create new conversation if none exists
c = Conversation(
c = ServerConversation(
sid, file_store=self.file_store, config=self.config, user_id=user_id
)
try:
@ -108,7 +108,7 @@ class StandaloneConversationManager(ConversationManager):
return None
end_time = time.time()
logger.info(
f'Conversation {c.sid} connected in {end_time - start_time} seconds'
f'ServerConversation {c.sid} connected in {end_time - start_time} seconds'
)
self._active_conversations[sid] = (c, 1)
return c
@ -129,7 +129,7 @@ class StandaloneConversationManager(ConversationManager):
agent_loop_info = await self.maybe_start_agent_loop(sid, settings, user_id)
return agent_loop_info
async def detach_from_conversation(self, conversation: Conversation):
async def detach_from_conversation(self, conversation: ServerConversation):
sid = conversation.sid
async with self._conversations_lock:
if sid in self._active_conversations:
@ -377,7 +377,7 @@ class StandaloneConversationManager(ConversationManager):
def get_instance(
cls,
sio: socketio.AsyncServer,
config: AppConfig,
config: OpenHandsConfig,
file_store: FileStore,
server_config: ServerConfig,
monitoring_listener: MonitoringListener | None,
@ -475,7 +475,7 @@ class StandaloneConversationManager(ConversationManager):
continue
results.append(self._agent_loop_info_from_session(session))
return results
def _agent_loop_info_from_session(self, session: Session):
return AgentLoopInfo(
conversation_id=session.sid,
@ -485,7 +485,7 @@ class StandaloneConversationManager(ConversationManager):
)
def _get_conversation_url(self, conversation_id: str):
return f"/api/conversations/{conversation_id}"
return f'/api/conversations/{conversation_id}'
def _last_updated_at_key(conversation: ConversationMetadata) -> float:

View File

@ -1,7 +1,7 @@
import os
import re
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.server.shared import config as shared_config
@ -30,7 +30,7 @@ def sanitize_filename(filename: str) -> str:
def load_file_upload_config(
config: AppConfig = shared_config,
config: OpenHandsConfig = shared_config,
) -> tuple[int, bool, list[str]]:
"""Load file upload configuration from the config object.

View File

@ -1,4 +1,4 @@
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.events.event import Event
@ -33,6 +33,6 @@ class MonitoringListener:
@classmethod
def get_instance(
cls,
config: AppConfig,
config: OpenHandsConfig,
) -> 'MonitoringListener':
return cls()

View File

@ -103,7 +103,7 @@ async def search_events(
end_id: int | None = None,
reverse: bool = False,
filter: EventFilter | None = None,
limit: int = 20
limit: int = 20,
):
"""Search through the event stream with filtering and pagination.
Args:
@ -123,22 +123,24 @@ async def search_events(
"""
if not request.state.conversation:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail='Conversation not found'
status_code=status.HTTP_404_NOT_FOUND, detail='ServerConversation not found'
)
if limit < 0 or limit > 100:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail='Invalid limit'
)
# Get matching events from the stream
event_stream = request.state.conversation.event_stream
events = list(event_stream.search_events(
start_id=start_id,
end_id=end_id,
reverse=reverse,
filter=filter,
limit=limit + 1,
))
events = list(
event_stream.search_events(
start_id=start_id,
end_id=end_id,
reverse=reverse,
filter=filter,
limit=limit + 1,
)
)
# Check if there are more events
has_more = len(events) > limit
@ -156,4 +158,4 @@ async def search_events(
async def add_event(request: Request):
data = request.json()
conversation_manager.send_to_event_stream(request.state.sid, data)
return JSONResponse({"success": True})
return JSONResponse({'success': True})

View File

@ -1,27 +1,36 @@
import re
from typing import Annotated
from pydantic import Field
from fastmcp import FastMCP
from fastmcp.server.dependencies import get_http_request
from pydantic import Field
from openhands.core.logger import openhands_logger as logger
from openhands.integrations.github.github_service import GithubServiceImpl
from openhands.integrations.gitlab.gitlab_service import GitLabServiceImpl
from openhands.integrations.provider import ProviderToken
from openhands.integrations.service_types import ProviderType
from openhands.server.shared import ConversationStoreImpl, config
from openhands.server.user_auth import get_access_token, get_provider_tokens, get_user_id
from openhands.core.logger import openhands_logger as logger
from openhands.server.user_auth import (
get_access_token,
get_provider_tokens,
get_user_id,
)
from openhands.storage.data_models.conversation_metadata import ConversationMetadata
mcp_server = FastMCP('mcp')
async def save_pr_metadata(user_id: str, conversation_id: str, tool_result: str) -> None:
async def save_pr_metadata(
user_id: str, conversation_id: str, tool_result: str
) -> None:
conversation_store = await ConversationStoreImpl.get_instance(config, user_id)
conversation: ConversationMetadata = await conversation_store.get_metadata(conversation_id)
conversation: ConversationMetadata = await conversation_store.get_metadata(
conversation_id
)
pull_pattern = r"pull/(\d+)"
merge_request_pattern = r"merge_requests/(\d+)"
pull_pattern = r'pull/(\d+)'
merge_request_pattern = r'merge_requests/(\d+)'
# Check if the tool_result contains the PR number
pr_number = None
@ -33,11 +42,11 @@ async def save_pr_metadata(user_id: str, conversation_id: str, tool_result: str)
elif match_merge_request:
pr_number = int(match_merge_request.group(1))
if pr_number:
conversation.pr_number.append(pr_number)
await conversation_store.save_metadata(conversation)
@mcp_server.tool()
async def create_pr(
repo_name: Annotated[
@ -46,7 +55,7 @@ async def create_pr(
source_branch: Annotated[str, Field(description='Source branch on repo')],
target_branch: Annotated[str, Field(description='Target branch on repo')],
title: Annotated[str, Field(description='PR Title')],
body: Annotated[str | None, Field(description='PR body')]
body: Annotated[str | None, Field(description='PR body')],
) -> str:
"""Open a draft PR in GitHub"""
@ -54,20 +63,24 @@ async def create_pr(
request = get_http_request()
headers = request.headers
conversation_id = headers.get('X-OpenHands-Conversation-ID', None)
conversation_id = headers.get('X-OpenHands-ServerConversation-ID', None)
provider_tokens = await get_provider_tokens(request)
access_token = await get_access_token(request)
user_id = await get_user_id(request)
github_token = provider_tokens.get(ProviderType.GITHUB, ProviderToken()) if provider_tokens else ProviderToken()
github_token = (
provider_tokens.get(ProviderType.GITHUB, ProviderToken())
if provider_tokens
else ProviderToken()
)
github_service = GithubServiceImpl(
user_id=github_token.user_id,
external_auth_id=user_id,
external_auth_token=access_token,
token=github_token.token,
base_domain=github_token.host
base_domain=github_token.host,
)
try:
@ -76,7 +89,7 @@ async def create_pr(
source_branch=source_branch,
target_branch=target_branch,
title=title,
body=body
body=body,
)
if conversation_id and user_id:
@ -88,37 +101,41 @@ async def create_pr(
return response
@mcp_server.tool()
async def create_mr(
id: Annotated[
int | str, Field(description='GitLab repository (ID or URL-encoded path of the project)')
int | str,
Field(description='GitLab repository (ID or URL-encoded path of the project)'),
],
source_branch: Annotated[str, Field(description='Source branch on repo')],
target_branch: Annotated[str, Field(description='Target branch on repo')],
title: Annotated[str, Field(description='MR Title')],
description: Annotated[str | None, Field(description='MR description')]
description: Annotated[str | None, Field(description='MR description')],
) -> str:
"""Open a draft MR in GitLab"""
logger.info('Calling OpenHands MCP create_mr')
request = get_http_request()
headers = request.headers
conversation_id = headers.get('X-OpenHands-Conversation-ID', None)
conversation_id = headers.get('X-OpenHands-ServerConversation-ID', None)
provider_tokens = await get_provider_tokens(request)
access_token = await get_access_token(request)
user_id = await get_user_id(request)
github_token = provider_tokens.get(ProviderType.GITLAB, ProviderToken()) if provider_tokens else ProviderToken()
github_token = (
provider_tokens.get(ProviderType.GITLAB, ProviderToken())
if provider_tokens
else ProviderToken()
)
github_service = GitLabServiceImpl(
user_id=github_token.user_id,
external_auth_id=user_id,
external_auth_token=access_token,
token=github_token.token,
base_domain=github_token.host
base_domain=github_token.host,
)
try:
@ -137,5 +154,3 @@ async def create_mr(
response = str(e)
return response

View File

@ -1,16 +1,26 @@
from typing import Any
import uuid
from typing import Any
from openhands.core.logger import openhands_logger as logger
from openhands.events.action.message import MessageAction
from openhands.integrations.provider import CUSTOM_SECRETS_TYPE_WITH_JSON_SCHEMA, PROVIDER_TOKEN_TYPE
from openhands.integrations.provider import (
CUSTOM_SECRETS_TYPE_WITH_JSON_SCHEMA,
PROVIDER_TOKEN_TYPE,
)
from openhands.integrations.service_types import ProviderType
from openhands.server.data_models.agent_loop_info import AgentLoopInfo
from openhands.server.session.conversation_init_data import ConversationInitData
from openhands.server.shared import ConversationStoreImpl, SettingsStoreImpl, config, conversation_manager
from openhands.server.shared import (
ConversationStoreImpl,
SettingsStoreImpl,
config,
conversation_manager,
)
from openhands.server.types import LLMAuthenticationError, MissingSettingsError
from openhands.storage.data_models.conversation_metadata import ConversationMetadata, ConversationTrigger
from openhands.storage.data_models.conversation_metadata import (
ConversationMetadata,
ConversationTrigger,
)
from openhands.utils.conversation_summary import get_default_conversation_title
@ -69,7 +79,7 @@ async def create_new_conversation(
conversation_init_data = ConversationInitData(**session_init_args)
logger.info('Loading conversation store')
conversation_store = await ConversationStoreImpl.get_instance(config, user_id)
logger.info('Conversation store loaded')
logger.info('ServerConversation store loaded')
# For nested runtimes, we allow a single conversation id, passed in on container creation
if conversation_id is None:
@ -101,7 +111,7 @@ async def create_new_conversation(
extra={'user_id': user_id, 'session_id': conversation_id},
)
initial_message_action = None
if initial_user_msg or image_urls:
if initial_user_msg or image_urls:
initial_message_action = MessageAction(
content=initial_user_msg or '',
image_urls=image_urls or [],
@ -109,7 +119,7 @@ async def create_new_conversation(
if attach_convo_id and conversation_instructions:
conversation_instructions = conversation_instructions.format(conversation_id)
agent_loop_info = await conversation_manager.maybe_start_agent_loop(
conversation_id,
conversation_init_data,

View File

@ -9,15 +9,18 @@ from openhands.controller import AgentController
from openhands.controller.agent import Agent
from openhands.controller.replay import ReplayManager
from openhands.controller.state.state import State
from openhands.core.config import AgentConfig, AppConfig, LLMConfig
from openhands.core.config import AgentConfig, LLMConfig, OpenHandsConfig
from openhands.core.exceptions import AgentRuntimeUnavailableError
from openhands.core.logger import OpenHandsLoggerAdapter
from openhands.core.schema.agent import AgentState
from openhands.events.action import ChangeAgentStateAction, MessageAction
from openhands.events.event import Event, EventSource
from openhands.events.stream import EventStream
from openhands.integrations.provider import CUSTOM_SECRETS_TYPE, PROVIDER_TOKEN_TYPE, ProviderHandler
from openhands.integrations.service_types import ProviderType
from openhands.integrations.provider import (
CUSTOM_SECRETS_TYPE,
PROVIDER_TOKEN_TYPE,
ProviderHandler,
)
from openhands.mcp import add_mcp_tools_to_agent
from openhands.memory.memory import Memory
from openhands.microagent.microagent import BaseMicroagent
@ -80,7 +83,7 @@ class AgentSession:
async def start(
self,
runtime_name: str,
config: AppConfig,
config: OpenHandsConfig,
agent: Agent,
max_iterations: int,
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
@ -118,7 +121,9 @@ class AgentSession:
finished = False # For monitoring
runtime_connected = False
custom_secrets_handler = UserSecrets(custom_secrets=custom_secrets if custom_secrets else {})
custom_secrets_handler = UserSecrets(
custom_secrets=custom_secrets if custom_secrets else {}
)
try:
self._create_security_analyzer(config.security.security_analyzer)
@ -147,7 +152,7 @@ class AgentSession:
selected_repository=selected_repository,
repo_directory=repo_directory,
conversation_instructions=conversation_instructions,
custom_secrets_descriptions=custom_secrets_handler.get_custom_secrets_descriptions()
custom_secrets_descriptions=custom_secrets_handler.get_custom_secrets_descriptions(),
)
# NOTE: this needs to happen before controller is created
@ -232,7 +237,7 @@ class AgentSession:
initial_message: MessageAction | None,
replay_json: str,
agent: Agent,
config: AppConfig,
config: OpenHandsConfig,
max_iterations: int,
max_budget_per_task: float | None,
agent_to_llm_config: dict[str, LLMConfig] | None,
@ -271,11 +276,10 @@ class AgentSession:
security_analyzer, SecurityAnalyzer
)(self.event_stream)
def override_provider_tokens_with_custom_secret(
self,
git_provider_tokens: PROVIDER_TOKEN_TYPE | None,
custom_secrets: CUSTOM_SECRETS_TYPE | None
custom_secrets: CUSTOM_SECRETS_TYPE | None,
):
if git_provider_tokens and custom_secrets:
tokens = dict(git_provider_tokens)
@ -283,15 +287,14 @@ class AgentSession:
token_name = ProviderHandler.get_provider_env_key(provider)
if token_name in custom_secrets or token_name.upper() in custom_secrets:
del tokens[provider]
return MappingProxyType(tokens)
return git_provider_tokens
async def _create_runtime(
self,
runtime_name: str,
config: AppConfig,
config: OpenHandsConfig,
agent: Agent,
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
custom_secrets: CUSTOM_SECRETS_TYPE | None = None,
@ -317,10 +320,14 @@ class AgentSession:
self.logger.debug(f'Initializing runtime `{runtime_name}` now...')
runtime_cls = get_runtime_cls(runtime_name)
if runtime_cls == RemoteRuntime:
if runtime_cls == RemoteRuntime:
# If provider tokens is passed in custom secrets, then remove provider from provider tokens
# We prioritize provider tokens set in custom secrets
provider_tokens_without_gitlab = self.override_provider_tokens_with_custom_secret(git_provider_tokens, custom_secrets)
provider_tokens_without_gitlab = (
self.override_provider_tokens_with_custom_secret(
git_provider_tokens, custom_secrets
)
)
self.runtime = runtime_cls(
config=config,
@ -339,7 +346,7 @@ class AgentSession:
provider_tokens=git_provider_tokens
or cast(PROVIDER_TOKEN_TYPE, MappingProxyType({}))
)
# Merge git provider tokens with custom secrets before passing over to runtime
env_vars.update(await provider_handler.get_env_vars(expose_secrets=True))
self.runtime = runtime_cls(
@ -436,11 +443,11 @@ class AgentSession:
return controller
async def _create_memory(
self,
selected_repository: str | None,
repo_directory: str | None,
self,
selected_repository: str | None,
repo_directory: str | None,
conversation_instructions: str | None,
custom_secrets_descriptions: dict[str, str]
custom_secrets_descriptions: dict[str, str],
) -> Memory:
memory = Memory(
event_stream=self.event_stream,
@ -461,10 +468,7 @@ class AgentSession:
memory.load_user_workspace_microagents(microagents)
if selected_repository and repo_directory:
memory.set_repository_info(
selected_repository,
repo_directory
)
memory.set_repository_info(selected_repository, repo_directory)
return memory
def _maybe_restore_state(self) -> State | None:

View File

@ -1,6 +1,6 @@
import asyncio
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events.stream import EventStream
from openhands.runtime import get_runtime_cls
from openhands.runtime.base import Runtime
@ -9,7 +9,7 @@ from openhands.storage.files import FileStore
from openhands.utils.async_utils import call_sync_from_async
class Conversation:
class ServerConversation:
sid: str
file_store: FileStore
event_stream: EventStream
@ -17,7 +17,11 @@ class Conversation:
user_id: str | None
def __init__(
self, sid: str, file_store: FileStore, config: AppConfig, user_id: str | None
self,
sid: str,
file_store: FileStore,
config: OpenHandsConfig,
user_id: str | None,
):
self.sid = sid
self.config = config

View File

@ -6,7 +6,7 @@ from logging import LoggerAdapter
import socketio
from openhands.controller.agent import Agent
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.config.condenser_config import (
BrowserOutputCondenserConfig,
CondenserPipelineConfig,
@ -43,7 +43,7 @@ class Session:
is_alive: bool = True
agent_session: AgentSession
loop: asyncio.AbstractEventLoop
config: AppConfig
config: OpenHandsConfig
file_store: FileStore
user_id: str | None
logger: LoggerAdapter
@ -51,7 +51,7 @@ class Session:
def __init__(
self,
sid: str,
config: AppConfig,
config: OpenHandsConfig,
file_store: FileStore,
sio: socketio.AsyncServer | None,
user_id: str | None = None,
@ -128,9 +128,15 @@ class Session:
self.config.search_api_key = settings.search_api_key
# NOTE: this need to happen AFTER the config is updated with the search_api_key
self.config.mcp = settings.mcp_config or MCPConfig(sse_servers=[], stdio_servers=[])
self.config.mcp = settings.mcp_config or MCPConfig(
sse_servers=[], stdio_servers=[]
)
# Add OpenHands' MCP server by default
openhands_mcp_server, openhands_mcp_stdio_servers = OpenHandsMCPConfigImpl.create_default_mcp_server_config(self.config.mcp_host, self.config, self.user_id)
openhands_mcp_server, openhands_mcp_stdio_servers = (
OpenHandsMCPConfigImpl.create_default_mcp_server_config(
self.config.mcp_host, self.config, self.user_id
)
)
if openhands_mcp_server:
self.config.mcp.sse_servers.append(openhands_mcp_server)
self.config.mcp.stdio_servers.extend(openhands_mcp_stdio_servers)
@ -154,7 +160,7 @@ class Session:
),
]
)
self.logger.info(
f'Enabling pipeline condenser with:'
f' browser_output_masking(attention_window=2), '

View File

@ -3,8 +3,8 @@ import os
import socketio
from dotenv import load_dotenv
from openhands.core.config import load_app_config
from openhands.core.config.app_config import AppConfig
from openhands.core.config import load_openhands_config
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.server.config.server_config import ServerConfig, load_server_config
from openhands.server.conversation_manager.conversation_manager import (
ConversationManager,
@ -20,7 +20,7 @@ from openhands.utils.import_utils import get_impl
load_dotenv()
config: AppConfig = load_app_config()
config: OpenHandsConfig = load_openhands_config()
server_config_interface: ServerConfigInterface = load_server_config()
assert isinstance(server_config_interface, ServerConfig), (
'Loaded server config interface is not a ServerConfig, despite this being assumed'

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Iterable
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.storage.data_models.conversation_metadata import ConversationMetadata
from openhands.storage.data_models.conversation_metadata_result_set import (
ConversationMetadataResultSet,
@ -22,9 +22,7 @@ class ConversationStore(ABC):
async def get_metadata(self, conversation_id: str) -> ConversationMetadata:
"""Load conversation metadata."""
async def validate_metadata(
self, conversation_id: str, user_id: str
) -> bool:
async def validate_metadata(self, conversation_id: str, user_id: str) -> bool:
"""Validate that conversation belongs to the current user."""
metadata = await self.get_metadata(conversation_id)
if not metadata.user_id or metadata.user_id != user_id:
@ -57,6 +55,6 @@ class ConversationStore(ABC):
@classmethod
@abstractmethod
async def get_instance(
cls, config: AppConfig, user_id: str | None
cls, config: OpenHandsConfig, user_id: str | None
) -> ConversationStore:
"""Get a store for the user represented by the token given."""

View File

@ -6,7 +6,7 @@ from pathlib import Path
from pydantic import TypeAdapter
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.storage import get_file_store
from openhands.storage.conversation.conversation_store import ConversationStore
@ -42,7 +42,7 @@ class FileConversationStore(ConversationStore):
json_obj = json.loads(json_str)
if 'created_at' not in json_obj:
raise FileNotFoundError(path)
# Remove github_user_id if it exists
if 'github_user_id' in json_obj:
json_obj.pop('github_user_id')
@ -103,7 +103,7 @@ class FileConversationStore(ConversationStore):
@classmethod
async def get_instance(
cls, config: AppConfig, user_id: str | None
cls, config: OpenHandsConfig, user_id: str | None
) -> FileConversationStore:
file_store = get_file_store(config.file_store, config.file_store_path)
return FileConversationStore(file_store)

View File

@ -12,7 +12,7 @@ from pydantic.json import pydantic_encoder
from openhands.core.config.llm_config import LLMConfig
from openhands.core.config.mcp_config import MCPConfig
from openhands.core.config.utils import load_app_config
from openhands.core.config.utils import load_openhands_config
from openhands.storage.data_models.user_secrets import UserSecrets
@ -41,7 +41,6 @@ class Settings(BaseModel):
mcp_config: MCPConfig | None = None
search_api_key: SecretStr | None = None
model_config = {
'validate_assignment': True,
}
@ -54,7 +53,7 @@ class Settings(BaseModel):
"""
if api_key is None:
return None
context = info.context
if context and context.get('expose_secrets', False):
return api_key.get_secret_value()
@ -106,7 +105,7 @@ class Settings(BaseModel):
@staticmethod
def from_config() -> Settings | None:
app_config = load_app_config()
app_config = load_openhands_config()
llm_config: LLMConfig = app_config.get_llm_config()
if llm_config.api_key is None:
# If no api key has been set, we take this to mean that there is no reasonable default

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import json
from dataclasses import dataclass
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.storage import get_file_store
from openhands.storage.data_models.user_secrets import UserSecrets
from openhands.storage.files import FileStore
@ -37,7 +37,7 @@ class FileSecretsStore(SecretsStore):
@classmethod
async def get_instance(
cls, config: AppConfig, user_id: str | None
cls, config: OpenHandsConfig, user_id: str | None
) -> FileSecretsStore:
file_store = get_file_store(config.file_store, config.file_store_path)
return FileSecretsStore(file_store)

View File

@ -2,7 +2,7 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.storage.data_models.user_secrets import UserSecrets
@ -19,5 +19,7 @@ class SecretsStore(ABC):
@classmethod
@abstractmethod
async def get_instance(cls, config: AppConfig, user_id: str | None) -> SecretsStore:
async def get_instance(
cls, config: OpenHandsConfig, user_id: str | None
) -> SecretsStore:
"""Get a store for the user represented by the token given."""

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import json
from dataclasses import dataclass
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.storage import get_file_store
from openhands.storage.data_models.settings import Settings
from openhands.storage.files import FileStore
@ -31,7 +31,7 @@ class FileSettingsStore(SettingsStore):
@classmethod
async def get_instance(
cls, config: AppConfig, user_id: str | None
cls, config: OpenHandsConfig, user_id: str | None
) -> FileSettingsStore:
file_store = get_file_store(config.file_store, config.file_store_path)
return FileSettingsStore(file_store)

View File

@ -2,7 +2,7 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.storage.data_models.settings import Settings
@ -20,6 +20,6 @@ class SettingsStore(ABC):
@classmethod
@abstractmethod
async def get_instance(
cls, config: AppConfig, user_id: str | None
cls, config: OpenHandsConfig, user_id: str | None
) -> SettingsStore:
"""Get a store for the user represented by the token given."""

View File

@ -6,12 +6,12 @@ with warnings.catch_warnings():
warnings.simplefilter('ignore')
import litellm
from openhands.core.config import AppConfig, LLMConfig
from openhands.core.config import LLMConfig, OpenHandsConfig
from openhands.core.logger import openhands_logger as logger
from openhands.llm import bedrock
def get_supported_llm_models(config: AppConfig) -> list[str]:
def get_supported_llm_models(config: OpenHandsConfig) -> list[str]:
"""Get all models supported by LiteLLM.
This function combines models from litellm and Bedrock, removing any

View File

@ -7,7 +7,7 @@ import time
import pytest
from pytest import TempPathFactory
from openhands.core.config import AppConfig, MCPConfig, load_app_config
from openhands.core.config import MCPConfig, OpenHandsConfig, load_openhands_config
from openhands.core.logger import openhands_logger as logger
from openhands.events import EventStream
from openhands.runtime.base import Runtime
@ -218,14 +218,14 @@ def _load_runtime(
runtime_startup_env_vars: dict[str, str] | None = None,
docker_runtime_kwargs: dict[str, str] | None = None,
override_mcp_config: MCPConfig | None = None,
) -> tuple[Runtime, AppConfig]:
) -> tuple[Runtime, OpenHandsConfig]:
sid = 'rt_' + str(random.randint(100000, 999999))
# AgentSkills need to be initialized **before** Jupyter
# otherwise Jupyter will not access the proper dependencies installed by AgentSkills
plugins = [AgentSkillsRequirement(), JupyterRequirement()]
config = load_app_config()
config = load_openhands_config()
config.run_as_openhands = run_as_openhands
config.sandbox.force_rebuild_runtime = force_rebuild_runtime
config.sandbox.keep_runtime_alive = False

View File

@ -208,7 +208,7 @@ async def test_add_mcp_tools_from_microagents():
"""Test that add_mcp_tools_to_agent adds tools from microagents."""
# Import ActionExecutionClient for mocking
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.runtime.impl.action_execution.action_execution_client import (
ActionExecutionClient,
)
@ -219,8 +219,8 @@ async def test_add_mcp_tools_from_microagents():
mock_memory = MagicMock()
mock_mcp_config = MCPConfig()
# Create a mock AppConfig with the MCP config
mock_app_config = AppConfig(mcp=mock_mcp_config, search_api_key=None)
# Create a mock OpenHandsConfig with the MCP config
mock_app_config = OpenHandsConfig(mcp=mock_mcp_config, search_api_key=None)
# Configure the mock memory to return a microagent MCP config
mock_stdio_server = MCPStdioServerConfig(
@ -247,7 +247,7 @@ async def test_add_mcp_tools_from_microagents():
'openhands.mcp.utils.fetch_mcp_tools_from_config',
new=AsyncMock(return_value=[mock_tool]),
):
# Call the function with the AppConfig instead of MCPConfig
# Call the function with the OpenHandsConfig instead of MCPConfig
await add_mcp_tools_to_agent(
mock_agent, mock_runtime, mock_memory, mock_app_config
)

View File

@ -6,8 +6,8 @@ from pathlib import Path
from conftest import _close_test_runtime, _load_runtime
from openhands.controller.state.state import State
from openhands.core.config.app_config import AppConfig
from openhands.core.config.config_utils import OH_DEFAULT_AGENT
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.core.main import run_controller
from openhands.core.schema.agent import AgentState
from openhands.events.action.empty import NullAction
@ -17,7 +17,7 @@ from openhands.events.observation.commands import CmdOutputObservation
def _get_config(trajectory_name: str, agent: str = OH_DEFAULT_AGENT):
return AppConfig(
return OpenHandsConfig(
default_agent=agent,
run_as_openhands=False,
# do not mount workspace

View File

@ -37,8 +37,8 @@ from openhands.agenthub import Agent
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
AppConfig,
LLMConfig,
OpenHandsConfig,
SandboxConfig,
)
from openhands.core.logger import openhands_logger as logger
@ -60,8 +60,8 @@ AGENT_CLS_TO_FAKE_USER_RESPONSE_FN = {
}
def get_config() -> AppConfig:
config = AppConfig(
def get_config() -> OpenHandsConfig:
config = OpenHandsConfig(
run_as_openhands=False,
runtime=os.environ.get('RUNTIME', 'remote'),
sandbox=SandboxConfig(

View File

@ -1,135 +1,168 @@
import pytest
from openhands.core.config.agent_config import AgentConfig
from openhands.core.config.app_config import AppConfig
from openhands.core.config.utils import finalize_config, load_from_env, load_from_toml
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.core.config.utils import finalize_config
# Define a dummy agent name often used in tests or as a default
DEFAULT_AGENT_NAME = 'CodeActAgent'
def test_finalize_config_cli_disables_jupyter_and_browsing_when_true():
"""
Test that finalize_config sets enable_jupyter and enable_browsing to False
when runtime is 'cli' and they were initially True.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'cli'
agent_config = AgentConfig(enable_jupyter=True, enable_browsing=True)
app_config.agents[DEFAULT_AGENT_NAME] = agent_config
finalize_config(app_config)
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, \
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, (
"enable_jupyter should be False when runtime is 'cli'"
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, \
)
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, (
"enable_browsing should be False when runtime is 'cli'"
)
def test_finalize_config_cli_keeps_jupyter_and_browsing_false_when_false():
"""
Test that finalize_config keeps enable_jupyter and enable_browsing as False
when runtime is 'cli' and they were initially False.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'cli'
agent_config = AgentConfig(enable_jupyter=False, enable_browsing=False)
app_config.agents[DEFAULT_AGENT_NAME] = agent_config
finalize_config(app_config)
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, \
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, (
"enable_jupyter should remain False when runtime is 'cli' and initially False"
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, \
)
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, (
"enable_browsing should remain False when runtime is 'cli' and initially False"
)
def test_finalize_config_other_runtime_keeps_jupyter_and_browsing_true_by_default():
"""
Test that finalize_config keeps enable_jupyter and enable_browsing as True (default)
when runtime is not 'cli'.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'docker' # A non-cli runtime
# AgentConfig defaults enable_jupyter and enable_browsing to True
agent_config = AgentConfig()
app_config.agents[DEFAULT_AGENT_NAME] = agent_config
finalize_config(app_config)
assert app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, \
"enable_jupyter should remain True by default for non-cli runtimes"
assert app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, \
"enable_browsing should remain True by default for non-cli runtimes"
assert app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, (
'enable_jupyter should remain True by default for non-cli runtimes'
)
assert app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, (
'enable_browsing should remain True by default for non-cli runtimes'
)
def test_finalize_config_other_runtime_keeps_jupyter_and_browsing_false_if_set():
"""
Test that finalize_config keeps enable_jupyter and enable_browsing as False
when runtime is not 'cli' but they were explicitly set to False.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'docker' # A non-cli runtime
agent_config = AgentConfig(enable_jupyter=False, enable_browsing=False)
app_config.agents[DEFAULT_AGENT_NAME] = agent_config
finalize_config(app_config)
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, \
"enable_jupyter should remain False for non-cli runtimes if explicitly set to False"
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, \
"enable_browsing should remain False for non-cli runtimes if explicitly set to False"
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_jupyter, (
'enable_jupyter should remain False for non-cli runtimes if explicitly set to False'
)
assert not app_config.agents[DEFAULT_AGENT_NAME].enable_browsing, (
'enable_browsing should remain False for non-cli runtimes if explicitly set to False'
)
def test_finalize_config_no_agents_defined():
"""
Test that finalize_config runs without error if no agents are defined in the config,
even when runtime is 'cli'.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'cli'
# No agents are added to app_config.agents
try:
finalize_config(app_config)
except Exception as e:
pytest.fail(f"finalize_config raised an exception with no agents defined: {e}")
pytest.fail(f'finalize_config raised an exception with no agents defined: {e}')
def test_finalize_config_multiple_agents_cli_runtime():
"""
Test that finalize_config correctly disables jupyter and browsing for multiple agents
when runtime is 'cli'.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'cli'
agent_config1 = AgentConfig(enable_jupyter=True, enable_browsing=True)
agent_config2 = AgentConfig(enable_jupyter=True, enable_browsing=True)
app_config.agents['Agent1'] = agent_config1
app_config.agents['Agent2'] = agent_config2
finalize_config(app_config)
assert not app_config.agents['Agent1'].enable_jupyter, "Jupyter should be disabled for Agent1"
assert not app_config.agents['Agent1'].enable_browsing, "Browsing should be disabled for Agent1"
assert not app_config.agents['Agent2'].enable_jupyter, "Jupyter should be disabled for Agent2"
assert not app_config.agents['Agent2'].enable_browsing, "Browsing should be disabled for Agent2"
assert not app_config.agents['Agent1'].enable_jupyter, (
'Jupyter should be disabled for Agent1'
)
assert not app_config.agents['Agent1'].enable_browsing, (
'Browsing should be disabled for Agent1'
)
assert not app_config.agents['Agent2'].enable_jupyter, (
'Jupyter should be disabled for Agent2'
)
assert not app_config.agents['Agent2'].enable_browsing, (
'Browsing should be disabled for Agent2'
)
def test_finalize_config_multiple_agents_other_runtime():
"""
Test that finalize_config correctly keeps jupyter and browsing enabled (or as set)
for multiple agents when runtime is not 'cli'.
"""
app_config = AppConfig()
app_config = OpenHandsConfig()
app_config.runtime = 'docker'
agent_config1 = AgentConfig(enable_jupyter=True, enable_browsing=True) # Defaults
agent_config2 = AgentConfig(enable_jupyter=False, enable_browsing=False) # Explicitly false
agent_config1 = AgentConfig(enable_jupyter=True, enable_browsing=True) # Defaults
agent_config2 = AgentConfig(
enable_jupyter=False, enable_browsing=False
) # Explicitly false
app_config.agents['Agent1'] = agent_config1
app_config.agents['Agent2'] = agent_config2
finalize_config(app_config)
assert app_config.agents['Agent1'].enable_jupyter, "Jupyter should be True for Agent1"
assert app_config.agents['Agent1'].enable_browsing, "Browsing should be True for Agent1"
assert not app_config.agents['Agent2'].enable_jupyter, "Jupyter should be False for Agent2"
assert not app_config.agents['Agent2'].enable_browsing, "Browsing should be False for Agent2"
assert app_config.agents['Agent1'].enable_jupyter, (
'Jupyter should be True for Agent1'
)
assert app_config.agents['Agent1'].enable_browsing, (
'Browsing should be True for Agent1'
)
assert not app_config.agents['Agent2'].enable_jupyter, (
'Jupyter should be False for Agent2'
)
assert not app_config.agents['Agent2'].enable_browsing, (
'Browsing should be False for Agent2'
)

View File

@ -525,7 +525,10 @@ async def test_process_issue(
patch(
'openhands.resolver.issue_resolver.SandboxConfig', return_value=MagicMock()
),
patch('openhands.resolver.issue_resolver.AppConfig', return_value=MagicMock()),
patch(
'openhands.resolver.issue_resolver.OpenHandsConfig',
return_value=MagicMock(),
),
):
# Call the process_issue method
result = await resolver.process_issue(issue, base_commit, handler_instance)

View File

@ -4,13 +4,13 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from openhands.core.config import load_app_config
from openhands.core.config import load_openhands_config
from openhands.core.exceptions import UserCancelledError
from openhands.llm.async_llm import AsyncLLM
from openhands.llm.llm import LLM
from openhands.llm.streaming_llm import StreamingLLM
config = load_app_config()
config = load_openhands_config()
@pytest.fixture

View File

@ -12,7 +12,7 @@ from litellm import (
from openhands.controller.agent import Agent
from openhands.controller.agent_controller import AgentController
from openhands.controller.state.state import State, TrafficControlState
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.config.agent_config import AgentConfig
from openhands.core.main import run_controller
from openhands.core.schema import AgentState
@ -55,7 +55,7 @@ def mock_agent():
agent = MagicMock(spec=Agent)
agent.llm = MagicMock(spec=LLM)
agent.llm.metrics = Metrics()
agent.llm.config = AppConfig().get_llm_config()
agent.llm.config = OpenHandsConfig().get_llm_config()
# Add config with enable_mcp attribute
agent.config = MagicMock(spec=AgentConfig)
@ -235,7 +235,7 @@ async def test_react_to_content_policy_violation(
async def test_run_controller_with_fatal_error(
test_event_stream, mock_memory, mock_agent
):
config = AppConfig()
config = OpenHandsConfig()
def agent_step_fn(state):
print(f'agent_step_fn received state: {state}')
@ -300,7 +300,7 @@ async def test_run_controller_with_fatal_error(
async def test_run_controller_stop_with_stuck(
test_event_stream, mock_memory, mock_agent
):
config = AppConfig()
config = OpenHandsConfig()
def agent_step_fn(state):
print(f'agent_step_fn received state: {state}')
@ -653,7 +653,7 @@ async def test_reset_with_pending_action_no_metadata(
async def test_run_controller_max_iterations_has_metrics(
test_event_stream, mock_memory, mock_agent
):
config = AppConfig(
config = OpenHandsConfig(
max_iterations=3,
)
event_stream = test_event_stream
@ -805,7 +805,7 @@ async def test_context_window_exceeded_error_handling(
# handles the truncation correctly.
final_state = await asyncio.wait_for(
run_controller(
config=AppConfig(max_iterations=max_iterations),
config=OpenHandsConfig(max_iterations=max_iterations),
initial_user_action=MessageAction(content='INITIAL'),
runtime=mock_runtime,
sid='test',
@ -943,7 +943,7 @@ async def test_run_controller_with_context_window_exceeded_with_truncation(
try:
state = await asyncio.wait_for(
run_controller(
config=AppConfig(max_iterations=5),
config=OpenHandsConfig(max_iterations=5),
initial_user_action=MessageAction(content='INITIAL'),
runtime=mock_runtime,
sid='test',
@ -1019,7 +1019,7 @@ async def test_run_controller_with_context_window_exceeded_without_truncation(
try:
state = await asyncio.wait_for(
run_controller(
config=AppConfig(max_iterations=3),
config=OpenHandsConfig(max_iterations=3),
initial_user_action=MessageAction(content='INITIAL'),
runtime=mock_runtime,
sid='test',
@ -1063,7 +1063,7 @@ async def test_run_controller_with_context_window_exceeded_without_truncation(
@pytest.mark.asyncio
async def test_run_controller_with_memory_error(test_event_stream, mock_agent):
config = AppConfig()
config = OpenHandsConfig()
event_stream = test_event_stream
# Create a proper agent that returns an action without an ID
@ -1330,7 +1330,7 @@ async def test_first_user_message_with_identical_content(test_event_stream, mock
# Create an agent controller
mock_agent.llm = MagicMock(spec=LLM)
mock_agent.llm.metrics = Metrics()
mock_agent.llm.config = AppConfig().get_llm_config()
mock_agent.llm.config = OpenHandsConfig().get_llm_config()
controller = AgentController(
agent=mock_agent,
@ -1397,7 +1397,7 @@ async def test_agent_controller_processes_null_observation_with_cause():
mock_agent = MagicMock(spec=Agent)
mock_agent.llm = MagicMock(spec=LLM)
mock_agent.llm.metrics = Metrics()
mock_agent.llm.config = AppConfig().get_llm_config()
mock_agent.llm.config = OpenHandsConfig().get_llm_config()
# Create a controller with the mock agent
controller = AgentController(

View File

@ -5,7 +5,7 @@ import pytest
from openhands.controller.agent import Agent
from openhands.controller.agent_controller import AgentController
from openhands.controller.state.state import State
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events import EventSource
from openhands.events.action import CmdRunAction, MessageAction, RecallAction
from openhands.events.action.message import SystemMessageAction
@ -90,8 +90,8 @@ def controller_fixture():
mock_agent = MagicMock(spec=Agent)
mock_agent.llm = MagicMock(spec=LLM)
mock_agent.llm.metrics = Metrics()
mock_agent.llm.config = AppConfig().get_llm_config()
mock_agent.config = AppConfig().get_agent_config('CodeActAgent')
mock_agent.llm.config = OpenHandsConfig().get_llm_config()
mock_agent.config = OpenHandsConfig().get_agent_config('CodeActAgent')
mock_event_stream = MagicMock(spec=EventStream)
mock_event_stream.sid = 'test_sid'

View File

@ -5,7 +5,7 @@ import pytest
from openhands.controller.agent import Agent
from openhands.controller.agent_controller import AgentController
from openhands.controller.state.state import State
from openhands.core.config import AppConfig, LLMConfig
from openhands.core.config import LLMConfig, OpenHandsConfig
from openhands.core.config.agent_config import AgentConfig
from openhands.events import EventStream, EventStreamSubscriber
from openhands.llm import LLM
@ -110,7 +110,7 @@ async def test_agent_session_start_with_no_state(mock_agent):
):
await session.start(
runtime_name='test-runtime',
config=AppConfig(),
config=OpenHandsConfig(),
agent=mock_agent,
max_iterations=10,
)
@ -203,7 +203,7 @@ async def test_agent_session_start_with_restored_state(mock_agent):
):
await session.start(
runtime_name='test-runtime',
config=AppConfig(),
config=OpenHandsConfig(),
agent=mock_agent,
max_iterations=10,
)

View File

@ -5,8 +5,8 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from openhands.core.config.app_config import AppConfig
from openhands.core.config.llm_config import LLMConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.events.action import MessageAction
from openhands.events.event import EventSource
from openhands.events.stream import EventStream
@ -207,7 +207,7 @@ async def test_update_conversation_with_title():
# Create the conversation manager
manager = StandaloneConversationManager(
sio=sio,
config=AppConfig(),
config=OpenHandsConfig(),
file_store=file_store,
server_config=server_config,
monitoring_listener=MonitoringListener(),

View File

@ -13,7 +13,7 @@ from openhands.cli.commands import (
handle_status_command,
)
from openhands.cli.tui import UsageMetrics
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.schema import AgentState
from openhands.events import EventSource
from openhands.events.action import ChangeAgentStateAction, MessageAction
@ -27,7 +27,7 @@ class TestHandleCommands:
event_stream = MagicMock(spec=EventStream)
usage_metrics = MagicMock(spec=UsageMetrics)
sid = 'test-session-id'
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
current_dir = '/test/dir'
settings_store = MagicMock(spec=FileSettingsStore)
@ -278,7 +278,7 @@ class TestHandleInitCommand:
@pytest.mark.asyncio
@patch('openhands.cli.commands.init_repository')
async def test_init_local_runtime_successful(self, mock_init_repository):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
config.runtime = 'local'
event_stream = MagicMock(spec=EventStream)
current_dir = '/test/dir'
@ -306,7 +306,7 @@ class TestHandleInitCommand:
@pytest.mark.asyncio
@patch('openhands.cli.commands.init_repository')
async def test_init_local_runtime_unsuccessful(self, mock_init_repository):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
config.runtime = 'local'
event_stream = MagicMock(spec=EventStream)
current_dir = '/test/dir'
@ -330,7 +330,7 @@ class TestHandleInitCommand:
@patch('openhands.cli.commands.print_formatted_text')
@patch('openhands.cli.commands.init_repository')
async def test_init_non_local_runtime(self, mock_init_repository, mock_print):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
config.runtime = 'remote' # Not local
event_stream = MagicMock(spec=EventStream)
current_dir = '/test/dir'
@ -360,7 +360,7 @@ class TestHandleSettingsCommand:
mock_cli_confirm,
mock_display_settings,
):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
settings_store = MagicMock(spec=FileSettingsStore)
# Mock user selecting "Basic" settings
@ -384,7 +384,7 @@ class TestHandleSettingsCommand:
mock_cli_confirm,
mock_display_settings,
):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
settings_store = MagicMock(spec=FileSettingsStore)
# Mock user selecting "Basic" settings
@ -408,7 +408,7 @@ class TestHandleSettingsCommand:
mock_cli_confirm,
mock_display_settings,
):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
settings_store = MagicMock(spec=FileSettingsStore)
# Mock user selecting "Advanced" settings
@ -432,7 +432,7 @@ class TestHandleSettingsCommand:
mock_cli_confirm,
mock_display_settings,
):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
settings_store = MagicMock(spec=FileSettingsStore)
# Mock user selecting "Advanced" settings
@ -450,7 +450,7 @@ class TestHandleSettingsCommand:
@patch('openhands.cli.commands.display_settings')
@patch('openhands.cli.commands.cli_confirm')
async def test_settings_go_back(self, mock_cli_confirm, mock_display_settings):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
settings_store = MagicMock(spec=FileSettingsStore)
# Mock user selecting "Go back"

View File

@ -10,7 +10,7 @@ from openhands.cli.settings import (
modify_llm_settings_basic,
)
from openhands.cli.tui import UserCancelledError
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.storage.data_models.settings import Settings
from openhands.storage.settings.file_settings_store import FileSettingsStore
@ -30,7 +30,7 @@ class MockNoOpCondenserConfig:
class TestDisplaySettings:
@pytest.fixture
def app_config(self):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
llm_config = MagicMock()
llm_config.base_url = None
llm_config.model = 'openai/gpt-4'
@ -48,7 +48,7 @@ class TestDisplaySettings:
@pytest.fixture
def advanced_app_config(self):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
llm_config = MagicMock()
llm_config.base_url = 'https://custom-api.com'
llm_config.model = 'custom-model'
@ -114,7 +114,7 @@ class TestDisplaySettings:
class TestModifyLLMSettingsBasic:
@pytest.fixture
def app_config(self):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
llm_config = MagicMock()
llm_config.model = 'openai/gpt-4'
llm_config.api_key = SecretStr('test-api-key')
@ -313,7 +313,7 @@ class TestModifyLLMSettingsBasic:
class TestModifyLLMSettingsAdvanced:
@pytest.fixture
def app_config(self):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
llm_config = MagicMock()
llm_config.model = 'custom-model'
llm_config.api_key = SecretStr('test-api-key')

View File

@ -18,7 +18,7 @@ from openhands.cli.tui import (
get_session_duration,
read_confirmation_input,
)
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events import EventSource
from openhands.events.action import (
Action,
@ -74,7 +74,7 @@ class TestDisplayFunctions:
@patch('openhands.cli.tui.display_message')
def test_display_event_message_action(self, mock_display_message):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
message = MessageAction(content='Test message')
message._source = EventSource.AGENT
@ -84,7 +84,7 @@ class TestDisplayFunctions:
@patch('openhands.cli.tui.display_command')
def test_display_event_cmd_action(self, mock_display_command):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
cmd_action = CmdRunAction(command='echo test')
display_event(cmd_action, config)
@ -93,7 +93,7 @@ class TestDisplayFunctions:
@patch('openhands.cli.tui.display_command_output')
def test_display_event_cmd_output(self, mock_display_output):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
cmd_output = CmdOutputObservation(content='Test output', command='echo test')
display_event(cmd_output, config)
@ -102,7 +102,7 @@ class TestDisplayFunctions:
@patch('openhands.cli.tui.display_file_edit')
def test_display_event_file_edit_observation(self, mock_display_file_edit):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
file_edit_obs = FileEditObservation(path='test.py', content="print('hello')")
display_event(file_edit_obs, config)
@ -111,7 +111,7 @@ class TestDisplayFunctions:
@patch('openhands.cli.tui.display_file_read')
def test_display_event_file_read(self, mock_display_file_read):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
file_read = FileReadObservation(path='test.py', content="print('hello')")
display_event(file_read, config)
@ -120,7 +120,7 @@ class TestDisplayFunctions:
@patch('openhands.cli.tui.display_message')
def test_display_event_thought(self, mock_display_message):
config = MagicMock(spec=AppConfig)
config = MagicMock(spec=OpenHandsConfig)
action = Action()
action.thought = 'Thinking about this...'

View File

@ -5,7 +5,7 @@ import tempfile
import pytest
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.core.exceptions import LLMMalformedActionError
from openhands.events import EventStream
from openhands.runtime.impl.cli.cli_runtime import CLIRuntime
@ -24,7 +24,7 @@ def cli_runtime(temp_dir):
"""Create a CLIRuntime instance for testing."""
file_store = get_file_store('local', temp_dir)
event_stream = EventStream('test', file_store)
config = AppConfig()
config = OpenHandsConfig()
config.workspace_base = temp_dir
runtime = CLIRuntime(config, event_stream)
runtime._runtime_initialized = True # Skip initialization

View File

@ -6,14 +6,14 @@ import pytest
from openhands.core.config import (
AgentConfig,
AppConfig,
LLMConfig,
OpenHandsConfig,
finalize_config,
get_agent_config_arg,
get_llm_config_arg,
load_app_config,
load_from_env,
load_from_toml,
load_openhands_config,
)
from openhands.core.config.condenser_config import (
LLMSummarizingCondenserConfig,
@ -48,8 +48,8 @@ def temp_toml_file(tmp_path):
@pytest.fixture
def default_config(monkeypatch):
# Fixture to provide a default AppConfig instance
yield AppConfig()
# Fixture to provide a default OpenHandsConfig instance
yield OpenHandsConfig()
def test_compat_env_to_config(monkeypatch, setup_env):
@ -60,7 +60,7 @@ def test_compat_env_to_config(monkeypatch, setup_env):
monkeypatch.setenv('DEFAULT_AGENT', 'CodeActAgent')
monkeypatch.setenv('SANDBOX_TIMEOUT', '10')
config = AppConfig()
config = OpenHandsConfig()
load_from_env(config, os.environ)
finalize_config(config)
@ -413,7 +413,7 @@ def test_defaults_dict_after_updates(default_config):
assert initial_defaults['workspace_mount_path']['default'] is None
assert initial_defaults['default_agent']['default'] == 'CodeActAgent'
updated_config = AppConfig()
updated_config = OpenHandsConfig()
updated_config.get_llm_config().api_key = 'updated-api-key'
updated_config.get_llm_config('llm').api_key = 'updated-api-key'
updated_config.get_llm_config_from_agent('agent').api_key = 'updated-api-key'
@ -678,7 +678,7 @@ def test_sandbox_volumes_with_workspace_not_first(default_config):
def test_agent_config_condenser_with_no_enabled():
"""Test default agent condenser with enable_default_condenser=False."""
config = AppConfig(enable_default_condenser=False)
config = OpenHandsConfig(enable_default_condenser=False)
agent_config = config.get_agent_config()
assert isinstance(agent_config.condenser, NoOpCondenserConfig)
@ -986,8 +986,8 @@ def test_api_keys_repr_str():
f"Unexpected attribute '{attr_name}' contains 'token' in AgentConfig"
)
# Test AppConfig
app_config = AppConfig(
# Test OpenHandsConfig
app_config = OpenHandsConfig(
llms={'llm': llm_config},
agents={'agent': agent_config},
e2b_api_key='my_e2b_api_key',
@ -1010,7 +1010,7 @@ def test_api_keys_repr_str():
assert 'my_daytona_api_key' not in repr(app_config)
assert 'my_daytona_api_key' not in str(app_config)
# Check that no other attrs in AppConfig have 'key' or 'token' in their name
# Check that no other attrs in OpenHandsConfig have 'key' or 'token' in their name
# This will fail when new attrs are added, and attract attention
known_key_token_attrs_app = [
'e2b_api_key',
@ -1020,16 +1020,16 @@ def test_api_keys_repr_str():
'daytona_api_key',
'search_api_key',
]
for attr_name in AppConfig.model_fields.keys():
for attr_name in OpenHandsConfig.model_fields.keys():
if (
not attr_name.startswith('__')
and attr_name not in known_key_token_attrs_app
):
assert 'key' not in attr_name.lower(), (
f"Unexpected attribute '{attr_name}' contains 'key' in AppConfig"
f"Unexpected attribute '{attr_name}' contains 'key' in OpenHandsConfig"
)
assert 'token' not in attr_name.lower() or 'tokens' in attr_name.lower(), (
f"Unexpected attribute '{attr_name}' contains 'token' in AppConfig"
f"Unexpected attribute '{attr_name}' contains 'token' in OpenHandsConfig"
)
@ -1040,7 +1040,7 @@ max_iterations = 42
max_budget_per_task = 4.7
"""
config = AppConfig()
config = OpenHandsConfig()
with open(temp_toml_file, 'w') as f:
f.write(temp_toml)
@ -1138,7 +1138,7 @@ enable_prompt_extensions = false
f.write(temp_toml)
# just a sanity check that load app config wouldn't fail
app_config = load_app_config(config_file=temp_toml_file)
app_config = load_openhands_config(config_file=temp_toml_file)
assert app_config.max_iterations == 99
# run_infer in evaluation can use `get_agent_config_arg` to load custom

View File

@ -2,7 +2,7 @@ import os
import pytest
from openhands.core.config import AppConfig, load_from_env
from openhands.core.config import OpenHandsConfig, load_from_env
def test_load_from_env_with_dict(monkeypatch, default_config):
@ -35,5 +35,5 @@ def test_load_from_env_with_dict(monkeypatch, default_config):
@pytest.fixture
def default_config():
# Fixture to provide a default AppConfig instance
yield AppConfig()
# Fixture to provide a default OpenHandsConfig instance
yield OpenHandsConfig()

View File

@ -2,8 +2,8 @@ import os
import pytest
from openhands.core.config.app_config import AppConfig
from openhands.core.config.extended_config import ExtendedConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.core.config.utils import load_from_toml
@ -83,7 +83,7 @@ def test_extended_config_invalid_key():
def test_app_config_extended_from_toml(tmp_path: os.PathLike) -> None:
"""Test that the [extended] section in a TOML file is correctly loaded.
This test verifies that the [extended] section is loaded into AppConfig.extended
This test verifies that the [extended] section is loaded into OpenHandsConfig.extended
and that it accepts arbitrary keys.
"""
# Create a temporary TOML file with multiple sections including [extended]
@ -106,8 +106,8 @@ enable_prompt_extensions = true
config_file = tmp_path / 'config.toml'
config_file.write_text(config_content)
# Load the TOML into the AppConfig instance
config = AppConfig()
# Load the TOML into the OpenHandsConfig instance
config = OpenHandsConfig()
load_from_toml(config, str(config_file))
# Verify that extended section is applied
@ -121,7 +121,7 @@ def test_app_config_extended_default(tmp_path: os.PathLike) -> None:
"""Test default behavior when no [extended] section exists.
This test verifies that if there is no [extended] section in the TOML file,
AppConfig.extended remains its default (empty) ExtendedConfig.
OpenHandsConfig.extended remains its default (empty) ExtendedConfig.
"""
config_content = """
[core]
@ -137,7 +137,7 @@ enable_prompt_extensions = true
config_file = tmp_path / 'config.toml'
config_file.write_text(config_content)
config = AppConfig()
config = OpenHandsConfig()
load_from_toml(config, str(config_file))
# Extended config should be empty
@ -161,7 +161,7 @@ another_key = 3.14
config_file = tmp_path / 'config.toml'
config_file.write_text(config_content)
config = AppConfig()
config = OpenHandsConfig()
load_from_toml(config, str(config_file))
# Verify that extended config holds the arbitrary keys with correct values.

View File

@ -46,7 +46,7 @@ def _patch_store():
get_conversation_metadata_filename('some_conversation_id'),
json.dumps(
{
'title': 'Some Conversation',
'title': 'Some ServerConversation',
'selected_repository': 'foobar',
'conversation_id': 'some_conversation_id',
'user_id': '12345',
@ -140,7 +140,7 @@ async def test_search_conversations():
results=[
ConversationMetadata(
conversation_id='some_conversation_id',
title='Some Conversation',
title='Some ServerConversation',
created_at=datetime.fromisoformat(
'2025-01-01T00:00:00+00:00'
),
@ -164,7 +164,7 @@ async def test_search_conversations():
results=[
ConversationInfo(
conversation_id='some_conversation_id',
title='Some Conversation',
title='Some ServerConversation',
created_at=datetime.fromisoformat(
'2025-01-01T00:00:00+00:00'
),
@ -189,7 +189,7 @@ async def test_get_conversation():
mock_store.get_metadata = AsyncMock(
return_value=ConversationMetadata(
conversation_id='some_conversation_id',
title='Some Conversation',
title='Some ServerConversation',
created_at=datetime.fromisoformat('2025-01-01T00:00:00+00:00'),
last_updated_at=datetime.fromisoformat('2025-01-01T00:01:00+00:00'),
selected_repository='foobar',
@ -211,7 +211,7 @@ async def test_get_conversation():
expected = ConversationInfo(
conversation_id='some_conversation_id',
title='Some Conversation',
title='Some ServerConversation',
created_at=datetime.fromisoformat('2025-01-01T00:00:00+00:00'),
last_updated_at=datetime.fromisoformat('2025-01-01T00:01:00+00:00'),
status=ConversationStatus.STOPPED,
@ -418,7 +418,7 @@ async def test_delete_conversation():
mock_store.get_metadata = AsyncMock(
return_value=ConversationMetadata(
conversation_id='some_conversation_id',
title='Some Conversation',
title='Some ServerConversation',
created_at=datetime.fromisoformat('2025-01-01T00:00:00+00:00'),
last_updated_at=datetime.fromisoformat('2025-01-01T00:01:00+00:00'),
selected_repository='foobar',

View File

@ -2,7 +2,7 @@ from unittest.mock import MagicMock, patch
import pytest
from openhands.core.config import AppConfig
from openhands.core.config import OpenHandsConfig
from openhands.events import EventStream
from openhands.runtime.impl.docker.docker_runtime import DockerRuntime
@ -30,7 +30,7 @@ def mock_docker_client():
@pytest.fixture
def config():
config = AppConfig()
config = OpenHandsConfig()
config.sandbox.keep_runtime_alive = False
return config

View File

@ -108,7 +108,7 @@ async def test_search_pagination():
'conversation_id': f'conv{i}',
'user_id': '123',
'selected_repository': 'repo1',
'title': f'Conversation {i}',
'title': f'ServerConversation {i}',
'created_at': f'2025-01-{15 + i}T19:51:04Z',
}
)

View File

@ -2,7 +2,7 @@ from unittest.mock import MagicMock, patch
import pytest
from openhands.core.config.app_config import AppConfig
from openhands.core.config.openhands_config import OpenHandsConfig
from openhands.storage.data_models.settings import Settings
from openhands.storage.files import FileStore
from openhands.storage.settings.file_settings_store import FileSettingsStore
@ -21,8 +21,8 @@ def file_settings_store(mock_file_store):
@pytest.mark.asyncio
async def test_load_nonexistent_data(file_settings_store):
with patch(
'openhands.storage.data_models.settings.load_app_config',
MagicMock(return_value=AppConfig()),
'openhands.storage.data_models.settings.load_openhands_config',
MagicMock(return_value=OpenHandsConfig()),
):
file_settings_store.file_store.read.side_effect = FileNotFoundError()
assert await file_settings_store.load() is None
@ -74,7 +74,7 @@ async def test_store_and_load_data(file_settings_store):
@pytest.mark.asyncio
async def test_get_instance():
config = AppConfig(file_store='local', file_store_path='/test/path')
config = OpenHandsConfig(file_store='local', file_store_path='/test/path')
with patch(
'openhands.storage.settings.file_settings_store.get_file_store'

Some files were not shown because too many files have changed in this diff Show More