feat: add config fallback for gateway version compatibility check

This commit is contained in:
Abhimanyu Saharan
2026-02-23 01:23:09 +05:30
parent 6b09f124e6
commit 2d3c3ee3e4
2 changed files with 85 additions and 2 deletions

View File

@@ -6,8 +6,11 @@ import re
from dataclasses import dataclass
from app.core.config import settings
from app.core.logging import get_logger
from app.services.openclaw.gateway_rpc import (
GatewayConfig,
OpenClawGatewayError,
openclaw_call,
openclaw_connect_metadata,
)
@@ -16,6 +19,8 @@ _CALVER_PATTERN = re.compile(
re.IGNORECASE,
)
_CONNECT_VERSION_PATH: tuple[str, ...] = ("server", "version")
_CONFIG_VERSION_PATH: tuple[str, ...] = ("config", "meta", "lastTouchedVersion")
logger = get_logger(__name__)
@dataclass(frozen=True, slots=True)
@@ -84,6 +89,11 @@ def extract_connect_server_version(payload: object) -> str | None:
return _coerce_version_string(_value_at_path(payload, _CONNECT_VERSION_PATH))
def extract_config_last_touched_version(payload: object) -> str | None:
"""Extract a runtime version hint from config.get payload."""
return _coerce_version_string(_value_at_path(payload, _CONFIG_VERSION_PATH))
def evaluate_gateway_version(
*,
current_version: str | None,
@@ -150,9 +160,21 @@ async def check_gateway_version_compatibility(
*,
minimum_version: str | None = None,
) -> GatewayVersionCheckResult:
"""Use connect metadata server.version and evaluate compatibility."""
"""Evaluate gateway compatibility using connect metadata with config fallback."""
connect_payload = await openclaw_connect_metadata(config=config)
current_version = extract_connect_server_version(connect_payload)
if current_version is None or _parse_version_parts(current_version) is None:
try:
config_payload = await openclaw_call("config.get", config=config)
except OpenClawGatewayError as exc:
logger.debug(
"gateway.compat.config_get_fallback_unavailable reason=%s",
str(exc),
)
else:
fallback_version = extract_config_last_touched_version(config_payload)
if fallback_version is not None:
current_version = fallback_version
return evaluate_gateway_version(
current_version=current_version,
minimum_version=minimum_version,

View File

@@ -35,6 +35,26 @@ def test_extract_connect_server_version_returns_none_when_server_version_missing
assert gateway_compat.extract_connect_server_version(payload) is None
def test_extract_config_last_touched_version_reads_config_meta_last_touched_version() -> None:
payload = {
"config": {
"meta": {"lastTouchedVersion": "2026.2.9"},
"wizard": {"lastRunVersion": "2026.2.8"},
},
"parsed": {"meta": {"lastTouchedVersion": "2026.2.7"}},
}
assert gateway_compat.extract_config_last_touched_version(payload) == "2026.2.9"
def test_extract_config_last_touched_version_returns_none_without_config_meta_last_touched_version() -> None:
payload = {
"config": {"wizard": {"lastRunVersion": "2026.2.9"}},
}
assert gateway_compat.extract_config_last_touched_version(payload) is None
@pytest.mark.parametrize(
("current_version", "minimum_version", "expected_compatible"),
[
@@ -97,7 +117,12 @@ async def test_check_gateway_version_compatibility_uses_connect_server_version_o
"server": {"version": "2026.2.13"},
}
async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object:
_ = (method, params, config)
raise AssertionError("config.get fallback should not run for valid connect version")
monkeypatch.setattr(gateway_compat, "openclaw_connect_metadata", _fake_connect_metadata)
monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call)
result = await gateway_compat.check_gateway_version_compatibility(
GatewayConfig(url="ws://gateway.example/ws"),
@@ -116,7 +141,13 @@ async def test_check_gateway_version_compatibility_fails_without_server_version(
_ = config
return {"runtime": {"version": "2026.2.13"}}
async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object:
_ = (params, config)
assert method == "config.get"
return {"config": {}}
monkeypatch.setattr(gateway_compat, "openclaw_connect_metadata", _fake_connect_metadata)
monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call)
result = await gateway_compat.check_gateway_version_compatibility(
GatewayConfig(url="ws://gateway.example/ws"),
@@ -129,14 +160,44 @@ async def test_check_gateway_version_compatibility_fails_without_server_version(
@pytest.mark.asyncio
async def test_check_gateway_version_compatibility_rejects_non_calver_server_version(
async def test_check_gateway_version_compatibility_uses_config_get_fallback_when_connect_is_dev(
monkeypatch: pytest.MonkeyPatch,
) -> None:
async def _fake_connect_metadata(*, config: GatewayConfig) -> object | None:
_ = config
return {"server": {"version": "dev"}}
async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object:
_ = (params, config)
assert method == "config.get"
return {"config": {"meta": {"lastTouchedVersion": "2026.2.9"}}}
monkeypatch.setattr(gateway_compat, "openclaw_connect_metadata", _fake_connect_metadata)
monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call)
result = await gateway_compat.check_gateway_version_compatibility(
GatewayConfig(url="ws://gateway.example/ws"),
minimum_version="2026.1.30",
)
assert result.compatible is True
assert result.current_version == "2026.2.9"
@pytest.mark.asyncio
async def test_check_gateway_version_compatibility_rejects_non_calver_server_version_when_fallback_unavailable(
monkeypatch: pytest.MonkeyPatch,
) -> None:
async def _fake_connect_metadata(*, config: GatewayConfig) -> object | None:
_ = config
return {"server": {"version": "dev"}}
async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object:
_ = (method, params, config)
raise OpenClawGatewayError("method unavailable")
monkeypatch.setattr(gateway_compat, "openclaw_connect_metadata", _fake_connect_metadata)
monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call)
result = await gateway_compat.check_gateway_version_compatibility(
GatewayConfig(url="ws://gateway.example/ws"),