diff --git a/openhands-agent-server/openhands/agent_server/config.py b/openhands-agent-server/openhands/agent_server/config.py index a277e1e08b..3dbc375ee0 100644 --- a/openhands-agent-server/openhands/agent_server/config.py +++ b/openhands-agent-server/openhands/agent_server/config.py @@ -7,6 +7,7 @@ from openhands.agent_server.env_parser import from_env from openhands.sdk.utils.cipher import Cipher +from openhands.sdk.utils.deprecation import warn_deprecated # Environment variable constants @@ -47,6 +48,24 @@ def _default_secret_key() -> SecretStr | None: return None +def _default_web_url() -> str | None: + web_url = os.getenv("OH_WEB_URL") + if web_url: + return web_url + + legacy_web_url = os.getenv(V0_RUNTIME_URL) + if not legacy_web_url: + return None + + warn_deprecated( + "RUNTIME_URL environment variable", + deprecated_in="1.14.0", + removed_in="1.19.0", + details="Use OH_WEB_URL instead.", + ) + return legacy_web_url + + class WebhookSpec(BaseModel): """Spec to create a webhook. All webhook requests use POST method.""" @@ -165,7 +184,7 @@ class Config(BaseModel): ), ) web_url: str | None = Field( - default_factory=lambda: os.getenv(V0_RUNTIME_URL), + default_factory=_default_web_url, description=( "The URL where this agent server instance is available externally" ), diff --git a/tests/agent_server/test_api.py b/tests/agent_server/test_api.py index ca8680d897..22061cd572 100644 --- a/tests/agent_server/test_api.py +++ b/tests/agent_server/test_api.py @@ -2,16 +2,24 @@ import asyncio import tempfile +import warnings from pathlib import Path from unittest.mock import AsyncMock, patch import pytest +from deprecation import DeprecatedWarning from fastapi.testclient import TestClient from openhands.agent_server.api import _get_root_path, api_lifespan, create_app from openhands.agent_server.config import Config +@pytest.fixture(autouse=True) +def clear_web_url_env(monkeypatch): + monkeypatch.delenv("OH_WEB_URL", raising=False) + monkeypatch.delenv("RUNTIME_URL", raising=False) + + class TestStaticFilesServing: """Test static files serving functionality.""" @@ -403,21 +411,55 @@ class TestConfigWebUrl: """Tests for web_url configuration field.""" def test_web_url_default_is_none_when_env_not_set(self): - """Test that web_url defaults to None when RUNTIME_URL is not set.""" - # Ensure the env var is not set + """Test that web_url defaults to None when no env vars are set.""" with patch.dict("os.environ", {}, clear=True): config = Config() assert config.web_url is None - def test_web_url_reads_from_runtime_url_env(self): - """Test that web_url reads from RUNTIME_URL environment variable.""" - with patch.dict("os.environ", {"RUNTIME_URL": "https://test.example.com/path"}): + def test_web_url_reads_from_oh_web_url_env(self): + """Test that web_url reads from the canonical OH_WEB_URL env var.""" + with patch.dict("os.environ", {"OH_WEB_URL": "https://test.example.com/path"}): config = Config() assert config.web_url == "https://test.example.com/path" + def test_web_url_reads_from_runtime_url_env_with_warning(self): + """Test that legacy RUNTIME_URL still works but emits a deprecation warning.""" + with patch.dict("os.environ", {"RUNTIME_URL": "https://test.example.com/path"}): + with pytest.warns(DeprecatedWarning) as caught: + config = Config() + + assert config.web_url == "https://test.example.com/path" + assert "RUNTIME_URL environment variable is deprecated" in str( + caught[0].message + ) + assert "OH_WEB_URL" in str(caught[0].message) + assert "removed in 1.19.0" in str(caught[0].message) + + def test_web_url_prefers_oh_web_url_over_runtime_url(self): + """Test that the canonical env var wins without warnings.""" + with patch.dict( + "os.environ", + { + "OH_WEB_URL": "https://preferred.example.com/path", + "RUNTIME_URL": "https://legacy.example.com/path", + }, + ): + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + config = Config() + + assert config.web_url == "https://preferred.example.com/path" + assert caught == [] + def test_web_url_can_be_set_explicitly(self): - """Test that web_url can be set explicitly, overriding env var.""" - with patch.dict("os.environ", {"RUNTIME_URL": "https://env.example.com"}): + """Test that web_url can be set explicitly, overriding env vars.""" + with patch.dict( + "os.environ", + { + "OH_WEB_URL": "https://env.example.com/oh", + "RUNTIME_URL": "https://env.example.com/runtime", + }, + ): config = Config(web_url="https://explicit.example.com/custom") assert config.web_url == "https://explicit.example.com/custom"