From f37c4005d8a369cccecea7a121205781f7a3e6d9 Mon Sep 17 00:00:00 2001 From: Dorin Geman Date: Wed, 17 Dec 2025 13:23:09 +0200 Subject: [PATCH 1/3] fix(cli): correct ls command usage to show MODEL as optional Signed-off-by: Dorin Geman --- cmd/cli/commands/list.go | 2 +- cmd/cli/docs/reference/docker_model_list.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/cli/commands/list.go b/cmd/cli/commands/list.go index 171049d0a..491380b0f 100644 --- a/cmd/cli/commands/list.go +++ b/cmd/cli/commands/list.go @@ -22,7 +22,7 @@ import ( func newListCmd() *cobra.Command { var jsonFormat, openai, quiet bool c := &cobra.Command{ - Use: "list [OPTIONS]", + Use: "list [OPTIONS] [MODEL]", Aliases: []string{"ls"}, Short: "List the models pulled to your local environment", RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/cli/docs/reference/docker_model_list.yaml b/cmd/cli/docs/reference/docker_model_list.yaml index ee713d525..a48cfa09d 100644 --- a/cmd/cli/docs/reference/docker_model_list.yaml +++ b/cmd/cli/docs/reference/docker_model_list.yaml @@ -2,7 +2,7 @@ command: docker model list aliases: docker model list, docker model ls short: List the models pulled to your local environment long: List the models pulled to your local environment -usage: docker model list [OPTIONS] +usage: docker model list [OPTIONS] [MODEL] pname: docker model plink: docker_model.yaml options: From 42dd2bacfb58378e19ef3bd0a952c7e7dce5c1b7 Mon Sep 17 00:00:00 2001 From: Dorin Geman Date: Wed, 17 Dec 2025 13:34:17 +0200 Subject: [PATCH 2/3] fix(cli): apply model filter when listing in OpenAI format Signed-off-by: Dorin Geman --- cmd/cli/commands/list.go | 53 ++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/cmd/cli/commands/list.go b/cmd/cli/commands/list.go index 491380b0f..e1da7c5c1 100644 --- a/cmd/cli/commands/list.go +++ b/cmd/cli/commands/list.go @@ -58,50 +58,56 @@ func newListCmd() *cobra.Command { return c } +func normalizeModelFilter(filter string) string { + if !strings.Contains(filter, "/") { + return "ai/" + filter + } + return filter +} + +func matchesModelFilter(tag, filter string) bool { + if strings.Contains(filter, ":") { + return tag == filter + } + repository, _, _ := strings.Cut(tag, ":") + return repository == filter +} + func listModels(openai bool, desktopClient *desktop.Client, quiet bool, jsonFormat bool, modelFilter string) (string, error) { if openai { models, err := desktopClient.ListOpenAI() if err != nil { return "", handleClientError(err, "Failed to list models") } + if modelFilter != "" { + filter := normalizeModelFilter(modelFilter) + filtered := models.Data[:0] + for _, m := range models.Data { + if matchesModelFilter(m.ID, filter) { + filtered = append(filtered, m) + } + } + models.Data = filtered + } return formatter.ToStandardJSON(models) } + models, err := desktopClient.List() if err != nil { return "", handleClientError(err, "Failed to list models") } - if modelFilter != "" { - // If filter doesn't contain '/', prepend default namespace 'ai/' - if !strings.Contains(modelFilter, "/") { - modelFilter = "ai/" + modelFilter - } - + filter := normalizeModelFilter(modelFilter) var filteredModels []dmrm.Model - // Check if filter has a colon (i.e., includes a tag) - hasColon := strings.Contains(modelFilter, ":") - for _, m := range models { var matchingTags []string for _, tag := range m.Tags { - if hasColon { - // Filter includes a tag part - do exact match - // Tags are stored in normalized format by the backend - if tag == modelFilter { - matchingTags = append(matchingTags, tag) - } - } else { - // Filter has no colon - match repository name only (part before ':') - repository, _, _ := strings.Cut(tag, ":") - if repository == modelFilter { - matchingTags = append(matchingTags, tag) - } + if matchesModelFilter(tag, filter) { + matchingTags = append(matchingTags, tag) } } - // Only include the model if at least one tag matched, and only include matching tags if len(matchingTags) > 0 { - // Create a copy of the model with only the matching tags filteredModel := m filteredModel.Tags = matchingTags filteredModels = append(filteredModels, filteredModel) @@ -109,7 +115,6 @@ func listModels(openai bool, desktopClient *desktop.Client, quiet bool, jsonForm } models = filteredModels } - if jsonFormat { return formatter.ToStandardJSON(models) } From 131b86824aa3f5c6c9187f66316d47e0e1869ed4 Mon Sep 17 00:00:00 2001 From: Dorin Geman Date: Wed, 17 Dec 2025 13:46:05 +0200 Subject: [PATCH 3/3] fix(cli): reject extra arguments in list command Signed-off-by: Dorin Geman --- cmd/cli/commands/list.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cli/commands/list.go b/cmd/cli/commands/list.go index e1da7c5c1..0ef9ea7b0 100644 --- a/cmd/cli/commands/list.go +++ b/cmd/cli/commands/list.go @@ -25,6 +25,7 @@ func newListCmd() *cobra.Command { Use: "list [OPTIONS] [MODEL]", Aliases: []string{"ls"}, Short: "List the models pulled to your local environment", + Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if openai && quiet { return fmt.Errorf("--quiet flag cannot be used with --openai flag or OpenAI backend")