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
7 changes: 4 additions & 3 deletions go/adk/pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,10 @@ func CreateLLM(ctx context.Context, m adk.Model, log logr.Logger) (adkmodel.LLM,
}
// Use Bedrock Converse API for ALL models (including Anthropic)
cfg := &models.BedrockConfig{
TransportConfig: transportConfigFromBase(m.BaseModel, nil),
Model: modelName,
Region: region,
TransportConfig: transportConfigFromBase(m.BaseModel, nil),
Model: modelName,
Region: region,
AdditionalModelRequestFields: m.AdditionalModelRequestFields,
}
return models.NewBedrockModelWithLogger(ctx, cfg, log)

Expand Down
56 changes: 36 additions & 20 deletions go/adk/pkg/models/bedrock.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ func sanitizeBedrockToolID(id string, idMap map[string]string, counter *int) str
// BedrockConfig holds Bedrock configuration for the Converse API
type BedrockConfig struct {
TransportConfig
Model string
Region string
MaxTokens *int
Temperature *float64
TopP *float64
TopK *int
Model string
Region string
MaxTokens *int
Temperature *float64
TopP *float64
AdditionalModelRequestFields map[string]any
}

// BedrockModel implements model.LLM for Amazon Bedrock using the Converse API.
Expand Down Expand Up @@ -158,26 +158,41 @@ func (m *BedrockModel) GenerateContent(ctx context.Context, req *model.LLMReques
}
}

// Build model-specific additional fields (Claude top_k, thinking, etc.)
additionalFields := m.buildAdditionalModelRequestFields()

// Set telemetry attributes
telemetry.SetLLMRequestAttributes(ctx, modelName, req)

if stream {
m.generateStreaming(ctx, modelName, messages, systemPrompt, inferenceConfig, toolConfig, yield)
m.generateStreaming(ctx, modelName, messages, systemPrompt, inferenceConfig, toolConfig, additionalFields, yield)
} else {
m.generateNonStreaming(ctx, modelName, messages, systemPrompt, inferenceConfig, toolConfig, yield)
m.generateNonStreaming(ctx, modelName, messages, systemPrompt, inferenceConfig, toolConfig, additionalFields, yield)
}
}
}

// buildAdditionalModelRequestFields returns a document.Interface containing
// model-specific parameters that are not part of InferenceConfiguration.
// The raw map is forwarded as-is to the Bedrock Converse API.
// Returns nil when no extra fields are configured.
func (m *BedrockModel) buildAdditionalModelRequestFields() document.Interface {
Comment on lines +175 to +179
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior is introduced here (building AdditionalModelRequestFields for top_k / thinking), but there are no unit tests asserting when this is nil vs populated and what JSON is sent to Bedrock. Please add coverage (e.g., in go/adk/pkg/models/bedrock_test.go) for: no fields set → nil, only TopK set → {top_k: ...}, only ThinkingBudgetTokens set → {thinking: {...}}, and both set.

Copilot uses AI. Check for mistakes.
if len(m.Config.AdditionalModelRequestFields) == 0 {
return nil
}
return document.NewLazyDocument(m.Config.AdditionalModelRequestFields)
}

// generateStreaming handles streaming responses from Bedrock ConverseStream.
// It properly handles both text and tool use content blocks during streaming.
func (m *BedrockModel) generateStreaming(ctx context.Context, modelId string, messages []types.Message, systemPrompt []types.SystemContentBlock, inferenceConfig *types.InferenceConfiguration, toolConfig *types.ToolConfiguration, yield func(*model.LLMResponse, error) bool) {
func (m *BedrockModel) generateStreaming(ctx context.Context, modelId string, messages []types.Message, systemPrompt []types.SystemContentBlock, inferenceConfig *types.InferenceConfiguration, toolConfig *types.ToolConfiguration, additionalFields document.Interface, yield func(*model.LLMResponse, error) bool) {
output, err := m.Client.ConverseStream(ctx, &bedrockruntime.ConverseStreamInput{
ModelId: aws.String(modelId),
Messages: messages,
System: systemPrompt,
InferenceConfig: inferenceConfig,
ToolConfig: toolConfig,
ModelId: aws.String(modelId),
Messages: messages,
System: systemPrompt,
InferenceConfig: inferenceConfig,
ToolConfig: toolConfig,
AdditionalModelRequestFields: additionalFields,
})

if err != nil {
Expand Down Expand Up @@ -323,13 +338,14 @@ func (tc *streamingToolCall) parseArgs() map[string]any {
}

// generateNonStreaming handles non-streaming responses from Bedrock Converse.
func (m *BedrockModel) generateNonStreaming(ctx context.Context, modelId string, messages []types.Message, systemPrompt []types.SystemContentBlock, inferenceConfig *types.InferenceConfiguration, toolConfig *types.ToolConfiguration, yield func(*model.LLMResponse, error) bool) {
func (m *BedrockModel) generateNonStreaming(ctx context.Context, modelId string, messages []types.Message, systemPrompt []types.SystemContentBlock, inferenceConfig *types.InferenceConfiguration, toolConfig *types.ToolConfiguration, additionalFields document.Interface, yield func(*model.LLMResponse, error) bool) {
output, err := m.Client.Converse(ctx, &bedrockruntime.ConverseInput{
ModelId: aws.String(modelId),
Messages: messages,
System: systemPrompt,
InferenceConfig: inferenceConfig,
ToolConfig: toolConfig,
ModelId: aws.String(modelId),
Messages: messages,
System: systemPrompt,
InferenceConfig: inferenceConfig,
ToolConfig: toolConfig,
AdditionalModelRequestFields: additionalFields,
})

if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions go/api/adk/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ type Bedrock struct {
BaseModel
// Region is the AWS region where the model is available
Region string `json:"region,omitempty"`
// AdditionalModelRequestFields passes model-specific parameters to Bedrock's
// additionalModelRequestFields in the Converse API. Use this for provider-specific
// options outside the standard InferenceConfiguration block.
AdditionalModelRequestFields map[string]any `json:"additional_model_request_fields,omitempty"`
}

func (b *Bedrock) MarshalJSON() ([]byte, error) {
Expand Down
8 changes: 8 additions & 0 deletions go/api/config/crd/bases/kagent.dev_modelconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,14 @@ spec:
bedrock:
description: AWS Bedrock-specific configuration
properties:
additionalModelRequestFields:
description: |-
AdditionalModelRequestFields passes model-specific parameters to Bedrock's
additionalModelRequestFields in the Converse API. Use this for provider-specific
options that are not part of the standard InferenceConfiguration block, such as
Claude extended thinking or top_k. Values are forwarded as-is to the API.
Example: {"top_k": 5, "thinking": {"type": "enabled", "budget_tokens": 16000}}
x-kubernetes-preserve-unknown-fields: true
region:
description: AWS region where the Bedrock model is available (e.g.,
us-east-1, us-west-2)
Expand Down
10 changes: 10 additions & 0 deletions go/api/v1alpha2/modelconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1alpha2

import (
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -243,6 +244,15 @@ type BedrockConfig struct {
// AWS region where the Bedrock model is available (e.g., us-east-1, us-west-2)
// +required
Region string `json:"region"`

// AdditionalModelRequestFields passes model-specific parameters to Bedrock's
// additionalModelRequestFields in the Converse API. Use this for provider-specific
// options that are not part of the standard InferenceConfiguration block, such as
// Claude extended thinking or top_k. Values are forwarded as-is to the API.
// Example: {"top_k": 5, "thinking": {"type": "enabled", "budget_tokens": 16000}}
// +optional
// +kubebuilder:pruning:PreserveUnknownFields
AdditionalModelRequestFields *apiextensionsv1.JSON `json:"additionalModelRequestFields,omitempty"`
}

// SAPAICoreConfig contains SAP AI Core-specific configuration options.
Expand Down
8 changes: 7 additions & 1 deletion go/api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
_ "embed"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"maps"
Expand Down Expand Up @@ -683,12 +684,19 @@ func (a *adkApiTranslator) translateModel(ctx context.Context, namespace, modelC
}
}
}
var additionalFields map[string]any
if model.Spec.Bedrock.AdditionalModelRequestFields != nil {
if err := json.Unmarshal(model.Spec.Bedrock.AdditionalModelRequestFields.Raw, &additionalFields); err != nil {
return nil, nil, nil, fmt.Errorf("failed to unmarshal bedrock additionalModelRequestFields: %w", err)
}
}
bedrock := &adk.Bedrock{
BaseModel: adk.BaseModel{
Model: model.Spec.Model,
Headers: model.Spec.DefaultHeaders,
},
Region: model.Spec.Bedrock.Region,
Region: model.Spec.Bedrock.Region,
AdditionalModelRequestFields: additionalFields,
}

// Populate TLS fields in BaseModel
Expand Down
8 changes: 8 additions & 0 deletions helm/kagent-crds/templates/kagent.dev_modelconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,14 @@ spec:
bedrock:
description: AWS Bedrock-specific configuration
properties:
additionalModelRequestFields:
description: |-
AdditionalModelRequestFields passes model-specific parameters to Bedrock's
additionalModelRequestFields in the Converse API. Use this for provider-specific
options that are not part of the standard InferenceConfiguration block, such as
Claude extended thinking or top_k. Values are forwarded as-is to the API.
Example: {"top_k": 5, "thinking": {"type": "enabled", "budget_tokens": 16000}}
x-kubernetes-preserve-unknown-fields: true
region:
description: AWS region where the Bedrock model is available (e.g.,
us-east-1, us-west-2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class KAgentBedrockLlm(BaseLlm):
"""

extra_headers: Optional[dict[str, str]] = None
additional_model_request_fields: Optional[dict[str, Any]] = None
model_config = {"arbitrary_types_allowed": True}

@cached_property
Expand Down Expand Up @@ -244,6 +245,9 @@ async def generate_content_async(
if inference_config:
kwargs["inferenceConfig"] = inference_config

if self.additional_model_request_fields:
kwargs["additionalModelRequestFields"] = self.additional_model_request_fields

def _run_converse_stream(**kw):
resp = client.converse_stream(**kw)
return list(resp.get("stream", []))
Expand Down
5 changes: 5 additions & 0 deletions python/packages/kagent-adk/src/kagent/adk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ class Gemini(BaseLLM):

class Bedrock(BaseLLM):
region: str | None = None
# additional_model_request_fields passes model-specific parameters to Bedrock's
# additionalModelRequestFields in the Converse API. Use this for provider-specific
# options outside the standard InferenceConfiguration block.
additional_model_request_fields: dict | None = None
type: Literal["bedrock"]


Expand Down Expand Up @@ -569,6 +573,7 @@ def _create_llm_from_model_config(model_config: ModelUnion):
return KAgentBedrockLlm(
model=model_config.model,
extra_headers=extra_headers,
additional_model_request_fields=model_config.additional_model_request_fields,
)
if model_config.type == "sap_ai_core":
from .models._sap_ai_core import KAgentSAPAICoreLlm
Expand Down
Loading