mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-10-01 02:04:21 +00:00
* feat(service): bind network services to all interfaces * fix(service): keep network listeners local by default * fix(service): make remote access explicit
62 lines
2.2 KiB
Python
62 lines
2.2 KiB
Python
"""Base client abstraction."""
|
|
|
|
import json
|
|
import os
|
|
from abc import abstractmethod
|
|
from collections.abc import AsyncGenerator
|
|
|
|
from ..base_component import BaseComponent
|
|
from ...constants import (
|
|
REME_DEFAULT_HOST,
|
|
REME_DEFAULT_PORT,
|
|
REME_SERVICE_INFO,
|
|
normalize_connect_host,
|
|
)
|
|
from ...enumeration import ComponentEnum
|
|
|
|
|
|
class BaseClient(BaseComponent):
|
|
"""Abstract base for clients that communicate with ReMe services."""
|
|
|
|
component_type = ComponentEnum.CLIENT
|
|
|
|
def __init__(self, **kwargs) -> None:
|
|
super().__init__(**kwargs)
|
|
self.client = None
|
|
|
|
def _resolve_service_address(self, host: str | None, port: int | None) -> tuple[str, int]:
|
|
"""Resolve explicit, discovered, or default network coordinates."""
|
|
if not (host and port):
|
|
if service_info := os.environ.get(REME_SERVICE_INFO):
|
|
try:
|
|
data = json.loads(service_info)
|
|
host, port = data["host"], data["port"]
|
|
except Exception:
|
|
self.logger.warning(f"Invalid service info: {service_info}")
|
|
host, port = REME_DEFAULT_HOST, REME_DEFAULT_PORT
|
|
else:
|
|
host, port = REME_DEFAULT_HOST, REME_DEFAULT_PORT
|
|
return normalize_connect_host(host), port
|
|
|
|
async def _start(self) -> None:
|
|
"""Initialize the client."""
|
|
|
|
async def _close(self) -> None:
|
|
"""Close the client and release resources."""
|
|
|
|
@abstractmethod
|
|
def _execute(self, action: str, payload: dict) -> AsyncGenerator[str, None]:
|
|
"""Backend-specific execution; yield text chunks (single yield for non-streaming backends)."""
|
|
|
|
@abstractmethod
|
|
async def list_actions(self) -> list[dict]:
|
|
"""Discover available actions on the server; each dict is the raw backend descriptor."""
|
|
|
|
async def __call__(self, action: str, **kwargs) -> AsyncGenerator[str, None]:
|
|
"""Dispatch: action='list' returns the action catalog; otherwise delegate to _execute()."""
|
|
if action == "list":
|
|
actions = await self.list_actions()
|
|
yield json.dumps(actions, indent=2, ensure_ascii=False)
|
|
return
|
|
async for chunk in self._execute(action, kwargs):
|
|
yield chunk
|