import logging
import os
import grpc
from . import p2p_pb2
from . import p2p_pb2_grpc
logger = logging.getLogger("modelexpress.client")
def _parse_server_address(address: str) -> str:
if address.startswith("http://"):
return address[7:]
elif address.startswith("https://"):
return address[8:]
return address
def _get_server_url(explicit_url: str | None = None) -> str:
if explicit_url:
return _parse_server_address(explicit_url)
url = os.environ.get(
"MODEL_EXPRESS_URL",
os.environ.get("MX_SERVER_ADDRESS", "localhost:8001"),
)
return _parse_server_address(url)
class MxClient:
def __init__(
self,
server_url: str | None = None,
max_message_size: int = 100 * 1024 * 1024, ):
self.server_url = _get_server_url(server_url)
self._max_message_size = max_message_size
self._channel: grpc.Channel | None = None
self._stub: p2p_pb2_grpc.P2pServiceStub | None = None
@property
def stub(self) -> p2p_pb2_grpc.P2pServiceStub:
if self._channel is None:
options = [
("grpc.max_send_message_length", self._max_message_size),
("grpc.max_receive_message_length", self._max_message_size),
]
self._channel = grpc.insecure_channel(self.server_url, options=options)
self._stub = p2p_pb2_grpc.P2pServiceStub(self._channel)
logger.debug("MxClient connected to %s", self.server_url)
return self._stub
def close(self) -> None:
if self._channel is not None:
self._channel.close()
self._channel = None
self._stub = None
def publish_metadata(
self,
identity: "p2p_pb2.SourceIdentity",
worker: "p2p_pb2.WorkerMetadata",
worker_id: str,
) -> str:
request = p2p_pb2.PublishMetadataRequest(
identity=identity,
worker=worker,
worker_id=worker_id,
)
response = self.stub.PublishMetadata(request, timeout=30)
if not response.success:
raise RuntimeError(f"PublishMetadata failed: {response.message}")
return response.mx_source_id
def list_sources(
self,
identity: "p2p_pb2.SourceIdentity | None" = None,
status_filter: "p2p_pb2.SourceStatus | None" = None,
) -> "p2p_pb2.ListSourcesResponse":
request = p2p_pb2.ListSourcesRequest(
identity=identity,
status_filter=status_filter,
)
return self.stub.ListSources(request, timeout=30)
def get_metadata(
self,
mx_source_id: str,
worker_id: str,
) -> "p2p_pb2.GetMetadataResponse":
request = p2p_pb2.GetMetadataRequest(
mx_source_id=mx_source_id,
worker_id=worker_id,
)
return self.stub.GetMetadata(request, timeout=30)
def update_status(
self,
mx_source_id: str,
worker_id: str,
worker_rank: int,
status: "p2p_pb2.SourceStatus",
) -> bool:
request = p2p_pb2.UpdateStatusRequest(
mx_source_id=mx_source_id,
worker_id=worker_id,
worker_rank=worker_rank,
status=status,
)
response = self.stub.UpdateStatus(request, timeout=30)
if not response.success:
logger.error("UpdateStatus failed: %s", response.message)
return response.success