-
Notifications
You must be signed in to change notification settings - Fork 23
feat(compat): add Docker model-spec format conversion #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add compat package for bidirectional conversion between ModelPack and Docker model-spec formats. This enables interoperability with docker/model-runner as requested in issue modelpack#151. Changes: - Add compat/docker/v0 package with Docker config types and media types - Implement FromModelPack() and ToModelPack() conversion functions - Add compat.DetectFormat() for media type detection - Add docs/compatibility.md with usage examples - Update README.md with compatibility section Closes modelpack#151 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Summary of ChangesHello @thc1006, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the interoperability of ModelPack by introducing a dedicated compatibility layer for Docker model-spec formats. It provides tools for detecting model formats and performing bidirectional conversions, thereby enabling seamless integration with systems like 'docker/model-runner' and addressing a key community request for broader ecosystem compatibility. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a compatibility layer for converting between ModelPack and Docker model-spec formats, which is a great feature for interoperability. The implementation is well-structured with new packages for detection and conversion, accompanied by thorough tests and documentation. I've identified a few areas for improvement, mainly concerning error handling, code clarity, and testing practices. My main concerns are a potential silent error when parsing timestamps and the presence of some unused code. Overall, this is a solid contribution.
| t, err := time.Parse(time.RFC3339, cfg.Descriptor.CreatedAt) | ||
| if err == nil { | ||
| m.Descriptor.CreatedAt = &t | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors from time.Parse are silently ignored. If CreatedAt has an invalid format, it will be skipped without any indication, which could lead to silent data loss or inconsistencies. It's better to propagate the error to the caller.
t, err := time.Parse(time.RFC3339, cfg.Descriptor.CreatedAt)
if err != nil {
return nil, err
}
m.Descriptor.CreatedAt = &t| func TestDetectFormat(t *testing.T) { | ||
| for i, tt := range []struct { | ||
| mediaType string | ||
| want Format | ||
| }{ | ||
| // ModelPack 格式 | ||
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | ||
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | ||
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | ||
|
|
||
| // Docker 格式 | ||
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | ||
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | ||
| {"application/vnd.docker.ai.license", FormatDocker}, | ||
|
|
||
| // 未知格式 | ||
| {"application/json", FormatUnknown}, | ||
| {"application/octet-stream", FormatUnknown}, | ||
| {"", FormatUnknown}, | ||
| } { | ||
| got := DetectFormat(tt.mediaType) | ||
| if got != tt.want { | ||
| t.Errorf("test %d: DetectFormat(%q) = %v, want %v", i, tt.mediaType, got, tt.want) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test function can be improved for better maintainability and readability:
- Use English comments: The comments are in Chinese, while the rest of the codebase is in English. It's best to maintain consistency for all developers.
- Use subtests: For table-driven tests, using
t.Runprovides clearer output when a test case fails, as it names the specific test that failed. This makes debugging easier.
Here is a suggested refactoring that addresses both points:
func TestDetectFormat(t *testing.T) {
tests := []struct {
name string
mediaType string
want Format
}{
// ModelPack formats
{"ModelPack config", "application/vnd.cncf.model.config.v1+json", FormatModelPack},
{"ModelPack manifest", "application/vnd.cncf.model.manifest.v1+json", FormatModelPack},
{"ModelPack weights", "application/vnd.cncf.model.weight.v1.raw", FormatModelPack},
// Docker formats
{"Docker config", "application/vnd.docker.ai.model.config.v0.1+json", FormatDocker},
{"Docker GGUF", "application/vnd.docker.ai.gguf.v3", FormatDocker},
{"Docker license", "application/vnd.docker.ai.license", FormatDocker},
// Unknown formats
{"Generic JSON", "application/json", FormatUnknown},
{"Octet stream", "application/octet-stream", FormatUnknown},
{"Empty media type", "", FormatUnknown},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := DetectFormat(tt.mediaType)
if got != tt.want {
t.Errorf("DetectFormat(%q) = %v, want %v", tt.mediaType, got, tt.want)
}
})
}
}| if m.Config.ParamSize != "" { | ||
| cfg.ModelConfig.GGUF = map[string]any{ | ||
| "parameter_count": formatParamSizeHuman(m.Config.ParamSize), | ||
| } | ||
| } | ||
|
|
||
| if m.Config.Architecture != "" { | ||
| if cfg.ModelConfig.GGUF == nil { | ||
| cfg.ModelConfig.GGUF = make(map[string]any) | ||
| } | ||
| cfg.ModelConfig.GGUF["architecture"] = m.Config.Architecture | ||
| } | ||
|
|
||
| if m.Config.Quantization != "" { | ||
| if cfg.ModelConfig.GGUF == nil { | ||
| cfg.ModelConfig.GGUF = make(map[string]any) | ||
| } | ||
| cfg.ModelConfig.GGUF["quantization"] = m.Config.Quantization | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for populating the GGUF map involves multiple checks for cfg.ModelConfig.GGUF == nil. This can be simplified by creating the map once, populating it, and then assigning it to cfg.ModelConfig.GGUF only if it contains any fields. This improves readability and reduces redundancy.
| if m.Config.ParamSize != "" { | |
| cfg.ModelConfig.GGUF = map[string]any{ | |
| "parameter_count": formatParamSizeHuman(m.Config.ParamSize), | |
| } | |
| } | |
| if m.Config.Architecture != "" { | |
| if cfg.ModelConfig.GGUF == nil { | |
| cfg.ModelConfig.GGUF = make(map[string]any) | |
| } | |
| cfg.ModelConfig.GGUF["architecture"] = m.Config.Architecture | |
| } | |
| if m.Config.Quantization != "" { | |
| if cfg.ModelConfig.GGUF == nil { | |
| cfg.ModelConfig.GGUF = make(map[string]any) | |
| } | |
| cfg.ModelConfig.GGUF["quantization"] = m.Config.Quantization | |
| } | |
| gguf := make(map[string]any) | |
| if m.Config.ParamSize != "" { | |
| gguf["parameter_count"] = formatParamSizeHuman(m.Config.ParamSize) | |
| } | |
| if m.Config.Architecture != "" { | |
| gguf["architecture"] = m.Config.Architecture | |
| } | |
| if m.Config.Quantization != "" { | |
| gguf["quantization"] = m.Config.Quantization | |
| } | |
| if len(gguf) > 0 { | |
| cfg.ModelConfig.GGUF = gguf | |
| } |
| // parseParamSize parses parameter size string, e.g., "8b" -> 8000000000. | ||
| func parseParamSize(s string) int64 { | ||
| s = strings.TrimSpace(strings.ToLower(s)) | ||
| if s == "" { | ||
| return 0 | ||
| } | ||
|
|
||
| var numPart string | ||
| var unitPart string | ||
| for i, c := range s { | ||
| if (c >= '0' && c <= '9') || c == '.' { | ||
| numPart = s[:i+1] | ||
| } else { | ||
| unitPart = s[i:] | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if numPart == "" { | ||
| numPart = s | ||
| } | ||
|
|
||
| num, err := strconv.ParseFloat(numPart, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
|
|
||
| var multiplier int64 = 1 | ||
| switch strings.ToLower(unitPart) { | ||
| case "t": | ||
| multiplier = 1_000_000_000_000 | ||
| case "b": | ||
| multiplier = 1_000_000_000 | ||
| case "m": | ||
| multiplier = 1_000_000 | ||
| case "k": | ||
| multiplier = 1_000 | ||
| } | ||
|
|
||
| return int64(num * float64(multiplier)) | ||
| } | ||
|
|
||
| // formatParamSize converts a number to human-readable format, e.g., 8000000000 -> "8b". | ||
| func formatParamSize(n int64) string { | ||
| if n <= 0 { | ||
| return "" | ||
| } | ||
|
|
||
| switch { | ||
| case n >= 1_000_000_000_000: | ||
| return strconv.FormatFloat(float64(n)/1_000_000_000_000, 'f', -1, 64) + "t" | ||
| case n >= 1_000_000_000: | ||
| return strconv.FormatFloat(float64(n)/1_000_000_000, 'f', -1, 64) + "b" | ||
| case n >= 1_000_000: | ||
| return strconv.FormatFloat(float64(n)/1_000_000, 'f', -1, 64) + "m" | ||
| case n >= 1_000: | ||
| return strconv.FormatFloat(float64(n)/1_000, 'f', -1, 64) + "k" | ||
| default: | ||
| return strconv.FormatInt(n, 10) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The functions parseParamSize and formatParamSize are defined and tested, but they are not used within the conversion logic of this package (FromModelPack and ToModelPack use the ...Human variants). Since they are not exported, they appear to be dead code. If they are not intended for future use, they should be removed to keep the codebase clean.
| "github.com/opencontainers/go-digest" | ||
| ) | ||
|
|
||
| func TestFromModelPack(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds bidirectional conversion support between CNCF ModelPack and Docker model-spec formats, enabling interoperability with docker/model-runner. The implementation includes format detection utilities and comprehensive conversion functions with proper test coverage.
Key Changes:
- New
compatpackage with format detection based on media type prefixes - New
compat/docker/v0package implementing Docker model-spec v0.1 type definitions and conversion logic - Comprehensive documentation in
docs/compatibility.mdwith usage examples and field mapping tables
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
compat/detect.go |
Format detection utility using media type prefix matching |
compat/detect_test.go |
Test cases for format detection covering ModelPack, Docker, and unknown formats |
compat/docker/v0/config.go |
Type definitions for Docker model-spec v0.1 including Config, ModelConfig, and File structs |
compat/docker/v0/config_test.go |
JSON serialization tests ensuring compatibility with Docker model-spec |
compat/docker/v0/convert.go |
Bidirectional conversion functions with parameter size format transformation |
compat/docker/v0/convert_test.go |
Comprehensive conversion tests including edge cases and round-trip validation |
docs/compatibility.md |
Usage guide with code examples, field mapping table, and limitations documentation |
README.md |
Added compatibility section linking to the compatibility guide |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| // ToModelPack converts a Docker format config to ModelPack format. | ||
| func ToModelPack(cfg Config) (*v1.Model, error) { |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function signature returns an error, but the function never actually returns an error - it always returns nil. Consider either removing the error return value if errors are not expected, or properly handling and returning errors for invalid inputs such as malformed timestamps in the Descriptor or invalid GGUF field types.
| func parseParamSizeHuman(s string) string { | ||
| s = strings.TrimSpace(s) | ||
| if s == "" { | ||
| return "" | ||
| } | ||
|
|
||
| parts := strings.Fields(s) | ||
| if len(parts) != 2 { | ||
| return strings.ToLower(strings.ReplaceAll(s, " ", "")) | ||
| } | ||
|
|
||
| num := parts[0] | ||
| unit := strings.ToLower(parts[1]) | ||
|
|
||
| return num + unit | ||
| } | ||
|
|
||
| // parseParamSize parses parameter size string, e.g., "8b" -> 8000000000. | ||
| func parseParamSize(s string) int64 { | ||
| s = strings.TrimSpace(strings.ToLower(s)) | ||
| if s == "" { | ||
| return 0 | ||
| } | ||
|
|
||
| var numPart string | ||
| var unitPart string | ||
| for i, c := range s { | ||
| if (c >= '0' && c <= '9') || c == '.' { | ||
| numPart = s[:i+1] | ||
| } else { | ||
| unitPart = s[i:] | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if numPart == "" { | ||
| numPart = s | ||
| } | ||
|
|
||
| num, err := strconv.ParseFloat(numPart, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
|
|
||
| var multiplier int64 = 1 | ||
| switch strings.ToLower(unitPart) { | ||
| case "t": | ||
| multiplier = 1_000_000_000_000 | ||
| case "b": | ||
| multiplier = 1_000_000_000 | ||
| case "m": | ||
| multiplier = 1_000_000 | ||
| case "k": | ||
| multiplier = 1_000 | ||
| } | ||
|
|
||
| return int64(num * float64(multiplier)) | ||
| } | ||
|
|
||
| // formatParamSize converts a number to human-readable format, e.g., 8000000000 -> "8b". | ||
| func formatParamSize(n int64) string { | ||
| if n <= 0 { | ||
| return "" | ||
| } | ||
|
|
||
| switch { | ||
| case n >= 1_000_000_000_000: | ||
| return strconv.FormatFloat(float64(n)/1_000_000_000_000, 'f', -1, 64) + "t" | ||
| case n >= 1_000_000_000: | ||
| return strconv.FormatFloat(float64(n)/1_000_000_000, 'f', -1, 64) + "b" | ||
| case n >= 1_000_000: | ||
| return strconv.FormatFloat(float64(n)/1_000_000, 'f', -1, 64) + "m" | ||
| case n >= 1_000: | ||
| return strconv.FormatFloat(float64(n)/1_000, 'f', -1, 64) + "k" | ||
| default: | ||
| return strconv.FormatInt(n, 10) | ||
| } | ||
| } |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These helper functions parseParamSize and formatParamSize are defined and tested but never used in the actual conversion code. If they are intended for future use, consider adding a comment explaining this. Otherwise, consider removing them to reduce code maintenance burden.
| func parseParamSizeHuman(s string) string { | |
| s = strings.TrimSpace(s) | |
| if s == "" { | |
| return "" | |
| } | |
| parts := strings.Fields(s) | |
| if len(parts) != 2 { | |
| return strings.ToLower(strings.ReplaceAll(s, " ", "")) | |
| } | |
| num := parts[0] | |
| unit := strings.ToLower(parts[1]) | |
| return num + unit | |
| } | |
| // parseParamSize parses parameter size string, e.g., "8b" -> 8000000000. | |
| func parseParamSize(s string) int64 { | |
| s = strings.TrimSpace(strings.ToLower(s)) | |
| if s == "" { | |
| return 0 | |
| } | |
| var numPart string | |
| var unitPart string | |
| for i, c := range s { | |
| if (c >= '0' && c <= '9') || c == '.' { | |
| numPart = s[:i+1] | |
| } else { | |
| unitPart = s[i:] | |
| break | |
| } | |
| } | |
| if numPart == "" { | |
| numPart = s | |
| } | |
| num, err := strconv.ParseFloat(numPart, 64) | |
| if err != nil { | |
| return 0 | |
| } | |
| var multiplier int64 = 1 | |
| switch strings.ToLower(unitPart) { | |
| case "t": | |
| multiplier = 1_000_000_000_000 | |
| case "b": | |
| multiplier = 1_000_000_000 | |
| case "m": | |
| multiplier = 1_000_000 | |
| case "k": | |
| multiplier = 1_000 | |
| } | |
| return int64(num * float64(multiplier)) | |
| } | |
| // formatParamSize converts a number to human-readable format, e.g., 8000000000 -> "8b". | |
| func formatParamSize(n int64) string { | |
| if n <= 0 { | |
| return "" | |
| } | |
| switch { | |
| case n >= 1_000_000_000_000: | |
| return strconv.FormatFloat(float64(n)/1_000_000_000_000, 'f', -1, 64) + "t" | |
| case n >= 1_000_000_000: | |
| return strconv.FormatFloat(float64(n)/1_000_000_000, 'f', -1, 64) + "b" | |
| case n >= 1_000_000: | |
| return strconv.FormatFloat(float64(n)/1_000_000, 'f', -1, 64) + "m" | |
| case n >= 1_000: | |
| return strconv.FormatFloat(float64(n)/1_000, 'f', -1, 64) + "k" | |
| default: | |
| return strconv.FormatInt(n, 10) | |
| } | |
| } |
| // ModelPack 格式 | ||
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | ||
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | ||
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | ||
|
|
||
| // Docker 格式 | ||
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | ||
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | ||
| {"application/vnd.docker.ai.license", FormatDocker}, | ||
|
|
||
| // 未知格式 |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments are written in Chinese instead of English. The codebase should maintain consistency with English documentation and comments for broader accessibility. Please translate these comments to English.
| // ModelPack 格式 | |
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | |
| // Docker 格式 | |
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | |
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | |
| {"application/vnd.docker.ai.license", FormatDocker}, | |
| // 未知格式 | |
| // ModelPack format | |
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | |
| // Docker format | |
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | |
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | |
| {"application/vnd.docker.ai.license", FormatDocker}, | |
| // Unknown format |
| // ModelPack 格式 | ||
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | ||
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | ||
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | ||
|
|
||
| // Docker 格式 | ||
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | ||
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | ||
| {"application/vnd.docker.ai.license", FormatDocker}, | ||
|
|
||
| // 未知格式 |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments are written in Chinese instead of English. The codebase should maintain consistency with English documentation and comments for broader accessibility. Please translate these comments to English.
| // ModelPack 格式 | |
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | |
| // Docker 格式 | |
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | |
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | |
| {"application/vnd.docker.ai.license", FormatDocker}, | |
| // 未知格式 | |
| // ModelPack format | |
| {"application/vnd.cncf.model.config.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.manifest.v1+json", FormatModelPack}, | |
| {"application/vnd.cncf.model.weight.v1.raw", FormatModelPack}, | |
| // Docker format | |
| {"application/vnd.docker.ai.model.config.v0.1+json", FormatDocker}, | |
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | |
| {"application/vnd.docker.ai.license", FormatDocker}, | |
| // Unknown format |
| {"application/vnd.docker.ai.gguf.v3", FormatDocker}, | ||
| {"application/vnd.docker.ai.license", FormatDocker}, | ||
|
|
||
| // 未知格式 |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments are written in Chinese instead of English. The codebase should maintain consistency with English documentation and comments for broader accessibility. Please translate these comments to English.
| // 未知格式 | |
| // Unknown format |
|
|
||
| // FromModelPack converts a ModelPack model to Docker format. | ||
| // Note: The Size field cannot be derived from ModelPack and will be set to "0". | ||
| func FromModelPack(m v1.Model) (*Config, error) { |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function signature returns an error, but the function never actually returns an error - it always returns nil. Consider either removing the error return value if errors are not expected, or properly handling and returning errors for invalid inputs such as timestamp parsing failures or invalid format values.
|
@thc1006 Great work! Thank you very much for your excellent work. However, these compatibility code sections are more suitable to be in the implementation layer: docker-runner. It might be difficult to merge these modifications into the modelpack/model-spec, as it serves as the specification layer. Thank you again for your contribution, and feel free to discuss this further at any time! |
|
Dear @aftersnow, Thank you so much for taking the time to review this PR and for providing such valuable feedback! 🙏 I completely understand and agree with your point — model-spec serves as the specification layer, and compatibility/conversion logic is indeed more appropriate for the implementation layer like https://github.com/docker/model-runner. Based on your suggestion and the positive response from @ericcurtin in docker/model-spec#3 (where Docker welcomed PRs with backwards compatibility), I plan to:
I've also addressed all the code review feedback (error handling, unused code removal, comment translations) in my latest commit — hopefully the implementation here can serve as a useful reference for the model-runner integration. Quick question: Would you prefer me to close this PR now, or keep it open for reference while the docker/model-runner work is in progress? Thank you again for your guidance and for initiating this important interoperability discussion! Really appreciate your work on both ModelPack and bridging the ecosystem. 😊 Best regards |
|
@thc1006 That would be great, thank you! You can keep this PR open while the docker/model-runner work is in progress. |
Summary
compatpackage for bidirectional conversion between ModelPack and Docker model-spec formatsThis PR addresses the request from @aftersnow in #151 to help docker-runner become compatible with ModelPack format.
Changes
New Package:
compat/docker/v0config.go: Docker model-spec v0.1 type definitions and media type constantsconvert.go: Bidirectional conversion functionsFromModelPack(): Convert ModelPack model to Docker formatToModelPack(): Convert Docker config to ModelPack formatNew Package:
compatdetect.go: Format detection utility (DetectFormat())Documentation
docs/compatibility.md: Usage guide with code examples and field mapping tableREADME.mdwith compatibility sectionField Mapping
descriptor.createdAtdescriptor.createdAtconfig.formatconfig.formatconfig.paramSizeconfig.gguf.parameter_countconfig.architectureconfig.gguf.architectureconfig.quantizationconfig.gguf.quantizationmodelfs.diffIdsfiles[].diffIDTest plan
make test)go build ./...)Related Issues