Files
lightseekorg--tokenspeed/python/tokenspeed/runtime/engine/protocol.py
T
wehub-resource-sync 59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:31 +08:00

229 lines
7.6 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Serving-facing engine protocol.
``EngineClient`` is the narrow surface that the OpenAI serving layer
and ``http_server.py`` are allowed to depend on, letting callers stop
typing against the concrete ``AsyncLLM`` class.
``AsyncLLM(SchedulerControlClient, EngineClient)`` inherits the protocol so the
conformance is a class-definition-time invariant rather than duck
typing at every call site. The protocol stays ``@runtime_checkable`` so
``isinstance(engine, EngineClient)`` remains a lightweight check
(exercised by ``test/runtime/test_async_llm_protocol.py``).
What's on the protocol
----------------------
The surface covers the calls that the serving layer actually makes:
the generate/embed/abort/attribute path plus the administrative
RPCs (weights, cache, session, profile, expert-distribution,
load-query, internal-state, logging config).
What's intentionally off the protocol
-------------------------------------
Two categories stay concrete and are accessed via a narrower type
(or via ``isinstance`` casts in the caller):
1. Attribute escape hatches — ``rid_to_state``, ``server_status``.
``http_server.py`` reads them directly for liveness / health
reasons that are out of scope for the serving-facing protocol.
2. Purely internal coordination state — ``model_update_lock``,
``session_futures``, ``flush_cache_communicator`` and the other
``SchedulerControlClient`` ``*_communicator`` attributes.
If a caller needs any of the above, it must hold a concrete
``AsyncLLM`` reference, not an ``EngineClient``-typed one. This is
deliberate.
"""
from collections.abc import AsyncGenerator
from typing import (
Any,
Protocol,
runtime_checkable,
)
from tokenspeed.runtime.configs.model_config import ModelConfig
from tokenspeed.runtime.engine.io_struct import (
CloseSessionReqInput,
ConfigureLoggingReq,
EmbeddingReqInput,
FlushCacheReqOutput,
GenerateReqInput,
GetLoadReqOutput,
GetWeightsByNameReqInput,
InitWeightsUpdateGroupReqInput,
OpenSessionReqInput,
ReleaseMemoryOccupationReqInput,
ResumeMemoryOccupationReqInput,
SetInternalStateReq,
UpdateWeightFromDiskReqInput,
UpdateWeightsFromDistributedReqInput,
UpdateWeightsFromTensorReqInput,
)
from tokenspeed.runtime.utils.server_args import ServerArgs
@runtime_checkable
class EngineClient(Protocol):
"""Serving-facing async engine surface.
``AsyncLLM(SchedulerControlClient, EngineClient)`` inherits
this protocol explicitly, so conformance is structurally
guaranteed at class-definition time rather than relying on
duck typing at every call site.
"""
# ---- Configuration / identity ---------------------------------
# Mutable because ``update_weights_from_disk`` reassigns
# ``served_model_name`` and ``model_path`` on successful reloads
# (see ``_wait_for_model_update_from_disk``). Typed as their
# current runtime shape.
server_args: ServerArgs
model_config: ModelConfig
tokenizer: Any
model_path: str
served_model_name: str
is_generation: bool
is_image_gen: bool
# ---- Liveness state -------------------------------------------
# Monotonic epoch-second timestamp of the last message received
# from the scheduler's shared output socket. ``http_server.py``
# reads this for health / idle-timeout logic.
last_receive_tstamp: float
gracefully_exit: bool
# ---- Generate / embed path ------------------------------------
async def generate_request(
self,
obj: GenerateReqInput | EmbeddingReqInput,
) -> AsyncGenerator[dict[str, Any], None]: ...
def abort_request(self, rid: str) -> None: ...
# ---- Session management --------------------------------------
async def open_session(
self,
obj: OpenSessionReqInput,
) -> str | None: ...
async def close_session(
self,
obj: CloseSessionReqInput,
) -> None: ...
# ---- Cache / logging config ----------------------------------
async def flush_cache(self) -> FlushCacheReqOutput: ...
def configure_logging(self, obj: ConfigureLoggingReq) -> None: ...
# ---- Pause / resume (RLHF weight-update control) --------------
async def pause_scheduler(self, *, mode: str = "abort") -> bool: ...
async def resume_scheduler(self) -> bool: ...
async def is_scheduler_paused(self) -> bool: ...
# ---- Server lifecycle / health -------------------------------
def is_server_starting(self) -> bool: ...
def mark_server_up(self) -> None: ...
def mark_server_unhealthy(self) -> None: ...
def drop_request_state(self, rid: str) -> None: ...
# ---- Weight-update RPCs --------------------------------------
async def update_weights_from_disk(
self,
obj: UpdateWeightFromDiskReqInput,
) -> tuple[bool, str, Any]: ...
async def init_weights_update_group(
self,
obj: InitWeightsUpdateGroupReqInput,
) -> tuple[bool, str]: ...
async def update_weights_from_distributed(
self,
obj: UpdateWeightsFromDistributedReqInput,
) -> tuple[bool, str]: ...
async def update_weights_from_tensor(
self,
obj: UpdateWeightsFromTensorReqInput,
) -> tuple[bool, str]: ...
async def get_weights_by_name(
self,
obj: GetWeightsByNameReqInput,
) -> Any: ...
# ---- Memory occupation RPCs ----------------------------------
async def release_memory_occupation(
self,
obj: ReleaseMemoryOccupationReqInput,
) -> None: ...
async def resume_memory_occupation(
self,
obj: ResumeMemoryOccupationReqInput,
) -> None: ...
async def is_sleeping(self) -> bool: ...
# ---- Profiling / expert distribution -------------------------
async def start_profile(
self,
output_dir: str | None = None,
start_step: int | None = None,
num_steps: int | None = None,
activities: list[str] | None = None,
with_stack: bool | None = None,
record_shapes: bool | None = None,
profile_by_stage: bool = False,
) -> Any: ...
async def stop_profile(self) -> Any: ...
async def start_expert_distribution_record(self) -> None: ...
async def stop_expert_distribution_record(self) -> None: ...
async def dump_expert_distribution_record(self) -> None: ...
# ---- Engine internal state -----------------------------------
async def get_internal_state(self) -> list[dict[Any, Any]]: ...
async def set_internal_state(self, obj: SetInternalStateReq) -> list[bool]: ...
async def get_load(self) -> list[GetLoadReqOutput]: ...