127 lines
4.0 KiB
Python
127 lines
4.0 KiB
Python
"""Shared OpenClaw lifecycle primitives."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
from uuid import UUID, uuid4
|
|
|
|
from fastapi import HTTPException, status
|
|
|
|
from app.integrations.openclaw_gateway import GatewayConfig as _GatewayClientConfig
|
|
from app.integrations.openclaw_gateway import OpenClawGatewayError, ensure_session, send_message
|
|
from app.models.boards import Board
|
|
from app.models.gateways import Gateway
|
|
from app.services.openclaw.constants import (
|
|
_GATEWAY_AGENT_PREFIX,
|
|
_GATEWAY_AGENT_SUFFIX,
|
|
_GATEWAY_OPENCLAW_AGENT_PREFIX,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
|
|
|
GatewayClientConfig = _GatewayClientConfig
|
|
|
|
|
|
class GatewayAgentIdentity:
|
|
"""Naming and identity rules for Mission Control gateway-main agents."""
|
|
|
|
@classmethod
|
|
def session_key_for_id(cls, gateway_id: UUID) -> str:
|
|
return f"{_GATEWAY_AGENT_PREFIX}{gateway_id}{_GATEWAY_AGENT_SUFFIX}"
|
|
|
|
@classmethod
|
|
def session_key(cls, gateway: Gateway) -> str:
|
|
return cls.session_key_for_id(gateway.id)
|
|
|
|
@classmethod
|
|
def openclaw_agent_id_for_id(cls, gateway_id: UUID) -> str:
|
|
return f"{_GATEWAY_OPENCLAW_AGENT_PREFIX}{gateway_id}"
|
|
|
|
@classmethod
|
|
def openclaw_agent_id(cls, gateway: Gateway) -> str:
|
|
return cls.openclaw_agent_id_for_id(gateway.id)
|
|
|
|
|
|
async def optional_gateway_config_for_board(
|
|
session: AsyncSession,
|
|
board: Board,
|
|
) -> GatewayClientConfig | None:
|
|
"""Return gateway client config when board has a reachable configured gateway."""
|
|
if board.gateway_id is None:
|
|
return None
|
|
gateway = await Gateway.objects.by_id(board.gateway_id).first(session)
|
|
if gateway is None or not gateway.url:
|
|
return None
|
|
return GatewayClientConfig(url=gateway.url, token=gateway.token)
|
|
|
|
|
|
async def require_gateway_config_for_board(
|
|
session: AsyncSession,
|
|
board: Board,
|
|
) -> tuple[Gateway, GatewayClientConfig]:
|
|
"""Resolve board gateway and config, raising 422 when unavailable."""
|
|
if board.gateway_id is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Board is not attached to a gateway",
|
|
)
|
|
gateway = await Gateway.objects.by_id(board.gateway_id).first(session)
|
|
if gateway is None or not gateway.url:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Gateway is not configured for this board",
|
|
)
|
|
return gateway, GatewayClientConfig(url=gateway.url, token=gateway.token)
|
|
|
|
|
|
async def send_gateway_agent_message(
|
|
*,
|
|
session_key: str,
|
|
config: GatewayClientConfig,
|
|
agent_name: str,
|
|
message: str,
|
|
deliver: bool = False,
|
|
) -> None:
|
|
"""Ensure session and dispatch a message to an agent session."""
|
|
await ensure_session(session_key, config=config, label=agent_name)
|
|
await send_message(message, session_key=session_key, config=config, deliver=deliver)
|
|
|
|
|
|
async def send_gateway_agent_message_safe(
|
|
*,
|
|
session_key: str,
|
|
config: GatewayClientConfig,
|
|
agent_name: str,
|
|
message: str,
|
|
deliver: bool = False,
|
|
) -> GatewayTransportError | None:
|
|
"""Best-effort gateway dispatch returning transport error when one occurs."""
|
|
try:
|
|
await send_gateway_agent_message(
|
|
session_key=session_key,
|
|
config=config,
|
|
agent_name=agent_name,
|
|
message=message,
|
|
deliver=deliver,
|
|
)
|
|
except GatewayTransportError as exc:
|
|
return exc
|
|
return None
|
|
|
|
|
|
def resolve_trace_id(correlation_id: str | None, *, prefix: str) -> str:
|
|
"""Resolve a stable trace id from correlation id or generate a scoped fallback."""
|
|
normalized = (correlation_id or "").strip()
|
|
if normalized:
|
|
return normalized
|
|
return f"{prefix}:{uuid4().hex[:12]}"
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Keep integration exceptions behind the OpenClaw service boundary.
|
|
GatewayTransportError = OpenClawGatewayError
|