Skip to content
Closed
1 change: 1 addition & 0 deletions changes/12867.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the models-layer query conditions and orders for kernel scheduling history, covering filters on id, kernel, session, phase, status transition, result and timestamps.
1 change: 1 addition & 0 deletions changes/12868.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the kernel scheduling-history repository search scope, which scopes a query by session, by kernel, or by both, and rejects an empty scope rather than degenerating into a system-wide search.
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.
1 change: 1 addition & 0 deletions changes/12887.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix the legacy app_configs downgrade failing with a duplicate enum type error
1 change: 1 addition & 0 deletions changes/12963.enhance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Retire the dedicated `error_logs` cleanup timer (its rows are now purged by the DB record retention policy sweep) and redefine `clear-history` as a manual force-sweep plus `VACUUM` escape hatch over the retention policy layer.
3 changes: 3 additions & 0 deletions src/ai/backend/common/data/permission/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ class EntityType(enum.StrEnum):
SESSION_DIRECT_ACCESS = "session:direct_access"
SESSION_HISTORY = "session:history"
SESSION_SCOPED_HISTORY = "session:scoped_history"
# Kernel sub
KERNEL_HISTORY = "kernel:history"
KERNEL_SCOPED_HISTORY = "kernel:scoped_history"
# Deployment sub
DEPLOYMENT_REPLICA = "deployment:replica"
DEPLOYMENT_ROUTE = "deployment:route"
Expand Down
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,14 +8,16 @@
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

__all__ = (
"DeploymentHistoryOrderField",
"DeploymentHistoryScopeDTO",
"KernelHistoryOrderField",
"KernelHistoryScopeDTO",
"OrderDirection",
"RouteHistoryOrderField",
"RouteHistoryScopeDTO",
Expand Down Expand Up @@ -45,6 +47,13 @@ class SessionHistoryOrderField(StrEnum):
UPDATED_AT = "updated_at"


class KernelHistoryOrderField(StrEnum):
"""Fields available for ordering kernel scheduling history."""

CREATED_AT = "created_at"
UPDATED_AT = "updated_at"


class DeploymentHistoryOrderField(StrEnum):
"""Fields available for ordering deployment scheduling history."""

Expand Down Expand Up @@ -76,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
Empty file.
38 changes: 0 additions & 38 deletions src/ai/backend/common/events/event_types/log/anycast.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from typing import NewType
from uuid import UUID

__all__ = ("KernelSchedulingHistoryID",)

KernelSchedulingHistoryID = NewType("KernelSchedulingHistoryID", UUID)
8 changes: 3 additions & 5 deletions src/ai/backend/manager/api/rest/error_log/registry.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"""Error log module registrar.

Lifecycle management (GlobalTimer for log cleanup, event dispatcher
integration) is handled by the DependencyComposer:

* Event consumer: ``event_dispatcher.handlers.log_cleanup``
* GlobalTimer: ``dependencies.processing.log_cleanup_timer``
Old ``error_logs`` rows are purged by the DB record retention sweep under the
``logs`` category (BEP-1063); this module only registers the HTTP routes. A
manual immediate sweep can be triggered via the ``clear-history`` CLI.
"""

from __future__ import annotations
Expand Down
Loading
Loading