package main import ( "fmt" "maps" "slices" "strings" "github.com/yetone/magpie/internal/provider" "github.com/yetone/magpie/internal/settings" ) // A model's name and reasoning levels, from the terminal: magpie model. const modelUsage = `usage: magpie model name the name the model goes by magpie model name name it so everywhere: in magpie, in the gateway's model list, and in the lists magpie writes into the agents magpie model name --reset give it back its own name magpie model efforts the reasoning levels it offers, and those it has magpie model efforts , offer only these of them, e.g. low,medium,high magpie model efforts --reset offer every level it has again magpie model names the models you named or narrowed Only what agents are shown changes: they still pick the model, and requests still reach it, as . The same model from another provider keeps its own name and levels. e.g. magpie model name claude/claude-opus-5-5 "Opus 5.5" magpie model efforts openai/gpt-6 low,medium,high` func modelCmd(args []string) error { if len(args) == 0 { return fmt.Errorf("%s", modelUsage) } switch args[0] { case "names", "ls", "list": return modelNames() case "name", "rename": return modelName(args[1:]) case "efforts", "effort", "levels": return modelEfforts(args[1:]) case "help", "-h", "--help": fmt.Println(modelUsage) return nil } return fmt.Errorf("unknown command %q\n\n%s", args[0], modelUsage) } // modelRef is the provider and model a typed provider/model names. func modelRef(s string) (*provider.Provider, string, error) { ref := strings.TrimPrefix(strings.TrimSpace(s), "magpie/") pid, model, ok := strings.Cut(ref, "/") if !ok || model == "" { return nil, "", fmt.Errorf("name a model as provider/model, not %q (magpie models lists them)", s) } p, err := provider.Find(pid) if err != nil { return nil, "", err } return p, model, nil } func isReset(args []string) bool { return len(args) == 1 && slices.Contains([]string{"--reset", "-r", "--default", "default", "reset"}, args[0]) } func modelName(args []string) error { if len(args) == 0 { return fmt.Errorf("%s", modelUsage) } p, model, err := modelRef(args[0]) if err != nil { return err } own := model for _, m := range p.Available() { if m.ID == model && m.Name != "" { own = m.Name break } } id := p.ID + "/" + model rest := args[1:] if len(rest) == 0 { if n, ok := provider.ModelName(p.ID, model); ok { fmt.Println(bold.Render(n), muted.Render("· "+id+" · its own name is "+own)) } else { fmt.Println(bold.Render(own), muted.Render("· "+id+" · its own name")) } return nil } name := strings.Join(rest, " ") if isReset(rest) { name = "" } if err := provider.SetModelName(id, name); err != nil { return err } if name == "" { fmt.Println(green.Render("✓"), id, muted.Render("is called"), bold.Render(own), muted.Render("again")) } else { fmt.Println(green.Render("✓"), id, muted.Render("is called"), bold.Render(strings.Join(strings.Fields(name), " "))) } return nil } func modelEfforts(args []string) error { if len(args) == 0 { return fmt.Errorf("%s", modelUsage) } p, model, err := modelRef(args[0]) if err != nil { return err } id := p.ID + "/" + model all := p.Efforts(model) rest := args[1:] given := len(p.Known(model)) == 0 // levels aren't known: those it has are the user's if len(rest) == 0 { if len(all) == 0 { fmt.Println(muted.Render(id+" has no reasoning levels known"), faint.Render("· give it some of "+strings.Join(provider.Levels, ", "))) return nil } if given { fmt.Println(bold.Render(strings.Join(all, " ")), muted.Render("· given it · --reset takes them away")) return nil } kept, narrowed := p.ModelEfforts()[model] for _, e := range all { if !narrowed || slices.Contains(kept, e) { fmt.Print(bold.Render(e), " ") } else { fmt.Print(faint.Render(e), " ") } } if narrowed { fmt.Println(muted.Render("· only the bold ones are offered · --reset offers them all")) } else { fmt.Println(muted.Render("· all offered")) } return nil } var levels []string if !isReset(rest) { for _, a := range rest { for _, l := range strings.FieldsFunc(a, func(r rune) bool { return r == ',' || r == ' ' || r == '/' }) { levels = append(levels, l) } } } if err := provider.SetModelEfforts(id, levels); err != nil { return err } if given { if all = p.Efforts(model); len(all) > 0 { fmt.Println(green.Render("✓"), id, muted.Render("offers"), bold.Render(strings.Join(all, ", "))) } else { fmt.Println(green.Render("✓"), id, muted.Render("has no reasoning levels again")) } return nil } if kept, ok := p.ModelEfforts()[model]; ok { fmt.Println(green.Render("✓"), id, muted.Render("offers"), bold.Render(strings.Join(kept, ", "))) } else { fmt.Println(green.Render("✓"), id, muted.Render("offers every level it has"), faint.Render(strings.Join(all, ", "))) } return nil } func modelNames() error { s := settings.Load() keys := slices.Sorted(maps.Keys(s.ModelNames)) for k := range s.ModelEfforts { if !slices.Contains(keys, k) { keys = append(keys, k) } } slices.Sort(keys) if len(keys) == 0 { fmt.Println(muted.Render("no model is named or narrowed yet · magpie model name ")) return nil } w := 0 for _, k := range keys { w = max(w, len(k)) } for _, k := range keys { line := " " + pad(k, w) if n := s.ModelNames[k]; n != "" { line += " " + n } if es := s.ModelEfforts[k]; len(es) > 0 { line += " " + muted.Render(strings.Join(es, "/")) } fmt.Println(line) } return nil }