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:
Sanket Raut
2026-07-24 12:13:39 +05:30
committed by GitHub
parent 77cf9d97ae
commit bdbc3c7796
5 changed files with 283 additions and 4 deletions

View File

@@ -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.

View File

@@ -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
View 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
}

View 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)
}

View File

@@ -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 {