Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/12869.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the v2 DTOs and service actions for kernel scheduling-history search, forming the schema shared by REST v2 and GraphQL.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

from ai.backend.common.dto.manager.v2.scheduling_history.request import (
AdminSearchDeploymentHistoriesInput,
AdminSearchKernelHistoriesInput,
AdminSearchRouteHistoriesInput,
AdminSearchSessionHistoriesInput,
DeploymentHistoryFilter,
DeploymentHistoryOrder,
KernelHistoryFilter,
KernelHistoryOrder,
RouteHistoryFilter,
RouteHistoryOrder,
ScopedSearchKernelHistoriesInput,
SearchDeploymentHistoryInput,
SearchRouteHistoryInput,
SearchSessionHistoryInput,
Expand All @@ -18,9 +22,11 @@
)
from ai.backend.common.dto.manager.v2.scheduling_history.response import (
AdminSearchDeploymentHistoriesPayload,
AdminSearchKernelHistoriesPayload,
AdminSearchRouteHistoriesPayload,
AdminSearchSessionHistoriesPayload,
DeploymentHistoryNode,
KernelHistoryNode,
ListDeploymentHistoryPayload,
ListRouteHistoryPayload,
ListSessionHistoryPayload,
Expand All @@ -30,6 +36,8 @@
from ai.backend.common.dto.manager.v2.scheduling_history.types import (
DeploymentHistoryOrderField,
DeploymentHistoryScopeDTO,
KernelHistoryOrderField,
KernelHistoryScopeDTO,
OrderDirection,
RouteHistoryOrderField,
RouteHistoryScopeDTO,
Expand All @@ -43,6 +51,8 @@
# Types
"DeploymentHistoryOrderField",
"DeploymentHistoryScopeDTO",
"KernelHistoryOrderField",
"KernelHistoryScopeDTO",
"OrderDirection",
"RouteHistoryOrderField",
"RouteHistoryScopeDTO",
Expand All @@ -52,22 +62,28 @@
"SubStepResultInfo",
# Input models (request)
"AdminSearchDeploymentHistoriesInput",
"AdminSearchKernelHistoriesInput",
"AdminSearchRouteHistoriesInput",
"AdminSearchSessionHistoriesInput",
"DeploymentHistoryFilter",
"DeploymentHistoryOrder",
"KernelHistoryFilter",
"KernelHistoryOrder",
"RouteHistoryFilter",
"RouteHistoryOrder",
"ScopedSearchKernelHistoriesInput",
"SearchDeploymentHistoryInput",
"SearchRouteHistoryInput",
"SearchSessionHistoryInput",
"SessionHistoryFilter",
"SessionHistoryOrder",
# Response models
"AdminSearchDeploymentHistoriesPayload",
"AdminSearchKernelHistoriesPayload",
"AdminSearchRouteHistoriesPayload",
"AdminSearchSessionHistoriesPayload",
"DeploymentHistoryNode",
"KernelHistoryNode",
"ListDeploymentHistoryPayload",
"ListRouteHistoryPayload",
"ListSessionHistoryPayload",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

from .types import (
DeploymentHistoryOrderField,
KernelHistoryOrderField,
KernelHistoryScopeDTO,
OrderDirection,
RouteHistoryOrderField,
SchedulingResultType,
Expand All @@ -19,13 +21,17 @@

__all__ = (
"AdminSearchDeploymentHistoriesInput",
"AdminSearchKernelHistoriesInput",
"AdminSearchRouteHistoriesInput",
"AdminSearchSessionHistoriesInput",
"DeploymentHistoryFilter",
"DeploymentHistoryOrder",
"KernelHistoryFilter",
"KernelHistoryOrder",
"RouteHistoryFilter",
"RouteHistoryOrder",
"SchedulingResultFilter",
"ScopedSearchKernelHistoriesInput",
"SearchDeploymentHistoryInput",
"SearchRouteHistoryInput",
"SearchSessionHistoryInput",
Expand Down Expand Up @@ -86,6 +92,37 @@ class SearchSessionHistoryInput(BaseRequestModel):
offset: int = Field(default=0, ge=0, description="Number of items to skip")


class KernelHistoryFilter(BaseRequestModel):
"""Filter conditions for kernel scheduling history search."""

id: UUIDFilter | None = Field(default=None, description="Filter by history record ID")
kernel_id: UUIDFilter | None = Field(default=None, description="Filter by kernel ID")
session_id: UUIDFilter | None = Field(default=None, description="Filter by session ID")
phase: StringFilter | None = Field(default=None, description="Filter by scheduling phase")
from_status: list[str] | None = Field(default=None, description="Filter by from_status values")
to_status: list[str] | None = Field(default=None, description="Filter by to_status values")
result: SchedulingResultFilter | None = Field(
default=None, description="Filter by scheduling result"
)
error_code: StringFilter | None = Field(default=None, description="Filter by error code")
message: StringFilter | None = Field(default=None, description="Filter by message")
created_at: DateTimeFilter | None = Field(default=None, description="Filter by created_at")
updated_at: DateTimeFilter | None = Field(default=None, description="Filter by updated_at")
AND: list[KernelHistoryFilter] | None = Field(default=None, description="AND conjunction.")
OR: list[KernelHistoryFilter] | None = Field(default=None, description="OR conjunction.")
NOT: list[KernelHistoryFilter] | None = Field(default=None, description="NOT negation.")


KernelHistoryFilter.model_rebuild()


class KernelHistoryOrder(BaseRequestModel):
"""Order specification for kernel scheduling history."""

field: KernelHistoryOrderField = Field(description="Field to order by")
direction: OrderDirection = Field(default=OrderDirection.DESC, description="Order direction")


class DeploymentHistoryFilter(BaseRequestModel):
"""Filter conditions for deployment scheduling history search."""

Expand Down Expand Up @@ -182,6 +219,37 @@ class AdminSearchSessionHistoriesInput(BaseRequestModel):
offset: int | None = Field(default=None, description="Offset pagination: number to skip")


class AdminSearchKernelHistoriesInput(BaseRequestModel):
"""Input for admin search of kernel scheduling histories."""

filter: KernelHistoryFilter | None = Field(default=None, description="Filter conditions")
order: list[KernelHistoryOrder] | None = Field(default=None, description="Order specifications")
first: int | None = Field(default=None, description="Cursor pagination: number of items")
after: str | None = Field(default=None, description="Cursor pagination: after cursor")
last: int | None = Field(default=None, description="Cursor pagination: last N items")
before: str | None = Field(default=None, description="Cursor pagination: before cursor")
limit: int | None = Field(default=None, description="Offset pagination: maximum items")
offset: int | None = Field(default=None, description="Offset pagination: number to skip")


class ScopedSearchKernelHistoriesInput(BaseRequestModel):
"""Input for scoped search of kernel scheduling histories.

Unlike the session/deployment/route scoped searches, the scope is carried in the body
because a kernel query may be scoped by session_id, kernel_id, or both.
"""

scope: KernelHistoryScopeDTO = Field(description="Required. Restricts the rows returned.")
filter: KernelHistoryFilter | None = Field(default=None, description="Filter conditions")
order: list[KernelHistoryOrder] | None = Field(default=None, description="Order specifications")
first: int | None = Field(default=None, description="Cursor pagination: number of items")
after: str | None = Field(default=None, description="Cursor pagination: after cursor")
last: int | None = Field(default=None, description="Cursor pagination: last N items")
before: str | None = Field(default=None, description="Cursor pagination: before cursor")
limit: int | None = Field(default=None, description="Offset pagination: maximum items")
offset: int | None = Field(default=None, description="Offset pagination: number to skip")


class AdminSearchDeploymentHistoriesInput(BaseRequestModel):
"""Input for admin search of deployment histories."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

__all__ = (
"AdminSearchDeploymentHistoriesPayload",
"AdminSearchKernelHistoriesPayload",
"AdminSearchRouteHistoriesPayload",
"AdminSearchSessionHistoriesPayload",
"DeploymentHistoryNode",
"KernelHistoryNode",
"ListDeploymentHistoryPayload",
"ListRouteHistoryPayload",
"ListSessionHistoryPayload",
Expand Down Expand Up @@ -46,6 +48,26 @@ class SessionHistoryNode(BaseResponseModel):
updated_at: datetime = Field(description="Timestamp when the history record was last updated")


class KernelHistoryNode(BaseResponseModel):
"""Node model representing a kernel scheduling history record.

Has no ``sub_steps``: the ``kernel_scheduling_history`` table carries no such column.
"""

id: UUID = Field(description="History record ID")
kernel_id: UUID = Field(description="Kernel ID this history belongs to")
session_id: UUID = Field(description="Session owning the kernel")
phase: str = Field(description="Scheduling phase")
from_status: str | None = Field(default=None, description="Status before transition")
to_status: str | None = Field(default=None, description="Status after transition")
result: str = Field(description="Result of the scheduling attempt")
error_code: str | None = Field(default=None, description="Error code if scheduling failed")
message: str | None = Field(default=None, description="Human-readable message or error detail")
attempts: int = Field(description="Number of scheduling attempts made")
created_at: datetime = Field(description="Timestamp when the history record was created")
updated_at: datetime = Field(description="Timestamp when the history record was last updated")


class DeploymentHistoryNode(BaseResponseModel):
"""Node model representing a deployment scheduling history record.

Expand Down Expand Up @@ -127,6 +149,15 @@ class AdminSearchSessionHistoriesPayload(BaseResponseModel):
has_previous_page: bool = Field(description="Whether there is a previous page.")


class AdminSearchKernelHistoriesPayload(BaseResponseModel):
"""Payload for admin and scoped search of kernel scheduling histories."""

items: list[KernelHistoryNode] = Field(description="List of kernel history nodes.")
total_count: int = Field(description="Total number of records matching the filter.")
has_next_page: bool = Field(description="Whether there is a next page.")
has_previous_page: bool = Field(description="Whether there is a previous page.")


class AdminSearchDeploymentHistoriesPayload(BaseResponseModel):
"""Payload for admin search of deployment histories."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from enum import StrEnum
from uuid import UUID

from pydantic import Field
from pydantic import Field, model_validator

from ai.backend.common.api_handlers import BaseRequestModel, BaseResponseModel
from ai.backend.common.dto.manager.v2.common import OrderDirection
Expand All @@ -17,6 +17,7 @@
"DeploymentHistoryOrderField",
"DeploymentHistoryScopeDTO",
"KernelHistoryOrderField",
"KernelHistoryScopeDTO",
"OrderDirection",
"RouteHistoryOrderField",
"RouteHistoryScopeDTO",
Expand Down Expand Up @@ -84,6 +85,25 @@ class SessionHistoryScopeDTO(BaseRequestModel):
session_id: UUID = Field(description="Session ID to get history for.")


class KernelHistoryScopeDTO(BaseRequestModel):
"""Scope for kernel scheduling history queries.

Unlike the single-entity scopes above, a kernel query may be scoped either by the
owning session (all of its kernels) or by one kernel. At least one must be given.
"""

session_id: UUID | None = Field(
default=None, description="Restrict to the kernels of this session."
)
kernel_id: UUID | None = Field(default=None, description="Restrict to this kernel.")

@model_validator(mode="after")
def _check_non_empty(self) -> KernelHistoryScopeDTO:
if self.session_id is None and self.kernel_id is None:
raise ValueError("At least one of session_id or kernel_id must be given.")
return self


class DeploymentHistoryScopeDTO(BaseRequestModel):
"""Scope for deployment scheduling history queries."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
SearchDeploymentScopedHistoryAction,
SearchDeploymentScopedHistoryActionResult,
)
from .search_kernel_history import (
SearchKernelHistoryAction,
SearchKernelHistoryActionResult,
)
from .search_kernel_scoped_history import (
SearchKernelScopedHistoryAction,
SearchKernelScopedHistoryActionResult,
)
from .search_route_history import (
SearchRouteHistoryAction,
SearchRouteHistoryActionResult,
Expand All @@ -31,13 +39,17 @@
# Admin actions
"SearchSessionHistoryAction",
"SearchSessionHistoryActionResult",
"SearchKernelHistoryAction",
"SearchKernelHistoryActionResult",
"SearchDeploymentHistoryAction",
"SearchDeploymentHistoryActionResult",
"SearchRouteHistoryAction",
"SearchRouteHistoryActionResult",
# Scoped actions (added in 26.2.0)
"SearchSessionScopedHistoryAction",
"SearchSessionScopedHistoryActionResult",
"SearchKernelScopedHistoryAction",
"SearchKernelScopedHistoryActionResult",
"SearchDeploymentScopedHistoryAction",
"SearchDeploymentScopedHistoryActionResult",
"SearchRouteScopedHistoryAction",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import override

from ai.backend.common.data.permission.types import EntityType
from ai.backend.manager.actions.action import BaseActionResult
from ai.backend.manager.actions.types import ActionOperationType
from ai.backend.manager.data.kernel.types import KernelSchedulingHistoryData
from ai.backend.manager.repositories.base import BatchQuerier

from .base import SchedulingHistoryAction


@dataclass
class SearchKernelHistoryAction(SchedulingHistoryAction):
"""Action to search kernel scheduling history (admin API)."""

querier: BatchQuerier

@override
@classmethod
def entity_type(cls) -> EntityType:
return EntityType.KERNEL_HISTORY

@override
@classmethod
def operation_type(cls) -> ActionOperationType:
return ActionOperationType.SEARCH

@override
def entity_id(self) -> str | None:
return None


@dataclass
class SearchKernelHistoryActionResult(BaseActionResult):
"""Result of searching kernel scheduling history."""

histories: list[KernelSchedulingHistoryData]
total_count: int
has_next_page: bool
has_previous_page: bool

@override
def entity_id(self) -> str | None:
return None
Loading
Loading