← back to Exo
feat: Implement Many Interfaces
c0df8e5463041d2781d4f5954da1f00c8251a094 · 2025-07-01 01:37:00 +0100 · Arbion Halili
Files touched
M master/api.pyM master/idempotency.pyA shared/types/common.pyR100 shared/types/event_sourcing.py shared/types/events/common.pyR096 shared/types/events.py shared/types/events/events.pyA shared/types/models/common.pyA shared/types/models/metadata.pyR059 shared/types/model.py shared/types/models/sources.pyA shared/types/networking/edges.pyA shared/types/networking/services.pyA shared/types/networking/topology.pyA shared/types/profiling/common.pyA shared/types/states/master.pyA shared/types/states/shared.pyA shared/types/states/worker.pyA shared/types/tasks/common.pyA shared/types/worker/common.pyR061 shared/types/instance.py shared/types/worker/downloads.pyA shared/types/worker/instances.pyA shared/types/worker/shards.py
Diff
commit c0df8e5463041d2781d4f5954da1f00c8251a094
Author: Arbion Halili <99731180+ToxicPine@users.noreply.github.com>
Date: Tue Jul 1 01:37:00 2025 +0100
feat: Implement Many Interfaces
---
master/api.py | 45 ++++--------
master/idempotency.py | 2 +-
shared/types/common.py | 9 +++
.../types/{event_sourcing.py => events/common.py} | 0
shared/types/{ => events}/events.py | 14 +---
shared/types/models/common.py | 22 ++++++
shared/types/models/metadata.py | 8 ++
shared/types/{model.py => models/sources.py} | 27 +------
shared/types/networking/edges.py | 85 ++++++++++++++++++++++
shared/types/networking/services.py | 42 +++++++++++
shared/types/networking/topology.py | 22 ++++++
shared/types/profiling/common.py | 4 +
shared/types/states/master.py | 17 +++++
shared/types/states/shared.py | 10 +++
shared/types/states/worker.py | 14 ++++
shared/types/tasks/common.py | 39 ++++++++++
shared/types/worker/common.py | 13 ++++
shared/types/{instance.py => worker/downloads.py} | 59 +++++++--------
shared/types/worker/instances.py | 18 +++++
shared/types/worker/shards.py | 29 ++++++++
20 files changed, 384 insertions(+), 95 deletions(-)
diff --git a/master/api.py b/master/api.py
index b5455c1d..cc45c786 100644
--- a/master/api.py
+++ b/master/api.py
@@ -1,39 +1,26 @@
-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
+from typing import Protocol
+from shared.types.models.common import Model, ModelId
+from shared.types.models.sources import ModelSource
+from shared.types.networking.topology import Topology
+from shared.types.worker.common import InstanceId
+from shared.types.worker.downloads import DownloadProgress
+from shared.types.worker.instances import Instance
-RequestId = NewType("RequestId", str)
+class ControlPlaneAPI(Protocol):
+ def get_topology(self) -> Topology: ...
-class ControlPlaneAPI:
- def get_cluster_state() -> ClusterState:
- ...
+ def list_instances(self) -> list[Instance]: ...
- def get_topology() -> Topology:
- ...
+ def get_instance(self, instance_id: InstanceId) -> Instance: ...
- def get_instances() -> list[Instance]:
- ...
+ def create_instance(self, model_id: ModelId) -> InstanceId: ...
- def get_instance(instance_id: InstanceId) -> Instance:
- ...
+ def remove_instance(self, instance_id: InstanceId) -> None: ...
- def create_instance(model_id: ModelId) -> InstanceId:
- ...
-
- def delete_instance(instance_id: InstanceId) -> None:
- ...
-
- def get_model_meta(model_id: ModelId) -> ModelMeta:
- ...
+ def get_model_data(self, model_id: ModelId) -> Model: ...
- def download_model(model_source: ModelSource) -> InstanceId:
- ...
-
- def get_model_download_progress(instance_id: InstanceId) -> DownloadProgress:
- ...
+ def download_model(self, model_id: ModelId, model_source: ModelSource) -> None: ...
- def create_chat_completion_request() -> RequestId:
- ...
+ def get_download_progress(self, model_id: ModelId) -> DownloadProgress: ...
diff --git a/master/idempotency.py b/master/idempotency.py
index 661d1e44..d96be620 100644
--- a/master/idempotency.py
+++ b/master/idempotency.py
@@ -1,7 +1,7 @@
from hashlib import sha3_224 as hasher
from typing import Sequence, TypeVar
-from shared.types.event_sourcing import EventId, EventTypes, IdemKeyGenerator, State
+from shared.types.events.common import EventId, EventTypes, IdemKeyGenerator, State
EventTypeT = TypeVar("EventTypeT", bound=EventTypes)
diff --git a/shared/types/common.py b/shared/types/common.py
new file mode 100644
index 00000000..c0fc38ff
--- /dev/null
+++ b/shared/types/common.py
@@ -0,0 +1,9 @@
+from typing import Annotated
+from uuid import UUID
+
+from pydantic import TypeAdapter
+from pydantic.types import UuidVersion
+
+_NodeId = Annotated[UUID, UuidVersion(4)]
+NodeId = type("NodeId", (UUID,), {})
+NodeIdParser: TypeAdapter[NodeId] = TypeAdapter(_NodeId)
diff --git a/shared/types/event_sourcing.py b/shared/types/events/common.py
similarity index 100%
rename from shared/types/event_sourcing.py
rename to shared/types/events/common.py
diff --git a/shared/types/events.py b/shared/types/events/events.py
similarity index 96%
rename from shared/types/events.py
rename to shared/types/events/events.py
index 233221d0..50eb35d9 100644
--- a/shared/types/events.py
+++ b/shared/types/events/events.py
@@ -6,21 +6,15 @@ from uuid import UUID
from pydantic import BaseModel, TypeAdapter, UuidVersion
from shared.openai import FinishReason, chat
-from shared.types.event_sourcing import Event, EventTypes
-from shared.types.model import ModelId
-
-_NodeId = Annotated[UUID, UuidVersion(4)]
-NodeId = type("NodeId", (UUID,), {})
-NodeIdParser: TypeAdapter[NodeId] = TypeAdapter(_NodeId)
+from shared.types.common import NodeId
+from shared.types.events.common import Event, EventTypes
+from shared.types.models.common import ModelId
+from shared.types.worker.common import InstanceId
_RequestId = Annotated[UUID, UuidVersion(4)]
RequestId = type("RequestId", (UUID,), {})
RequestIdParser: TypeAdapter[RequestId] = TypeAdapter(_RequestId)
-_InstanceId = Annotated[UUID, UuidVersion(4)]
-InstanceId = type("InstanceId", (UUID,), {})
-InstanceIdParser: TypeAdapter[InstanceId] = TypeAdapter(_InstanceId)
-
_TimerId = Annotated[UUID, UuidVersion(4)]
TimerId = type("TimerId", (UUID,), {})
TimerIdParser: TypeAdapter[TimerId] = TypeAdapter(_TimerId)
diff --git a/shared/types/models/common.py b/shared/types/models/common.py
new file mode 100644
index 00000000..5e2c1127
--- /dev/null
+++ b/shared/types/models/common.py
@@ -0,0 +1,22 @@
+from typing import Annotated, Sequence, final
+from uuid import UUID
+
+from pydantic import BaseModel, TypeAdapter
+from pydantic.types import UuidVersion
+
+from shared.types.models.metadata import ModelMetadata
+from shared.types.models.sources import ModelSource
+
+_ModelId = Annotated[UUID, UuidVersion(4)]
+ModelId = type("ModelId", (UUID,), {})
+ModelIdParser: TypeAdapter[ModelId] = TypeAdapter(_ModelId)
+
+
+@final
+class Model(BaseModel):
+ model_id: ModelId
+ model_sources: Sequence[ModelSource]
+ model_metadata: ModelMetadata
+
+
+ModelIdAdapter: TypeAdapter[ModelId] = TypeAdapter(_ModelId)
diff --git a/shared/types/models/metadata.py b/shared/types/models/metadata.py
new file mode 100644
index 00000000..6a1b8481
--- /dev/null
+++ b/shared/types/models/metadata.py
@@ -0,0 +1,8 @@
+from typing import Annotated
+
+from pydantic import BaseModel, PositiveInt
+
+
+class ModelMetadata(BaseModel):
+ storage_size_kilobytes: Annotated[int, PositiveInt]
+ n_layers: Annotated[int, PositiveInt]
diff --git a/shared/types/model.py b/shared/types/models/sources.py
similarity index 59%
rename from shared/types/model.py
rename to shared/types/models/sources.py
index 907366c7..e00e8650 100644
--- a/shared/types/model.py
+++ b/shared/types/models/sources.py
@@ -1,17 +1,13 @@
-from typing import Annotated, Any, Generic, Literal, Sequence, TypeVar, Union, final
-from uuid import UUID
+from typing import Annotated, Any, Generic, Literal, TypeVar, Union, final
-from pydantic import AnyHttpUrl, BaseModel, Field, PositiveInt, TypeAdapter
-from pydantic.types import UuidVersion
+from pydantic import AnyHttpUrl, BaseModel, Field, TypeAdapter
+
+from shared.types.models.common import ModelId
SourceType = Literal["HuggingFace", "GitHub"]
T = TypeVar("T", bound=SourceType)
-_ModelId = Annotated[UUID, UuidVersion(4)]
-ModelId = type("ModelId", (UUID,), {})
-ModelIdParser: TypeAdapter[ModelId] = TypeAdapter(_ModelId)
-
RepoPath = Annotated[str, Field(pattern=r"^[^/]+/[^/]+$")]
@@ -43,11 +39,6 @@ class GitHubModelSource(BaseModelSource[Literal["GitHub"]]):
source_data: GitHubModelSourceData
-class ModelMetadata(BaseModel):
- storage_size_kilobytes: Annotated[int, PositiveInt]
- n_layers: Annotated[int, PositiveInt]
-
-
_ModelSource = Annotated[
Union[
HuggingFaceModelSource,
@@ -56,14 +47,4 @@ _ModelSource = Annotated[
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)
diff --git a/shared/types/networking/edges.py b/shared/types/networking/edges.py
new file mode 100644
index 00000000..3866fc2e
--- /dev/null
+++ b/shared/types/networking/edges.py
@@ -0,0 +1,85 @@
+from dataclasses import dataclass
+from enum import Enum
+from typing import Annotated, Generic, NamedTuple, TypeVar, final
+from uuid import UUID
+
+from pydantic import BaseModel, IPvAnyAddress, TypeAdapter
+from pydantic.types import UuidVersion
+
+from shared.types.common import NodeId
+
+_EdgeId = Annotated[UUID, UuidVersion(4)]
+EdgeId = type("EdgeId", (UUID,), {})
+EdgeIdParser: TypeAdapter[EdgeId] = TypeAdapter(_EdgeId)
+
+
+@final
+class EdgeDataTransferRate(BaseModel):
+ throughput: float
+ latency: float
+ jitter: float
+
+
+class AddressingProtocol(str, Enum):
+ IPvAny = "IPvAny"
+
+
+class ApplicationProtocol(str, Enum):
+ MLX = "MLX"
+
+
+TE = TypeVar("TE", bound=AddressingProtocol)
+TF = TypeVar("TF", bound=ApplicationProtocol)
+
+
+@final
+class EdgeType(BaseModel, Generic[TE, TF]):
+ addressing_protocol: TE
+ application_protocol: TF
+
+
+@final
+class EdgeDirection(NamedTuple):
+ source: NodeId
+ sink: NodeId
+
+
+@dataclass
+class EdgeMetadata(BaseModel, Generic[TE, TF]): ...
+
+
+@final
+@dataclass
+class MLXEdgeContext(EdgeMetadata[AddressingProtocol.IPvAny, ApplicationProtocol.MLX]):
+ source_ip: IPvAnyAddress
+ sink_ip: IPvAnyAddress
+
+
+@final
+class EdgeInfo(BaseModel, Generic[TE, TF]):
+ edge_type: EdgeType[TE, TF]
+ edge_data_transfer_rate: EdgeDataTransferRate
+ edge_metadata: EdgeMetadata[TE, TF]
+
+
+@final
+class DirectedEdge(BaseModel, Generic[TE, TF]):
+ edge_direction: EdgeDirection
+ edge_identifier: EdgeId
+ edge_info: EdgeInfo[TE, TF]
+
+
+"""
+an_edge: DirectedEdge[Literal[AddressingProtocol.IPvAny], Literal[ApplicationProtocol.MLX]] = DirectedEdge(
+ edge_identifier=UUID(),
+ edge_direction=EdgeDirection(source=NodeId("1"), sink=NodeId("2")),
+ edge_info=EdgeInfo(
+ edge_type=EdgeType(
+ addressing_protocol=AddressingProtocol.ipv4,
+ application_protocol=ApplicationProtocol.mlx
+ ),
+ edge_data_transfer_rate=EdgeDataTransferRate(throughput=1000, latency=0.1, jitter=0.01),
+ edge_metadata=MLXEdgeContext(source_ip=IpV4Addr("192.168.1.1"), sink_ip=IpV4Addr("192.168.1.2"))
+ )
+)
+"""
diff --git a/shared/types/networking/services.py b/shared/types/networking/services.py
new file mode 100644
index 00000000..bce1d3e6
--- /dev/null
+++ b/shared/types/networking/services.py
@@ -0,0 +1,42 @@
+from typing import Annotated, Callable, NewType, Protocol
+
+from pydantic import BaseModel, Field
+
+from shared.types.common import NodeId
+from shared.types.networking.edges import (
+ AddressingProtocol,
+ ApplicationProtocol,
+ EdgeDirection,
+ EdgeId,
+ EdgeInfo,
+)
+
+TopicName = NewType("TopicName", str)
+
+
+class WrappedMessage(BaseModel):
+ node_id: NodeId
+ unix_timestamp: Annotated[int, Field(gt=0)]
+
+
+PubSubMessageHandler = Callable[[TopicName, WrappedMessage], None]
+NodeConnectedHandler = Callable[
+ [EdgeId, EdgeDirection, EdgeInfo[AddressingProtocol, ApplicationProtocol]], None
+]
+NodeDisconnectedHandler = Callable[[EdgeId], None]
+
+
+class DiscoveryService(Protocol):
+ def register_node_connected_handler(
+ self, handler: NodeConnectedHandler
+ ) -> None: ...
+ def register_node_disconnected_handler(
+ self, handler: NodeDisconnectedHandler
+ ) -> None: ...
+
+
+class PubSubService(Protocol):
+ def register_handler(
+ self, key: str, topic_name: TopicName, handler: PubSubMessageHandler
+ ) -> None: ...
+ def deregister_handler(self, key: str) -> None: ...
diff --git a/shared/types/networking/topology.py b/shared/types/networking/topology.py
new file mode 100644
index 00000000..33b1e191
--- /dev/null
+++ b/shared/types/networking/topology.py
@@ -0,0 +1,22 @@
+from collections.abc import Sequence
+
+from pydantic import BaseModel
+
+from shared.types.networking.edges import (
+ AddressingProtocol,
+ ApplicationProtocol,
+ EdgeDirection,
+ EdgeId,
+ EdgeInfo,
+)
+
+
+class Topology(BaseModel):
+ edges: dict[
+ EdgeId, tuple[EdgeDirection, EdgeInfo[AddressingProtocol, ApplicationProtocol]]
+ ]
+
+
+class NetworkState(BaseModel):
+ topology: Topology
+ history: Sequence[Topology]
diff --git a/shared/types/profiling/common.py b/shared/types/profiling/common.py
new file mode 100644
index 00000000..5faffb43
--- /dev/null
+++ b/shared/types/profiling/common.py
@@ -0,0 +1,4 @@
+from pydantic import BaseModel
+
+
+class NodeProfile(BaseModel): ...
diff --git a/shared/types/states/master.py b/shared/types/states/master.py
new file mode 100644
index 00000000..d9dff550
--- /dev/null
+++ b/shared/types/states/master.py
@@ -0,0 +1,17 @@
+from queue import Queue
+
+from pydantic import BaseModel
+
+from shared.types.common import NodeId
+from shared.types.networking.topology import NetworkState
+from shared.types.profiling.common import NodeProfile
+
+
+class ExternalCommand(BaseModel): ...
+
+
+class MasterState(BaseModel):
+ network_state: NetworkState
+ node_profiles: dict[NodeId, NodeProfile]
+ job_inbox: Queue[ExternalCommand]
+ job_outbox: Queue[ExternalCommand]
diff --git a/shared/types/states/shared.py b/shared/types/states/shared.py
new file mode 100644
index 00000000..f5c55c09
--- /dev/null
+++ b/shared/types/states/shared.py
@@ -0,0 +1,10 @@
+from pydantic import BaseModel
+
+from shared.types.common import NodeId
+from shared.types.worker.common import InstanceId
+from shared.types.worker.shards import ShardPlacement
+
+
+class SharedState(BaseModel):
+ node_id: NodeId
+ compute_instances: dict[InstanceId, ShardPlacement]
diff --git a/shared/types/states/worker.py b/shared/types/states/worker.py
new file mode 100644
index 00000000..041e4cf5
--- /dev/null
+++ b/shared/types/states/worker.py
@@ -0,0 +1,14 @@
+from typing import Tuple
+
+from shared.types.models.common import ModelId
+from shared.types.states.shared import SharedState
+from shared.types.tasks.common import Task, TaskId, TaskType
+from shared.types.worker.downloads import BaseDownloadProgress, DownloadStatus
+from shared.types.worker.shards import ShardData, ShardType
+
+
+class WorkerState(SharedState):
+ download_state: dict[
+ Tuple[ModelId, ShardData[ShardType]], BaseDownloadProgress[DownloadStatus]
+ ]
+ compute_tasks: dict[TaskId, Task[TaskType]]
diff --git a/shared/types/tasks/common.py b/shared/types/tasks/common.py
new file mode 100644
index 00000000..e680caf1
--- /dev/null
+++ b/shared/types/tasks/common.py
@@ -0,0 +1,39 @@
+from enum import Enum
+from typing import Annotated, Any, Generic, Literal, TypeVar
+from uuid import UUID
+
+import openai.types.chat as openai
+from pydantic import BaseModel, TypeAdapter
+from pydantic.types import UuidVersion
+
+_TaskId = Annotated[UUID, UuidVersion(4)]
+TaskId = type("TaskId", (UUID,), {})
+TaskIdParser: TypeAdapter[TaskId] = TypeAdapter(_TaskId)
+
+
+class TaskType(str, Enum):
+ ChatCompletionNonStreaming = "ChatCompletionNonStreaming"
+ ChatCompletionStreaming = "ChatCompletionStreaming"
+
+
+TaskTypeT = TypeVar("TaskTypeT", bound=TaskType)
+
+
+class Task(BaseModel, Generic[TaskTypeT]):
+ task_id: TaskId
+ task_type: TaskTypeT
+ task_data: Any
+
+
+class ChatCompletionNonStreamingTask(Task[TaskType.ChatCompletionNonStreaming]):
+ task_type: Literal[TaskType.ChatCompletionNonStreaming] = (
+ TaskType.ChatCompletionNonStreaming
+ )
+ task_data: openai.completion_create_params.CompletionCreateParams
+
+
+class ChatCompletionStreamingTask(Task[TaskType.ChatCompletionStreaming]):
+ task_type: Literal[TaskType.ChatCompletionStreaming] = (
+ TaskType.ChatCompletionStreaming
+ )
+ task_data: openai.completion_create_params.CompletionCreateParams
diff --git a/shared/types/worker/common.py b/shared/types/worker/common.py
new file mode 100644
index 00000000..0ec0b74b
--- /dev/null
+++ b/shared/types/worker/common.py
@@ -0,0 +1,13 @@
+from typing import Annotated
+from uuid import UUID
+
+from pydantic import TypeAdapter
+from pydantic.types import UuidVersion
+
+_InstanceId = Annotated[UUID, UuidVersion(4)]
+InstanceId = type("InstanceId", (UUID,), {})
+InstanceIdParser: TypeAdapter[InstanceId] = TypeAdapter(_InstanceId)
+
+_RunnerId = Annotated[UUID, UuidVersion(4)]
+RunnerId = type("RunnerId", (UUID,), {})
+RunnerIdParser: TypeAdapter[RunnerId] = TypeAdapter(_RunnerId)
diff --git a/shared/types/instance.py b/shared/types/worker/downloads.py
similarity index 61%
rename from shared/types/instance.py
rename to shared/types/worker/downloads.py
index 67f48285..c46da775 100644
--- a/shared/types/instance.py
+++ b/shared/types/worker/downloads.py
@@ -1,33 +1,21 @@
-from typing import Annotated, Literal, Generic, TypeVar, Union
from enum import Enum
-from pydantic import BaseModel, UUID4, PositiveInt, Field
+from typing import (
+ Annotated,
+ Callable,
+ Generic,
+ Literal,
+ NewType,
+ Sequence,
+ TypeVar,
+ Union,
+)
-from shared.types.model import ModelId
+from pydantic import BaseModel, Field, PositiveInt
-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]]
+from shared.types.common import NodeId
+from shared.types.models.common import ModelId
+from shared.types.models.sources import ModelSource
+from shared.types.worker.shards import ShardData, ShardType
class DownloadProgressData(BaseModel):
@@ -81,10 +69,17 @@ DownloadProgress = Annotated[
]
-class Instance(ShardPlacement):
- instance_id: InstanceId
+BytesToDownload = NewType("BytesToDownload", int)
+BytesDownloaded = NewType("BytesDownloaded", int)
+
+DownloadEffectHandler = Callable[
+ [ModelId, DownloadStatus, BytesToDownload, BytesDownloaded], None
+]
-class InstanceDownloadProgress(BaseModel, Generic[DownloadStatusT]):
- instance_id: InstanceId
- download_progress: BaseDownloadProgress[DownloadStatusT]
+def download_shard(
+ model_id: ModelId,
+ model_source: ModelSource,
+ shard_data: ShardData[ShardType],
+ effect_handlers: Sequence[DownloadEffectHandler],
+) -> None: ...
diff --git a/shared/types/worker/instances.py b/shared/types/worker/instances.py
new file mode 100644
index 00000000..447552d7
--- /dev/null
+++ b/shared/types/worker/instances.py
@@ -0,0 +1,18 @@
+from typing import Generic, TypeVar
+
+from pydantic import BaseModel
+
+from shared.types.worker.common import InstanceId
+from shared.types.worker.downloads import BaseDownloadProgress, DownloadStatus
+from shared.types.worker.shards import ShardPlacement
+
+DownloadStatusT = TypeVar("DownloadStatusT", bound=DownloadStatus)
+
+
+class Instance(ShardPlacement):
+ instance_id: InstanceId
+
+
+class InstanceDownloadProgress(BaseModel, Generic[DownloadStatusT]):
+ instance_id: InstanceId
+ download_progress: BaseDownloadProgress[DownloadStatusT]
diff --git a/shared/types/worker/shards.py b/shared/types/worker/shards.py
new file mode 100644
index 00000000..aa6df9ad
--- /dev/null
+++ b/shared/types/worker/shards.py
@@ -0,0 +1,29 @@
+from enum import Enum
+from typing import Generic, TypeVar
+
+from pydantic import BaseModel
+
+from shared.types.common import NodeId
+from shared.types.models.common import ModelId
+from shared.types.worker.common import RunnerId
+
+
+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]]
← 899d8820 Merge Seth's Control Plane API Work into Alex's Events Branc
·
back to Exo
·
refactor: Replace Literal with Enum in sources.py d5033e65 →