feat(BA-5982): expose revision_number, separate Spec/Data on DeploymentInfo read path#11529
Draft
jopemachine wants to merge 10 commits intomainfrom
Draft
feat(BA-5982): expose revision_number, separate Spec/Data on DeploymentInfo read path#11529jopemachine wants to merge 10 commits intomainfrom
jopemachine wants to merge 10 commits intomainfrom
Conversation
The `deployment_revisions.revision_number` column has been settable by clients via filter and sort fields on `ModelRevisionFilter` and `ModelRevisionOrderField`, but the value itself was dropped at every layer above the row. Plumb it end-to-end so callers can render "Revision #N" labels and order revisions client-side without an extra round-trip. * `ModelRevisionData` (data layer) gains a required `revision_number` and is populated from `DeploymentRevisionRow.to_data()`. * `ModelRevisionSpec` gains an optional `revision_number` so the existing draft → spec construction path (which has no row yet) is unaffected, while the row → spec path (`to_model_revision_spec()`) carries the number forward. The legacy `_convert_deployment_info_to_data` bridge asserts the spec carries a number and propagates it. * `RevisionNode` DTO and the adapter `_revision_data_to_dto` mapping pass the field through to REST v2 responses. * `ModelRevision` GraphQL type exposes the field via `gql_added_field` with `NEXT_RELEASE_VERSION`. BA-5982
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR propagates deployment_revisions.revision_number through the manager read path so clients can display “Revision #N” and order revisions client-side without extra queries.
Changes:
- Add
revision_numbertoModelRevisionData(required) andModelRevisionSpec(optional) and populate fromDeploymentRevisionRow. - Expose
revision_numbervia REST v2RevisionNodeand GraphQLModelRevision(gated withgql_added_field(NEXT_RELEASE_VERSION)). - Update unit-test fixtures to include the new required field.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/manager/services/deployment/test_deployment_service.py | Update fixtures to construct ModelRevisionData/Spec with revision_number. |
| tests/unit/manager/services/deployment/test_deployment_crud_actions.py | Update fixtures to include revision_number in ModelRevisionData. |
| tests/unit/manager/api/adapters/test_deployment_adapter.py | Update adapter test fixture to include revision_number. |
| src/ai/backend/manager/services/deployment/service.py | Require revision_number when bridging DeploymentInfo → ModelRevisionData. |
| src/ai/backend/manager/models/deployment_revision/row.py | Populate revision_number in row → spec/data projections. |
| src/ai/backend/manager/data/deployment/types.py | Add revision_number field to spec/data layer types and document semantics. |
| src/ai/backend/manager/api/gql/deployment/types/revision.py | Add GraphQL revision_number field on ModelRevision. |
| src/ai/backend/manager/api/adapters/deployment/adapter.py | Map ModelRevisionData.revision_number into RevisionNode DTO. |
| src/ai/backend/common/dto/manager/v2/deployment/response.py | Add revision_number to REST v2 RevisionNode response model. |
| changes/11529.feature.md | Changelog entry for exposing revision_number over GraphQL/REST. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
252
to
260
| if rev is not None: | ||
| if rev.revision_id is None: | ||
| raise ValueError(f"ModelRevisionSpec has no revision_id for deployment {info.id}") | ||
| if rev.revision_number is None: | ||
| raise ValueError(f"ModelRevisionSpec has no revision_number for deployment {info.id}") | ||
| revision = ModelRevisionData( | ||
| id=rev.revision_id, | ||
| revision_number=rev.revision_number, | ||
| cluster_config=ClusterConfigData( |
Comment on lines
+882
to
+884
| # ``deployment_revisions.revision_number`` (UNIQUE per endpoint). Useful | ||
| # for surfacing "Revision #N" labels and for client-side ordering | ||
| # without having to re-query the server. |
Co-authored-by: octodog <mu001@lablup.com>
HyeockJinKim
reviewed
May 8, 2026
Comment on lines
+433
to
+435
| class ModelRevisionSpec(ConfiguredModel): | ||
| revision_id: DeploymentRevisionID | None = None | ||
| revision_number: int | None = None |
Collaborator
There was a problem hiding this comment.
I don't think there's any reason to go in there, is there?
…delRevisionData on read `ModelRevisionSpec` is by convention a write/create-time type (matches `KernelSpec`/`SessionSpec` docstrings — "Full spec to create one Row"), yet `DeploymentInfo.model_revisions` carried it on the read path, forcing `service._convert_deployment_info_to_data` to unpack the spec back into a `ModelRevisionData` field-by-field, and bleeding the write-shaped `revision_number` field onto the spec. - Drop `ModelRevisionSpec.revision_number`; `Row.to_model_revision_spec` no longer fills it. `revision_number` now lives only on `ModelRevisionData` where it belongs. - Switch `DeploymentInfo.model_revisions` to `list[ModelRevisionData]` and rename `resolve_revision_spec` -> `resolve_revision_data`. - `EndpointRow.to_deployment_info` populates revisions via `rev_row.to_data()` instead of `to_model_revision_spec()`. - Read sites collapse: the 35-line spec-unpack in `_convert_deployment_info_to_data` becomes a 6-line lookup; `api/rest/service/handler._resolve_target_revision_data` reads the same fields via `ModelRevisionData` (`model_mount_config.vfolder_id`, `extra_vfolder_mounts`, ...). - Sokovan paths that legitimately need the spec shape — session validation in `deployment_controller.update_deployment` and route draft assembly in `route/executor` — fetch it explicitly from the repository. Add `DeploymentRepository.get_revision_spec(revision_id)` alongside the existing `get_current_revision_spec(endpoint_id)`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e-time fields on ModelRevisionData
Spec-suffix types are write-only by convention (matches `KernelSpec` /
`SessionSpec` docstrings). Reading a `ModelRevisionSpec` from DB
violated that, but was needed because `ModelRevisionData` lacked the
fields downstream write paths consumed.
- Add `startup_command`, `bootstrap_script`, `callback_url`,
`extra_vfolder_mounts`, `preset_values` to `ModelRevisionData`;
populate them in `Row.to_data()`.
- Remove `Row.to_model_revision_spec()`,
`DeploymentRepository.get_current_revision_spec` /
`get_revision_spec`, and the matching `db_source` methods. Spec is
no longer loadable from DB.
- Rewire write-side callers to start from `ModelRevisionData`:
- `service._build_creator_from_revision_data` reconstructs
`ResourceSpec` / `ExecutionSpec` from the data fields.
- `SessionValidationSpec.from_revision` now takes
`ModelRevisionData`.
- `DeploymentSessionDraftBuilder.build` now takes
`ModelRevisionData`; route executor and controller fetch via
`repository.get_revision` / `get_current_revision`.
- `RevisionDraft.to_model_revision_spec` (the legitimate
draft -> spec write transform) is unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…CountData `ReplicaSpec` was leaking onto the read path through `DeploymentInfo.replica_spec`, mirroring the `ModelRevisionSpec` violation. Add a `ReplicaCountData` dataclass that carries the same two counts (and `target_replica_count` derivation) and is the read-side type. `ReplicaSpec` stays on Creator / Draft / Updater for write flows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
revision_number on ModelRevision read path
jopemachine
commented
May 8, 2026
Comment on lines
-900
to
-903
| override. Fields that do not live on ``ModelRevisionData`` | ||
| (``startup_command``, ``bootstrap_script``, ``callback_url``) remain | ||
| ``None`` — the reader pipeline resolves them from preset / | ||
| ``deployment-config.yaml`` / ``model-definition.yaml`` / user override. |
Member
Author
There was a problem hiding this comment.
don't delete existing comment
…tSessionSpec `DeploymentNetworkSpec` was the last `Spec` type still leaking onto the read path via `DeploymentInfo.network` and `ModelDeploymentData.network_access`. Add `DeploymentNetworkData` for the read side; `DeploymentNetworkSpec` stays on Creator / Draft. Also drop `DeploymentSessionSpec` — no callers anywhere. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Two related changes on the deployment read path:
1. Expose
revision_numberend-to-enddeployment_revisions.revision_numberwas settable as aModelRevisionFilter/ModelRevisionOrderFieldkey but the value was dropped at every layer above the row, so clients could filter/order by it without ever reading it back. It now flows throughModelRevisionData->RevisionNodeDTO -> the GraphQLModelRevisiontype (added viagql_added_field(NEXT_RELEASE_VERSION)).2. Stop using
Spectypes on the read pathPer project convention (
KernelSpec/SessionSpecdocstrings — "Full spec to create one Row"),XXXSpectypes are write-only. ButModelRevisionSpec,ReplicaSpec, andDeploymentNetworkSpecwere all being loaded from DB and passed around the read path viaDeploymentInfo, forcing the write-shapedrevision_numberfield ontoModelRevisionSpecand bleeding write types into output DTOs.Audit + fixes per type:
ModelRevisionSpecRow.to_model_revision_spec(); onDeploymentInfo.model_revisionsDeploymentInfo.model_revisions: list[ModelRevisionData]ReplicaSpecDeploymentInfo.replica_specReplicaCountData(carriesreplica_count/desired_replica_count/target_replica_count) onDeploymentInfo.replica_countsDeploymentNetworkSpecDeploymentInfo.networkandModelDeploymentData.network_accessDeploymentNetworkDataon both fieldsDeploymentSessionSpecResourceSpec/ExecutionSpec/PresetValueSpecModelRevisionSpec/ Creator / Draft)Concrete restructuring to support the rule:
ModelRevisionSpec.revision_number.revision_numberlives only onModelRevisionDatanow.startup_command,bootstrap_script,callback_url,extra_vfolder_mounts,preset_values) ontoModelRevisionDataso write paths can rebuild specs from data without round-tripping through the DB.Row.to_model_revision_spec(),DeploymentRepository.get_current_revision_spec/get_revision_specand the matchingdb_sourcemethods. Spec is no longer loadable from DB._build_creator_from_revision_data,SessionValidationSpec.from_revision, andDeploymentSessionDraftBuilder.buildall takeModelRevisionDataand reconstructResourceSpec/ExecutionSpeclocally.RevisionDraft.to_model_revision_spec(the legitimate draft -> spec write transform) is unchanged.EndpointRow.to_deployment_infopopulates revisions viarev_row.to_data()instead ofto_model_revision_spec().update_deploymentvalidation, route executor draft assembly) fetch viarepository.get_revision/get_current_revision— both returnModelRevisionData.Side effect:
_convert_deployment_info_to_dataNow that
info.model_revisionsis alreadyModelRevisionData, the 35-line spec-unpack inservice._convert_deployment_info_to_datacollapses to a 6-line lookup.Resolves BA-5982.
Test plan
pants test tests/unit/manager::(services, sokovan, repositories, api adapters)revisionHistoryquery —revision_numberis populated.revision_numberviaModelRevisionFilter/ModelRevisionOrderField._build_creator_from_revision_dataproduces a valid new revision.repository.get_revision).