Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 31 additions & 25 deletions cmd/cli/commands/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ 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",
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")
Expand Down Expand Up @@ -58,58 +59,63 @@ 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)
}
}
models = filteredModels
}

if jsonFormat {
return formatter.ToStandardJSON(models)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cli/docs/reference/docker_model_list.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading