Skip to content

Commit 2e6fc88

Browse files
littleKitchenYuxuan Che
andauthored
feat: add 'docker model show' command (#615)
* feat: add 'docker model show' command Implements a new 'show' command that displays model information in a human-readable format. This complements the existing 'inspect' command which outputs raw JSON. The show command displays: - Model ID - Tags - Created date - Format, Architecture, Parameters, Size, Quantization - Context size (if available) - GGUF/Safetensors/Diffusers metadata (if available) Usage: docker model show MODEL [--remote] Fixes #612 * refactor: apply Gemini suggestions and generate docs - Refactor config field printing to use data-driven approach with slice of structs - Extract metadata printing into a helper function to reduce duplication - Generate documentation for the new show command * docs: fix generated docs to match upstream format Remove machine-specific options section that was added when generating docs locally. The upstream docs don't include the Docker CLI options with user-specific paths. * docs: add missing trailing newline --------- Co-authored-by: Yuxuan Che <yuxuanche@Yuxuans-Mac-mini.local>
1 parent 36c9c73 commit 2e6fc88

7 files changed

Lines changed: 238 additions & 0 deletions

File tree

cmd/cli/commands/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func NewRootCmd(cli *command.DockerCli) *cobra.Command {
105105
newLogsCmd(),
106106
newRemoveCmd(),
107107
newInspectCmd(),
108+
newShowCmd(),
108109
newComposeCmd(),
109110
newTagCmd(),
110111
newConfigureCmd(),

cmd/cli/commands/show.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/docker/model-runner/cmd/cli/commands/completion"
9+
"github.com/docker/model-runner/cmd/cli/desktop"
10+
"github.com/docker/model-runner/pkg/distribution/types"
11+
dmrm "github.com/docker/model-runner/pkg/inference/models"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
func newShowCmd() *cobra.Command {
16+
var remote bool
17+
c := &cobra.Command{
18+
Use: "show MODEL",
19+
Short: "Show information for a model",
20+
Long: "Display detailed information about a model in a human-readable format.",
21+
Args: requireExactArgs(1, "show", "MODEL"),
22+
RunE: func(cmd *cobra.Command, args []string) error {
23+
output, err := showModel(args[0], remote, desktopClient)
24+
if err != nil {
25+
return err
26+
}
27+
cmd.Print(output)
28+
return nil
29+
},
30+
ValidArgsFunction: completion.ModelNames(getDesktopClient, 1),
31+
}
32+
c.Flags().BoolVarP(&remote, "remote", "r", false, "Show info for remote models")
33+
return c
34+
}
35+
36+
func showModel(modelName string, remote bool, desktopClient *desktop.Client) (string, error) {
37+
model, err := desktopClient.Inspect(modelName, remote)
38+
if err != nil {
39+
return "", handleClientError(err, "Failed to get model "+modelName)
40+
}
41+
return formatModelInfo(model), nil
42+
}
43+
44+
func formatModelInfo(model dmrm.Model) string {
45+
var sb strings.Builder
46+
47+
// Model ID
48+
sb.WriteString(fmt.Sprintf("Model: %s\n", model.ID))
49+
50+
// Tags
51+
if len(model.Tags) > 0 {
52+
sb.WriteString(fmt.Sprintf("Tags: %s\n", strings.Join(model.Tags, ", ")))
53+
}
54+
55+
// Created date
56+
if model.Created > 0 {
57+
created := time.Unix(model.Created, 0)
58+
sb.WriteString(fmt.Sprintf("Created: %s\n", created.Format(time.RFC3339)))
59+
}
60+
61+
// Config details
62+
if model.Config != nil {
63+
sb.WriteString("\n")
64+
65+
if cfg, ok := model.Config.(*types.Config); ok {
66+
// Config fields using data-driven approach
67+
fields := []struct {
68+
label string
69+
value string
70+
}{
71+
{"Format:", string(cfg.Format)},
72+
{"Architecture:", cfg.Architecture},
73+
{"Parameters:", cfg.Parameters},
74+
{"Size:", cfg.Size},
75+
{"Quantization:", cfg.Quantization},
76+
}
77+
78+
for _, field := range fields {
79+
if field.value != "" {
80+
sb.WriteString(fmt.Sprintf("%-14s%s\n", field.label, field.value))
81+
}
82+
}
83+
84+
if cfg.ContextSize != nil {
85+
sb.WriteString(fmt.Sprintf("%-14s%d\n", "Context Size:", *cfg.ContextSize))
86+
}
87+
88+
// Helper function to print metadata sections
89+
printMetadata := func(title string, data map[string]string) {
90+
if len(data) > 0 {
91+
sb.WriteString(fmt.Sprintf("\n%s:\n", title))
92+
for k, v := range data {
93+
sb.WriteString(fmt.Sprintf(" %s: %s\n", k, v))
94+
}
95+
}
96+
}
97+
98+
printMetadata("GGUF Metadata", cfg.GGUF)
99+
printMetadata("Safetensors Metadata", cfg.Safetensors)
100+
printMetadata("Diffusers Metadata", cfg.Diffusers)
101+
}
102+
}
103+
104+
return sb.String()
105+
}

cmd/cli/commands/show_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package commands
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/docker/model-runner/pkg/distribution/types"
8+
dmrm "github.com/docker/model-runner/pkg/inference/models"
9+
)
10+
11+
func TestFormatModelInfo(t *testing.T) {
12+
contextSize := int32(4096)
13+
tests := []struct {
14+
name string
15+
model dmrm.Model
16+
contains []string
17+
}{
18+
{
19+
name: "basic model info",
20+
model: dmrm.Model{
21+
ID: "sha256:abc123",
22+
Tags: []string{"ai/gemma3:latest", "mymodel:v1"},
23+
Created: 1704067200, // 2024-01-01 00:00:00 UTC
24+
Config: &types.Config{
25+
Format: "gguf",
26+
Architecture: "llama",
27+
Parameters: "7B",
28+
Size: "4.5GB",
29+
Quantization: "Q4_K_M",
30+
ContextSize: &contextSize,
31+
},
32+
},
33+
contains: []string{
34+
"Model: sha256:abc123",
35+
"Tags: ai/gemma3:latest, mymodel:v1",
36+
"Format: gguf",
37+
"Architecture: llama",
38+
"Parameters: 7B",
39+
"Size: 4.5GB",
40+
"Quantization: Q4_K_M",
41+
"Context Size: 4096",
42+
},
43+
},
44+
{
45+
name: "model with GGUF metadata",
46+
model: dmrm.Model{
47+
ID: "sha256:def456",
48+
Tags: []string{"test:latest"},
49+
Created: 1704067200,
50+
Config: &types.Config{
51+
Format: "gguf",
52+
GGUF: map[string]string{
53+
"version": "3",
54+
"tensor_type": "F16",
55+
},
56+
},
57+
},
58+
contains: []string{
59+
"Model: sha256:def456",
60+
"GGUF Metadata:",
61+
"version: 3",
62+
"tensor_type: F16",
63+
},
64+
},
65+
{
66+
name: "model with no config",
67+
model: dmrm.Model{
68+
ID: "sha256:noconfig",
69+
Tags: []string{"empty:latest"},
70+
Created: 1704067200,
71+
Config: nil,
72+
},
73+
contains: []string{
74+
"Model: sha256:noconfig",
75+
"Tags: empty:latest",
76+
},
77+
},
78+
}
79+
80+
for _, tt := range tests {
81+
t.Run(tt.name, func(t *testing.T) {
82+
output := formatModelInfo(tt.model)
83+
for _, expected := range tt.contains {
84+
if !strings.Contains(output, expected) {
85+
t.Errorf("formatModelInfo() output missing expected string %q\nGot:\n%s", expected, output)
86+
}
87+
}
88+
})
89+
}
90+
}

cmd/cli/docs/reference/docker_model.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ cname:
2323
- docker model rm
2424
- docker model run
2525
- docker model search
26+
- docker model show
2627
- docker model start-runner
2728
- docker model status
2829
- docker model stop-runner
@@ -48,6 +49,7 @@ clink:
4849
- docker_model_rm.yaml
4950
- docker_model_run.yaml
5051
- docker_model_search.yaml
52+
- docker_model_show.yaml
5153
- docker_model_start-runner.yaml
5254
- docker_model_status.yaml
5355
- docker_model_stop-runner.yaml
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
command: docker model show
2+
short: Show information for a model
3+
long: Display detailed information about a model in a human-readable format.
4+
usage: docker model show MODEL
5+
pname: docker model
6+
plink: docker_model.yaml
7+
options:
8+
- option: remote
9+
shorthand: r
10+
value_type: bool
11+
default_value: "false"
12+
description: Show info for remote models
13+
deprecated: false
14+
hidden: false
15+
experimental: false
16+
experimentalcli: false
17+
kubernetes: false
18+
swarm: false
19+
deprecated: false
20+
hidden: false
21+
experimental: false
22+
experimentalcli: false
23+
kubernetes: false
24+
swarm: false
25+

cmd/cli/docs/reference/model.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Docker Model Runner
2424
| [`rm`](model_rm.md) | Remove local models downloaded from Docker Hub |
2525
| [`run`](model_run.md) | Run a model and interact with it using a submitted prompt or chat mode |
2626
| [`search`](model_search.md) | Search for models on Docker Hub and HuggingFace |
27+
| [`show`](model_show.md) | Show information for a model |
2728
| [`start-runner`](model_start-runner.md) | Start Docker Model Runner (Docker Engine only) |
2829
| [`status`](model_status.md) | Check if the Docker Model Runner is running |
2930
| [`stop-runner`](model_stop-runner.md) | Stop Docker Model Runner (Docker Engine only) |
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# docker model show
2+
3+
<!---MARKER_GEN_START-->
4+
Display detailed information about a model in a human-readable format.
5+
6+
### Options
7+
8+
| Name | Type | Default | Description |
9+
|:-----------------|:-------|:--------|:----------------------------|
10+
| `-r`, `--remote` | `bool` | | Show info for remote models |
11+
12+
13+
<!---MARKER_GEN_END-->
14+

0 commit comments

Comments
 (0)