Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions go/adk/pkg/models/bedrock.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ type BedrockConfig struct {
GuardrailTrace string
}

// BedrockRuntimeOrigin returns the HTTPS origin of Bedrock's runtime endpoint
// for a region.
//
// The endpoint is derived from the region rather than stored on the model
// spec, so every consumer has to reconstruct it: credential compilation binds
// the bearer token to this origin, the harness compilers allowlist it, and the
// ADK builder puts it on the agent's egress list. Copying the format at each
// site let the credential binding and the allowlist drift apart, and a request
// those two disagreed on was rejected before it left the pod. This is the one
// definition they all call; a consumer that needs the egress form appends
// ":443".
func BedrockRuntimeOrigin(region string) string {
return "https://bedrock-runtime." + region + ".amazonaws.com"
}

// DecodeBedrockRequestFields preserves provider JSON numbers using the AWS
// document decoder, whose number type is understood by the Converse encoder.
func DecodeBedrockRequestFields(raw []byte) (map[string]any, error) {
Expand Down
5 changes: 5 additions & 0 deletions go/adk/pkg/models/bedrock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import (
"google.golang.org/genai"
)

func TestBedrockRuntimeOrigin(t *testing.T) {
require.Equal(t, "https://bedrock-runtime.us-east-1.amazonaws.com", BedrockRuntimeOrigin("us-east-1"))
require.Equal(t, "https://bedrock-runtime.eu-central-1.amazonaws.com", BedrockRuntimeOrigin("eu-central-1"))
}

func TestBedrockProviderJSONReachesConverseUnchanged(t *testing.T) {
t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
raw := `{"thinking":{"budget_tokens":1024},"large":9007199254740993,"values":[0,false,null,0.25]}`
Expand Down
9 changes: 9 additions & 0 deletions go/core/internal/translator/adkconfig/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,15 @@ func agentConfigDestinations(cfg *configv1.ADKAgentConfig, modelConfig *v1alpha3
if mistral := modelConfig.Spec.Mistral; mistral == nil || mistral.BaseURL == nil || *mistral.BaseURL == "" {
destinations = append(destinations, "https://api.mistral.ai:443")
}
case v1alpha3.ModelProviderBedrock:
// Bedrock's endpoint is derived from the region, which is not part of
// the serialized model, so the walk above never sees it. The bearer
// token is bound to this origin in CompileCredentials; without the
// matching allowlist entry CanonicalAccess rejects the credential and
// the revision cannot be admitted.
if bedrock := modelConfig.Spec.Bedrock; bedrock != nil {
destinations = append(destinations, models.BedrockRuntimeOrigin(bedrock.Region)+":443")
}
case v1alpha3.ModelProviderOllama:
// Ollama's endpoint is the provider's own field and is not part of the
// serialized model, so the walk above never sees it. Unlike the
Expand Down
26 changes: 26 additions & 0 deletions go/core/internal/translator/adkconfig/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,32 @@ func TestOllamaEgressDestination(t *testing.T) {
})
}

// TestBedrockEgressDestination covers the provider whose endpoint is derived
// from the region on the ModelConfig's own Bedrock field rather than from the
// serialized model. CompileCredentials binds the bearer token to that regional
// runtime origin, so the origin has to be in the allowlist or CanonicalAccess
// rejects the credential and the revision cannot be admitted.
func TestBedrockEgressDestination(t *testing.T) {
model := &v1alpha3.ModelConfig{
ObjectMeta: metav1.ObjectMeta{Name: "bedrock", Namespace: "test"},
Spec: v1alpha3.ModelConfigSpec{
Provider: v1alpha3.ModelProviderBedrock, Model: "anthropic.claude-sonnet-4-5-20250929-v1:0",
APIKeySecret: "bedrock-auth", APIKeySecretKey: env.AWSBearerTokenBedrock.Name(),
Bedrock: &v1alpha3.BedrockConfig{Region: "us-east-1"},
},
}
collections := contextTestCollections(t, model)
result, err := NewBuilder(krt.TestingDummyContext{}, collections).Build(context.Background(),
&translator.HarnessInput{
Harness: &translator.HarnessConfiguration{Spec: v1alpha3.HarnessSpec{Kagent: &v1alpha3.KagentHarness{}}},
Root: &translator.AgentInput{
Template: &translator.TemplateConfiguration{Name: "pi", Namespace: "test"},
ResolvedModelConfig: resolvedModel(t, collections, "bedrock"),
}})
require.NoError(t, err)
require.Contains(t, result.Egress, "https://bedrock-runtime.us-east-1.amazonaws.com:443")
}

func contextTestCollections(t *testing.T, models ...*v1alpha3.ModelConfig) translator.Collections {
t.Helper()
objects := make([]any, 0, 2*len(models))
Expand Down
3 changes: 2 additions & 1 deletion go/core/internal/translator/claude/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"

"github.com/kagent-dev/kagent/go/adk/pkg/models"
configv1 "github.com/kagent-dev/kagent/go/api/gen/kagent/config/v1"
"github.com/kagent-dev/kagent/go/api/v1alpha3"
"github.com/kagent-dev/kagent/go/core/internal/egress"
Expand Down Expand Up @@ -257,7 +258,7 @@ func (c *Compiler) provider(ctx context.Context, resolved *translator.ResolvedMo
environment = append(environment, secretEnvironment(claudeconfig.AWSSessionTokenEnvName, secret.Name, claudeconfig.AWSSessionTokenEnvName))
}
}
return environment, []string{"https://bedrock-runtime." + model.Spec.Bedrock.Region + ".amazonaws.com:443"}, nil
return environment, []string{models.BedrockRuntimeOrigin(model.Spec.Bedrock.Region) + ":443"}, nil

case v1alpha3.ModelProviderAnthropicVertexAI:
if model.Spec.AnthropicVertexAI == nil || strings.TrimSpace(model.Spec.AnthropicVertexAI.ProjectID) == "" || strings.TrimSpace(model.Spec.AnthropicVertexAI.Location) == "" {
Expand Down
3 changes: 2 additions & 1 deletion go/core/internal/translator/codex/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"

"github.com/kagent-dev/kagent/go/adk/pkg/models"
configv1 "github.com/kagent-dev/kagent/go/api/gen/kagent/config/v1"
"github.com/kagent-dev/kagent/go/api/v1alpha3"
"github.com/kagent-dev/kagent/go/core/internal/egress"
Expand Down Expand Up @@ -227,7 +228,7 @@ func (c *Compiler) compileProvider(ctx context.Context, resolved *translator.Res
environment = append(environment, secretEnvironment(awsSessionTokenEnv, secret.Name, awsSessionTokenEnv))
}
}
return codexconfig.Provider{Name: "amazon-bedrock"}, environment, []string{"https://bedrock-runtime." + region + ".amazonaws.com:443"}, nil
return codexconfig.Provider{Name: "amazon-bedrock"}, environment, []string{models.BedrockRuntimeOrigin(region) + ":443"}, nil
default:
return codexconfig.Provider{}, nil, nil, translator.NewValidationError("Codex does not support ModelConfig provider %q", model.Spec.Provider)
}
Expand Down
2 changes: 1 addition & 1 deletion go/core/internal/translator/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func modelCredentialTarget(resolved *ResolvedModelConfig) (name, endpoint, heade
case v1alpha3.ModelProviderBedrock:
name, header, prefix = env.AWSBearerTokenBedrock.Name(), "authorization", "Bearer "
if spec.Bedrock != nil {
endpoint = fmt.Sprintf("https://bedrock-runtime.%s.amazonaws.com", spec.Bedrock.Region)
endpoint = models.BedrockRuntimeOrigin(spec.Bedrock.Region)
}
case v1alpha3.ModelProviderOllama:
// Ollama Cloud is the only keyed Ollama endpoint, so a binding is
Expand Down