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

Skip to content
Merged
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
134 changes: 120 additions & 14 deletions coderd/ai_providers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ func TestAIProvidersCRUD(t *testing.T) {
BaseURL: "https://api.anthropic.com/",
Settings: codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
},
},
}
Expand Down Expand Up @@ -140,8 +142,9 @@ func TestAIProvidersCRUD(t *testing.T) {
Enabled: &disabled,
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-west-2",
Model: "anthropic.claude-3-5-sonnet",
Region: "us-west-2",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
},
},
})
Expand Down Expand Up @@ -558,6 +561,8 @@ func TestAIProvidersCRUD(t *testing.T) {
Settings: codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-fixture"), //nolint:gosec // test fixture
AccessKeySecret: ptr.Ref("bedrock-fixture"), //nolint:gosec // test fixture
},
Expand All @@ -583,7 +588,11 @@ func TestAIProvidersCRUD(t *testing.T) {
require.NoError(t, err)
_, err = client.UpdateAIProvider(ctx, provider.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-1"},
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
},
},
})
require.Error(t, err)
Expand All @@ -592,6 +601,78 @@ func TestAIProvidersCRUD(t *testing.T) {
require.Contains(t, sdkErr.Message, "Bedrock settings are only valid for type=anthropic")
})

t.Run("BedrockRequiresModelsForInvokeModel", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
ctx := testutil.Context(t, testutil.WaitLong)

// requireMissingModels asserts a 400 naming both model fields.
requireMissingModels := func(t *testing.T, err error) {
t.Helper()
require.Error(t, err)
var sdkErr *codersdk.Error
require.ErrorAs(t, err, &sdkErr)
require.Equal(t, http.StatusBadRequest, sdkErr.StatusCode())
require.Contains(t, sdkErr.Message, "Invalid AI provider request")
fields := make([]string, 0, len(sdkErr.Validations))
for _, v := range sdkErr.Validations {
fields = append(fields, v.Field)
}
require.Contains(t, fields, "settings.model")
require.Contains(t, fields, "settings.small_fast_model")
}

// The invoke-model protocol does not work without models.
req := codersdk.CreateAIProviderRequest{
Type: codersdk.AIProviderTypeBedrock,
Name: "bedrock-region-only",
Enabled: true,
BaseURL: "https://bedrock.us-east-2.amazonaws.com",
Settings: codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-2"},
},
}
//nolint:gocritic // Owner role is the audience for this endpoint.
_, err := client.CreateAIProvider(ctx, req)
requireMissingModels(t, err)

// The same provider with both models is accepted.
req.Settings.Bedrock.Model = "anthropic.claude-3-5-sonnet"
req.Settings.Bedrock.SmallFastModel = "anthropic.claude-3-5-haiku"
//nolint:gocritic // Owner role is the audience for this endpoint.
created, err := client.CreateAIProvider(ctx, req)
require.NoError(t, err)
require.Equal(t, "anthropic.claude-3-5-sonnet", created.Settings.Bedrock.Model)
require.Equal(t, "anthropic.claude-3-5-haiku", created.Settings.Bedrock.SmallFastModel)

// A PATCH that drops the models is rejected the same way.
//nolint:gocritic // Owner role is the audience for this endpoint.
_, err = client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-west-2"},
},
})
requireMissingModels(t, err)

// The same PATCH with both models is accepted, and the stored provider

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3 [CRF-3] The comment claims the rejected PATCH left the stored provider untouched, but no assertion can show that; the check passes either way. (Netero)

The rejected PATCH (line 649-656) sets Region: "us-west-2". The following accepted PATCH sets Region: "us-west-2" as well, and the assertions run only after it. If a future change let a validation failure partially apply, this test would still pass. Same for the model fields: they are only ever read after the successful PATCH.

A comment asserting coverage that does not exist is worse than no comment: the next reader trusts it and does not add the assertion. Either read the provider back between the rejected and the accepted PATCH and assert Region == "us-east-2" with both models at their created values, or drop the second clause of the comment.

🤖

// is untouched by the rejected one above.
//nolint:gocritic // Owner role is the audience for this endpoint.
updated, err := client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-west-2",
Model: "anthropic.claude-3-7-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
},
},
})
require.NoError(t, err)
require.Equal(t, "us-west-2", updated.Settings.Bedrock.Region)
require.Equal(t, "anthropic.claude-3-7-sonnet", updated.Settings.Bedrock.Model)
require.Equal(t, "anthropic.claude-3-5-haiku", updated.Settings.Bedrock.SmallFastModel)
})

t.Run("BedrockSecretsHidden", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
Expand All @@ -611,6 +692,7 @@ func TestAIProvidersCRUD(t *testing.T) {
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-leak"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("bedrock-supersecret"),
},
Expand Down Expand Up @@ -881,6 +963,7 @@ func TestAIProvidersKeyManagement(t *testing.T) {
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-test"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("bedrock-test-secret"),
},
Expand Down Expand Up @@ -909,6 +992,7 @@ func TestAIProvidersKeyManagement(t *testing.T) {
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-test"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("bedrock-test-secret"),
},
Expand Down Expand Up @@ -1381,6 +1465,7 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-old"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("secret-old"),
},
Expand All @@ -1391,8 +1476,9 @@ func TestAIProviderSettingsMerge(t *testing.T) {
_, err = client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-west-2",
Model: "anthropic.claude-3-5-haiku",
Region: "us-west-2",
Model: "anthropic.claude-3-5-haiku",
SmallFastModel: "anthropic.claude-3-5-haiku",
},
},
})
Expand Down Expand Up @@ -1432,6 +1518,8 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Settings: codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-old"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("secret-old"),
},
Expand All @@ -1443,6 +1531,8 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref(""),
AccessKeySecret: ptr.Ref(""),
},
Expand Down Expand Up @@ -1477,6 +1567,8 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Settings: codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-old"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("secret-old"),
},
Expand All @@ -1488,6 +1580,8 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-new"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("secret-new"),
},
Expand Down Expand Up @@ -1524,6 +1618,8 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Settings: codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref("AKIA-old"), //nolint:gosec // test fixture, not a real credential
AccessKeySecret: ptr.Ref("secret-old"),
},
Expand All @@ -1535,6 +1631,8 @@ func TestAIProviderSettingsMerge(t *testing.T) {
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{
Region: "us-east-1",
Model: "anthropic.claude-3-5-sonnet",
SmallFastModel: "anthropic.claude-3-5-haiku",
AccessKey: ptr.Ref(""),
AccessKeySecret: ptr.Ref(""),
RoleARN: "arn:aws:iam::123456789012:role/target",
Expand Down Expand Up @@ -1572,6 +1670,14 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {
externalIDReadOnlyMsg = "The Bedrock external ID is server-generated and cannot be changed."
)

// withModels supplies the model identifiers the invoke-model protocol
// requires, keeping the fixtures below focused on external ID behavior.
withModels := func(b codersdk.AIProviderBedrockSettings) *codersdk.AIProviderBedrockSettings {
b.Model = "anthropic.claude-3-5-sonnet"
b.SmallFastModel = "anthropic.claude-3-5-haiku"
return &b
}

createBedrock := func(t *testing.T, client *codersdk.Client, name string, b codersdk.AIProviderBedrockSettings) (codersdk.AIProvider, error) {
t.Helper()
ctx := testutil.Context(t, testutil.WaitLong)
Expand All @@ -1581,7 +1687,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {
Name: name,
Enabled: true,
BaseURL: "https://bedrock-runtime.us-east-1.amazonaws.com",
Settings: codersdk.AIProviderSettings{Bedrock: &b},
Settings: codersdk.AIProviderSettings{Bedrock: withModels(b)},
})
}

Expand Down Expand Up @@ -1649,7 +1755,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {

updated, err := client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-west-2", RoleARN: roleARN},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-west-2", RoleARN: roleARN}),
},
})
require.NoError(t, err)
Expand All @@ -1676,7 +1782,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {
// Removing the role retains the external ID.
cleared, err := client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-1"},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-east-1"}),
},
})
require.NoError(t, err)
Expand All @@ -1687,7 +1793,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {
// regenerating it, so a trust policy referencing it keeps working.
readded, err := client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleB},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleB}),
},
})
require.NoError(t, err)
Expand All @@ -1713,7 +1819,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {
// external ID. Echoing the same value is allowed.
updated, err := client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-west-2", RoleARN: roleARN, ExternalID: original},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-west-2", RoleARN: roleARN, ExternalID: original}),
},
})
require.NoError(t, err)
Expand All @@ -1736,7 +1842,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {

_, err = client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleARN, ExternalID: "client-tries-to-change-it"},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleARN, ExternalID: "client-tries-to-change-it"}),
},
})
sdkErr := requireSDKError(t, err, http.StatusBadRequest)
Expand All @@ -1755,7 +1861,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {

updated, err := client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleARN},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleARN}),
},
})
require.NoError(t, err)
Expand All @@ -1775,7 +1881,7 @@ func TestAIProvidersBedrockExternalID(t *testing.T) {
// No value is stored yet, so any client value is a change and is rejected.
_, err = client.UpdateAIProvider(ctx, created.Name, codersdk.UpdateAIProviderRequest{
Settings: &codersdk.AIProviderSettings{
Bedrock: &codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleARN, ExternalID: "client-supplied-value"},
Bedrock: withModels(codersdk.AIProviderBedrockSettings{Region: "us-east-1", RoleARN: roleARN, ExternalID: "client-supplied-value"}),
},
})
sdkErr := requireSDKError(t, err, http.StatusBadRequest)
Expand Down
34 changes: 34 additions & 0 deletions codersdk/aiproviders.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ func (req CreateAIProviderRequest) Validate() []ValidationError {
})
}
validations = append(validations, validateAIProviderBedrockMantleRegion(*req.Settings.Bedrock)...)
validations = append(validations, validateAIProviderBedrockModels(*req.Settings.Bedrock)...)
}
if req.Type == AIProviderTypeCopilot && len(req.APIKeys) > 0 {
validations = append(validations, ValidationError{
Expand Down Expand Up @@ -335,10 +336,17 @@ func (req UpdateAIProviderRequest) Validate() []ValidationError {
if req.APIKeys != nil {
validations = append(validations, validateAIProviderKeyMutations(*req.APIKeys)...)
}
// Despite arriving on a PATCH, a bedrock settings blob is a full
// replacement rather than a per-field patch: the caller must set every
// field, except AccessKey, AccessKeySecret, and ExternalID, which
// mergeAIProviderSettings carries forward from the stored row when
// omitted. Omitting any other field clears it, so the checks below apply
// to the patch exactly as they would to what gets stored.
if req.Settings != nil && req.Settings.Bedrock != nil {
validations = append(validations, validateAIProviderRoleARN(req.Settings.Bedrock.RoleARN)...)
validations = append(validations, validateAIProviderBedrockProtocol(req.Settings.Bedrock.Protocol)...)
validations = append(validations, validateAIProviderBedrockMantleRegion(*req.Settings.Bedrock)...)
validations = append(validations, validateAIProviderBedrockModels(*req.Settings.Bedrock)...)
}
return validations
}
Expand Down Expand Up @@ -385,6 +393,32 @@ func validateAIProviderBedrockMantleRegion(b AIProviderBedrockSettings) []Valida
return nil
}

// validateAIProviderBedrockModels requires the model identifiers that the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note [CRF-5] Existing rows that already carry the broken shape are unaffected by this PR. (Netero)

Validation only guards new writes. Rows seeded from env, inserted by dbgen, or promoted by BackfillBedrockProviderType [...] keep 404ing with a WARN until an operator re-saves them through the UI, which now supplies both models. Nothing in the diff detects or reports them.

Recorded, not a request to change this PR. Whether AIGOV-564 is closed for deployments already in the broken state, and whether that needs a backfill or a surfaced provider-build status, is a human call. Say which one you intend, here or in the issue.

🤖

// invoke-model protocol substitutes into every upstream request. Without them
// the provider cannot be constructed at runtime (see
// config.AWSBedrock.Validate), so it would be skipped at gateway startup and
// every request to it would 404. The mantle protocol forwards the client's
// model unchanged and needs neither field.
func validateAIProviderBedrockModels(b AIProviderBedrockSettings) []ValidationError {
if b.ResolvedProtocol() != AIProviderBedrockProtocolInvokeModel {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3 [CRF-2] The models gate fires on any non-nil bedrock blob, including one that is not a Bedrock provider at all, so a bearer-token Anthropic create carrying settings.bedrock: {} is now rejected with a Bedrock model error. (Netero)

Every other Bedrock gate in this area first asks whether the blob is a Bedrock provider: create's own type=bedrock requires bedrock settings uses IsConfigured(), bedrockConfig returns nil for an unconfigured blob [...] validateAIProviderBedrockModels skips that question.

create {type: anthropic, api_keys: [sk-test], settings.bedrock: {}}
  -> settings.model: model is required for the invoke-model protocol
     settings.small_fast_model: small_fast_model is required for the invoke-model protocol

The error names fields the caller has no reason to set. There is a second consequence on the PATCH path: req.Validate() runs before the handler's type check, so a PATCH placing bedrock settings on an openai provider without models now returns "model is required for the invoke-model protocol" instead of Bedrock settings are only valid for type=anthropic or type=bedrock. That is what forced the fixture edit at coderd/ai_providers_test.go:588-598; the existing subtest now only reaches the type check because it supplies models.

One correction to the reviewer's proposed fix. IsBedrockConfigured(baseURL, b) is baseURL != "" || b.IsConfigured(), and base URL is required on every provider, so that gate would fire for every request carrying a bedrock blob, which is the current behavior. The runtime's own "is this Bedrock" test is IsConfigured() on the settings blob alone (cli/aibridged.go:341-348, whose comment states base URL cannot serve as a detection signal), and a bedrock-typed provider with IsConfigured() == false is already rejected on create and refused at build time. Gating on IsConfigured() is what makes the API agree with the runtime.

Not reachable from the UI: providerFormApiMap.ts only emits a bedrock blob when type === "bedrock". API clients can hit it.

🤖

return nil
}
var validations []ValidationError
if b.Model == "" {
validations = append(validations, ValidationError{
Field: "settings.model",
Detail: "model is required for the invoke-model protocol",
})
}
if b.SmallFastModel == "" {
validations = append(validations, ValidationError{
Field: "settings.small_fast_model",
Detail: "small_fast_model is required for the invoke-model protocol",
})
}
return validations
}

func validateAIProviderRoleARN(roleARN string) []ValidationError {
if roleARN == "" {
return nil
Expand Down
10 changes: 3 additions & 7 deletions codersdk/aiproviders_bedrock.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,9 @@ func (b AIProviderBedrockSettings) ResolvedProtocol() AIProviderBedrockProtocol
// indicating that the operator wants the provider to authenticate via
// AWS Bedrock rather than as a bearer-token Anthropic provider.
//
// Model and SmallFastModel are intentionally excluded: they have

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3 [CRF-4] The PR deletes the rationale for why Model and SmallFastModel are excluded from IsConfigured(), and the rationale is still true. (Netero)

Those defaults still exist: codersdk/deployment.go:1992 (global.anthropic.claude-sonnet-4-5-20250929-v1:0) and :2002 (global.anthropic.claude-haiku-4-5-20251001-v1:0) [...] Three other sites still repeat the same reason as a secondhand claim: cli/server.go:3404-3407, coderd/ai_providers_migrate.go:219-220, and this file's own surviving "have no defaults" sentence.

the reason now lives everywhere except the definition it constrains, so the next reader who wants a stronger Bedrock detection signal sees only "region and credentials have no defaults" and no statement that adding Model would misfire on every deployment running the defaults.

Confirmed both default declarations are still present at those lines. This PR makes the models required at the API, which is exactly the change that invites a reader to also add them to IsConfigured(), so this is the worst moment to remove the sentence explaining why that breaks. Restore it.

🤖

// deployment-level defaults declared in codersdk/deployment.go, so
// they're always non-empty in a real deployment and cannot serve as
// a detection signal. Region and credentials have no defaults and
// therefore reliably indicate operator intent. Credentials alone are
// not required because Bedrock can also authenticate via the AWS
// environment (instance profile, AWS_PROFILE, IRSA, etc.).
// Region and credentials have no defaults and therefore reliably indicate
// operator intent. Credentials alone are not required because Bedrock can
// also authenticate via the AWS environment (instance profile, AWS_PROFILE, IRSA, etc.).
func (b AIProviderBedrockSettings) IsConfigured() bool {
if b.Region != "" {
return true
Expand Down
Loading
Loading