diff --git a/README.md b/README.md index 7ef917a1..294dbd3b 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,15 @@ gh models list Example output: ```shell -Name Friendly Name Publisher -AI21-Jamba-Instruct AI21-Jamba-Instruct AI21 Labs -gpt-4o OpenAI GPT-4o Azure OpenAI Service -gpt-4o-mini OpenAI GPT-4o mini Azure OpenAI Service -Cohere-command-r Cohere Command R cohere -Cohere-command-r-plus Cohere Command R+ cohere +ID DISPLAY NAME +ai21-labs/ai21-jamba-1.5-large AI21 Jamba 1.5 Large +openai/gpt-4.1 OpenAI GPT-4.1 +openai/gpt-4o-mini OpenAI GPT-4o mini +cohere/cohere-command-r Cohere Command R +deepseek/deepseek-v3-0324 Deepseek-V3-0324 ``` -Use the value in the "Name" column when specifying the model on the command-line. +Use the value in the "ID" column when specifying the model on the command-line. #### Running inference @@ -58,12 +58,12 @@ In REPL mode, use `/help` to list available commands. Otherwise just type your p Run the extension in single-shot mode. This will print the model output and exit. ```shell -gh models run gpt-4o-mini "why is the sky blue?" +gh models run openai/gpt-4o-mini "why is the sky blue?" ``` Run the extension with output from a command. This uses single-shot mode. ```shell -cat README.md | gh models run gpt-4o-mini "summarize this text" +cat README.md | gh models run openai/gpt-4o-mini "summarize this text" ``` ## Notice diff --git a/cmd/list/list.go b/cmd/list/list.go index d82f74b2..e1da8ab9 100644 --- a/cmd/list/list.go +++ b/cmd/list/list.go @@ -4,10 +4,10 @@ package list import ( "fmt" + "github.com/MakeNowJust/heredoc" "github.com/cli/go-gh/v2/pkg/tableprinter" "github.com/github/gh-models/internal/azuremodels" "github.com/github/gh-models/pkg/command" - "github.com/MakeNowJust/heredoc" "github.com/mgutz/ansi" "github.com/spf13/cobra" ) @@ -27,7 +27,7 @@ func NewListCommand(cfg *command.Config) *cobra.Command { Values from the "MODEL NAME" column can be used as the %[1]s[model]%[1]s argument in other commands. `, "`"), - Args: cobra.NoArgs, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() client := cfg.Client @@ -49,12 +49,12 @@ func NewListCommand(cfg *command.Config) *cobra.Command { printer := cfg.NewTablePrinter() - printer.AddHeader([]string{"DISPLAY NAME", "MODEL NAME"}, tableprinter.WithColor(lightGrayUnderline)) + printer.AddHeader([]string{"ID", "DISPLAY NAME"}, tableprinter.WithColor(lightGrayUnderline)) printer.EndRow() for _, model := range models { + printer.AddField(azuremodels.FormatIdentifier(model.Publisher, model.Name)) printer.AddField(model.FriendlyName) - printer.AddField(model.Name) printer.EndRow() } diff --git a/cmd/list/list_test.go b/cmd/list/list_test.go index 0b564e74..1068092d 100644 --- a/cmd/list/list_test.go +++ b/cmd/list/list_test.go @@ -39,9 +39,9 @@ func TestList(t *testing.T) { output := buf.String() require.Contains(t, output, "Showing 1 available chat models") require.Contains(t, output, "DISPLAY NAME") - require.Contains(t, output, "MODEL NAME") + require.Contains(t, output, "ID") require.Contains(t, output, modelSummary.FriendlyName) - require.Contains(t, output, modelSummary.Name) + require.Contains(t, output, azuremodels.FormatIdentifier(modelSummary.Publisher, modelSummary.Name)) }) t.Run("--help prints usage info", func(t *testing.T) { diff --git a/cmd/root.go b/cmd/root.go index 4b1775f1..9e9b94ff 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/MakeNowJust/heredoc" "github.com/cli/go-gh/v2/pkg/auth" "github.com/cli/go-gh/v2/pkg/term" "github.com/github/gh-models/cmd/list" @@ -13,7 +14,6 @@ import ( "github.com/github/gh-models/internal/azuremodels" "github.com/github/gh-models/pkg/command" "github.com/github/gh-models/pkg/util" - "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" ) diff --git a/cmd/run/run.go b/cmd/run/run.go index fab9eee1..4c60f034 100644 --- a/cmd/run/run.go +++ b/cmd/run/run.go @@ -13,12 +13,12 @@ import ( "time" "github.com/AlecAivazis/survey/v2" + "github.com/MakeNowJust/heredoc" "github.com/briandowns/spinner" "github.com/github/gh-models/internal/azuremodels" "github.com/github/gh-models/internal/sse" "github.com/github/gh-models/pkg/command" "github.com/github/gh-models/pkg/util" - "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -205,8 +205,8 @@ func NewRunCommand(cfg *command.Config) *cobra.Command { The return value will be the response to your prompt from the selected model. `, "`"), - Example: "gh models run gpt-4o-mini \"how many types of hyena are there?\"", - Args: cobra.ArbitraryArgs, + Example: "gh models run openai/gpt-4o-mini \"how many types of hyena are there?\"", + Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { cmdHandler := newRunCommandHandler(cmd, cfg, args) if cmdHandler == nil { @@ -413,7 +413,7 @@ func (h *runCommandHandler) getModelNameFromArgs(models []*azuremodels.ModelSumm if !model.IsChatModel() { continue } - prompt.Options = append(prompt.Options, model.FriendlyName) + prompt.Options = append(prompt.Options, azuremodels.FormatIdentifier(model.Publisher, model.Name)) } err := survey.AskOne(prompt, &modelName, survey.WithPageSize(10)) @@ -438,7 +438,6 @@ func validateModelName(modelName string, models []*azuremodels.ModelSummary) (st foundMatch := false for _, model := range models { if model.HasName(modelName) { - modelName = model.Name foundMatch = true break } diff --git a/cmd/run/run_test.go b/cmd/run/run_test.go index b9ae85d0..5b88cfa6 100644 --- a/cmd/run/run_test.go +++ b/cmd/run/run_test.go @@ -50,7 +50,7 @@ func TestRun(t *testing.T) { buf := new(bytes.Buffer) cfg := command.NewConfig(buf, buf, client, true, 80) runCmd := NewRunCommand(cfg) - runCmd.SetArgs([]string{modelSummary.Name, "this is my prompt"}) + runCmd.SetArgs([]string{azuremodels.FormatIdentifier(modelSummary.Publisher, modelSummary.Name), "this is my prompt"}) _, err := runCmd.ExecuteC() diff --git a/cmd/view/view.go b/cmd/view/view.go index 89b24ab5..bec37f73 100644 --- a/cmd/view/view.go +++ b/cmd/view/view.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" + "github.com/MakeNowJust/heredoc" "github.com/github/gh-models/internal/azuremodels" "github.com/github/gh-models/pkg/command" - "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" ) @@ -25,8 +25,8 @@ func NewViewCommand(cfg *command.Config) *cobra.Command { If you know which model you want information for, you can run the request in a single command as %[1]sgh models view [model]%[1]s `, "`"), - Example: "gh models view gpt-4o", - Args: cobra.ArbitraryArgs, + Example: "gh models view openai/gpt-4.1", + Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() client := cfg.Client @@ -50,7 +50,7 @@ func NewViewCommand(cfg *command.Config) *cobra.Command { if !model.IsChatModel() { continue } - prompt.Options = append(prompt.Options, model.FriendlyName) + prompt.Options = append(prompt.Options, azuremodels.FormatIdentifier(model.Publisher, model.Name)) } err = survey.AskOne(prompt, &modelName, survey.WithPageSize(10)) diff --git a/cmd/view/view_test.go b/cmd/view/view_test.go index 3cf252b3..cde08747 100644 --- a/cmd/view/view_test.go +++ b/cmd/view/view_test.go @@ -49,7 +49,7 @@ func TestView(t *testing.T) { buf := new(bytes.Buffer) cfg := command.NewConfig(buf, buf, client, true, 80) viewCmd := NewViewCommand(cfg) - viewCmd.SetArgs([]string{modelSummary.Name}) + viewCmd.SetArgs([]string{azuremodels.FormatIdentifier(modelSummary.Publisher, modelSummary.Name)}) _, err := viewCmd.ExecuteC() diff --git a/internal/azuremodels/azure_client_config.go b/internal/azuremodels/azure_client_config.go index 7675aa63..58433e83 100644 --- a/internal/azuremodels/azure_client_config.go +++ b/internal/azuremodels/azure_client_config.go @@ -1,7 +1,7 @@ package azuremodels const ( - defaultInferenceURL = "https://models.inference.ai.azure.com/chat/completions" + defaultInferenceURL = "https://models.github.ai/inference/chat/completions" defaultAzureAiStudioURL = "https://api.catalog.azureml.ms" defaultModelsURL = defaultAzureAiStudioURL + "/asset-gallery/v1.0/models" ) diff --git a/internal/azuremodels/model_details.go b/internal/azuremodels/model_details.go index 26836487..ecd135ac 100644 --- a/internal/azuremodels/model_details.go +++ b/internal/azuremodels/model_details.go @@ -1,6 +1,9 @@ package azuremodels -import "fmt" +import ( + "fmt" + "strings" +) // ModelDetails includes detailed information about a model. type ModelDetails struct { @@ -22,3 +25,15 @@ type ModelDetails struct { func (m *ModelDetails) ContextLimits() string { return fmt.Sprintf("up to %d input tokens and %d output tokens", m.MaxInputTokens, m.MaxOutputTokens) } + +// FormatIdentifier formats the model identifier based on the publisher and model name. +func FormatIdentifier(publisher, name string) string { + formatPart := func(s string) string { + // Replace spaces with dashes and convert to lowercase + result := strings.ToLower(s) + result = strings.ReplaceAll(result, " ", "-") + return result + } + + return fmt.Sprintf("%s/%s", formatPart(publisher), formatPart(name)) +} diff --git a/internal/azuremodels/model_details_test.go b/internal/azuremodels/model_details_test.go index 8a41f062..ae795327 100644 --- a/internal/azuremodels/model_details_test.go +++ b/internal/azuremodels/model_details_test.go @@ -12,4 +12,12 @@ func TestModelDetails(t *testing.T) { result := details.ContextLimits() require.Equal(t, "up to 123 input tokens and 456 output tokens", result) }) + + t.Run("FormatIdentifier", func(t *testing.T) { + publisher := "Open AI" + name := "GPT 3" + expected := "open-ai/gpt-3" + result := FormatIdentifier(publisher, name) + require.Equal(t, expected, result) + }) } diff --git a/internal/azuremodels/model_summary.go b/internal/azuremodels/model_summary.go index 9b7798a6..53076654 100644 --- a/internal/azuremodels/model_summary.go +++ b/internal/azuremodels/model_summary.go @@ -25,7 +25,8 @@ func (m *ModelSummary) IsChatModel() bool { // HasName checks if the model has the given name. func (m *ModelSummary) HasName(name string) bool { - return strings.EqualFold(m.FriendlyName, name) || strings.EqualFold(m.Name, name) + modelID := FormatIdentifier(m.Publisher, m.Name) + return strings.EqualFold(modelID, name) } var ( @@ -49,9 +50,9 @@ func SortModels(models []*ModelSummary) { // Otherwise, sort by friendly name // Note: sometimes the casing returned by the API is inconsistent, so sort using lowercase values. - friendlyNameI := strings.ToLower(models[i].FriendlyName) - friendlyNameJ := strings.ToLower(models[j].FriendlyName) + idI := FormatIdentifier(models[i].Publisher, models[i].Name) + idJ := FormatIdentifier(models[j].Publisher, models[j].Name) - return friendlyNameI < friendlyNameJ + return idI < idJ }) } diff --git a/internal/azuremodels/model_summary_test.go b/internal/azuremodels/model_summary_test.go index 82358f0e..978da7ee 100644 --- a/internal/azuremodels/model_summary_test.go +++ b/internal/azuremodels/model_summary_test.go @@ -18,27 +18,26 @@ func TestModelSummary(t *testing.T) { }) t.Run("HasName", func(t *testing.T) { - model := &ModelSummary{Name: "foo123", FriendlyName: "Foo 123"} + model := &ModelSummary{Name: "foo123", Publisher: "bar"} - require.True(t, model.HasName(model.Name)) - require.True(t, model.HasName("FOO123")) - require.True(t, model.HasName(model.FriendlyName)) - require.True(t, model.HasName("fOo 123")) + require.True(t, model.HasName(FormatIdentifier(model.Publisher, model.Name))) + require.True(t, model.HasName("BaR/foO123")) require.False(t, model.HasName("completely different value")) require.False(t, model.HasName("foo")) + require.False(t, model.HasName("bar")) }) - t.Run("SortModels sorts given slice in-place by friendly name, case-insensitive", func(t *testing.T) { - modelA := &ModelSummary{Name: "z", FriendlyName: "AARDVARK"} - modelB := &ModelSummary{Name: "y", FriendlyName: "betta"} - modelC := &ModelSummary{Name: "x", FriendlyName: "Cat"} - models := []*ModelSummary{modelB, modelA, modelC} + t.Run("SortModels sorts given slice in-place by publisher/name", func(t *testing.T) { + modelA := &ModelSummary{Publisher: "a", Name: "z"} + modelB := &ModelSummary{Publisher: "a", Name: "Y"} + modelC := &ModelSummary{Publisher: "b", Name: "x"} + models := []*ModelSummary{modelC, modelB, modelA} SortModels(models) require.Equal(t, 3, len(models)) - require.Equal(t, "AARDVARK", models[0].FriendlyName) - require.Equal(t, "betta", models[1].FriendlyName) - require.Equal(t, "Cat", models[2].FriendlyName) + require.Equal(t, "Y", models[0].Name) + require.Equal(t, "z", models[1].Name) + require.Equal(t, "x", models[2].Name) }) }