-
Notifications
You must be signed in to change notification settings - Fork 1.5k
fix: validate bedrock protocol at provider construction #27234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d8878a5
f55d714
c31a5c1
8be08b2
41add5c
5429e02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,10 +70,20 @@ type AWSBedrock struct { | |
| Protocol BedrockProtocol | ||
| } | ||
|
|
||
| // ResolvedProtocol returns the configured protocol, mapping the empty value to | ||
| // the legacy InvokeModel protocol so existing providers keep the legacy | ||
| // behavior. | ||
| func (c AWSBedrock) ResolvedProtocol() BedrockProtocol { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 [CRF-5] The empty-value default now lives in three spots: here,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit [CRF-7]
|
||
| if c.Protocol == "" { | ||
| return BedrockProtocolInvokeModel | ||
| } | ||
| return c.Protocol | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potentially a larger refactor, so take it or leave it: unexport Alternatively, make
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think this requires too many changes. We'd need to add a constructor and update dozens of call sites.
That's an interesting idea—I hadn't considered it when I first implemented this.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't consider this confusing: type BedrockProtocol string
const (
BedrockProtocolDefault = ""
BedrockProtocolInvokeModel = "invoke-model"
BedrockProtocolMantle = "mantle"
)
...
switch (proto) {
case BedrockProtocolDefault, BedrockProtocolInvokeModel:
// handle invoke-model
case BedrockProtocolMantle:
// handle mantle
default:
// unknown, error
}But it's fair enough to leave it as a follow-up. We do still have a window to change the bedrock protocol stuff around before the next release though!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought you mean: type BedrockProtocol string
const (
BedrockProtocolInvokeModel = ""
BedrockProtocolMantle = "mantle"
)
...
switch (proto) {
case BedrockProtocolInvokeModel:
// handle invoke-model
case BedrockProtocolMantle:
// handle mantle
default:
// unknown, error
} |
||
|
|
||
| // Validate verifies protocol-specific Bedrock configuration. | ||
| func (c AWSBedrock) Validate() error { | ||
| switch c.Protocol { | ||
| case "", BedrockProtocolInvokeModel: | ||
| switch c.ResolvedProtocol() { | ||
| case BedrockProtocolInvokeModel: | ||
| if c.Region == "" && c.BaseURL == "" { | ||
| return xerrors.New("region or base url required") | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -133,14 +133,13 @@ func (i *interceptionBase) CorrelatingToolCallID() *string { | |
| // isBedrockMantle reports whether the interception targets the Bedrock mantle | ||
| // protocol. | ||
| func (i *interceptionBase) isBedrockMantle() bool { | ||
| return i.bedrock != nil && i.bedrock.Cfg.Protocol == aibconfig.BedrockProtocolMantle | ||
| return i.bedrock != nil && i.bedrock.Cfg.ResolvedProtocol() == aibconfig.BedrockProtocolMantle | ||
| } | ||
|
|
||
| // isBedrockInvokeModel reports whether the interception targets the Bedrock | ||
| // InvokeModel protocol. | ||
| func (i *interceptionBase) isBedrockInvokeModel() bool { | ||
| return i.bedrock != nil && | ||
| (i.bedrock.Cfg.Protocol == "" || i.bedrock.Cfg.Protocol == aibconfig.BedrockProtocolInvokeModel) | ||
| return i.bedrock != nil && i.bedrock.Cfg.ResolvedProtocol() == aibconfig.BedrockProtocolInvokeModel | ||
| } | ||
|
|
||
| func (i *interceptionBase) Model() string { | ||
|
|
@@ -164,7 +163,7 @@ func (i *interceptionBase) Model() string { | |
| } | ||
|
|
||
| func (i *interceptionBase) baseTraceAttributes(r *http.Request, streaming bool) []attribute.KeyValue { | ||
| return []attribute.KeyValue{ | ||
| attrs := []attribute.KeyValue{ | ||
| attribute.String(tracing.RequestPath, r.URL.Path), | ||
| attribute.String(tracing.InterceptionID, i.id.String()), | ||
| attribute.String(tracing.InitiatorID, aibcontext.ActorIDFromContext(r.Context())), | ||
|
|
@@ -173,6 +172,10 @@ func (i *interceptionBase) baseTraceAttributes(r *http.Request, streaming bool) | |
| attribute.Bool(tracing.Streaming, streaming), | ||
| attribute.Bool(tracing.IsBedrock, i.bedrock != nil), | ||
| } | ||
| if i.bedrock != nil { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 [CRF-2] The new
If the mapping regresses (emits
|
||
| attrs = append(attrs, attribute.String(tracing.BedrockProtocol, string(i.bedrock.Cfg.ResolvedProtocol()))) | ||
| } | ||
| return attrs | ||
| } | ||
|
|
||
| func (i *interceptionBase) injectTools() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,6 +114,42 @@ func TestNewAnthropic_KeyResolution(t *testing.T) { | |
| } | ||
| } | ||
|
|
||
| // NOTE: no t.Parallel() because the subtests use t.Setenv. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs to be a nolint
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It passed CI |
||
| func TestNewAnthropic_BedrockRegionResolution(t *testing.T) { | ||
| t.Run("mantle_region_from_env", func(t *testing.T) { | ||
| t.Setenv("AWS_REGION", "us-west-2") | ||
|
|
||
| p, err := NewAnthropic(context.Background(), config.Anthropic{}, &config.AWSBedrock{ | ||
| BaseURL: "https://bedrock-mantle.us-west-2.api.aws/anthropic", | ||
| Protocol: config.BedrockProtocolMantle, | ||
| AccessKey: "test-key", | ||
| AccessKeySecret: "test-secret", | ||
| }) | ||
| require.NoError(t, err) | ||
| require.NotNil(t, p.bedrock) | ||
| require.Equal(t, "us-west-2", p.bedrock.Cfg.Region) | ||
| }) | ||
|
|
||
| t.Run("mantle_no_region_anywhere", func(t *testing.T) { | ||
| // Clear every source the AWS SDK consults for a region so none | ||
| // resolves, then confirm construction rejects the mantle provider. | ||
| t.Setenv("AWS_REGION", "") | ||
| t.Setenv("AWS_DEFAULT_REGION", "") | ||
| t.Setenv("AWS_PROFILE", "") | ||
| t.Setenv("AWS_CONFIG_FILE", "/dev/null") | ||
| t.Setenv("AWS_SHARED_CREDENTIALS_FILE", "/dev/null") | ||
| t.Setenv("AWS_EC2_METADATA_DISABLED", "true") | ||
|
|
||
| _, err := NewAnthropic(context.Background(), config.Anthropic{}, &config.AWSBedrock{ | ||
| BaseURL: "https://proxy.internal", | ||
| Protocol: config.BedrockProtocolMantle, | ||
| AccessKey: "test-key", | ||
| AccessKeySecret: "test-secret", | ||
| }) | ||
| require.ErrorContains(t, err, "region required") | ||
| }) | ||
| } | ||
|
|
||
| func TestAnthropic_CreateInterceptor(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit [CRF-6] The
ResolvedProtocoldoc says "legacy" twice in one sentence. (Gon)"mapping the empty value to the legacy InvokeModel protocol so existing providers keep the legacy behavior." Drop the first: "returns the configured protocol, or BedrockProtocolInvokeModel when unset, so existing providers keep legacy behavior."