Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a0446bc
fix: resolve Bedrock application inference profile ARNs
evgeniy-scherbina Sep 1, 2026
57f583a
refactor(aibridge): name Bedrock model accessors by configured and re…
evgeniy-scherbina Sep 1, 2026
0e031e7
refactor(aibridge/intercept/messages): keep Model() structure unchanged
evgeniy-scherbina Sep 1, 2026
1fd6ec5
refactor(aibridge/intercept/messages): extract upstreamModel
evgeniy-scherbina Sep 1, 2026
7742db1
refactor(aibridge/intercept/messages): read small/fast classification…
evgeniy-scherbina Sep 1, 2026
431d2e7
docs: minor changes
evgeniy-scherbina Sep 1, 2026
54508c4
docs(aibridge/provider): clarify which Bedrock identifiers are resolved
evgeniy-scherbina Sep 1, 2026
e9a1916
test(aibridge/provider): resolve inference profiles against a mock en…
evgeniy-scherbina Sep 2, 2026
1ee5a5b
refactor(aibridge/provider): return the loaded AWS config from buildB…
evgeniy-scherbina Sep 2, 2026
a928bb5
refactor(aibridge/provider): call the inference profile resolver dire…
evgeniy-scherbina Sep 2, 2026
6e837d5
docs: minor changes
evgeniy-scherbina Sep 2, 2026
457b81a
test(aibridge/provider): make Bedrock model setup explicit
evgeniy-scherbina Sep 2, 2026
3d65242
docs(docs/ai-coder/ai-gateway): move application inference profiles a…
evgeniy-scherbina Sep 3, 2026
a567995
docs(docs/ai-coder/ai-gateway): clarify application inference profile…
evgeniy-scherbina Sep 3, 2026
0ba11af
test(aibridge/intercept/messages): cover small fast capture in the in…
evgeniy-scherbina Sep 4, 2026
958c981
docs(aibridge/provider): record why the first inference profile model…
evgeniy-scherbina Sep 4, 2026
abda63c
fix(aibridge/provider): stop attributing every profile lookup failure…
evgeniy-scherbina Sep 4, 2026
070d51e
feat(aibridge): resolve bedrock inference profiles on first request
evgeniy-scherbina Sep 4, 2026
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
5 changes: 3 additions & 2 deletions aibridge/aibridgetest/aibridgetest.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
)

// NewAnthropicProvider builds an Anthropic provider for tests, failing the test
// if credential resolution fails.
// if credential resolution fails. Each call gets its own inference profile
// cache so tests do not share resolutions.
func NewAnthropicProvider(t testing.TB, cfg aibridge.AnthropicConfig, bedrockCfg *aibridge.AWSBedrockConfig) aibridge.Provider {
t.Helper()
p, err := aibridge.NewAnthropicProvider(context.Background(), cfg, bedrockCfg)
p, err := aibridge.NewAnthropicProvider(context.Background(), cfg, bedrockCfg, aibridge.NewInferenceProfileCache())
require.NoError(t, err)
return p
}
14 changes: 12 additions & 2 deletions aibridge/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,24 @@ type (
AWSBedrockConfig = config.AWSBedrock
OpenAIConfig = config.OpenAI
CopilotConfig = config.Copilot

// InferenceProfileCache caches Bedrock application inference profile
// resolutions across provider reloads. Create one per process.
InferenceProfileCache = provider.InferenceProfileCache
)

// NewInferenceProfileCache returns a cache shared by every Bedrock provider in
// the process.
func NewInferenceProfileCache() *InferenceProfileCache {
return provider.NewInferenceProfileCache()
}

func AsActor(ctx context.Context, actorID string, metadata recorder.Metadata) context.Context {
return aibcontext.AsActor(ctx, actorID, metadata)
}

func NewAnthropicProvider(ctx context.Context, cfg config.Anthropic, bedrockCfg *config.AWSBedrock) (provider.Provider, error) {
return provider.NewAnthropic(ctx, cfg, bedrockCfg)
func NewAnthropicProvider(ctx context.Context, cfg config.Anthropic, bedrockCfg *config.AWSBedrock, profiles *InferenceProfileCache) (provider.Provider, error) {
return provider.NewAnthropic(ctx, cfg, bedrockCfg, profiles)
}

func NewOpenAIProvider(cfg config.OpenAI) provider.Provider {
Expand Down
81 changes: 71 additions & 10 deletions aibridge/intercept/messages/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,49 @@ var bedrockSupportedBetaFlags = map[string]bool{
type BedrockRuntime struct {
Cfg aibconfig.AWSBedrock
Creds aws.CredentialsProvider

resolvedModel string
resolvedSmallFastModel string
}

// NewBedrockRuntime bundles the Bedrock config and credentials with the model
// IDs behind the configured identifiers. The resolved IDs differ from the
// configured ones only when those are application inference profile ARNs, which
// are opaque and must be resolved through AWS; every other identifier resolves
// to itself.
func NewBedrockRuntime(cfg aibconfig.AWSBedrock, creds aws.CredentialsProvider, resolvedModel, resolvedSmallFastModel string) *BedrockRuntime {
return &BedrockRuntime{
Cfg: cfg,
Creds: creds,
resolvedModel: resolvedModel,
resolvedSmallFastModel: resolvedSmallFastModel,
}
}

// ConfiguredModel returns the identifier the operator configured, which may be
// an application inference profile ARN. Requests carry it as the model because
// AWS attributes spend to a profile only when the profile itself is invoked.
func (b *BedrockRuntime) ConfiguredModel() string {
return b.Cfg.Model
}

// ConfiguredSmallFastModel is [BedrockRuntime.ConfiguredModel] for the
// small/fast model.
func (b *BedrockRuntime) ConfiguredSmallFastModel() string {
return b.Cfg.SmallFastModel
}

// ResolvedModel returns the Bedrock model ID behind the configured identifier.
// Model capabilities, usage records, pricing, and metrics all key off this
// rather than the configured identifier.
func (b *BedrockRuntime) ResolvedModel() string {
return b.resolvedModel
}

// ResolvedSmallFastModel is [BedrockRuntime.ResolvedModel] for the small/fast
// model.
func (b *BedrockRuntime) ResolvedSmallFastModel() string {
return b.resolvedSmallFastModel
}

type interceptionBase struct {
Expand All @@ -86,6 +129,14 @@ type interceptionBase struct {
// clientHeaders are the original HTTP headers from the client request.
clientHeaders http.Header

// isSmallFastModel reports whether the client requested a small/fast model
// (Haiku 3.5), which is optimized for tasks like code autocomplete and other
// small, quick operations. It is captured at construction because the Bedrock
// InvokeModel remap overwrites the model in the request payload.
// See `ANTHROPIC_SMALL_FAST_MODEL`: https://docs.anthropic.com/en/docs/claude-code/settings#environment-variables
// https://docs.claude.com/en/docs/claude-code/costs#background-token-usage
isSmallFastModel bool

logger slog.Logger
tracer trace.Tracer

Expand Down Expand Up @@ -169,16 +220,26 @@ func (i *interceptionBase) Model() string {
// passthrough, non-Bedrock providers) returns the model the client sent in
// the body.
if i.isBedrockInvokeModel() {
model := i.bedrock.Cfg.Model
if i.isSmallFastModel() {
model = i.bedrock.Cfg.SmallFastModel
model := i.bedrock.ResolvedModel()
if i.isSmallFastModel {
model = i.bedrock.ResolvedSmallFastModel()
}
return model
}

return i.reqPayload.model()
}

// upstreamModel returns the identifier sent to Bedrock as the invocation
// target, which may be an application inference profile ARN.
func (i *interceptionBase) upstreamModel() string {
model := i.bedrock.ConfiguredModel()
if i.isSmallFastModel {
model = i.bedrock.ConfiguredSmallFastModel()
}
return model
}

func (i *interceptionBase) baseTraceAttributes(r *http.Request, streaming bool) []attribute.KeyValue {
attrs := []attribute.KeyValue{
attribute.String(tracing.RequestPath, r.URL.Path),
Expand Down Expand Up @@ -261,12 +322,9 @@ func (*interceptionBase) extractModelThoughts(msg *anthropic.Message) []*recorde
return thoughtRecords
}

// IsSmallFastModel checks if the model is a small/fast model (Haiku 3.5).
// These models are optimized for tasks like code autocomplete and other small, quick operations.
// See `ANTHROPIC_SMALL_FAST_MODEL`: https://docs.anthropic.com/en/docs/claude-code/settings#environment-variables
// https://docs.claude.com/en/docs/claude-code/costs#background-token-usage
func (i *interceptionBase) isSmallFastModel() bool {
return strings.Contains(i.reqPayload.model(), "haiku")
// isSmallFastModel reports whether the client requested a small/fast model.
func isSmallFastModel(model string) bool {
return strings.Contains(model, "haiku")
}

// newMessagesService builds the SDK service used for upstream calls.
Expand Down Expand Up @@ -415,13 +473,16 @@ func (i *interceptionBase) withBedrockMantleOptions(ctx context.Context) ([]opti
// Anthropics' model names. It also converts adaptive thinking to enabled with a budget for models that
// don't support adaptive thinking natively, or enabled thinking to adaptive for models that only support
// adaptive.
//
// The request carries the configured identifier, which may be an application
// inference profile ARN, while capability decisions use the model ID behind it.
func (i *interceptionBase) augmentRequestForBedrockInvokeModel() {
if i.bedrock == nil {
return
}

model := i.Model()
updated, err := i.reqPayload.withModel(model)
updated, err := i.reqPayload.withModel(i.upstreamModel())
if err != nil {
i.logger.Warn(context.Background(), "failed to set model in request payload for Bedrock", slog.Error(err))
return
Expand Down
Loading
Loading