199 lines
5.8 KiB
Go
199 lines
5.8 KiB
Go
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 <provider/model> the name the model goes by
|
|
magpie model name <provider/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 <provider/model> --reset give it back its own name
|
|
magpie model efforts <provider/model> the reasoning levels it offers, and those it has
|
|
magpie model efforts <provider/model> <l>,<l> offer only these of them, e.g. low,medium,high
|
|
magpie model efforts <provider/model> --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 <provider/model>. 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 <provider/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
|
|
}
|