Skip to content

Commit

Permalink
feat: added the ability to set a user default AI provider (#427)
Browse files Browse the repository at this point in the history
* feat: added the ability to set a user default AI provider

Signed-off-by: Alex Jones <[email protected]>

* feat: added the ability to set a user default AI provider

Signed-off-by: Alex Jones <[email protected]>

* chore: added provider to json output

Signed-off-by: Alex Jones <[email protected]>

---------

Signed-off-by: Alex Jones <[email protected]>
  • Loading branch information
AlexsJones authored May 16, 2023
1 parent 032576c commit cbe2fb4
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 26 deletions.
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,38 @@ curl -X GET "http://localhost:8080/analyze?namespace=k8sgpt&explain=false"

## Additional AI providers

### Setting a new default AI provider

<details>

There may be scenarios where you wish to have K8sGPT plugged into several default AI providers. In this case you may wish to use one as a new default, other than OpenAI which is the project default.

_To view available providers_

```
k8sgpt auth list
Default:
> openai
Active:
> openai
> azureopenai
Unused:
> localai
> noopai
```

_To set a new default provider_

```
k8sgpt auth default -p azureopenai
Default provider set to azureopenai
```


</details>


### Azure OpenAI
<em>Prerequisites:</em> an Azure OpenAI deployment is needed, please visit MS official [documentation](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource) to create your own.

Expand All @@ -286,7 +318,7 @@ To authenticate with k8sgpt, you will need the Azure OpenAI endpoint of your ten
### Run k8sgpt
To run k8sgpt, run `k8sgpt auth` with the `azureopenai` backend:
```
k8sgpt auth --backend azureopenai --baseurl https://<your Azure OpenAI endpoint> --engine <deployment_name> --model <model_name>
k8sgpt auth new --backend azureopenai --baseurl https://<your Azure OpenAI endpoint> --engine <deployment_name> --model <model_name>
```
Lastly, enter your Azure API key, after the prompt.

Expand Down
2 changes: 2 additions & 0 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ func init() {
AuthCmd.AddCommand(newCmd)
// add subcommand to remove new backend provider
AuthCmd.AddCommand(removeCmd)
// add subcommand to set default backend provider
AuthCmd.AddCommand(defaultCmd)
}
79 changes: 79 additions & 0 deletions cmd/auth/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2023 The K8sGPT Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package auth

import (
"os"
"strings"

"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var (
providerName string
)

var defaultCmd = &cobra.Command{
Use: "default",
Short: "Set your default AI backend provider",
Long: "The command to set your new default AI backend provider (default is openai)",
Run: func(cmd *cobra.Command, args []string) {
err := viper.UnmarshalKey("ai", &configAI)
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
if providerName == "" {
if configAI.DefaultProvider != "" {
color.Yellow("Your default provider is %s", configAI.DefaultProvider)
} else {
color.Yellow("Your default provider is openai")
}
os.Exit(0)
}
// lowercase the provider name
providerName = strings.ToLower(providerName)

// Check if the provider is in the provider list
providerExists := false
for _, provider := range configAI.Providers {
if provider.Name == providerName {
providerExists = true
}
}
if !providerExists {
color.Red("Error: Provider %s does not exist", providerName)
os.Exit(1)
}
// Set the default provider
configAI.DefaultProvider = providerName

viper.Set("ai", configAI)
// Viper write config
err = viper.WriteConfig()
if err != nil {
color.Red("Error: %v", err)
os.Exit(1)
}
// Print acknowledgement
color.Green("Default provider set to %s", providerName)
},
}

func init() {
// provider name flag
defaultCmd.Flags().StringVarP(&providerName, "provider", "p", "", "The name of the provider to set as default")
}
38 changes: 32 additions & 6 deletions cmd/auth/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"os"

"github.com/fatih/color"
"github.com/k8sgpt-ai/k8sgpt/pkg/ai"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -35,12 +36,37 @@ var listCmd = &cobra.Command{
os.Exit(1)
}

// iterate over the provider list and prints each provider name
for _, provider := range configAI.Providers {
if len(configAI.Providers) == 0 {
color.Red("Provider list is currently empty.")
} else {
fmt.Printf("> %s\n", provider.Name)
// Print the default if it is set
fmt.Print(color.YellowString("Default: \n"))
if configAI.DefaultProvider != "" {
fmt.Printf("> %s\n", color.BlueString(configAI.DefaultProvider))
} else {
fmt.Printf("> %s\n", color.BlueString("openai"))
}

// Get list of all AI Backends and only print htem if they are not in the provider list
fmt.Print(color.YellowString("Active: \n"))
for _, aiBackend := range ai.Backends {
providerExists := false
for _, provider := range configAI.Providers {
if provider.Name == aiBackend {
providerExists = true
}
}
if providerExists {
fmt.Printf("> %s\n", color.GreenString(aiBackend))
}
}
fmt.Print(color.YellowString("Unused: \n"))
for _, aiBackend := range ai.Backends {
providerExists := false
for _, provider := range configAI.Providers {
if provider.Name == aiBackend {
providerExists = true
}
}
if !providerExists {
fmt.Printf("> %s\n", color.RedString(aiBackend))
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion pkg/ai/iai.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ func NewClient(provider string) IAI {
}

type AIConfiguration struct {
Providers []AIProvider `mapstructure:"providers"`
Providers []AIProvider `mapstructure:"providers"`
DefaultProvider string `mapstructure:"defaultprovider"`
}

type AIProvider struct {
Expand Down
45 changes: 27 additions & 18 deletions pkg/analysis/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,17 @@ import (
)

type Analysis struct {
Context context.Context
Filters []string
Client *kubernetes.Client
AIClient ai.IAI
Results []common.Result
Errors []string
Namespace string
Cache cache.ICache
Explain bool
MaxConcurrency int
Context context.Context
Filters []string
Client *kubernetes.Client
AIClient ai.IAI
Results []common.Result
Errors []string
Namespace string
Cache cache.ICache
Explain bool
MaxConcurrency int
AnalysisAIProvider string // The name of the AI Provider used for this analysis
}

type AnalysisStatus string
Expand All @@ -55,6 +56,7 @@ const (
)

type JsonOutput struct {
Provider string `json:"provider"`
Errors AnalysisErrors `json:"errors"`
Status AnalysisStatus `json:"status"`
Problems int `json:"problems"`
Expand All @@ -74,6 +76,12 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
os.Exit(1)
}

// Backend string will have high priority than a default provider
// Backend as "openai" represents the default CLI argument passed through
if configAI.DefaultProvider != "" && backend == "openai" {
backend = configAI.DefaultProvider
}

var aiProvider ai.AIProvider
for _, provider := range configAI.Providers {
if backend == provider.Name {
Expand Down Expand Up @@ -105,14 +113,15 @@ func NewAnalysis(backend string, language string, filters []string, namespace st
}

return &Analysis{
Context: ctx,
Filters: filters,
Client: client,
AIClient: aiClient,
Namespace: namespace,
Cache: cache.New(noCache),
Explain: explain,
MaxConcurrency: maxConcurrency,
Context: ctx,
Filters: filters,
Client: client,
AIClient: aiClient,
Namespace: namespace,
Cache: cache.New(noCache),
Explain: explain,
MaxConcurrency: maxConcurrency,
AnalysisAIProvider: backend,
}, nil
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/analysis/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (a *Analysis) jsonOutput() ([]byte, error) {
}

result := JsonOutput{
Provider: a.AnalysisAIProvider,
Problems: problems,
Results: a.Results,
Errors: a.Errors,
Expand All @@ -56,6 +57,10 @@ func (a *Analysis) jsonOutput() ([]byte, error) {

func (a *Analysis) textOutput() ([]byte, error) {
var output strings.Builder

// Print the AI provider used for this analysis
output.WriteString(fmt.Sprintf("AI Provider: %s\n", color.YellowString(a.AnalysisAIProvider)))

if len(a.Errors) != 0 {
output.WriteString("\n")
output.WriteString(color.YellowString("Warnings : \n"))
Expand Down

0 comments on commit cbe2fb4

Please sign in to comment.