← back to Exo
Merge Seth's Control Plane API Work into Alex's Events Branch
899d8820ddcd2f0c8cb949531a1d590c59d49a4b · 2025-06-30 23:54:41 +0100 · Arbion Halili
Co-authored-by: Seth Howes <sethshowes@gmail.com>
Files touched
A master/api.pyA shared/types/instance.pyM shared/types/model.py
Diff
commit 899d8820ddcd2f0c8cb949531a1d590c59d49a4b
Author: Arbion Halili <99731180+ToxicPine@users.noreply.github.com>
Date: Mon Jun 30 23:54:41 2025 +0100
Merge Seth's Control Plane API Work into Alex's Events Branch
Co-authored-by: Seth Howes <sethshowes@gmail.com>
---
master/api.py | 39 +++++++++++++++++++++
shared/types/instance.py | 90 ++++++++++++++++++++++++++++++++++++++++++++++++
shared/types/model.py | 30 ++++++++++++----
3 files changed, 153 insertions(+), 6 deletions(-)
diff --git a/master/api.py b/master/api.py
new file mode 100644
index 00000000..b5455c1d
--- /dev/null
+++ b/master/api.py
@@ -0,0 +1,39 @@
+from typing import NewType
+from shared.types.model import ModelId, DownloadProgress, ModelMeta, ModelSource
+from shared.types.instance import InstanceId, Instance
+from shared.types.model import Topology
+
+
+RequestId = NewType("RequestId", str)
+
+
+class ControlPlaneAPI:
+ def get_cluster_state() -> ClusterState:
+ ...
+
+ def get_topology() -> Topology:
+ ...
+
+ def get_instances() -> list[Instance]:
+ ...
+
+ def get_instance(instance_id: InstanceId) -> Instance:
+ ...
+
+ def create_instance(model_id: ModelId) -> InstanceId:
+ ...
+
+ def delete_instance(instance_id: InstanceId) -> None:
+ ...
+
+ def get_model_meta(model_id: ModelId) -> ModelMeta:
+ ...
+
+ def download_model(model_source: ModelSource) -> InstanceId:
+ ...
+
+ def get_model_download_progress(instance_id: InstanceId) -> DownloadProgress:
+ ...
+
+ def create_chat_completion_request() -> RequestId:
+ ...
diff --git a/shared/types/instance.py b/shared/types/instance.py
new file mode 100644
index 00000000..67f48285
--- /dev/null
+++ b/shared/types/instance.py
@@ -0,0 +1,90 @@
+from typing import Annotated, Literal, Generic, TypeVar, Union
+from enum import Enum
+from pydantic import BaseModel, UUID4, PositiveInt, Field
+
+from shared.types.model import ModelId
+
+InstanceId = Annotated[str, UUID4]
+NodeId = Annotated[str, UUID4]
+RunnerId = Annotated[str, UUID4]
+
+
+class ShardType(str, Enum):
+ PipelineParallel = "PipelineParallel"
+
+
+ShardTypeT = TypeVar("ShardTypeT", bound=ShardType)
+
+
+class ShardData(BaseModel, Generic[ShardTypeT]):
+ shard_type: ShardTypeT
+
+
+class Shard(BaseModel, Generic[ShardTypeT]):
+ shard_data: ShardData[ShardTypeT]
+ runner_id: RunnerId
+
+
+class ShardPlacement(BaseModel):
+ model_id: ModelId
+ shard_assignments: dict[NodeId, Shard[ShardType]]
+
+
+class DownloadProgressData(BaseModel):
+ total_bytes: Annotated[int, PositiveInt]
+ downloaded_bytes: Annotated[int, PositiveInt]
+
+
+class DownloadStatus(str, Enum):
+ Pending = "Pending"
+ Downloading = "Downloading"
+ Completed = "Completed"
+ Failed = "Failed"
+
+
+DownloadStatusT = TypeVar("DownloadStatusT", bound=DownloadStatus)
+
+
+class BaseDownloadProgress(BaseModel, Generic[DownloadStatusT]):
+ node_id: NodeId
+ download_status: DownloadStatusT
+
+
+class DownloadPending(BaseDownloadProgress[DownloadStatus.Pending]):
+ download_status: Literal[DownloadStatus.Pending] = Field(DownloadStatus.Pending)
+
+
+class DownloadCompleted(BaseDownloadProgress[DownloadStatus.Completed]):
+ download_status: Literal[DownloadStatus.Completed] = Field(DownloadStatus.Completed)
+
+
+class DownloadFailed(BaseDownloadProgress[DownloadStatus.Failed]):
+ download_status: Literal[DownloadStatus.Failed] = Field(DownloadStatus.Failed)
+ error_message: str
+
+
+class DownloadOngoing(BaseDownloadProgress[DownloadStatus.Downloading]):
+ download_status: Literal[DownloadStatus.Downloading] = Field(
+ DownloadStatus.Downloading
+ )
+ download_progress: DownloadProgressData
+
+
+DownloadProgress = Annotated[
+ Union[
+ DownloadPending,
+ DownloadCompleted,
+ DownloadFailed,
+ DownloadOngoing,
+ ],
+ Field(discriminator="download_status"),
+]
+
+
+class Instance(ShardPlacement):
+ instance_id: InstanceId
+
+
+class InstanceDownloadProgress(BaseModel, Generic[DownloadStatusT]):
+ instance_id: InstanceId
+ download_progress: BaseDownloadProgress[DownloadStatusT]
diff --git a/shared/types/model.py b/shared/types/model.py
index d0e11ed6..907366c7 100644
--- a/shared/types/model.py
+++ b/shared/types/model.py
@@ -1,7 +1,7 @@
-from typing import Annotated, Any, Generic, Literal, TypeVar, final
+from typing import Annotated, Any, Generic, Literal, Sequence, TypeVar, Union, final
from uuid import UUID
-from pydantic import AnyHttpUrl, BaseModel, Field, TypeAdapter
+from pydantic import AnyHttpUrl, BaseModel, Field, PositiveInt, TypeAdapter
from pydantic.types import UuidVersion
SourceType = Literal["HuggingFace", "GitHub"]
@@ -13,7 +13,6 @@ ModelId = type("ModelId", (UUID,), {})
ModelIdParser: TypeAdapter[ModelId] = TypeAdapter(_ModelId)
RepoPath = Annotated[str, Field(pattern=r"^[^/]+/[^/]+$")]
-RepoURL = Annotated[str, AnyHttpUrl]
class BaseModelSource(BaseModel, Generic[T]):
@@ -44,8 +43,27 @@ class GitHubModelSource(BaseModelSource[Literal["GitHub"]]):
source_data: GitHubModelSourceData
-RepoType = BaseModelSource[SourceType]
+class ModelMetadata(BaseModel):
+ storage_size_kilobytes: Annotated[int, PositiveInt]
+ n_layers: Annotated[int, PositiveInt]
-RepoValidatorThing = Annotated[RepoType, Field(discriminator="source_type")]
-RepoValidator: TypeAdapter[RepoValidatorThing] = TypeAdapter(RepoValidatorThing)
+_ModelSource = Annotated[
+ Union[
+ HuggingFaceModelSource,
+ GitHubModelSource,
+ ],
+ Field(discriminator="source_type"),
+]
+ModelSource = BaseModelSource[SourceType]
+
+
+@final
+class Model(BaseModel):
+ model_id: ModelId
+ model_sources: Sequence[ModelSource]
+ model_metadata: ModelMetadata
+
+
+ModelIdAdapter: TypeAdapter[ModelId] = TypeAdapter(_ModelId)
+ModelSourceAdapter: TypeAdapter[ModelSource] = TypeAdapter(_ModelSource)
← 53d5d238 refactor: Use enums
·
back to Exo
·
feat: Implement Many Interfaces c0df8e54 →