mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2026-07-25 22:37:39 +00:00
feat: add Amazon Bedrock Mantle (OpenAI-compatible) AI provider (#1640)
* feat: support amazonbedrock converse api (#1627) * feat: add amazon bedrock converse api support Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> * docs(amazonbedrockconverse): add backend amazonbedrockconverse details Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> * fix(amazonbedrockconverse): error statements and comment cleanup Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> * test(amazonbedrockconverse): add unit tests Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> * fix(amazonbedrockconverse): linting, test coverage, converse output review Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> --------- Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * chore(main): release 0.4.31 (#1619) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * fix(deps): update module google.golang.org/grpc to v1.79.3 [security] (#1626) Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * feat: add Amazon Bedrock Mantle (OpenAI-compatible) AI provider Add bedrockmantle as a new AI backend that uses Amazon Bedrock's OpenAI-compatible Chat Completions endpoint (bedrock-mantle.{region}.api.aws). - New provider: pkg/ai/bedrockmantle.go - Reads API key from AWS_BEARER_TOKEN_BEDROCK env var - Auto-constructs endpoint URL from providerRegion - Reuses existing go-openai library - Registered in clients, Backends, and passwordlessProviders - providerRegion required via cmd/auth/add.go PreRun - Unit tests with mock HTTP server - Updated SUPPORTED_MODELS.md with supported models Signed-off-by: Sanket <sankeyraut@gmail.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * fix: address errcheck lint errors in bedrockmantle_test.go Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * fix: recognize GKE built-in ingress classes 'gce' and 'gce-internal' (#1599) * fix: recognize GKE built-in ingress classes 'gce' and 'gce-internal' Skip IngressClass existence validation for GKE's built-in ingress classes that work without an explicit IngressClass resource in the cluster. This fixes false positive errors when using 'gce' or 'gce-internal' ingress class on GKE. Closes #849 Signed-off-by: majiayu000 <1835304752@qq.com> * chore: trigger CI re-run Signed-off-by: majiayu000 <1835304752@qq.com> --------- Signed-off-by: majiayu000 <1835304752@qq.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * fix: improve ConfigMap usage detection for sidecar patterns (#1602) - Add detection for dynamically loaded ConfigMaps (Grafana sidecar) - Support grafana_dashboard and grafana_datasource labels - Support prometheus_rule and fluentd_config labels - Add k8sgpt.ai/dynamically-loaded label for custom patterns - Add k8sgpt.ai/skip-usage-check annotation to opt-out - Add comprehensive test cases for sidecar patterns Fixes false positives where ConfigMaps loaded dynamically by sidecar containers (via Kubernetes API watches with label selectors) were incorrectly flagged as unused. Tested on production cluster with kube-prometheus-stack: - Before: 29 ConfigMaps incorrectly flagged as unused - After: No false positives (29 eliminated - 100% reduction) Signed-off-by: sqautboy <migonyoung01@gmail.com> Co-authored-by: Alex Jones <1235925+AlexsJones@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * feat: add Azure API Type Support and add Custom HTTP Header (#1638) Signed-off-by: lawrencelo8 <lawrencelo8@users.noreply.github.com> Co-authored-by: Lo, Jungshih <lawrencelo8@users.noreply.github.com> Co-authored-by: Alex Jones <1235925+AlexsJones@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * feat: add daemonset analyzer and special cases for pod and job (#1636) Signed-off-by: Semyon Inokov <semen.inokov@gmail.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * fix: amazonbedrockconverse claude models temp and topp (#1629) Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> * chore(main): release 0.4.32 (#1628) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> --------- Signed-off-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> Signed-off-by: Sanket Raut <sankeyraut@gmail.com> Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Signed-off-by: Sanket <sankeyraut@gmail.com> Signed-off-by: majiayu000 <1835304752@qq.com> Signed-off-by: sqautboy <migonyoung01@gmail.com> Signed-off-by: lawrencelo8 <lawrencelo8@users.noreply.github.com> Signed-off-by: Semyon Inokov <semen.inokov@gmail.com> Signed-off-by: Sanket Raut <sankeyraut@users.noreply.github.com> Co-authored-by: CradleKing24 <44717227+CradleKing24@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: lif <1835304752@qq.com> Co-authored-by: lee jaeyoung <migonyoung01@gmail.com> Co-authored-by: Alex Jones <1235925+AlexsJones@users.noreply.github.com> Co-authored-by: lawrencelo8 <177652791+lawrencelo8@users.noreply.github.com> Co-authored-by: Lo, Jungshih <lawrencelo8@users.noreply.github.com> Co-authored-by: Semyon Inokov <146972583+doppoluv@users.noreply.github.com>
This commit is contained in:
@@ -83,6 +83,28 @@ K8sGPT supports a variety of AI/LLM providers (backends). Some providers have a
|
||||
### Groq
|
||||
- **Model:** User-configurable (any model supported by Groq, e.g., `llama-3.3-70b-versatile`, `mixtral-8x7b-32768`)
|
||||
|
||||
### Amazon Bedrock Mantle (OpenAI-compatible)
|
||||
- **Auth:** Reads the API key from the `AWS_BEARER_TOKEN_BEDROCK` environment variable
|
||||
- **Region:** Required — the endpoint is auto-constructed as `https://bedrock-mantle.{region}.api.aws/v1`
|
||||
- **API:** Uses the OpenAI-compatible Chat Completions API (`/v1/chat/completions`)
|
||||
- **Supported Models:**
|
||||
|
||||
| Provider | Models |
|
||||
|---|---|
|
||||
| Anthropic | Claude Mythos Preview, Claude Opus 4.7 |
|
||||
| DeepSeek | DeepSeek V3.2, DeepSeek-V3.1 |
|
||||
| Google | Gemma 3 4B IT, Gemma 3 12B IT, Gemma 3 27B PT |
|
||||
| MiniMax | MiniMax M2, M2.1, M2.5 |
|
||||
| Mistral AI | Devstral 2 123B, Magistral Small 2509, Ministral 14B 3.0, Ministral 3 8B, Ministral 3B, Mistral Large 3, Voxtral Mini 3B, Voxtral Small 24B |
|
||||
| Moonshot AI | Kimi K2 Thinking, Kimi K2.5 |
|
||||
| NVIDIA | Nemotron Nano 9B v2, Nemotron Nano 12B v2 VL, Nemotron Nano 3 30B, Nemotron 3 Super 120B |
|
||||
| OpenAI | gpt-oss-120b, gpt-oss-20b, GPT OSS Safeguard 120B, GPT OSS Safeguard 20B |
|
||||
| Qwen | Qwen3 235B, Qwen3 32B, Qwen3 Coder 480B, Qwen3 Coder Next, Qwen3 Next 80B, Qwen3 VL 235B, Qwen3-Coder-30B |
|
||||
| Writer | Palmyra Vision 7B |
|
||||
| Z.AI | GLM 4.7, GLM 4.7 Flash, GLM 5 |
|
||||
|
||||
> **Note:** For the latest model availability, see [Endpoint availability](https://docs.aws.amazon.com/bedrock/latest/userguide/models-endpoint-availability.html).
|
||||
|
||||
---
|
||||
|
||||
For more details on configuring each provider and model, refer to the official K8sGPT documentation and the provider's own documentation.
|
||||
|
||||
@@ -54,6 +54,9 @@ var addCmd = &cobra.Command{
|
||||
if strings.ToLower(backend) == "amazonbedrockconverse" {
|
||||
_ = cmd.MarkFlagRequired("providerRegion")
|
||||
}
|
||||
if strings.ToLower(backend) == "bedrockmantle" {
|
||||
_ = cmd.MarkFlagRequired("providerRegion")
|
||||
}
|
||||
if strings.ToLower(backend) == "ibmwatsonxai" {
|
||||
_ = cmd.MarkFlagRequired("providerId")
|
||||
}
|
||||
@@ -184,9 +187,9 @@ func init() {
|
||||
addCmd.Flags().Float32VarP(&temperature, "temperature", "t", 0.7, "The sampling temperature, value ranges between 0 ( output be more deterministic) and 1 (more random)")
|
||||
// add flag for azure open ai engine/deployment name
|
||||
addCmd.Flags().StringVarP(&engine, "engine", "e", "", "Azure AI deployment name (only for azureopenai backend)")
|
||||
// add flag for amazonbedrock region name
|
||||
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name (only for amazonbedrock, amazonbedrockconverse, googlevertexai backend)")
|
||||
// add flag for vertexAI/WatsonxAI Project ID
|
||||
//add flag for amazonbedrock region name
|
||||
addCmd.Flags().StringVarP(&providerRegion, "providerRegion", "r", "", "Provider Region name (only for amazonbedrock, amazonbedrockconverse, bedrockmantle, googlevertexai backend)")
|
||||
//add flag for vertexAI/WatsonxAI Project ID
|
||||
addCmd.Flags().StringVarP(&providerId, "providerId", "i", "", "Provider specific ID for e.g. project (only for googlevertexai/ibmwatsonxai backend)")
|
||||
// add flag for OCI Compartment ID
|
||||
addCmd.Flags().StringVarP(&compartmentId, "compartmentId", "k", "", "Compartment ID for generative AI model (only for oci backend)")
|
||||
|
||||
94
pkg/ai/bedrockmantle.go
Normal file
94
pkg/ai/bedrockmantle.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
const bedrockMantleClientName = "bedrockmantle"
|
||||
|
||||
type AmazonBedrockMantleClient struct {
|
||||
nopCloser
|
||||
|
||||
client *openai.Client
|
||||
model string
|
||||
temperature float32
|
||||
topP float32
|
||||
}
|
||||
|
||||
func (c *AmazonBedrockMantleClient) Configure(config IAIConfig) error {
|
||||
region := getRegion(config.GetProviderRegion())
|
||||
if region == "" {
|
||||
return errors.New("provider region is required for bedrockmantle (e.g. us-east-1)")
|
||||
}
|
||||
|
||||
token := os.Getenv("AWS_BEARER_TOKEN_BEDROCK")
|
||||
if token == "" {
|
||||
return errors.New("AWS_BEARER_TOKEN_BEDROCK environment variable is required for bedrockmantle")
|
||||
}
|
||||
defaultConfig := openai.DefaultConfig(token)
|
||||
|
||||
baseURL := config.GetBaseURL()
|
||||
if baseURL != "" {
|
||||
defaultConfig.BaseURL = baseURL
|
||||
} else {
|
||||
defaultConfig.BaseURL = fmt.Sprintf("https://bedrock-mantle.%s.api.aws/v1", region)
|
||||
}
|
||||
|
||||
transport := &http.Transport{}
|
||||
if proxyEndpoint := config.GetProxyEndpoint(); proxyEndpoint != "" {
|
||||
proxyUrl, err := url.Parse(proxyEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transport.Proxy = http.ProxyURL(proxyUrl)
|
||||
}
|
||||
|
||||
defaultConfig.HTTPClient = &http.Client{
|
||||
Transport: &OpenAIHeaderTransport{
|
||||
Origin: transport,
|
||||
Headers: config.GetCustomHeaders(),
|
||||
},
|
||||
}
|
||||
|
||||
client := openai.NewClientWithConfig(defaultConfig)
|
||||
if client == nil {
|
||||
return errors.New("error creating Bedrock Mantle client")
|
||||
}
|
||||
c.client = client
|
||||
c.model = config.GetModel()
|
||||
c.temperature = config.GetTemperature()
|
||||
c.topP = config.GetTopP()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmazonBedrockMantleClient) GetCompletion(ctx context.Context, prompt string) (string, error) {
|
||||
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
|
||||
Model: c.model,
|
||||
Messages: []openai.ChatCompletionMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: prompt,
|
||||
},
|
||||
},
|
||||
Temperature: c.temperature,
|
||||
MaxTokens: maxToken,
|
||||
PresencePenalty: presencePenalty,
|
||||
FrequencyPenalty: frequencyPenalty,
|
||||
TopP: c.topP,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
func (c *AmazonBedrockMantleClient) GetName() string {
|
||||
return bedrockMantleClientName
|
||||
}
|
||||
158
pkg/ai/bedrockmantle_test.go
Normal file
158
pkg/ai/bedrockmantle_test.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBedrockMantleGetName(t *testing.T) {
|
||||
client := &AmazonBedrockMantleClient{}
|
||||
assert.Equal(t, "bedrockmantle", client.GetName())
|
||||
}
|
||||
|
||||
func TestBedrockMantleConfigure(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envRegion string
|
||||
envToken string
|
||||
cfg *AIProvider
|
||||
expectError bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "missing region",
|
||||
envRegion: "",
|
||||
envToken: "test-key",
|
||||
cfg: &AIProvider{
|
||||
Model: "test-model",
|
||||
},
|
||||
expectError: true,
|
||||
errContains: "provider region is required",
|
||||
},
|
||||
{
|
||||
name: "missing token",
|
||||
envRegion: "",
|
||||
envToken: "",
|
||||
cfg: &AIProvider{
|
||||
Model: "test-model",
|
||||
ProviderRegion: "us-east-1",
|
||||
},
|
||||
expectError: true,
|
||||
errContains: "AWS_BEARER_TOKEN_BEDROCK",
|
||||
},
|
||||
{
|
||||
name: "success with region and token",
|
||||
envRegion: "",
|
||||
envToken: "test-key",
|
||||
cfg: &AIProvider{
|
||||
Model: "anthropic.claude-3-sonnet",
|
||||
ProviderRegion: "us-east-1",
|
||||
Temperature: 0.7,
|
||||
TopP: 0.9,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "env region overrides config",
|
||||
envRegion: "eu-west-1",
|
||||
envToken: "test-key",
|
||||
cfg: &AIProvider{
|
||||
Model: "test-model",
|
||||
ProviderRegion: "us-east-1",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Setenv("AWS_DEFAULT_REGION", tt.envRegion)
|
||||
t.Setenv("AWS_BEARER_TOKEN_BEDROCK", tt.envToken)
|
||||
|
||||
client := &AmazonBedrockMantleClient{}
|
||||
err := client.Configure(tt.cfg)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.errContains)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.cfg.Model, client.model)
|
||||
assert.Equal(t, tt.cfg.Temperature, client.temperature)
|
||||
assert.Equal(t, tt.cfg.TopP, client.topP)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBedrockMantleConfigure_CustomBaseURL(t *testing.T) {
|
||||
t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "test-key")
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
cfg := &AIProvider{
|
||||
Model: "test-model",
|
||||
BaseURL: server.URL,
|
||||
ProviderRegion: "us-east-1",
|
||||
}
|
||||
client := &AmazonBedrockMantleClient{}
|
||||
err := client.Configure(cfg)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestBedrockMantleGetCompletion_Success(t *testing.T) {
|
||||
t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "test-key")
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := map[string]interface{}{
|
||||
"id": "chatcmpl-123",
|
||||
"object": "chat.completion",
|
||||
"choices": []map[string]interface{}{{"index": 0, "message": map[string]string{"role": "assistant", "content": "mock response"}, "finish_reason": "stop"}},
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
cfg := &AIProvider{
|
||||
Model: "test-model",
|
||||
BaseURL: server.URL,
|
||||
ProviderRegion: "us-east-1",
|
||||
}
|
||||
client := &AmazonBedrockMantleClient{}
|
||||
err := client.Configure(cfg)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, err := client.GetCompletion(context.Background(), "hello")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "mock response", result)
|
||||
}
|
||||
|
||||
func TestBedrockMantleGetCompletion_Error(t *testing.T) {
|
||||
t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "test-key")
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": map[string]string{"message": "model error", "type": "server_error"},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
cfg := &AIProvider{
|
||||
Model: "test-model",
|
||||
BaseURL: server.URL,
|
||||
ProviderRegion: "us-east-1",
|
||||
}
|
||||
client := &AmazonBedrockMantleClient{}
|
||||
err := client.Configure(cfg)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = client.GetCompletion(context.Background(), "hello")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
@@ -37,6 +37,7 @@ var (
|
||||
&CustomRestClient{},
|
||||
&IBMWatsonxAIClient{},
|
||||
&GroqClient{},
|
||||
&AmazonBedrockMantleClient{},
|
||||
}
|
||||
Backends = []string{
|
||||
openAIClientName,
|
||||
@@ -56,6 +57,7 @@ var (
|
||||
CustomRestClientName,
|
||||
ibmWatsonxAIClientName,
|
||||
groqAIClientName,
|
||||
bedrockMantleClientName,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -207,7 +209,7 @@ func (p *AIProvider) GetCustomHeaders() []http.Header {
|
||||
return p.CustomHeaders
|
||||
}
|
||||
|
||||
var passwordlessProviders = []string{"localai", "ollama", "amazonsagemaker", "amazonbedrock", "amazonbedrockconverse", "googlevertexai", "oci", "customrest"}
|
||||
var passwordlessProviders = []string{"localai", "ollama", "amazonsagemaker", "amazonbedrock", "amazonbedrockconverse", "googlevertexai", "oci", "customrest", "bedrockmantle"}
|
||||
|
||||
func NeedPassword(backend string) bool {
|
||||
for _, b := range passwordlessProviders {
|
||||
|
||||
Reference in New Issue
Block a user