Skip to content

Commit 0c2a928

Browse files
jopemachineclaude
andcommitted
feat: expose deployment lifecycle sub_step on v2 GQL/REST surfaces
The v1 ``DeploymentDTO.sub_step`` had no v2 counterpart, so the GQL ``ModelDeployment`` node and the v2 REST ``DeploymentNode`` were missing the rolling-update progress signal. Promote ``DeploymentLifecycleSubStep`` to ``common.data.model_deployment.types`` so the v2 DTO layer can reach it (``common/`` cannot import from ``manager/``), repoint every existing importer at the common location, then surface the enum on: - ``DeploymentNode.sub_step`` (v2 REST DTO) - ``ModelDeployment.subStep`` (GQL, backed by a new ``DeploymentLifecycleSubStep`` GQL enum) The v2 adapter pipes ``ModelDeploymentData.sub_step`` through to the new DTO field, and the v2 schema dump picks up the enum + field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 13a39f5 commit 0c2a928

18 files changed

Lines changed: 110 additions & 42 deletions

File tree

docs/manager/graphql-reference/supergraph.graphql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5220,6 +5220,17 @@ enum DeploymentHistoryOrderField
52205220
UPDATED_AT @join__enumValue(graph: STRAWBERRY)
52215221
}
52225222

5223+
"""
5224+
Added in UNRELEASED. Sub-step within a deployment's current lifecycle phase. ``None`` when the deployment is not in a sub-step-bearing phase.
5225+
"""
5226+
enum DeploymentLifecycleSubStep
5227+
@join__type(graph: STRAWBERRY)
5228+
{
5229+
DEPLOYING_PROVISIONING @join__enumValue(graph: STRAWBERRY)
5230+
DEPLOYING_ROLLING_BACK @join__enumValue(graph: STRAWBERRY)
5231+
DEPLOYING_COMPLETED @join__enumValue(graph: STRAWBERRY)
5232+
}
5233+
52235234
"""Added in UNRELEASED. Deployment options payload response."""
52245235
type DeploymentOptionsInfoGQL
52255236
@join__type(graph: STRAWBERRY)
@@ -9306,6 +9317,11 @@ type ModelDeployment implements Node
93069317
"""
93079318
scalingState: ScalingState!
93089319

9320+
"""
9321+
Added in UNRELEASED. Sub-step within the current lifecycle phase. ``None`` when the deployment is not in a sub-step-bearing phase.
9322+
"""
9323+
subStep: DeploymentLifecycleSubStep
9324+
93099325
"""
93109326
Added in 26.4.3. The current active revision of this deployment, resolved via DataLoader.
93119327
"""

docs/manager/graphql-reference/v2-schema.graphql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3486,6 +3486,15 @@ enum DeploymentHistoryOrderField {
34863486
UPDATED_AT
34873487
}
34883488

3489+
"""
3490+
Added in UNRELEASED. Sub-step within a deployment's current lifecycle phase. ``None`` when the deployment is not in a sub-step-bearing phase.
3491+
"""
3492+
enum DeploymentLifecycleSubStep {
3493+
DEPLOYING_PROVISIONING
3494+
DEPLOYING_ROLLING_BACK
3495+
DEPLOYING_COMPLETED
3496+
}
3497+
34893498
"""Added in UNRELEASED. Deployment options payload response."""
34903499
type DeploymentOptionsInfoGQL {
34913500
"""Handler-keyed scheduler policy (timeout + retry)."""
@@ -6109,6 +6118,11 @@ type ModelDeployment implements Node {
61096118
"""
61106119
scalingState: ScalingState!
61116120

6121+
"""
6122+
Added in UNRELEASED. Sub-step within the current lifecycle phase. ``None`` when the deployment is not in a sub-step-bearing phase.
6123+
"""
6124+
subStep: DeploymentLifecycleSubStep
6125+
61126126
"""
61136127
Added in 26.4.3. The current active revision of this deployment, resolved via DataLoader.
61146128
"""

src/ai/backend/common/data/model_deployment/types.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from __future__ import annotations
2+
3+
import enum
14
from typing import Any, Self
25

36
from ai.backend.common.data.endpoint.types import EndpointLifecycle
@@ -50,7 +53,7 @@ def _missing_(cls, value: Any) -> Self | None:
5053
return super()._missing_(value)
5154

5255
@classmethod
53-
def from_lifecycle(cls, lifecycle: EndpointLifecycle) -> "ModelDeploymentStatus":
56+
def from_lifecycle(cls, lifecycle: EndpointLifecycle) -> ModelDeploymentStatus:
5457
match lifecycle:
5558
case EndpointLifecycle.PENDING | EndpointLifecycle.CREATED:
5659
return cls.PENDING
@@ -69,6 +72,27 @@ class DeploymentStrategy(CIStrEnum):
6972
BLUE_GREEN = "BLUE_GREEN"
7073

7174

75+
class DeploymentLifecycleSubStep(enum.StrEnum):
76+
"""Sub-steps within deployment lifecycle phases.
77+
78+
Member names are prefixed with the lifecycle phase they belong to
79+
(e.g. ``DEPLOYING_``). String values are stored in the database as-is.
80+
"""
81+
82+
# -- DEPLOYING phase --
83+
DEPLOYING_PROVISIONING = "deploying_provisioning"
84+
"""New revision routes are being provisioned and old routes are being drained."""
85+
DEPLOYING_ROLLING_BACK = "deploying_rolling_back"
86+
"""Clearing deploying_revision and transitioning to READY."""
87+
DEPLOYING_COMPLETED = "deploying_completed"
88+
"""All strategy conditions satisfied; triggers revision swap."""
89+
90+
@classmethod
91+
def deploying_handler_sub_steps(cls) -> tuple[DeploymentLifecycleSubStep, ...]:
92+
"""Sub-steps that have their own deploying handler (excludes COMPLETED, which is an evaluator outcome)."""
93+
return (cls.DEPLOYING_PROVISIONING, cls.DEPLOYING_ROLLING_BACK)
94+
95+
7296
class RouteStatus(CIStrEnum):
7397
"""Lifecycle status of a route in the deployment."""
7498

src/ai/backend/common/dto/manager/v2/deployment/response.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from ai.backend.common.data.endpoint.types import ScalingState
1616
from ai.backend.common.data.model_deployment.types import (
1717
ActivenessStatus,
18+
DeploymentLifecycleSubStep,
1819
LivenessStatus,
1920
ReadinessStatus,
2021
RouteHealthStatus,
@@ -175,6 +176,13 @@ class DeploymentNode(BaseResponseModel):
175176
default=None,
176177
description="ID of the revision currently being deployed (in progress, not yet active)",
177178
)
179+
sub_step: DeploymentLifecycleSubStep | None = Field(
180+
default=None,
181+
description=(
182+
"Sub-step within the current lifecycle phase. ``None`` when the"
183+
" deployment is not in a sub-step-bearing phase."
184+
),
185+
)
178186

179187

180188
class RouteNode(BaseResponseModel):

src/ai/backend/manager/api/adapters/deployment/adapter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,6 +2255,7 @@ def _deployment_data_to_dto(data: ModelDeploymentData) -> DeploymentNode:
22552255
scaling_state=data.scaling_state,
22562256
current_revision_id=data.current_revision_id,
22572257
deploying_revision_id=data.deploying_revision_id,
2258+
sub_step=data.sub_step,
22582259
)
22592260

22602261
@staticmethod

src/ai/backend/manager/api/gql/deployment/types/deployment.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from ai.backend.common.data.endpoint.types import ScalingState
1515
from ai.backend.common.data.model_deployment.types import (
16+
DeploymentLifecycleSubStep,
1617
ModelDeploymentStatus,
1718
)
1819
from ai.backend.common.dto.manager.v2.deployment.request import (
@@ -195,6 +196,19 @@
195196
)
196197

197198

199+
DeploymentLifecycleSubStepGQL: type[DeploymentLifecycleSubStep] = gql_enum(
200+
BackendAIGQLMeta(
201+
added_version=NEXT_RELEASE_VERSION,
202+
description=(
203+
"Sub-step within a deployment's current lifecycle phase."
204+
" ``None`` when the deployment is not in a sub-step-bearing phase."
205+
),
206+
),
207+
DeploymentLifecycleSubStep,
208+
name="DeploymentLifecycleSubStep",
209+
)
210+
211+
198212
@gql_pydantic_type(
199213
BackendAIGQLMeta(
200214
added_version="25.19.0",
@@ -351,6 +365,16 @@ class ModelDeployment(PydanticNodeMixin[DeploymentNodeDTO]):
351365
),
352366
)
353367
)
368+
sub_step: DeploymentLifecycleSubStepGQL | None = gql_added_field(
369+
BackendAIGQLMeta(
370+
added_version=NEXT_RELEASE_VERSION,
371+
description=(
372+
"Sub-step within the current lifecycle phase."
373+
" ``None`` when the deployment is not in a sub-step-bearing phase."
374+
),
375+
),
376+
default=None,
377+
)
354378

355379
@gql_added_field(
356380
BackendAIGQLMeta(

src/ai/backend/manager/data/deployment/types.py

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ai.backend.common.data.endpoint.types import EndpointLifecycle, ScalingState
1717
from ai.backend.common.data.model_deployment.types import (
1818
ActivenessStatus,
19+
DeploymentLifecycleSubStep,
1920
DeploymentStrategy,
2021
LivenessStatus,
2122
ModelDeploymentStatus,
@@ -164,30 +165,6 @@ class RouteSubStatus(enum.StrEnum):
164165
WARMING_UP = "warming_up"
165166

166167

167-
# ========== Status Transition Types (BEP-1030) ==========
168-
169-
170-
class DeploymentLifecycleSubStep(enum.StrEnum):
171-
"""Sub-steps within deployment lifecycle phases.
172-
173-
Member names are prefixed with the lifecycle phase they belong to
174-
(e.g. ``DEPLOYING_``). String values are stored in the database as-is.
175-
"""
176-
177-
# -- DEPLOYING phase --
178-
DEPLOYING_PROVISIONING = "deploying_provisioning"
179-
"""New revision routes are being provisioned and old routes are being drained."""
180-
DEPLOYING_ROLLING_BACK = "deploying_rolling_back"
181-
"""Clearing deploying_revision and transitioning to READY."""
182-
DEPLOYING_COMPLETED = "deploying_completed"
183-
"""All strategy conditions satisfied; triggers revision swap."""
184-
185-
@classmethod
186-
def deploying_handler_sub_steps(cls) -> tuple[DeploymentLifecycleSubStep, ...]:
187-
"""Sub-steps that have their own deploying handler (excludes COMPLETED, which is an evaluator outcome)."""
188-
return (cls.DEPLOYING_PROVISIONING, cls.DEPLOYING_ROLLING_BACK)
189-
190-
191168
@dataclass(frozen=True)
192169
class DeploymentLifecycleStatus:
193170
"""Target state for a deployment status transition.

src/ai/backend/manager/event_dispatcher/handlers/schedule.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22

3+
from ai.backend.common.data.model_deployment.types import DeploymentLifecycleSubStep
34
from ai.backend.common.events.event_types.agent.anycast import AgentStartedEvent
45
from ai.backend.common.events.event_types.schedule.anycast import (
56
DoDeploymentLifecycleEvent,
@@ -17,7 +18,6 @@
1718
from ai.backend.common.events.hub.hub import EventHub
1819
from ai.backend.common.types import AgentId
1920
from ai.backend.logging.utils import BraceStyleAdapter
20-
from ai.backend.manager.data.deployment.types import DeploymentLifecycleSubStep
2121
from ai.backend.manager.scheduler.types import ScheduleType
2222
from ai.backend.manager.sokovan.deployment.coordinator import DeploymentCoordinator
2323
from ai.backend.manager.sokovan.deployment.route.coordinator import RouteCoordinator

src/ai/backend/manager/models/endpoint/conditions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
UUIDEqualMatchSpec,
1515
UUIDInMatchSpec,
1616
)
17+
from ai.backend.common.data.model_deployment.types import DeploymentLifecycleSubStep
1718
from ai.backend.common.identifier.deployment import DeploymentID
18-
from ai.backend.manager.data.deployment.types import DeploymentLifecycleSubStep
1919
from ai.backend.manager.models.condition_utils import make_string_in_factory
2020
from ai.backend.manager.models.endpoint import (
2121
EndpointAutoScalingRuleRow,

src/ai/backend/manager/models/endpoint/row.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
from ai.backend.common.config import model_definition_iv
3737
from ai.backend.common.data.model_deployment.types import (
38+
DeploymentLifecycleSubStep,
3839
DeploymentStrategy,
3940
ModelDeploymentStatus,
4041
)
@@ -69,7 +70,6 @@
6970
)
7071
from ai.backend.manager.data.deployment.types import (
7172
DeploymentInfo,
72-
DeploymentLifecycleSubStep,
7373
DeploymentMetadata,
7474
DeploymentNetworkData,
7575
DeploymentOptions,

0 commit comments

Comments
 (0)