Skip to content

Commit 2d50b46

Browse files
jopemachineclaude
andcommitted
feat(BA-6883): expose kernel scheduling history over GraphQL v2
Add the kernel slice of the Strawberry scheduling-history schema, mirroring the session, deployment, and route slices: - `adminKernelSchedulingHistories` — super-admin, system-wide - `kernelScopedSchedulingHistories` — scoped to one kernel via `KernelScope` The node exposes `kernel` and `session` back-references through the existing data loaders, and `resolve_nodes` is backed by a new `kernel_history_loader` over `batch_load_kernel_histories_by_ids`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent db44f52 commit 2d50b46

9 files changed

Lines changed: 525 additions & 0 deletions

File tree

changes/12989.feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expose kernel scheduling history over GraphQL v2 with `adminKernelSchedulingHistories` (super-admin, system-wide) and `scopedKernelSchedulingHistories` (scoped to one kernel), alongside the existing session, deployment, and route history roots.

docs/manager/graphql-reference/supergraph.graphql

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8508,6 +8508,101 @@ enum KernelResourceAllocationOrderField
85088508
USED @join__enumValue(graph: STRAWBERRY)
85098509
}
85108510

8511+
"""Added in 26.8.0. Kernel scheduling history record."""
8512+
type KernelSchedulingHistory implements Node
8513+
@join__implements(graph: STRAWBERRY, interface: "Node")
8514+
@join__type(graph: STRAWBERRY)
8515+
{
8516+
"""The Globally Unique ID of this object"""
8517+
id: ID!
8518+
kernelId: ID!
8519+
sessionId: ID!
8520+
phase: String!
8521+
fromStatus: String
8522+
toStatus: String
8523+
result: SchedulingResult!
8524+
errorCode: String
8525+
message: String
8526+
attempts: Int!
8527+
createdAt: DateTime!
8528+
updatedAt: DateTime!
8529+
8530+
"""Added in 26.8.0. The kernel this history record belongs to."""
8531+
kernel: KernelV2
8532+
8533+
"""Added in 26.8.0. The session owning the kernel."""
8534+
session: SessionV2
8535+
}
8536+
8537+
"""Added in 26.8.0. Kernel scheduling history connection."""
8538+
type KernelSchedulingHistoryConnection
8539+
@join__type(graph: STRAWBERRY)
8540+
{
8541+
"""Pagination data for this connection"""
8542+
pageInfo: PageInfo!
8543+
8544+
"""Contains the nodes in this connection"""
8545+
edges: [KernelSchedulingHistoryEdge!]!
8546+
count: Int!
8547+
}
8548+
8549+
"""An edge in a connection."""
8550+
type KernelSchedulingHistoryEdge
8551+
@join__type(graph: STRAWBERRY)
8552+
{
8553+
"""A cursor for use in pagination"""
8554+
cursor: String!
8555+
8556+
"""The item at the end of the edge"""
8557+
node: KernelSchedulingHistory!
8558+
}
8559+
8560+
"""Added in 26.8.0. Filter for kernel scheduling history"""
8561+
input KernelSchedulingHistoryFilter
8562+
@join__type(graph: STRAWBERRY)
8563+
{
8564+
id: UUIDFilter = null
8565+
kernelId: UUIDFilter = null
8566+
sessionId: UUIDFilter = null
8567+
phase: StringFilter = null
8568+
fromStatus: [String!] = null
8569+
toStatus: [String!] = null
8570+
result: SchedulingResultFilter = null
8571+
errorCode: StringFilter = null
8572+
message: StringFilter = null
8573+
createdAt: DateTimeFilter = null
8574+
updatedAt: DateTimeFilter = null
8575+
AND: [KernelSchedulingHistoryFilter!] = null
8576+
OR: [KernelSchedulingHistoryFilter!] = null
8577+
NOT: [KernelSchedulingHistoryFilter!] = null
8578+
}
8579+
8580+
"""Added in 26.8.0. Order by specification for kernel scheduling history"""
8581+
input KernelSchedulingHistoryOrderBy
8582+
@join__type(graph: STRAWBERRY)
8583+
{
8584+
field: KernelSchedulingHistoryOrderField!
8585+
direction: OrderDirection! = DESC
8586+
}
8587+
8588+
"""
8589+
Added in 26.8.0. Fields available for ordering kernel scheduling history
8590+
"""
8591+
enum KernelSchedulingHistoryOrderField
8592+
@join__type(graph: STRAWBERRY)
8593+
{
8594+
CREATED_AT @join__enumValue(graph: STRAWBERRY)
8595+
UPDATED_AT @join__enumValue(graph: STRAWBERRY)
8596+
}
8597+
8598+
"""Added in 26.8.0. Scope for kernel scheduling history query"""
8599+
input KernelScope
8600+
@join__type(graph: STRAWBERRY)
8601+
{
8602+
"""Kernel ID to get history for"""
8603+
kernelId: UUID!
8604+
}
8605+
85118606
"""
85128607
Added in 26.2.0. Represents a kernel (compute container) in Backend.AI.
85138608
"""
@@ -14709,6 +14804,9 @@ type Query
1470914804
"""Added in 26.4.2. List session scheduling history (admin only)"""
1471014805
adminSessionSchedulingHistories(filter: SessionSchedulingHistoryFilter = null, orderBy: [SessionSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): SessionSchedulingHistoryConnection @join__field(graph: STRAWBERRY)
1471114806

14807+
"""Added in 26.8.0. List kernel scheduling history (superadmin only)"""
14808+
adminKernelSchedulingHistories(filter: KernelSchedulingHistoryFilter = null, orderBy: [KernelSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): KernelSchedulingHistoryConnection @join__field(graph: STRAWBERRY)
14809+
1471214810
"""Added in 25.16.0. List all deployments (superadmin only)."""
1471314811
adminDeployments(filter: DeploymentFilter = null, orderBy: [DeploymentOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): ModelDeploymentConnection @join__field(graph: STRAWBERRY)
1471414812

@@ -14990,6 +15088,9 @@ type Query
1499015088
"""Added in 26.2.0. Get scheduling history for a specific session."""
1499115089
sessionScopedSchedulingHistories(scope: SessionScope!, filter: SessionSchedulingHistoryFilter = null, orderBy: [SessionSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): SessionSchedulingHistoryConnection @join__field(graph: STRAWBERRY)
1499215090

15091+
"""Added in 26.8.0. Get scheduling history for a specific kernel."""
15092+
scopedKernelSchedulingHistories(scope: KernelScope!, filter: KernelSchedulingHistoryFilter = null, orderBy: [KernelSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): KernelSchedulingHistoryConnection @join__field(graph: STRAWBERRY)
15093+
1499315094
"""Added in 26.2.0. Get scheduling history for a specific deployment."""
1499415095
deploymentScopedSchedulingHistories(scope: DeploymentScope!, filter: DeploymentHistoryFilter = null, orderBy: [DeploymentHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): DeploymentHistoryConnection @join__field(graph: STRAWBERRY)
1499515096

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

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5632,6 +5632,86 @@ enum KernelResourceAllocationOrderField {
56325632
USED
56335633
}
56345634

5635+
"""Added in 26.8.0. Kernel scheduling history record."""
5636+
type KernelSchedulingHistory implements Node {
5637+
"""The Globally Unique ID of this object"""
5638+
id: ID!
5639+
kernelId: ID!
5640+
sessionId: ID!
5641+
phase: String!
5642+
fromStatus: String
5643+
toStatus: String
5644+
result: SchedulingResult!
5645+
errorCode: String
5646+
message: String
5647+
attempts: Int!
5648+
createdAt: DateTime!
5649+
updatedAt: DateTime!
5650+
5651+
"""Added in 26.8.0. The kernel this history record belongs to."""
5652+
kernel: KernelV2
5653+
5654+
"""Added in 26.8.0. The session owning the kernel."""
5655+
session: SessionV2
5656+
}
5657+
5658+
"""Added in 26.8.0. Kernel scheduling history connection."""
5659+
type KernelSchedulingHistoryConnection {
5660+
"""Pagination data for this connection"""
5661+
pageInfo: PageInfo!
5662+
5663+
"""Contains the nodes in this connection"""
5664+
edges: [KernelSchedulingHistoryEdge!]!
5665+
count: Int!
5666+
}
5667+
5668+
"""An edge in a connection."""
5669+
type KernelSchedulingHistoryEdge {
5670+
"""A cursor for use in pagination"""
5671+
cursor: String!
5672+
5673+
"""The item at the end of the edge"""
5674+
node: KernelSchedulingHistory!
5675+
}
5676+
5677+
"""Added in 26.8.0. Filter for kernel scheduling history"""
5678+
input KernelSchedulingHistoryFilter {
5679+
id: UUIDFilter = null
5680+
kernelId: UUIDFilter = null
5681+
sessionId: UUIDFilter = null
5682+
phase: StringFilter = null
5683+
fromStatus: [String!] = null
5684+
toStatus: [String!] = null
5685+
result: SchedulingResultFilter = null
5686+
errorCode: StringFilter = null
5687+
message: StringFilter = null
5688+
createdAt: DateTimeFilter = null
5689+
updatedAt: DateTimeFilter = null
5690+
AND: [KernelSchedulingHistoryFilter!] = null
5691+
OR: [KernelSchedulingHistoryFilter!] = null
5692+
NOT: [KernelSchedulingHistoryFilter!] = null
5693+
}
5694+
5695+
"""Added in 26.8.0. Order by specification for kernel scheduling history"""
5696+
input KernelSchedulingHistoryOrderBy {
5697+
field: KernelSchedulingHistoryOrderField!
5698+
direction: OrderDirection! = DESC
5699+
}
5700+
5701+
"""
5702+
Added in 26.8.0. Fields available for ordering kernel scheduling history
5703+
"""
5704+
enum KernelSchedulingHistoryOrderField {
5705+
CREATED_AT
5706+
UPDATED_AT
5707+
}
5708+
5709+
"""Added in 26.8.0. Scope for kernel scheduling history query"""
5710+
input KernelScope {
5711+
"""Kernel ID to get history for"""
5712+
kernelId: UUID!
5713+
}
5714+
56355715
"""
56365716
Added in 26.2.0. Represents a kernel (compute container) in Backend.AI.
56375717
"""
@@ -9748,6 +9828,9 @@ type Query {
97489828
"""Added in 26.4.2. List session scheduling history (admin only)"""
97499829
adminSessionSchedulingHistories(filter: SessionSchedulingHistoryFilter = null, orderBy: [SessionSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): SessionSchedulingHistoryConnection
97509830

9831+
"""Added in 26.8.0. List kernel scheduling history (superadmin only)"""
9832+
adminKernelSchedulingHistories(filter: KernelSchedulingHistoryFilter = null, orderBy: [KernelSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): KernelSchedulingHistoryConnection
9833+
97519834
"""Added in 25.16.0. List all deployments (superadmin only)."""
97529835
adminDeployments(filter: DeploymentFilter = null, orderBy: [DeploymentOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): ModelDeploymentConnection
97539836

@@ -10029,6 +10112,9 @@ type Query {
1002910112
"""Added in 26.2.0. Get scheduling history for a specific session."""
1003010113
sessionScopedSchedulingHistories(scope: SessionScope!, filter: SessionSchedulingHistoryFilter = null, orderBy: [SessionSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): SessionSchedulingHistoryConnection
1003110114

10115+
"""Added in 26.8.0. Get scheduling history for a specific kernel."""
10116+
scopedKernelSchedulingHistories(scope: KernelScope!, filter: KernelSchedulingHistoryFilter = null, orderBy: [KernelSchedulingHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): KernelSchedulingHistoryConnection
10117+
1003210118
"""Added in 26.2.0. Get scheduling history for a specific deployment."""
1003310119
deploymentScopedSchedulingHistories(scope: DeploymentScope!, filter: DeploymentHistoryFilter = null, orderBy: [DeploymentHistoryOrderBy!] = null, before: String = null, after: String = null, first: Int = null, last: Int = null, limit: Int = null, offset: Int = null): DeploymentHistoryConnection
1003410120

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
SessionHistoryNode,
3232
)
3333
from ai.backend.common.dto.manager.v2.scheduling_history.types import SubStepResultInfo
34+
from ai.backend.common.identifier.kernel_scheduling_history import KernelSchedulingHistoryID
3435
from ai.backend.common.identifier.replica import ReplicaID
3536
from ai.backend.common.types import KernelId
3637
from ai.backend.manager.api.adapter_options.pagination.pagination import PaginationSpec
@@ -162,6 +163,27 @@ async def batch_load_session_histories_by_ids(
162163
history_map = {h.id: self._session_data_to_dto(h) for h in action_result.histories}
163164
return [history_map.get(history_id) for history_id in ids]
164165

166+
async def batch_load_kernel_histories_by_ids(
167+
self, ids: Sequence[KernelSchedulingHistoryID]
168+
) -> list[KernelHistoryNode | None]:
169+
"""Batch load kernel scheduling histories by their IDs for DataLoader use.
170+
171+
Returns KernelHistoryNode DTOs in the same order as the input ids list.
172+
"""
173+
if not ids:
174+
return []
175+
querier = BatchQuerier(
176+
pagination=OffsetPagination(limit=len(ids)),
177+
conditions=[KernelSchedulingHistoryConditions.by_ids(ids)],
178+
)
179+
action_result = (
180+
await self._processors.scheduling_history.search_kernel_history.wait_for_complete(
181+
SearchKernelHistoryAction(querier=querier)
182+
)
183+
)
184+
history_map = {h.id: self._kernel_data_to_dto(h) for h in action_result.items}
185+
return [history_map.get(history_id) for history_id in ids]
186+
165187
async def batch_load_deployment_histories_by_ids(
166188
self, ids: Sequence[UUID]
167189
) -> list[DeploymentHistoryNode | None]:

src/ai/backend/manager/api/gql/data_loader/data_loaders.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ai.backend.common.identifier.app_config_allow_list import AppConfigAllowListID
1010
from ai.backend.common.identifier.app_config_definition import AppConfigDefinitionID
1111
from ai.backend.common.identifier.deployment import DeploymentID
12+
from ai.backend.common.identifier.kernel_scheduling_history import KernelSchedulingHistoryID
1213
from ai.backend.common.types import AgentId, ImageID, KernelId, SessionId
1314
from ai.backend.manager.data.permission.id import ObjectId
1415

@@ -102,6 +103,7 @@
102103
)
103104
from ai.backend.manager.api.gql.scheduling_history.types import ( # pants: no-infer-dep
104105
DeploymentHistory,
106+
KernelSchedulingHistoryGQL,
105107
RouteHistory,
106108
SessionSchedulingHistory,
107109
)
@@ -718,6 +720,24 @@ async def load_fn(ids: list[uuid.UUID]) -> list[SessionSchedulingHistory | None]
718720

719721
return DataLoader(load_fn=load_fn)
720722

723+
@cached_property
724+
def kernel_history_loader(
725+
self,
726+
) -> DataLoader[KernelSchedulingHistoryID, KernelSchedulingHistoryGQL | None]:
727+
adapter = self._adapters.scheduling_history
728+
729+
async def load_fn(
730+
ids: list[KernelSchedulingHistoryID],
731+
) -> list[KernelSchedulingHistoryGQL | None]:
732+
from ai.backend.manager.api.gql.scheduling_history.types import ( # pants: no-infer-dep
733+
KernelSchedulingHistoryGQL as KSH,
734+
)
735+
736+
dtos = await adapter.batch_load_kernel_histories_by_ids(ids)
737+
return [KSH.from_pydantic(dto) if dto is not None else None for dto in dtos]
738+
739+
return DataLoader(load_fn=load_fn)
740+
721741
@cached_property
722742
def deployment_history_loader(
723743
self,

src/ai/backend/manager/api/gql/scheduling_history/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
from .resolver import (
44
DeploymentHistoryConnection,
5+
KernelSchedulingHistoryConnectionGQL,
56
RouteHistoryConnection,
67
SessionSchedulingHistoryConnection,
78
admin_deployment_histories,
9+
admin_kernel_scheduling_histories,
810
admin_route_histories,
911
admin_session_scheduling_histories,
1012
deployment_histories,
1113
deployment_scoped_scheduling_histories,
1214
route_histories,
1315
route_scoped_scheduling_histories,
16+
scoped_kernel_scheduling_histories,
1417
session_scheduling_histories,
1518
session_scoped_scheduling_histories,
1619
)
@@ -19,6 +22,10 @@
1922
DeploymentHistoryFilter,
2023
DeploymentHistoryOrderBy,
2124
DeploymentScope,
25+
KernelSchedulingHistoryFilterGQL,
26+
KernelSchedulingHistoryGQL,
27+
KernelSchedulingHistoryOrderByGQL,
28+
KernelScopeGQL,
2229
RouteHistory,
2330
RouteHistoryFilter,
2431
RouteHistoryOrderBy,
@@ -37,29 +44,36 @@
3744
# Types
3845
"SubStepResultGQL",
3946
"SessionSchedulingHistory",
47+
"KernelSchedulingHistoryGQL",
4048
"DeploymentHistory",
4149
"RouteHistory",
4250
# Filters
4351
"SessionSchedulingHistoryFilter",
4452
"SessionSchedulingHistoryOrderBy",
53+
"KernelSchedulingHistoryFilterGQL",
54+
"KernelSchedulingHistoryOrderByGQL",
4555
"DeploymentHistoryFilter",
4656
"DeploymentHistoryOrderBy",
4757
"RouteHistoryFilter",
4858
"RouteHistoryOrderBy",
4959
# Scope types (added in 26.2.0)
5060
"SessionScope",
61+
"KernelScopeGQL",
5162
"DeploymentScope",
5263
"RouteScope",
5364
# Connections
5465
"SessionSchedulingHistoryConnection",
66+
"KernelSchedulingHistoryConnectionGQL",
5567
"DeploymentHistoryConnection",
5668
"RouteHistoryConnection",
5769
# Queries - Admin
5870
"admin_session_scheduling_histories",
71+
"admin_kernel_scheduling_histories",
5972
"admin_deployment_histories",
6073
"admin_route_histories",
6174
# Queries - Scoped (added in 26.2.0)
6275
"session_scoped_scheduling_histories",
76+
"scoped_kernel_scheduling_histories",
6377
"deployment_scoped_scheduling_histories",
6478
"route_scoped_scheduling_histories",
6579
# Queries - Legacy (deprecated)

0 commit comments

Comments
 (0)