mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
Feat: settings default (#6328)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com> Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, SecretStr, SerializationInfo, field_serializer
|
||||
from pydantic.json import pydantic_encoder
|
||||
|
||||
from openhands.core.config.llm_config import LLMConfig
|
||||
from openhands.core.config.utils import load_app_config
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
"""
|
||||
@@ -28,3 +33,24 @@ class Settings(BaseModel):
|
||||
return llm_api_key.get_secret_value()
|
||||
|
||||
return pydantic_encoder(llm_api_key)
|
||||
|
||||
@staticmethod
|
||||
def from_config() -> Settings | None:
|
||||
app_config = load_app_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
|
||||
return None
|
||||
security = app_config.security
|
||||
settings = Settings(
|
||||
language='en',
|
||||
agent=app_config.default_agent,
|
||||
max_iterations=app_config.max_iterations,
|
||||
security_analyzer=security.security_analyzer,
|
||||
confirmation_mode=security.confirmation_mode,
|
||||
llm_model=llm_config.model,
|
||||
llm_api_key=llm_config.api_key,
|
||||
llm_base_url=llm_config.base_url,
|
||||
remote_runtime_resource_factor=app_config.sandbox.remote_runtime_resource_factor,
|
||||
)
|
||||
return settings
|
||||
|
||||
@@ -23,7 +23,7 @@ class FileSettingsStore(SettingsStore):
|
||||
settings = Settings(**kwargs)
|
||||
return settings
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
return Settings.from_config()
|
||||
|
||||
async def store(self, settings: Settings):
|
||||
json_str = settings.model_dump_json(context={'expose_secrets': True})
|
||||
|
||||
@@ -20,8 +20,12 @@ def file_settings_store(mock_file_store):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_nonexistent_data(file_settings_store):
|
||||
file_settings_store.file_store.read.side_effect = FileNotFoundError()
|
||||
assert await file_settings_store.load() is None
|
||||
with patch(
|
||||
'openhands.server.settings.load_app_config',
|
||||
MagicMock(return_value=AppConfig()),
|
||||
):
|
||||
file_settings_store.file_store.read.side_effect = FileNotFoundError()
|
||||
assert await file_settings_store.load() is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
67
tests/unit/test_settings.py
Normal file
67
tests/unit/test_settings.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from pydantic import SecretStr
|
||||
|
||||
from openhands.core.config.app_config import AppConfig
|
||||
from openhands.core.config.llm_config import LLMConfig
|
||||
from openhands.core.config.sandbox_config import SandboxConfig
|
||||
from openhands.core.config.security_config import SecurityConfig
|
||||
from openhands.server.settings import Settings
|
||||
|
||||
|
||||
def test_settings_from_config():
|
||||
# Mock configuration
|
||||
mock_app_config = AppConfig(
|
||||
default_agent='test-agent',
|
||||
max_iterations=100,
|
||||
security=SecurityConfig(
|
||||
security_analyzer='test-analyzer', confirmation_mode=True
|
||||
),
|
||||
llms={
|
||||
'llm': LLMConfig(
|
||||
model='test-model',
|
||||
api_key=SecretStr('test-key'),
|
||||
base_url='https://test.example.com',
|
||||
)
|
||||
},
|
||||
sandbox=SandboxConfig(remote_runtime_resource_factor=2),
|
||||
)
|
||||
|
||||
with patch(
|
||||
'openhands.server.settings.load_app_config', return_value=mock_app_config
|
||||
):
|
||||
settings = Settings.from_config()
|
||||
|
||||
assert settings is not None
|
||||
assert settings.language == 'en'
|
||||
assert settings.agent == 'test-agent'
|
||||
assert settings.max_iterations == 100
|
||||
assert settings.security_analyzer == 'test-analyzer'
|
||||
assert settings.confirmation_mode is True
|
||||
assert settings.llm_model == 'test-model'
|
||||
assert settings.llm_api_key.get_secret_value() == 'test-key'
|
||||
assert settings.llm_base_url == 'https://test.example.com'
|
||||
assert settings.remote_runtime_resource_factor == 2
|
||||
|
||||
|
||||
def test_settings_from_config_no_api_key():
|
||||
# Mock configuration without API key
|
||||
mock_app_config = AppConfig(
|
||||
default_agent='test-agent',
|
||||
max_iterations=100,
|
||||
security=SecurityConfig(
|
||||
security_analyzer='test-analyzer', confirmation_mode=True
|
||||
),
|
||||
llms={
|
||||
'llm': LLMConfig(
|
||||
model='test-model', api_key=None, base_url='https://test.example.com'
|
||||
)
|
||||
},
|
||||
sandbox=SandboxConfig(remote_runtime_resource_factor=2),
|
||||
)
|
||||
|
||||
with patch(
|
||||
'openhands.server.settings.load_app_config', return_value=mock_app_config
|
||||
):
|
||||
settings = Settings.from_config()
|
||||
assert settings is None
|
||||
Reference in New Issue
Block a user