refactor: rename compatibility check function and update version extraction logic #159

This commit is contained in:
Abhimanyu Saharan
2026-02-22 22:05:39 +05:30
parent 6f4ad41dba
commit 6b09f124e6
4 changed files with 115 additions and 191 deletions

View File

@@ -28,7 +28,7 @@ from app.services.openclaw.db_agent_state import (
)
from app.services.openclaw.db_service import OpenClawDBService
from app.services.openclaw.error_messages import normalize_gateway_error_message
from app.services.openclaw.gateway_compat import check_gateway_runtime_compatibility
from app.services.openclaw.gateway_compat import check_gateway_version_compatibility
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
from app.services.openclaw.gateway_rpc import OpenClawGatewayError, openclaw_call
from app.services.openclaw.provisioning import OpenClawGatewayProvisioner
@@ -200,7 +200,7 @@ class GatewayAdminLifecycleService(OpenClawDBService):
disable_device_pairing=disable_device_pairing,
)
try:
result = await check_gateway_runtime_compatibility(config)
result = await check_gateway_version_compatibility(config)
except OpenClawGatewayError as exc:
detail = normalize_gateway_error_message(str(exc))
raise HTTPException(

View File

@@ -4,30 +4,18 @@ from __future__ import annotations
import re
from dataclasses import dataclass
from typing import Any
from app.core.config import settings
from app.services.openclaw.gateway_rpc import (
GatewayConfig,
OpenClawGatewayError,
openclaw_call,
openclaw_connect_metadata,
)
_VERSION_PATTERN = re.compile(r"(?i)v?(?P<version>\d+(?:\.\d+)+)")
_PRIMARY_VERSION_PATHS: tuple[tuple[str, ...], ...] = (
("version",),
("gatewayVersion",),
("appVersion",),
("buildVersion",),
("gateway", "version"),
("app", "version"),
("server", "version"),
("runtime", "version"),
("meta", "version"),
("build", "version"),
("info", "version"),
_CALVER_PATTERN = re.compile(
r"^v?(?P<year>\d{4})\.(?P<month>\d{1,2})\.(?P<day>\d{1,2})(?:-(?P<rev>\d+))?$",
re.IGNORECASE,
)
_CONNECT_VERSION_PATH: tuple[str, ...] = ("server", "version")
@dataclass(frozen=True, slots=True)
@@ -46,11 +34,18 @@ def _normalized_minimum_version() -> str:
def _parse_version_parts(value: str) -> tuple[int, ...] | None:
match = _VERSION_PATTERN.search(value.strip())
match = _CALVER_PATTERN.match(value.strip())
if match is None:
return None
numeric = match.group("version")
return tuple(int(part) for part in numeric.split("."))
year = int(match.group("year"))
month = int(match.group("month"))
day = int(match.group("day"))
revision = int(match.group("rev") or 0)
if month < 1 or month > 12:
return None
if day < 1 or day > 31:
return None
return (year, month, day, revision)
def _compare_versions(left: tuple[int, ...], right: tuple[int, ...]) -> int:
@@ -84,36 +79,9 @@ def _coerce_version_string(value: object) -> str | None:
return None
def _iter_fallback_version_values(payload: object) -> list[str]:
if not isinstance(payload, dict):
return []
stack: list[dict[str, Any]] = [payload]
discovered: list[str] = []
while stack:
node = stack.pop()
for key, value in node.items():
if isinstance(value, dict):
stack.append(value)
key_lower = key.lower()
if "version" not in key_lower or "protocol" in key_lower:
continue
candidate = _coerce_version_string(value)
if candidate is not None:
discovered.append(candidate)
return discovered
def extract_gateway_version(payload: object) -> str | None:
"""Extract a gateway runtime version string from status/health payloads."""
for path in _PRIMARY_VERSION_PATHS:
candidate = _coerce_version_string(_value_at_path(payload, path))
if candidate is not None:
return candidate
for candidate in _iter_fallback_version_values(payload):
if _parse_version_parts(candidate) is not None:
return candidate
return None
def extract_connect_server_version(payload: object) -> str | None:
"""Extract the canonical runtime version from connect metadata."""
return _coerce_version_string(_value_at_path(payload, _CONNECT_VERSION_PATH))
def evaluate_gateway_version(
@@ -127,7 +95,7 @@ def evaluate_gateway_version(
if min_parts is None:
msg = (
"Server configuration error: GATEWAY_MIN_VERSION is invalid. "
f"Expected a dotted numeric version, got '{min_version}'."
f"Expected CalVer 'YYYY.M.D' or 'YYYY.M.D-REV', got '{min_version}'."
)
return GatewayVersionCheckResult(
compatible=False,
@@ -177,64 +145,14 @@ def evaluate_gateway_version(
)
async def _fetch_runtime_metadata(config: GatewayConfig) -> object:
last_error: OpenClawGatewayError | None = None
for method in ("status", "health"):
try:
return await openclaw_call(method, config=config)
except OpenClawGatewayError as exc:
last_error = exc
continue
if last_error is not None:
raise last_error
return {}
async def _fetch_schema_metadata(config: GatewayConfig) -> object | None:
try:
return await openclaw_call("config.schema", config=config)
except OpenClawGatewayError:
return None
async def _fetch_connect_metadata(config: GatewayConfig) -> object | None:
try:
return await openclaw_connect_metadata(config=config)
except OpenClawGatewayError:
return None
async def check_gateway_runtime_compatibility(
async def check_gateway_version_compatibility(
config: GatewayConfig,
*,
minimum_version: str | None = None,
) -> GatewayVersionCheckResult:
"""Fetch runtime metadata and evaluate gateway version compatibility."""
connect_payload = await _fetch_connect_metadata(config)
current_version = extract_gateway_version(connect_payload)
if current_version is not None:
return evaluate_gateway_version(
current_version=current_version,
minimum_version=minimum_version,
)
schema_payload = await _fetch_schema_metadata(config)
current_version = extract_gateway_version(schema_payload)
if current_version is not None:
return evaluate_gateway_version(
current_version=current_version,
minimum_version=minimum_version,
)
payload = await _fetch_runtime_metadata(config)
current_version = extract_gateway_version(payload)
if current_version is None:
try:
health_payload = await openclaw_call("health", config=config)
except OpenClawGatewayError:
health_payload = None
if health_payload is not None:
current_version = extract_gateway_version(health_payload)
"""Use connect metadata server.version and evaluate compatibility."""
connect_payload = await openclaw_connect_metadata(config=config)
current_version = extract_connect_server_version(connect_payload)
return evaluate_gateway_version(
current_version=current_version,
minimum_version=minimum_version,

View File

@@ -21,7 +21,7 @@ from app.schemas.gateway_api import (
)
from app.services.openclaw.db_service import OpenClawDBService
from app.services.openclaw.error_messages import normalize_gateway_error_message
from app.services.openclaw.gateway_compat import check_gateway_runtime_compatibility
from app.services.openclaw.gateway_compat import check_gateway_version_compatibility
from app.services.openclaw.gateway_resolver import gateway_client_config, require_gateway_for_board
from app.services.openclaw.gateway_rpc import GatewayConfig as GatewayClientConfig
from app.services.openclaw.gateway_rpc import (
@@ -197,7 +197,7 @@ class GatewaySessionService(OpenClawDBService):
board, config, main_session = await self.resolve_gateway(params, user=user)
self._require_same_org(board, organization_id)
try:
compatibility = await check_gateway_runtime_compatibility(config)
compatibility = await check_gateway_version_compatibility(config)
except OpenClawGatewayError as exc:
return GatewaysStatusResponse(
connected=False,