Files
ray-project--ray/python/ray/llm/_internal/common/placement.py
T
2026-07-13 13:17:40 +08:00

80 lines
2.9 KiB
Python

from typing import List, Literal, Optional
from pydantic import ConfigDict, Field, model_validator
from ray.llm._internal.common.base_pydantic import BaseModelExtended
class BundleConfig(BaseModelExtended):
"""Configuration for a single placement group bundle.
Resource counts are floats to align with Ray's internal resource
representation, which supports fractional values (e.g. GPU=0.5).
Both CPU and GPU default to 0.0 — the schema does not inject hidden
resource requests. Extra fields are allowed for custom Ray resources (e.g. TPU,
accelerator_type:L4).
"""
model_config = ConfigDict(extra="allow")
CPU: float = Field(
default=0.0,
ge=0.0,
description="The number of CPUs per bundle.",
)
GPU: float = Field(
default=0.0,
ge=0.0,
description="The number of GPUs per bundle.",
)
@model_validator(mode="after")
def validate_extra_resources(self):
"""Ensure custom resources (TPU, accelerator_type, etc.) are non-negative."""
extra_resources = self.model_extra
if not extra_resources:
return self
for key, value in extra_resources.items():
if not isinstance(value, (int, float)):
raise ValueError(
f"Resource '{key}' must be a number, got {type(value).__name__}"
)
if value < 0:
raise ValueError(f"Resource '{key}' must be non-negative, got {value}")
return self
class PlacementGroupConfig(BaseModelExtended):
"""Configuration for placement group."""
bundle_per_worker: Optional[BundleConfig] = Field(
default=None,
description=(
"Resource bundle specification for each worker. "
"Auto-replicated based on tensor_parallel_size * pipeline_parallel_size. "
"Cannot be used together with 'bundles'."
),
)
bundles: Optional[List[BundleConfig]] = Field(
default=None, description="List of resource bundles"
)
strategy: Literal["PACK", "SPREAD", "STRICT_PACK", "STRICT_SPREAD"] = Field(
default="PACK", description="Placement group strategy"
)
@model_validator(mode="after")
def validate_bundle_options(self):
if self.bundle_per_worker is not None and self.bundles is not None:
raise ValueError(
"Cannot specify both 'bundle_per_worker' and 'bundles' in "
"placement_group_config. Use 'bundle_per_worker' for simple "
"per-worker resource specification (auto-replicated by tp*pp), "
"or 'bundles' for full control."
)
if self.bundle_per_worker is None and self.bundles is None:
raise ValueError(
"placement_group_config must specify either 'bundle_per_worker' "
"or 'bundles'."
)
return self