diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 3870747e..80064d7b 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -156,3 +156,32 @@ func ids(as []*Agent) []string { sort.Strings(s) return s } + +// Spell puts a value typed for field key the way this agent spells it: a +// catalog model is "copilot/gpt-6" in some agents and "magpie/copilot/gpt-6" +// in others, and either is taken in both. A value the picker offers as is +// stays, and so does one it doesn't know (a model the agent reaches on its +// own that isn't listed); a "magpie/…" value the catalog doesn't have is an +// error rather than a model the agent would ask its own vendor for. +func (a *Agent) Spell(key, v string) (string, error) { + f := a.Field(key) + if f == nil || f.Options == nil || v == "" { + return v, nil + } + opts := f.Options(a.Values()) + for _, o := range opts { + if o.Value == v { + return v, nil + } + } + ref, prefixed := strings.CutPrefix(v, magpieID+"/") + for _, o := range opts { + if o.Ref != "" && o.Ref == ref { + return o.Value, nil + } + } + if prefixed { + return "", fmt.Errorf("%s isn't a model in magpie's catalog (magpie models lists them)", ref) + } + return v, nil +} diff --git a/internal/agent/spell_test.go b/internal/agent/spell_test.go new file mode 100644 index 00000000..ec7fa864 --- /dev/null +++ b/internal/agent/spell_test.go @@ -0,0 +1,37 @@ +package agent + +import "testing" + +func TestSpell(t *testing.T) { + opts := func(prefix string) func(map[string]string) []Option { + return func(map[string]string) []Option { + return []Option{ + {Value: "gpt-6"}, + {Value: prefix + "copilot/gpt-6-sol", Ref: "copilot/gpt-6-sol"}, + } + } + } + get := func() string { return "" } + bare := &Agent{Fields: []Field{{Key: "model", Get: get, Options: opts("")}}} + prefixed := &Agent{Fields: []Field{{Key: "model", Get: get, Options: opts("magpie/")}}} + for _, c := range []struct { + a *Agent + in, out string + err bool + }{ + {bare, "copilot/gpt-6-sol", "copilot/gpt-6-sol", false}, + {bare, "magpie/copilot/gpt-6-sol", "copilot/gpt-6-sol", false}, + {prefixed, "copilot/gpt-6-sol", "magpie/copilot/gpt-6-sol", false}, + {prefixed, "magpie/copilot/gpt-6-sol", "magpie/copilot/gpt-6-sol", false}, + {bare, "gpt-6", "gpt-6", false}, + {bare, "some-unlisted-model", "some-unlisted-model", false}, + {bare, "", "", false}, + {bare, "magpie/copilot/nope", "", true}, + {prefixed, "magpie/copilot/nope", "", true}, + } { + got, err := c.a.Spell("model", c.in) + if got != c.out || (err != nil) != c.err { + t.Errorf("%q: got %q, %v; want %q, err %v", c.in, got, err, c.out, c.err) + } + } +} diff --git a/main.go b/main.go index d7e2d2e1..43a164d2 100644 --- a/main.go +++ b/main.go @@ -192,6 +192,10 @@ func set(a *agent.Agent, key, value string) error { } return fmt.Errorf("%s has no field %q (fields: %s)", a.Name, key, strings.Join(keys, ", ")) } + value, err := a.Spell(f.Key, value) + if err != nil { + return err + } if err := f.Set(value); err != nil { return err }