Skip to content

Commit 441e848

Browse files
committed
fix(build): emit legacy vnd.cnai.* identifiers for registry compat
PR #415 switched modctl from dragonflyoss/model-spec to modelpack/ model-spec, which moved every wire-format identifier from the vnd.cnai.* / org.cnai.* family to vnd.cncf.* / org.cncf.*. Registries whose MODEL artifact processor is still keyed on the legacy identifiers cannot dispatch the new manifests and return "processor for artifact MODEL not found" on /additions/* endpoints. Centralize the legacy identifiers in pkg/backend/build/legacy.go and point every build-path write site at them: manifest artifactType, config blob mediaType, the eight layer mediaTypes, layer annotations (filepath, file metadata), the manifest-level modctl modelfile annotation, plus two raw-weight cache-key compares in computeDigestAndSize so the digest cache stays consistent with the emitted strings. The Go-level modelspec package and the read/inspect paths (inspect.go, pull.go, fetch.go, attach.go, codec/raw.go) are untouched, so artifacts produced by either identifier family remain readable. Delete pkg/backend/build/legacy.go and revert the call sites to modelspec.* once downstream registries register processors for the modelpack identifiers. Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
1 parent 17c7a96 commit 441e848

7 files changed

Lines changed: 71 additions & 26 deletions

File tree

pkg/backend/build.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"path/filepath"
2525

2626
retry "github.com/avast/retry-go/v4"
27-
modelspec "github.com/modelpack/model-spec/specs-go/v1"
2827
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2928
"github.com/sirupsen/logrus"
3029

@@ -40,7 +39,9 @@ import (
4039

4140
const (
4241
// annotationModelfile is the annotation key for the Modelfile.
43-
annotationModelfile = "org.cncf.modctl.modelfile"
42+
// Uses the legacy "org.cnai.*" prefix to remain compatible with registry
43+
// processors keyed on the pre-CNCF identifiers; see pkg/backend/build/legacy.go.
44+
annotationModelfile = "org.cnai.modctl.modelfile"
4445
)
4546

4647
// Build builds the user materials into the model artifact which follows the Model Spec.
@@ -166,33 +167,33 @@ func (b *backend) getProcessors(modelfile modelfile.Modelfile, cfg *config.Build
166167
processors := []processor.Processor{}
167168

168169
if configs := modelfile.GetConfigs(); len(configs) > 0 {
169-
mediaType := modelspec.MediaTypeModelWeightConfig
170+
mediaType := build.LegacyMediaTypeModelWeightConfig
170171
if cfg.Raw {
171-
mediaType = modelspec.MediaTypeModelWeightConfigRaw
172+
mediaType = build.LegacyMediaTypeModelWeightConfigRaw
172173
}
173174
processors = append(processors, processor.NewModelConfigProcessor(b.store, mediaType, configs, ""))
174175
}
175176

176177
if models := modelfile.GetModels(); len(models) > 0 {
177-
mediaType := modelspec.MediaTypeModelWeight
178+
mediaType := build.LegacyMediaTypeModelWeight
178179
if cfg.Raw {
179-
mediaType = modelspec.MediaTypeModelWeightRaw
180+
mediaType = build.LegacyMediaTypeModelWeightRaw
180181
}
181182
processors = append(processors, processor.NewModelProcessor(b.store, mediaType, models, ""))
182183
}
183184

184185
if codes := modelfile.GetCodes(); len(codes) > 0 {
185-
mediaType := modelspec.MediaTypeModelCode
186+
mediaType := build.LegacyMediaTypeModelCode
186187
if cfg.Raw {
187-
mediaType = modelspec.MediaTypeModelCodeRaw
188+
mediaType = build.LegacyMediaTypeModelCodeRaw
188189
}
189190
processors = append(processors, processor.NewCodeProcessor(b.store, mediaType, codes, ""))
190191
}
191192

192193
if docs := modelfile.GetDocs(); len(docs) > 0 {
193-
mediaType := modelspec.MediaTypeModelDoc
194+
mediaType := build.LegacyMediaTypeModelDoc
194195
if cfg.Raw {
195-
mediaType = modelspec.MediaTypeModelDocRaw
196+
mediaType = build.LegacyMediaTypeModelDocRaw
196197
}
197198
processors = append(processors, processor.NewDocProcessor(b.store, mediaType, docs, ""))
198199
}

pkg/backend/build/builder.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func (ab *abstractBuilder) BuildConfig(ctx context.Context, config modelspec.Mod
218218
}
219219

220220
digest := fmt.Sprintf("sha256:%x", sha256.Sum256(configJSON))
221-
return ab.strategy.OutputConfig(ctx, modelspec.MediaTypeModelConfig, digest, int64(len(configJSON)), bytes.NewReader(configJSON), hooks)
221+
return ab.strategy.OutputConfig(ctx, LegacyMediaTypeModelConfig, digest, int64(len(configJSON)), bytes.NewReader(configJSON), hooks)
222222
}
223223

224224
func (ab *abstractBuilder) BuildManifest(ctx context.Context, layers []ocispec.Descriptor, config ocispec.Descriptor, annotations map[string]string, hooks hooks.Hooks) (ocispec.Descriptor, error) {
@@ -227,7 +227,7 @@ func (ab *abstractBuilder) BuildManifest(ctx context.Context, layers []ocispec.D
227227
SchemaVersion: 2,
228228
},
229229
Annotations: annotations,
230-
ArtifactType: modelspec.ArtifactTypeModelManifest,
230+
ArtifactType: LegacyArtifactTypeModelManifest,
231231
Config: ocispec.Descriptor{
232232
MediaType: config.MediaType,
233233
Digest: config.Digest,
@@ -249,7 +249,7 @@ func (ab *abstractBuilder) BuildManifest(ctx context.Context, layers []ocispec.D
249249
// computeDigestAndSize computes the digest and size for the encoded content, using cache if available.
250250
func (ab *abstractBuilder) computeDigestAndSize(ctx context.Context, mediaType, path, workDirPath string, info os.FileInfo, reader io.Reader, codec pkgcodec.Codec) (io.Reader, string, int64, error) {
251251
// Try to retrieve valid digest from cache for raw model weights.
252-
if mediaType == modelspec.MediaTypeModelWeightRaw {
252+
if mediaType == LegacyMediaTypeModelWeightRaw {
253253
if digest, size, ok := ab.retrieveCache(ctx, path, info); ok {
254254
return reader, digest, size, nil
255255
}
@@ -273,7 +273,7 @@ func (ab *abstractBuilder) computeDigestAndSize(ctx context.Context, mediaType,
273273
}
274274

275275
// Update cache.
276-
if mediaType == modelspec.MediaTypeModelWeightRaw {
276+
if mediaType == LegacyMediaTypeModelWeightRaw {
277277
if err := ab.updateCache(ctx, path, info.ModTime(), size, digest); err != nil {
278278
logrus.Warnf("builder: failed to update cache for file %s: %s", path, err)
279279
}
@@ -405,7 +405,7 @@ func addFileMetadata(desc *ocispec.Descriptor, path, relPath string) error {
405405
if desc.Annotations == nil {
406406
desc.Annotations = make(map[string]string)
407407
}
408-
desc.Annotations[modelspec.AnnotationFileMetadata] = string(metadataStr)
408+
desc.Annotations[LegacyAnnotationFileMetadata] = string(metadataStr)
409409
return nil
410410
}
411411

pkg/backend/build/builder_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"testing"
3030
"time"
3131

32-
modelspec "github.com/modelpack/model-spec/specs-go/v1"
3332
godigest "github.com/opencontainers/go-digest"
3433
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3534
"github.com/stretchr/testify/assert"
@@ -148,7 +147,7 @@ func (s *BuilderTestSuite) TestBuildLayer() {
148147
func (s *BuilderTestSuite) TestBuildConfig() {
149148
s.Run("successful build config", func() {
150149
expectedDesc := ocispec.Descriptor{
151-
MediaType: modelspec.MediaTypeModelConfig,
150+
MediaType: LegacyMediaTypeModelConfig,
152151
Digest: "sha256:test",
153152
Size: 100,
154153
}
@@ -166,7 +165,7 @@ func (s *BuilderTestSuite) TestBuildConfig() {
166165
config, err := BuildModelConfig(modelConfig, []ocispec.Descriptor{})
167166
s.NoError(err)
168167

169-
s.mockOutputStrategy.On("OutputConfig", mock.Anything, modelspec.MediaTypeModelConfig, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
168+
s.mockOutputStrategy.On("OutputConfig", mock.Anything, LegacyMediaTypeModelConfig, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
170169
Return(expectedDesc, nil).Once()
171170

172171
desc, err := s.builder.BuildConfig(context.Background(), config, hooks.NewHooks())
@@ -190,7 +189,7 @@ func (s *BuilderTestSuite) TestBuildConfig() {
190189
config, err := BuildModelConfig(modelConfig, []ocispec.Descriptor{})
191190
s.NoError(err)
192191

193-
s.mockOutputStrategy.On("OutputConfig", mock.Anything, modelspec.MediaTypeModelConfig, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
192+
s.mockOutputStrategy.On("OutputConfig", mock.Anything, LegacyMediaTypeModelConfig, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
194193
Return(ocispec.Descriptor{}, errors.New("output error")).Once()
195194

196195
_, err = s.builder.BuildConfig(context.Background(), config, hooks.NewHooks())
@@ -209,7 +208,7 @@ func (s *BuilderTestSuite) TestBuildManifest() {
209208
},
210209
}
211210
config := ocispec.Descriptor{
212-
MediaType: modelspec.MediaTypeModelConfig,
211+
MediaType: LegacyMediaTypeModelConfig,
213212
Digest: "sha256:config",
214213
Size: 50,
215214
}

pkg/backend/build/legacy.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2026 The ModelPack Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package build
18+
19+
// Transitional wire-format identifiers (vnd.cnai.* / org.cnai.*).
20+
//
21+
// The source-of-truth model-spec moved from github.com/dragonflyoss/model-spec
22+
// (vnd.cnai.* / org.cnai.*) to github.com/modelpack/model-spec (vnd.cncf.* /
23+
// org.cncf.*) when the project graduated into the CNCF sandbox. Registries
24+
// that registered their MODEL artifact processor against the legacy
25+
// identifiers cannot dispatch vnd.cncf.* manifests and return
26+
// "processor for artifact MODEL not found" on /additions endpoints.
27+
//
28+
// These constants pin the wire-format strings emitted by modctl to the
29+
// legacy values for compatibility with such registries. Once downstream
30+
// registries register processors for the modelpack identifiers, delete
31+
// this file and switch the build-path call sites back to modelspec.*.
32+
const (
33+
LegacyArtifactTypeModelManifest = "application/vnd.cnai.model.manifest.v1+json"
34+
35+
LegacyMediaTypeModelConfig = "application/vnd.cnai.model.config.v1+json"
36+
37+
LegacyMediaTypeModelWeight = "application/vnd.cnai.model.weight.v1.tar"
38+
LegacyMediaTypeModelWeightRaw = "application/vnd.cnai.model.weight.v1.raw"
39+
LegacyMediaTypeModelWeightConfig = "application/vnd.cnai.model.weight.config.v1.tar"
40+
LegacyMediaTypeModelWeightConfigRaw = "application/vnd.cnai.model.weight.config.v1.raw"
41+
LegacyMediaTypeModelCode = "application/vnd.cnai.model.code.v1.tar"
42+
LegacyMediaTypeModelCodeRaw = "application/vnd.cnai.model.code.v1.raw"
43+
LegacyMediaTypeModelDoc = "application/vnd.cnai.model.doc.v1.tar"
44+
LegacyMediaTypeModelDocRaw = "application/vnd.cnai.model.doc.v1.raw"
45+
46+
LegacyAnnotationFilepath = "org.cnai.model.filepath"
47+
LegacyAnnotationFileMetadata = "org.cnai.model.file.metadata+json"
48+
)

pkg/backend/build/local.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"fmt"
2222
"io"
2323

24-
modelspec "github.com/modelpack/model-spec/specs-go/v1"
2524
godigest "github.com/opencontainers/go-digest"
2625
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2726

@@ -63,7 +62,7 @@ func (lo *localOutput) OutputLayer(ctx context.Context, mediaType, relPath, dest
6362
Digest: godigest.Digest(digest),
6463
Size: size,
6564
Annotations: map[string]string{
66-
modelspec.AnnotationFilepath: destPath,
65+
LegacyAnnotationFilepath: destPath,
6766
},
6867
}
6968

pkg/backend/build/local_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"strings"
2424
"testing"
2525

26-
modelspec "github.com/modelpack/model-spec/specs-go/v1"
2726
godigest "github.com/opencontainers/go-digest"
2827
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2928
"github.com/stretchr/testify/mock"
@@ -78,7 +77,7 @@ func (s *LocalOutputTestSuite) TestOutputLayer() {
7877
s.Equal("test/mediatype", desc.MediaType)
7978
s.Equal(godigest.Digest(expectedDigest), desc.Digest)
8079
s.Equal(expectedSize, desc.Size)
81-
s.Equal("test-file.txt", desc.Annotations[modelspec.AnnotationFilepath])
80+
s.Equal("test-file.txt", desc.Annotations[LegacyAnnotationFilepath])
8281
s.mockStorage.AssertExpectations(s.T())
8382
})
8483

pkg/backend/build/remote.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"fmt"
2222
"io"
2323

24-
modelspec "github.com/modelpack/model-spec/specs-go/v1"
2524
godigest "github.com/opencontainers/go-digest"
2625
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2726

@@ -61,7 +60,7 @@ func (ro *remoteOutput) OutputLayer(ctx context.Context, mediaType, relPath, des
6160
Digest: godigest.Digest(digest),
6261
Size: size,
6362
Annotations: map[string]string{
64-
modelspec.AnnotationFilepath: destPath,
63+
LegacyAnnotationFilepath: destPath,
6564
},
6665
}
6766

0 commit comments

Comments
 (0)