feat(BA-5947): expose model_cards connection on VFolderGQL#11480
Open
feat(BA-5947): expose model_cards connection on VFolderGQL#11480
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a reverse GraphQL relationship from VFolderGQL to model cards, so vfolder queries can surface the ModelCardV2Connection for cards backed by that folder. This extends the existing model-card/vfolder linkage through the repository, service, adapter, DataLoader, and GraphQL layers.
Changes:
- Added
batch_load_by_vfolder_idsplumbing across model-card repository/service/action/adapter layers. - Added
model_cardsonVFolderGQL, backed by a newmodel_cards_by_vfolder_loader. - Added repository-level tests for grouping model cards by vfolder and preserving input order.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tests/unit/manager/repositories/model_card/test_batch_load_by_vfolder_ids.py |
Adds repository tests for vfolder-grouped model-card batch loading. |
src/ai/backend/manager/services/model_card/service.py |
Adds service method delegating batch loads by vfolder IDs. |
src/ai/backend/manager/services/model_card/processors.py |
Registers the new model-card batch-load action processor. |
src/ai/backend/manager/services/model_card/actions/batch_load_by_vfolder_ids.py |
Defines the new action/result pair for vfolder-based model-card loading. |
src/ai/backend/manager/repositories/model_card/repository.py |
Exposes repository batch loading by vfolder IDs. |
src/ai/backend/manager/repositories/model_card/db_source/db_source.py |
Implements the DB query that groups model cards by backing vfolder. |
src/ai/backend/manager/api/gql/vfolder_v2/types/node.py |
Adds the new model_cards GraphQL field on VFolderGQL. |
src/ai/backend/manager/api/gql/data_loader/data_loaders.py |
Adds a DataLoader for batched model-card lookup by vfolder ID. |
src/ai/backend/manager/api/adapters/model_card/adapter.py |
Adds adapter support for the new batched vfolder lookup flow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
jopemachine
reviewed
May 5, 2026
fregataa
added a commit
that referenced
this pull request
May 6, 2026
55d6448 to
d58b1e5
Compare
fregataa
added a commit
that referenced
this pull request
May 6, 2026
d58b1e5 to
bf19575
Compare
Mirror ModelCardV2.vfolder with a reverse-direction model_cards field on VFolderGQL, returning a ModelCardV2Connection. A vfolder may back multiple model cards (the (name, domain, project) unique constraint does not extend to vfolder), so the field is plural. Resolved via a new DataLoader keyed by VFolderUUID to avoid N+1 fetches. The full layered wiring was added: ModelCardDBSource.batch_load_by_vfolder_ids, ModelCardRepository, BatchLoadModelCardsByVFolderIdsAction/Result, ModelCardService.batch_load_by_vfolder_ids, the processor, and the adapter method. No RBAC filtering happens at this loader -- visibility is gated by the parent vfolder, matching the existing forward-direction resolver. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: octodog <mu001@lablup.com>
Add ``ModelCardRow.id`` as a deterministic secondary key on the batch_load_by_vfolder_ids query so siblings sharing a ``created_at`` (possible when ``bulk_upsert_scan`` writes them in one statement) keep a stable per-vfolder order across requests, matching the tiebreaker on the existing model-card pagination spec. Also clarify the rationale for the test's brief ``asyncio.sleep`` so future readers don't mistake the prior comment about second-precision timestamps for fact -- Postgres ``now()`` is microsecond-precise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… deterministic test - Trim the GQL field description down to the one-line summary; the rest belonged in the implementation rather than the public schema. - Rename the ``ModelCardProcessors`` attribute to ``batch_load_model_cards_by_vfolder_ids`` to match the entity-prefixed convention used by the vfolder processor. - Drop the ``asyncio.sleep`` in the repository test and pin ``created_at`` explicitly so the most-recent-first assertion is deterministic without relying on wall-clock timing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: octodog <mu001@lablup.com>
…ction 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>
GraphQL propagates resolver errors up to the nearest nullable ancestor, so a non-null ``model_cards`` field would null the parent ``VFolder`` on any failure (DB hiccup, RBAC denial, etc.). When the resolver is called from a vfolder list query, that propagation can wipe out an entire ``VFolder`` edge for a side-channel lookup that the caller did not specifically request. Declaring ``ModelCardV2Connection | None`` contains the failure to this field; ``errors`` still carries the diagnostic and the surrounding ``VFolder`` data survives. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: octodog <mu001@lablup.com>
…r-scoped search Replaces the DataLoader-backed batch lookup with a per-call adapter search method `search_in_vfolder(scope, input)`, mirroring the deployment pattern (`search_revisions(scope=RevisionSearchScope, …)`). The nested resolver now accepts the full Relay argument set (filter, order_by, before/after, first/last, limit/offset) and delegates scoping to a typed `VFolderModelCardSearchScope`, leaving the public `ModelCardFilter` DTO untouched.
aee1824 to
0cb050e
Compare
0cb050e to
3ee4a31
Compare
Adds a thin façade so the VFolder field resolver reads naturally as `adapters.vfolder.search_model_cards(scope, input)` instead of reaching into the model_card adapter. The implementation still lives in `ModelCardAdapter.search_in_vfolder`; VFolderAdapter delegates via a constructor-injected reference, mirroring how the deployment adapter exposes `search_revisions` from the parent entity.
3ee4a31 to
55ffcbe
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
modelCardsconnection onVFolderGQL(a vfolder may back multiple cards since the model card unique constraint is on(name, domain, project), not onvfolder).filter,orderBy,before/after,first/last,limit/offset) and is nullable per BA-5956.VFolderModelCardSearchScope; the resolver callsadapters.model_card.search_by_vfolder(scope, input)(sibling to the existingadmin_search/project_search).Test plan
pants check/pants lintcleanmodelCardsfield nullable, no leakage ofvfolder_idinto the publicModelCardV2FilterResolves BA-5947