mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2025-07-16 08:25:54 +00:00
feat: add azure openai provider (#309)
* feat: add azure openai provider Signed-off-by: Aris Boutselis <aris.boutselis@senseon.io> * feat: validate backend name Signed-off-by: Aris Boutselis <aris.boutselis@senseon.io> * fix: remove BaseURL from the mandatory env variables Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com> * fix: conflicts Signed-off-by: Aris Boutselis <aris.boutselis@senseon.io> * chore: updated logo (#365) Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * chore: added changing banners (#367) Signed-off-by: Alex Jones <alexsimonjones@gmail.com> * feat: add additionalLabels to Service Monitor (#366) * feat: add additionalLabels to Service Monitor Signed-off-by: Brad McCoy <bradmccoydev@gmail.com> * feat: update additionalLabels Signed-off-by: Brad McCoy <bradmccoydev@gmail.com> --------- Signed-off-by: Brad McCoy <bradmccoydev@gmail.com> * fix: update README file's ai provider section. Signed-off-by: Aris Boutselis <aris.boutselis@senseon.io> --------- Signed-off-by: Aris Boutselis <aris.boutselis@senseon.io> Signed-off-by: Aris Boutselis <arisboutselis08@gmail.com> Signed-off-by: Alex Jones <alexsimonjones@gmail.com> Signed-off-by: Brad McCoy <bradmccoydev@gmail.com> Co-authored-by: Aris Boutselis <arisboutselis08@gmail.com> Co-authored-by: Alex Jones <alexsimonjones@gmail.com> Co-authored-by: Brad McCoy <bradmccoydev@gmail.com>
This commit is contained in:
parent
a89a5cfa2e
commit
d8357ceb94
80
README.md
80
README.md
@ -262,6 +262,55 @@ _Analysis with serve mode_
|
||||
```
|
||||
curl -X GET "http://localhost:8080/analyze?namespace=k8sgpt&explain=false"
|
||||
```
|
||||
</details>
|
||||
|
||||
## Additional AI providers
|
||||
|
||||
### 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.
|
||||
|
||||
To authenticate with k8sgpt, you will need the Azure OpenAI endpoint of your tenant `"https://your Azure OpenAI Endpoint"`, the api key to access your deployment, the deployment name of your model and the model name itself.
|
||||
<details>
|
||||
|
||||
### 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>
|
||||
```
|
||||
Lastly, enter your Azure API key, after the prompt.
|
||||
|
||||
Now you are ready to analyze with the azure openai backend:
|
||||
```
|
||||
k8sgpt analyze --explain --backend azureopenai
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Running local models
|
||||
|
||||
To run local models, it is possible to use OpenAI compatible APIs, for instance [LocalAI](https://github.com/go-skynet/LocalAI) which uses [llama.cpp](https://github.com/ggerganov/llama.cpp) and [ggml](https://github.com/ggerganov/ggml) to run inference on consumer-grade hardware. Models supported by LocalAI for instance are Vicuna, Alpaca, LLaMA, Cerebras, GPT4ALL, GPT4ALL-J and koala.
|
||||
|
||||
<details>
|
||||
|
||||
To run local inference, you need to download the models first, for instance you can find `ggml` compatible models in [huggingface.com](https://huggingface.co/models?search=ggml) (for example vicuna, alpaca and koala).
|
||||
|
||||
### Start the API server
|
||||
|
||||
To start the API server, follow the instruction in [LocalAI](https://github.com/go-skynet/LocalAI#example-use-gpt4all-j-model).
|
||||
|
||||
### Run k8sgpt
|
||||
|
||||
To run k8sgpt, run `k8sgpt auth` with the `localai` backend:
|
||||
|
||||
```
|
||||
k8sgpt auth --backend localai --model <model_name> --baseurl http://localhost:8080/v1
|
||||
```
|
||||
|
||||
Now you can analyze with the `localai` backend:
|
||||
|
||||
```
|
||||
k8sgpt analyze --explain --backend localai
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@ -295,37 +344,6 @@ The Kubernetes system is trying to scale a StatefulSet named fake-deployment usi
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## Running local models
|
||||
|
||||
To run local models, it is possible to use OpenAI compatible APIs, for instance [LocalAI](https://github.com/go-skynet/LocalAI) which uses [llama.cpp](https://github.com/ggerganov/llama.cpp) and [ggml](https://github.com/ggerganov/ggml) to run inference on consumer-grade hardware. Models supported by LocalAI for instance are Vicuna, Alpaca, LLaMA, Cerebras, GPT4ALL, GPT4ALL-J and koala.
|
||||
|
||||
<details>
|
||||
|
||||
To run local inference, you need to download the models first, for instance you can find `ggml` compatible models in [huggingface.co](https://huggingface.co/models?search=ggml) (for example vicuna, alpaca and koala).
|
||||
|
||||
### Start the API server
|
||||
|
||||
To start the API server, follow the instruction in [LocalAI](https://github.com/go-skynet/LocalAI#example-use-gpt4all-j-model).
|
||||
|
||||
### Run k8sgpt
|
||||
|
||||
To run k8sgpt, run `k8sgpt auth` with the `localai` backend:
|
||||
|
||||
```
|
||||
k8sgpt auth --backend localai --model <model_name> --baseurl http://localhost:8080/v1
|
||||
```
|
||||
|
||||
When being asked for an API key, just press enter.
|
||||
|
||||
Now you can analyze with the `localai` backend:
|
||||
|
||||
```
|
||||
k8sgpt analyze --explain --backend localai
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Configuration
|
||||
|
||||
<details>
|
||||
|
@ -31,6 +31,7 @@ var (
|
||||
password string
|
||||
baseURL string
|
||||
model string
|
||||
engine string
|
||||
)
|
||||
|
||||
// authCmd represents the auth command
|
||||
@ -38,6 +39,13 @@ var AuthCmd = &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Authenticate with your chosen backend",
|
||||
Long: `Provide the necessary credentials to authenticate with your chosen backend.`,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
backend, _ := cmd.Flags().GetString("backend")
|
||||
if strings.ToLower(backend) == "azureopenai" {
|
||||
cmd.MarkFlagRequired("engine")
|
||||
cmd.MarkFlagRequired("baseurl")
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
// get ai configuration
|
||||
@ -57,9 +65,18 @@ var AuthCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
// check if backend is not empty
|
||||
if backend == "" {
|
||||
color.Red("Error: Backend AI cannot be empty.")
|
||||
validBackend := func(validBackends []string, backend string) bool {
|
||||
for _, b := range validBackends {
|
||||
if b == backend {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check if backend is not empty and a valid value
|
||||
if backend == "" || !validBackend(ai.Backends, backend) {
|
||||
color.Red("Error: Backend AI cannot be empty and accepted values are '%v'", strings.Join(ai.Backends, ", "))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@ -88,6 +105,7 @@ var AuthCmd = &cobra.Command{
|
||||
Model: model,
|
||||
Password: password,
|
||||
BaseURL: baseURL,
|
||||
Engine: engine,
|
||||
}
|
||||
|
||||
if providerIndex == -1 {
|
||||
@ -117,4 +135,6 @@ func init() {
|
||||
AuthCmd.Flags().StringVarP(&password, "password", "p", "", "Backend AI password")
|
||||
// add flag for url
|
||||
AuthCmd.Flags().StringVarP(&baseURL, "baseurl", "u", "", "URL AI provider, (e.g `http://localhost:8080/v1`)")
|
||||
// add flag for azure open ai engine/deployment name
|
||||
AuthCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name")
|
||||
}
|
||||
|
@ -47,17 +47,17 @@ var ServeCmd = &cobra.Command{
|
||||
password := os.Getenv("K8SGPT_PASSWORD")
|
||||
model := os.Getenv("K8SGPT_MODEL")
|
||||
baseURL := os.Getenv("K8SGPT_BASEURL")
|
||||
|
||||
engine := os.Getenv("K8SGPT_ENGINE")
|
||||
// If the envs are set, allocate in place to the aiProvider
|
||||
// else exit with error
|
||||
envIsSet := backend != "" || password != "" || model != "" || baseURL != ""
|
||||
|
||||
envIsSet := backend != "" || password != "" || model != ""
|
||||
if envIsSet {
|
||||
aiProvider = &ai.AIProvider{
|
||||
Name: backend,
|
||||
Password: password,
|
||||
Model: model,
|
||||
BaseURL: baseURL,
|
||||
Engine: engine,
|
||||
}
|
||||
|
||||
configAI.Providers = append(configAI.Providers, *aiProvider)
|
||||
@ -75,7 +75,7 @@ var ServeCmd = &cobra.Command{
|
||||
if aiProvider == nil {
|
||||
for _, provider := range configAI.Providers {
|
||||
if backend == provider.Name {
|
||||
// he pointer to the range variable is not really an issue here, as there
|
||||
// the pointer to the range variable is not really an issue here, as there
|
||||
// is a break right after, but to prevent potential future issues, a temp
|
||||
// variable is assigned
|
||||
p := provider
|
||||
|
94
pkg/ai/azureopenai.go
Normal file
94
pkg/ai/azureopenai.go
Normal file
@ -0,0 +1,94 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/util"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
type AzureAIClient struct {
|
||||
client *openai.Client
|
||||
language string
|
||||
model string
|
||||
}
|
||||
|
||||
func (c *AzureAIClient) Configure(config IAIConfig, lang string) error {
|
||||
token := config.GetPassword()
|
||||
baseURL := config.GetBaseURL()
|
||||
engine := config.GetEngine()
|
||||
defaultConfig := openai.DefaultAzureConfig(token, baseURL, engine)
|
||||
client := openai.NewClientWithConfig(defaultConfig)
|
||||
if client == nil {
|
||||
return errors.New("error creating Azure OpenAI client")
|
||||
}
|
||||
c.language = lang
|
||||
c.client = client
|
||||
c.model = config.GetModel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AzureAIClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
|
||||
// Create a completion request
|
||||
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
|
||||
Model: c.model,
|
||||
Messages: []openai.ChatCompletionMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: fmt.Sprintf(default_prompt, c.language, prompt),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
func (a *AzureAIClient) Parse(ctx context.Context, prompt []string, cache cache.ICache) (string, error) {
|
||||
inputKey := strings.Join(prompt, " ")
|
||||
// Check for cached data
|
||||
cacheKey := util.GetCacheKey(a.GetName(), a.language, inputKey)
|
||||
|
||||
if !cache.IsCacheDisabled() && cache.Exists(cacheKey) {
|
||||
response, err := cache.Load(cacheKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response != "" {
|
||||
output, err := base64.StdEncoding.DecodeString(response)
|
||||
if err != nil {
|
||||
color.Red("error decoding cached data: %v", err)
|
||||
return "", nil
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
}
|
||||
|
||||
response, err := a.GetCompletion(ctx, inputKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = cache.Store(cacheKey, base64.StdEncoding.EncodeToString([]byte(response)))
|
||||
|
||||
if err != nil {
|
||||
color.Red("error storing value to cache: %v", err)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (a *AzureAIClient) GetName() string {
|
||||
return "azureopenai"
|
||||
}
|
@ -19,6 +19,21 @@ import (
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
clients = []IAI{
|
||||
&OpenAIClient{},
|
||||
&AzureAIClient{},
|
||||
&LocalAIClient{},
|
||||
&NoOpAIClient{},
|
||||
}
|
||||
Backends = []string{
|
||||
"openai",
|
||||
"localai",
|
||||
"azureopenai",
|
||||
"noopai",
|
||||
}
|
||||
)
|
||||
|
||||
type IAI interface {
|
||||
Configure(config IAIConfig, language string) error
|
||||
GetCompletion(ctx context.Context, prompt string) (string, error)
|
||||
@ -30,19 +45,17 @@ type IAIConfig interface {
|
||||
GetPassword() string
|
||||
GetModel() string
|
||||
GetBaseURL() string
|
||||
GetEngine() string
|
||||
}
|
||||
|
||||
func NewClient(provider string) IAI {
|
||||
switch provider {
|
||||
case "openai":
|
||||
return &OpenAIClient{}
|
||||
case "localai":
|
||||
return &LocalAIClient{}
|
||||
case "noopai":
|
||||
return &NoOpAIClient{}
|
||||
default:
|
||||
return &OpenAIClient{}
|
||||
for _, c := range clients {
|
||||
if provider == c.GetName() {
|
||||
return c
|
||||
}
|
||||
}
|
||||
// default client
|
||||
return &OpenAIClient{}
|
||||
}
|
||||
|
||||
type AIConfiguration struct {
|
||||
@ -52,8 +65,9 @@ type AIConfiguration struct {
|
||||
type AIProvider struct {
|
||||
Name string `mapstructure:"name"`
|
||||
Model string `mapstructure:"model"`
|
||||
Password string `mapstructure:"password"`
|
||||
BaseURL string `mapstructure:"baseurl"`
|
||||
Password string `mapstructure:"password" yaml:"password,omitempty"`
|
||||
BaseURL string `mapstructure:"baseurl" yaml:"baseurl,omitempty"`
|
||||
Engine string `mapstructure:"engine" yaml:"engine,omitempty"`
|
||||
}
|
||||
|
||||
func (p *AIProvider) GetBaseURL() string {
|
||||
@ -68,6 +82,10 @@ func (p *AIProvider) GetModel() string {
|
||||
return p.Model
|
||||
}
|
||||
|
||||
func (p *AIProvider) GetEngine() string {
|
||||
return p.Engine
|
||||
}
|
||||
|
||||
func NeedPassword(backend string) bool {
|
||||
return backend != "localai"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user