Skip to content

Commit d58b1e5

Browse files
fregataaclaude
andcommitted
refactor(BA-5947): route model_cards loader through SearchModelCardsAction
Replace the bespoke batch_load_by_vfolder_ids action/service/processor/repo chain with the same shape used by deployment.batch_load_revisions_by_ids: the adapter constructs a ``BatchQuerier`` (``NoPagination`` + ``ModelCardConditions.by_vfolder_ids`` + most-recent/id ordering) and delegates to the existing ``SearchModelCardsAction``. The grouping by ``vfolder_id`` happens once in the adapter where the GQL connection is also assembled. This removes the standalone batch_load_by_vfolder_ids action file, the service method, the processor attribute, and the repository/db_source helpers. The ``test_batch_load_by_vfolder_ids`` repo test is dropped together with the db_source method it pinned; the search path is already covered by the broader model_card search tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 79bc236 commit d58b1e5

8 files changed

Lines changed: 27 additions & 455 deletions

File tree

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,13 @@
6868
from ai.backend.manager.models.model_card.orders import ModelCardOrders
6969
from ai.backend.manager.models.model_card.row import ModelCardRow
7070
from ai.backend.manager.repositories.base import (
71+
BatchQuerier,
7172
QueryCondition,
7273
QueryOrder,
7374
combine_conditions_or,
7475
negate_conditions,
7576
)
77+
from ai.backend.manager.repositories.base.pagination import NoPagination
7678
from ai.backend.manager.repositories.base.rbac.entity_creator import RBACEntityCreator
7779
from ai.backend.manager.repositories.base.updater import Updater
7880
from ai.backend.manager.repositories.model_card.creators import ModelCardCreatorSpec
@@ -82,9 +84,6 @@
8284
from ai.backend.manager.services.model_card.actions.available_presets import (
8385
AvailablePresetsAction,
8486
)
85-
from ai.backend.manager.services.model_card.actions.batch_load_by_vfolder_ids import (
86-
BatchLoadModelCardsByVFolderIdsAction,
87-
)
8887
from ai.backend.manager.services.model_card.actions.create import CreateModelCardAction
8988
from ai.backend.manager.services.model_card.actions.delete import DeleteModelCardAction
9089
from ai.backend.manager.services.model_card.actions.scan import ScanProjectModelCardsAction
@@ -229,10 +228,23 @@ async def batch_load_by_vfolder_ids(
229228
"""
230229
if not vfolder_ids:
231230
return []
232-
action_result = await self._processors.model_card.batch_load_model_cards_by_vfolder_ids.wait_for_complete(
233-
BatchLoadModelCardsByVFolderIdsAction(vfolder_ids=list(vfolder_ids))
231+
querier = BatchQuerier(
232+
pagination=NoPagination(),
233+
conditions=[ModelCardConditions.by_vfolder_ids(list(vfolder_ids))],
234+
orders=[
235+
ModelCardOrders.created_at(ascending=False),
236+
ModelCardOrders.id(ascending=False),
237+
],
234238
)
235-
return [[self._data_to_node(item) for item in cards] for cards in action_result.data]
239+
result = await self._processors.model_card.search.wait_for_complete(
240+
SearchModelCardsAction(querier=querier)
241+
)
242+
cards_by_vfolder: dict[VFolderUUID, list[ModelCardNode]] = {}
243+
for data in result.items:
244+
cards_by_vfolder.setdefault(VFolderUUID(data.vfolder_id), []).append(
245+
self._data_to_node(data)
246+
)
247+
return [cards_by_vfolder.get(vfolder_id, []) for vfolder_id in vfolder_ids]
236248

237249
async def get(self, card_id: UUID) -> ModelCardNode:
238250
conditions: list[QueryCondition] = [lambda: ModelCardRow.id == card_id]

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
from collections.abc import Collection
56
from uuid import UUID
67

78
import sqlalchemy as sa
@@ -11,6 +12,7 @@
1112
UUIDEqualMatchSpec,
1213
UUIDInMatchSpec,
1314
)
15+
from ai.backend.common.identifier.vfolder import VFolderUUID
1416
from ai.backend.manager.models.condition_utils import (
1517
make_nested_string_in_factory,
1618
make_string_in_factory,
@@ -23,6 +25,13 @@
2325

2426

2527
class ModelCardConditions:
28+
@staticmethod
29+
def by_vfolder_ids(vfolder_ids: Collection[VFolderUUID]) -> QueryCondition:
30+
def inner() -> sa.sql.expression.ColumnElement[bool]:
31+
return ModelCardRow.vfolder.in_(vfolder_ids)
32+
33+
return inner
34+
2635
@staticmethod
2736
def by_domain(domain_name: str) -> QueryCondition:
2837
def inner() -> sa.sql.expression.ColumnElement[bool]:

src/ai/backend/manager/repositories/model_card/db_source/db_source.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
SearchDeploymentRevisionPresetsInput,
2020
)
2121
from ai.backend.common.dto.manager.v2.model_card.request import DeleteModelCardOptions
22-
from ai.backend.common.identifier.vfolder import VFolderUUID
2322
from ai.backend.common.types import VFolderID, VFolderUsageMode
2423
from ai.backend.logging.utils import BraceStyleAdapter
2524
from ai.backend.manager.data.group.types import ProjectType
@@ -90,35 +89,6 @@ async def get_by_id(self, card_id: UUID) -> ModelCardData:
9089
raise ModelCardNotFound()
9190
return row.to_data()
9291

93-
async def batch_load_by_vfolder_ids(
94-
self,
95-
vfolder_ids: Sequence[VFolderUUID],
96-
) -> list[list[ModelCardData]]:
97-
"""Group all model cards backed by each requested vfolder.
98-
99-
Returns one inner list per input vfolder (same order); each inner list
100-
is sorted by ``created_at`` descending so the most recent card appears
101-
first. The (name, domain, project) unique constraint does not extend
102-
to ``vfolder``, so a vfolder may legitimately back multiple cards.
103-
"""
104-
if not vfolder_ids:
105-
return []
106-
async with self._db.begin_readonly_session() as session:
107-
# ``id`` tiebreaks cards that share a ``created_at`` (e.g. siblings
108-
# written by ``bulk_upsert_scan`` in one statement) so the per-vfolder
109-
# order is stable across requests, matching the tiebreaker used by
110-
# ``_model_card_pagination_spec``.
111-
stmt = (
112-
sa.select(ModelCardRow)
113-
.where(ModelCardRow.vfolder.in_(list(vfolder_ids)))
114-
.order_by(ModelCardRow.created_at.desc(), ModelCardRow.id.desc())
115-
)
116-
rows = (await session.execute(stmt)).scalars().all()
117-
cards_by_vfolder: dict[VFolderUUID, list[ModelCardData]] = {}
118-
for row in rows:
119-
cards_by_vfolder.setdefault(row.vfolder, []).append(row.to_data())
120-
return [cards_by_vfolder.get(vfolder_id, []) for vfolder_id in vfolder_ids]
121-
12292
async def update(self, updater: Updater[ModelCardRow]) -> ModelCardData:
12393
async with self._db.begin_session() as session:
12494
result = await execute_updater(session, updater)

src/ai/backend/manager/repositories/model_card/repository.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
SearchDeploymentRevisionPresetsInput,
99
)
1010
from ai.backend.common.dto.manager.v2.model_card.request import DeleteModelCardOptions
11-
from ai.backend.common.identifier.vfolder import VFolderUUID
1211
from ai.backend.logging import BraceStyleAdapter
1312
from ai.backend.manager.data.model_card.types import ModelCardData, VFolderScanData
1413
from ai.backend.manager.models.model_card.row import ModelCardRow
@@ -40,12 +39,6 @@ async def create(self, creator: RBACEntityCreator[ModelCardRow]) -> ModelCardDat
4039
async def get_by_id(self, card_id: UUID) -> ModelCardData:
4140
return await self._db_source.get_by_id(card_id)
4241

43-
async def batch_load_by_vfolder_ids(
44-
self,
45-
vfolder_ids: Sequence[VFolderUUID],
46-
) -> list[list[ModelCardData]]:
47-
return await self._db_source.batch_load_by_vfolder_ids(vfolder_ids)
48-
4942
async def update(self, updater: Updater[ModelCardRow]) -> ModelCardData:
5043
return await self._db_source.update(updater)
5144

src/ai/backend/manager/services/model_card/actions/batch_load_by_vfolder_ids.py

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/ai/backend/manager/services/model_card/processors.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
AvailablePresetsAction,
1010
AvailablePresetsActionResult,
1111
)
12-
from ai.backend.manager.services.model_card.actions.batch_load_by_vfolder_ids import (
13-
BatchLoadModelCardsByVFolderIdsAction,
14-
BatchLoadModelCardsByVFolderIdsActionResult,
15-
)
1612
from ai.backend.manager.services.model_card.actions.create import (
1713
CreateModelCardAction,
1814
CreateModelCardActionResult,
@@ -50,10 +46,6 @@ class ModelCardProcessors(AbstractProcessorPackage):
5046
]
5147
scan: ActionProcessor[ScanProjectModelCardsAction, ScanProjectModelCardsActionResult]
5248
available_presets: ActionProcessor[AvailablePresetsAction, AvailablePresetsActionResult]
53-
batch_load_model_cards_by_vfolder_ids: ActionProcessor[
54-
BatchLoadModelCardsByVFolderIdsAction,
55-
BatchLoadModelCardsByVFolderIdsActionResult,
56-
]
5749

5850
def __init__(
5951
self,
@@ -74,9 +66,6 @@ def __init__(
7466
)
7567
self.scan = ActionProcessor(service.scan, action_monitors)
7668
self.available_presets = ActionProcessor(service.available_presets, action_monitors)
77-
self.batch_load_model_cards_by_vfolder_ids = ActionProcessor(
78-
service.batch_load_by_vfolder_ids, action_monitors
79-
)
8069

8170
@override
8271
def supported_actions(self) -> list[ActionSpec]:
@@ -88,5 +77,4 @@ def supported_actions(self) -> list[ActionSpec]:
8877
SearchModelCardsInProjectAction.spec(),
8978
ScanProjectModelCardsAction.spec(),
9079
AvailablePresetsAction.spec(),
91-
BatchLoadModelCardsByVFolderIdsAction.spec(),
9280
]

src/ai/backend/manager/services/model_card/service.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
AvailablePresetsAction,
1717
AvailablePresetsActionResult,
1818
)
19-
from ai.backend.manager.services.model_card.actions.batch_load_by_vfolder_ids import (
20-
BatchLoadModelCardsByVFolderIdsAction,
21-
BatchLoadModelCardsByVFolderIdsActionResult,
22-
)
2319
from ai.backend.manager.services.model_card.actions.create import (
2420
CreateModelCardAction,
2521
CreateModelCardActionResult,
@@ -77,12 +73,6 @@ async def delete(self, action: DeleteModelCardAction) -> DeleteModelCardActionRe
7773
data = await self._repository.delete(action.id, action.options)
7874
return DeleteModelCardActionResult(model_card=data)
7975

80-
async def batch_load_by_vfolder_ids(
81-
self, action: BatchLoadModelCardsByVFolderIdsAction
82-
) -> BatchLoadModelCardsByVFolderIdsActionResult:
83-
data = await self._repository.batch_load_by_vfolder_ids(action.vfolder_ids)
84-
return BatchLoadModelCardsByVFolderIdsActionResult(data=data)
85-
8676
async def search(self, action: SearchModelCardsAction) -> SearchModelCardsActionResult:
8777
result = await self._repository.search(action.querier)
8878
return SearchModelCardsActionResult(

0 commit comments

Comments
 (0)