Skip to content

feat: add AWS Bedrock LLM provider via native Converse API - #713

Merged
ilblackdragon merged 16 commits into
mainfrom
takeover/345-aws-bedrock-provider
Mar 9, 2026
Merged

ilblackdragon merged 16 commits into
mainfrom
takeover/345-aws-bedrock-provider

Conversation

@ilblackdragon

Copy link
Copy Markdown
Member

Summary

Continuation of #345 by @cgorski.

Adds native AWS Bedrock support using aws-sdk-bedrockruntime's Converse API, bypassing the OpenAI-compatible layer. Feature-gated behind --features bedrock to avoid pulling heavy AWS SDK dependencies by default. Supports IAM credentials, SSO profiles, instance roles, and cross-region inference routing.

Changes from original

  • Merged with latest main (resolved 7 merge conflicts from registry-based provider refactor)
  • Adapted Bedrock provider to work with new RegistryProviderConfig / string-based backend system
  • Added missing cache_creation_input_tokens and cache_read_input_tokens fields (from Anthropic prompt caching feature)
  • Added missing content_parts field in test ChatMessage struct
  • Fixed string literal type mismatches in wizard write_bootstrap_env()
  • Removed non-functional bearer token auth (AWS_BEARER_TOKEN_BEDROCK) from wizard and docs per reviewer feedback
  • Removed stale BEDROCK_ACCESS_KEY proxy entry from provider table
  • Added bedrock_profile fallback from settings in config resolution

Original PR

#345 - feat: add AWS Bedrock LLM provider via native Converse API

Review comments addressed

  • @zmanian and @serrrfirat: Bearer token auth doesn't work with Bedrock Converse API in Rust SDK — removed from wizard and all documentation
  • Compilation fixes for main branch API changes (cache tokens, content_parts, registry architecture)

Test plan

  • All 31 bedrock unit tests pass
  • All 148 config tests pass
  • cargo clippy --all --all-features — zero warnings
  • cargo check --features bedrock and cargo check --features bedrock --tests — clean
  • CI green

Co-Authored-By: cgorski cgorski@users.noreply.github.com

Generated with Claude Code

cgorski and others added 8 commits March 2, 2026 06:47
- Safe u32→i32 cast for max_tokens using try_from with clamp
- Remove brittle string-based error detection fallback for tool results
- Validate BEDROCK_CROSS_REGION against allowed values (us/eu/apac/global)
- Validate message list is non-empty before Converse API call
- Log when using default us-east-1 region
- Update llm_backend doc comment to list all backends
- Add tests for build_inference_config and empty message handling
The wizard collected the profile name but only printed a hint to set
it manually. Now it saves to settings and writes AWS_PROFILE to the
bootstrap .env, consistent with how BEDROCK_REGION and other Bedrock
settings are persisted.
The AWS SDK dependencies (aws-config, aws-sdk-bedrockruntime,
aws-smithy-types) require cmake and a C compiler to build aws-lc-sys.
Gate them behind an opt-in `bedrock` feature flag so default builds
are unaffected.

Build with: cargo build --features bedrock
All config, settings, and wizard code stays unconditional (no AWS deps)
so users can configure Bedrock even without the feature compiled — they
get a clear error at startup directing them to rebuild.
…rchitecture (takeover #345)

- Resolve merge conflicts with main's registry-based provider system
- Add missing cache_creation_input_tokens/cache_read_input_tokens fields
- Add missing content_parts field in test ChatMessage
- Fix string literal type mismatches in wizard env_vars (.to_string())
- Remove non-functional bearer token auth (AWS_BEARER_TOKEN_BEDROCK) from
  wizard and documentation per reviewer feedback from @zmanian and @serrrfirat
- Remove stale BEDROCK_ACCESS_KEY proxy entry from provider table
- Update Bedrock provider to use is_bedrock string check (LlmBackend enum removed)
- Add bedrock_profile fallback from settings in config resolution

[skip-regression-check]

Co-Authored-By: cgorski <cgorski@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 8, 2026 03:04
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@github-actions github-actions Bot added scope: llm LLM integration scope: config Configuration scope: setup Onboarding / setup scope: docs Documentation scope: dependencies Dependency updates size: XL 500+ changed lines risk: high Safety, secrets, auth, or critical infrastructure contributor: core 20+ merged PRs labels Mar 8, 2026

Copilot AI left a comment

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.

Pull request overview

This PR adds native AWS Bedrock LLM provider support using aws-sdk-bedrockruntime's Converse API, bypassing the OpenAI-compatible layer. It is a continuation/rebase of #345, adapted to work with the current registry-based provider architecture and feature-gated behind --features bedrock.

Changes:

  • New src/llm/bedrock.rs implementing LlmProvider for AWS Bedrock via the Converse API, with message conversion, tool support, and error mapping
  • Config system extended: BedrockConfig struct, env var resolution (BEDROCK_REGION, BEDROCK_MODEL, BEDROCK_CROSS_REGION, AWS_PROFILE), and feature-gated provider instantiation
  • Setup wizard updated with a Bedrock-specific flow (region, auth, cross-region) and documentation updated across docs/LLM_PROVIDERS.md, FEATURE_PARITY.md, CLAUDE.md, and CHANGELOG.md

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/llm/bedrock.rs New 1127-line Bedrock provider with full Converse API implementation and 31 unit tests
src/llm/mod.rs Routes backend == "bedrock" to the new provider; adds bedrock: None to test config
src/config/llm.rs Adds BedrockConfig struct and resolves Bedrock env vars in LlmConfig::resolve()
src/config/mod.rs Exports BedrockConfig; adds vestigial bedrock_api_key → AWS_BEARER_TOKEN_BEDROCK secret injection
src/settings.rs Adds bedrock_region, bedrock_cross_region, bedrock_profile fields to Settings
src/setup/wizard.rs Adds setup_bedrock(), setup_bedrock_cross_region(), and Bedrock model selection in wizard
src/setup/README.md Documents Bedrock in provider table with outdated AWS_BEARER_TOKEN_BEDROCK env var reference
docs/LLM_PROVIDERS.md Adds full Bedrock documentation section
FEATURE_PARITY.md Updates Bedrock row to reflect native API instead of LiteLLM proxy
CLAUDE.md Adds Bedrock env vars to the configuration reference
CHANGELOG.md Adds unreleased entry (with inaccurate mention of bearer token auth)
Cargo.toml / Cargo.lock Adds aws-config, aws-sdk-bedrockruntime, aws-smithy-types as optional dependencies
Comments suppressed due to low confidence (1)

src/config/llm.rs:320

  • In LlmConfig::resolve(), when is_bedrock is true, config.backend is set to backend_lower (line 319), which can be "aws_bedrock" or "aws" — not normalized to "bedrock". However, create_llm_provider in src/llm/mod.rs only checks config.backend == "bedrock" (line 69). When LLM_BACKEND=aws_bedrock or LLM_BACKEND=aws is used, config.backend will hold "aws_bedrock" or "aws" respectively, config.provider will be None, and the function will return LlmError::AuthFailed instead of creating the Bedrock provider. The fix is to normalize the stored backend name to "bedrock" for all three aliases in the Ok(Self { backend: ... }) expression, or to check all three aliases in create_llm_provider.
        Ok(Self {
            backend: if is_nearai {
                "nearai".to_string()
            } else if let Some(ref p) = provider {
                p.provider_id.clone()
            } else {
                backend_lower
            },

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/setup/README.md Outdated
| Ollama | None | - | - |
| OpenRouter¹ | API key | `llm_compatible_api_key` | `LLM_API_KEY` |
| OpenAI-compatible¹ | Optional API key | `llm_compatible_api_key` | `LLM_API_KEY` |
| AWS Bedrock | API key or AWS credentials | `bedrock_api_key` | `AWS_BEARER_TOKEN_BEDROCK` |

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The README table entry for AWS Bedrock says "Auth Method: API key or AWS credentials" and "Env Var: AWS_BEARER_TOKEN_BEDROCK". However, this PR explicitly removes bearer token auth as non-functional. The bedrock_api_key secret and AWS_BEARER_TOKEN_BEDROCK env var mapping added in src/config/mod.rs is vestigial and misleading — it maps a bedrock_api_key secret to an env var that the new native Bedrock provider never reads. The README and the secret injection mapping in mod.rs should be updated to reflect that the actual auth is through standard AWS credential chain, not a bearer token or API key stored in the secrets system.

Suggested change
| AWS Bedrock | API key or AWS credentials | `bedrock_api_key` | `AWS_BEARER_TOKEN_BEDROCK` |
| AWS Bedrock | AWS credential chain (no app-managed secret) | - | - |

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 51061e7 — updated README table to remove bearer token/API key references. Bedrock now shows "AWS credentials (IAM, SSO, instance roles)" with no secret name or env var.

Comment thread src/config/mod.rs Outdated
let mut mappings: Vec<(&str, &str)> = vec![
("llm_nearai_api_key", "NEARAI_API_KEY"),
("llm_anthropic_oauth_token", "ANTHROPIC_OAUTH_TOKEN"),
("bedrock_api_key", "AWS_BEARER_TOKEN_BEDROCK"),

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The bedrock_api_key → AWS_BEARER_TOKEN_BEDROCK mapping added in inject_llm_keys_from_secrets is a leftover from the removed bearer-token auth approach. The new native Bedrock provider reads only BEDROCK_REGION, BEDROCK_MODEL, BEDROCK_CROSS_REGION, and AWS_PROFILE — it never reads AWS_BEARER_TOKEN_BEDROCK. This mapping will silently load a stale secret and set an env var that has no effect, potentially causing confusion. It should be removed.

Suggested change
("bedrock_api_key", "AWS_BEARER_TOKEN_BEDROCK"),

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 51061e7 — removed the dead bedrock_api_key → AWS_BEARER_TOKEN_BEDROCK mapping from inject_llm_keys_from_secrets.

Comment thread src/llm/bedrock.rs
Comment on lines +238 to +259
fn build_inference_config(
temperature: Option<f32>,
max_tokens: Option<u32>,
) -> Option<InferenceConfiguration> {
let mut builder = InferenceConfiguration::builder();
let mut needs_config = false;

if let Some(temp) = temperature {
builder = builder.temperature(temp);
needs_config = true;
}
if let Some(tokens) = max_tokens {
builder = builder.max_tokens(i32::try_from(tokens).unwrap_or(i32::MAX));
needs_config = true;
}

if needs_config {
Some(builder.build())
} else {
None
}
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The build_inference_config function only handles temperature and max_tokens, but ignores stop_sequences from both CompletionRequest and ToolCompletionRequest. The AWS Bedrock InferenceConfiguration builder supports .stop_sequences(). If callers pass stop sequences (e.g. the agent loop uses them to detect end-of-turn markers), they will be silently ignored, causing incorrect behavior. The stop_sequences parameter should be passed through to build_inference_config and applied via builder.stop_sequences().

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 51061e7 — build_inference_config now accepts stop_sequences: Option<&[String]> and passes them to the Bedrock InferenceConfiguration. Added 2 tests (with sequences + empty sequences).

Comment thread src/config/llm.rs Outdated
),
});
}
let profile = optional_env("AWS_PROFILE")?;

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The PR description claims "Added bedrock_profile fallback from settings in config resolution", but in the actual code (line 302), profile is only resolved from the AWS_PROFILE environment variable — there is no fallback to settings.bedrock_profile. For completeness the resolution should be optional_env("AWS_PROFILE")?.or_else(|| settings.bedrock_profile.clone()). Without this fallback, when AWS_PROFILE is not set as an env var but was saved to settings.bedrock_profile by the wizard, the profile will be silently ignored unless it was also written to the .env file via write_bootstrap_env. While write_bootstrap_env does write AWS_PROFILE, the fallback from settings is still missing for environments where the wizard settings are used directly without loading from .env.

Suggested change
let profile = optional_env("AWS_PROFILE")?;
let profile =
optional_env("AWS_PROFILE")?.or_else(|| settings.bedrock_profile.clone());

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Already fixed in 6009cdb — added .or_else(|| settings.bedrock_profile.clone()) fallback for AWS_PROFILE, consistent with region and cross_region resolution.

Comment thread CHANGELOG.md Outdated

### Added

- AWS Bedrock LLM provider via native Converse API with bearer token, IAM, and SSO auth support (feature-gated: `--features bedrock`)

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The CHANGELOG entry says "with bearer token, IAM, and SSO auth support", but bearer token auth (AWS_BEARER_TOKEN_BEDROCK) was explicitly removed in this PR per reviewer feedback (the PR description states "Removed non-functional bearer token auth"). The entry should instead describe the supported authentication methods: IAM credentials, SSO profiles, and instance roles.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 51061e7 — CHANGELOG entry now says "with IAM and SSO auth support" instead of "with bearer token, IAM, and SSO auth support".

ilblackdragon and others added 2 commits March 7, 2026 22:24
Regenerating Cargo.lock from scratch caused transitive dependency version
drift that broke the html_to_markdown fixture test in CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…file fallback

- Move is_bedrock check before unknown-backend warning to prevent
  spurious "unknown backend" log for bedrock users
- Normalize backend aliases ("aws", "aws_bedrock") to "bedrock" so
  the provider factory matches correctly
- Add settings.bedrock_profile fallback for AWS_PROFILE, consistent
  with region and cross_region resolution

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 8, 2026 06:31
@ilblackdragon

Copy link
Copy Markdown
Member Author

Self-Review

Bugs Fixed (commits after initial push)

# Severity File:Line Issue Fix
1 Critical src/config/llm.rs:215 Spurious "unknown backend" warning for Bedrock users — is_bedrock was computed after the warning check Moved is_bedrock before the warning condition
2 Critical src/config/llm.rs:319 + src/llm/mod.rs:69 Backend aliases "aws" / "aws_bedrock" not normalized to "bedrock" — provider factory wouldn't match Added is_bedrock branch to normalize backend string
3 Medium src/config/llm.rs:302 AWS_PROFILE not falling back to settings.bedrock_profile (inconsistent with region/cross_region) Added .or_else(|| settings.bedrock_profile.clone())
4 Medium Cargo.lock Regenerated lockfile caused html-to-markdown fixture test drift Restored main's Cargo.lock as base

Known Limitations (acceptable for initial PR)

# Severity Issue Notes
1 Medium stop_sequences from CompletionRequest silently ignored Bedrock InferenceConfiguration supports it; can add in follow-up
2 Medium content_parts (multimodal) silently dropped Bedrock supports images; can add in follow-up
3 Low Per-request model override silently ignored Consistent with RigAdapter behavior
4 Low AWS_BEARER_TOKEN_BEDROCK secret injection in config/mod.rs:308 is now dead code Harmless; could be cleaned up in follow-up
5 Low map_sdk_error and is_error tool result paths have no test coverage Core happy paths well tested (31 tests)
6 Low Code duplication between complete() and complete_with_tools() Common in other providers; can refactor later

Generated with Claude Code

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/setup/wizard.rs Outdated

// Bedrock is a special case (native AWS SDK, not registry-based)
options.push(
"AWS Bedrock - Claude & other models via AWS (API key, IAM, SSO)".to_string(),

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

The wizard menu entry description says "Claude & other models via AWS (API key, IAM, SSO)" but the setup_bedrock() method only offers AWS default credentials (env vars, ~/.aws/credentials, IAM roles) and named profiles. The mention of "API key" is misleading since bearer token auth was removed. It should be updated to match the actual auth options, e.g., "Claude & other models via AWS (IAM, SSO profiles, instance roles)".

Suggested change
"AWS Bedrock - Claude & other models via AWS (API key, IAM, SSO)".to_string(),
"AWS Bedrock - Claude & other models via AWS (IAM, SSO profiles, instance roles)"
.to_string(),

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 51061e7 — wizard menu now says "Claude & other models via AWS (IAM, SSO)" without "API key".

Comment thread src/setup/wizard.rs
Comment on lines +2406 to +2416
if self.settings.llm_backend.as_deref() == Some("bedrock") {
if let Some(ref model) = self.settings.selected_model {
env_vars.push(("BEDROCK_MODEL".to_string(), model.clone()));
}
if let Some(ref cross) = self.settings.bedrock_cross_region {
env_vars.push(("BEDROCK_CROSS_REGION".to_string(), cross.clone()));
}
if let Some(ref profile) = self.settings.bedrock_profile {
env_vars.push(("AWS_PROFILE".to_string(), profile.clone()));
}
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

When the backend is "bedrock", write_bootstrap_env() writes the model to BEDROCK_MODEL here (line 2408) and then later also writes it to LLM_MODEL via registry.model_env_var("bedrock"), which returns "LLM_MODEL" as a fallback for unknown registry providers. This causes the bedrock model ID to be written redundantly to LLM_MODEL. If the user later switches to an OpenAI-compatible provider that uses LLM_MODEL, the stale Bedrock model name would be picked up. The generic model write below should be skipped when backend_str == "bedrock" since BEDROCK_MODEL is already set.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 51061e7 — write_bootstrap_env now skips the LLM_MODEL write when backend is bedrock, since BEDROCK_MODEL is already written above.

ilblackdragon and others added 2 commits March 8, 2026 00:20
…uences, model dedup

- Remove stale bearer token refs from setup README and CHANGELOG
- Remove dead bedrock_api_key secret injection mapping
- Pass stop_sequences through to Bedrock InferenceConfiguration
- Remove "API key" from wizard menu description (bearer token removed)
- Skip duplicate LLM_MODEL write for bedrock backend in wizard
- Fix cargo fmt formatting

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ock-provider

# Conflicts:
#	src/config/llm.rs
#	src/llm/mod.rs
#	src/setup/wizard.rs
Copilot AI review requested due to automatic review settings March 8, 2026 08:46

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/setup/wizard.rs
Comment on lines +1276 to +1281
0 => {
// Default AWS credentials
print_info(
"Using default AWS credential chain (env vars, ~/.aws/credentials, IAM roles).",
);
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

When auth choice 0 (default AWS credentials) is selected in setup_bedrock(), the existing self.settings.bedrock_profile is not cleared. If the user previously configured a named profile and then re-runs the wizard choosing "default credentials", the stale profile will persist in settings and will be written to .env as AWS_PROFILE (line 2431-2433 in write_bootstrap_env). This means the SDK will continue to use the old profile instead of the default credential chain. The match arm for 0 should explicitly set self.settings.bedrock_profile = None;.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 13f151c — setup_bedrock() now clears self.settings.bedrock_profile = None when auth choice 0 (default credentials) is selected. Added regression test test_bedrock_clears_stale_profile_on_default_creds.

Comment thread src/setup/wizard.rs Outdated
Comment on lines +1247 to +1250
self.settings.llm_backend = Some("bedrock".to_string());
if self.settings.selected_model.is_some() {
self.settings.selected_model = None;
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

setup_bedrock() unconditionally resets selected_model = None at the start, even when the user was already on bedrock and confirmed "Keep current provider?" in the wizard. All other provider setup functions (e.g. setup_openai_compatible_generic, setup_ollama_generic) only clear the model when switching to a different provider (checked with if self.settings.llm_backend.as_deref() != Some(backend_id)). This means running the wizard again on an already-configured Bedrock install will always clear the previously selected model, forcing re-entry. The guard should be if self.settings.llm_backend.as_deref() != Some("bedrock") before clearing.

Suggested change
self.settings.llm_backend = Some("bedrock".to_string());
if self.settings.selected_model.is_some() {
self.settings.selected_model = None;
}
// Clear model only when switching providers (old model may be invalid)
if self.settings.llm_backend.as_deref() != Some("bedrock") {
self.settings.selected_model = None;
}
self.settings.llm_backend = Some("bedrock".to_string());

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 13f151c — setup_bedrock() now uses the established pattern from #600: if self.settings.llm_backend.as_deref() != Some("bedrock") { self.settings.selected_model = None; }. Model is preserved when re-entering the same provider, cleared when switching. Added regression test test_bedrock_same_provider_preserves_model.

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not redundant with #676 -- this is a significant upgrade from proxy-based (LiteLLM) to native Converse API. Eliminates an external dependency, uses standard AWS credential chain, and handles Bedrock-specific features (ToolResult status, GuardrailIntervened, ModelContextWindowExceeded). Well-structured (1153 lines) with 31 unit tests.

Issues to address:

1. Dead `providers.json` entry (must fix). The PR intercepts `bedrock` before the registry lookup, making the existing `providers.json` bedrock entry unreachable dead code. Either remove it or rename to `bedrock_proxy` for users who want the LiteLLM path.

2. CMake build dependency (must document). `aws-lc-sys` (transitive dep via `aws-sdk-bedrockruntime`) requires CMake at build time. This could break CI or user builds. Document this requirement in the Bedrock section of provider docs.

3. `block_in_place` in constructor (should fix). `BedrockProvider::new` uses `tokio::task::block_in_place` + `Handle::current().block_on()` to load AWS config. This panics in `current_thread` runtimes (tests). Consider making `new` async.

4. No streaming support (note for follow-up). The Bedrock Converse API supports `converse_stream()` but this uses `converse()` only. Users see nothing until the full response completes.

…ard fixes

- Remove dead LiteLLM-based bedrock entry from providers.json (native
  Converse API intercepts before registry lookup)
- Make BedrockProvider::new() async to avoid block_in_place panic in
  current_thread runtimes; propagate async to create_llm_provider,
  build_provider_chain, and init_llm
- Document CMake build prerequisite in docs/LLM_PROVIDERS.md
- Clear bedrock_profile when user selects "default credentials" in wizard
- Fix selected_model clearing to match established pattern (conditional
  on provider switch, not unconditional)
- Add regression tests for bedrock model preservation and profile clearing

Addresses review feedback from @zmanian on PR #713.
Streaming support tracked in #741.

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ilblackdragon

Copy link
Copy Markdown
Member Author

@zmanian Addressed all review feedback in 13f151c:

  1. LiteLLM bedrock entry removed from providers.json — native Converse API intercepts before registry lookup, so the proxy-based entry was dead code.
  2. CMake build prerequisite documented in docs/LLM_PROVIDERS.md Bedrock section (aws-lc-sys needs CMake).
  3. BedrockProvider::new() made async — eliminated block_in_place that panics in current_thread runtimes. Propagated async through create_llm_provider, build_provider_chain, and init_llm.
  4. Streaming support tracked in feat: add Bedrock streaming support via converse_stream() #741 — will use converse_stream() when IronClaw adds streaming LlmProvider support.
  5. Stale profile cleared — bedrock_profile is now set to None when user selects "default credentials" (auth choice 0).
  6. Model clearing fixed — now uses the established conditional pattern from When re-running onboarding most previous settings are remembered, but not Model name #600 (only clears selected_model when switching providers, preserves when re-entering bedrock).

All changes include regression tests. CI should be green (2633 unit tests pass; the 2 db::tls failures are pre-existing).

Copilot AI review requested due to automatic review settings March 8, 2026 21:53
@ilblackdragon
ilblackdragon requested a review from zmanian March 8, 2026 21:59

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CLAUDE.md Outdated
@@ -502,6 +509,8 @@ OBSERVABILITY_BACKEND=none # none/noop (default) or log

Backends: `nearai` (default), `openai`, `anthropic`, `ollama`, `openai_compatible`, `tinfoil` — set via `LLM_BACKEND`. See [src/llm/CLAUDE.md](src/llm/CLAUDE.md) for per-provider auth and configuration details.

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

Line 510 in CLAUDE.md lists the LLM backends as nearai, openai, anthropic, ollama, openai_compatible, tinfoil — but bedrock is now also a supported backend. The inline list should include bedrock (noting the --features bedrock requirement) so developers know it exists when scanning this quick reference.

Suggested change
Backends: `nearai` (default), `openai`, `anthropic`, `ollama`, `openai_compatible`, `tinfoil` — set via `LLM_BACKEND`. See [src/llm/CLAUDE.md](src/llm/CLAUDE.md) for per-provider auth and configuration details.
Backends: `nearai` (default), `openai`, `anthropic`, `ollama`, `openai_compatible`, `tinfoil`, `bedrock` (requires `--features bedrock`) — set via `LLM_BACKEND`. See [src/llm/CLAUDE.md](src/llm/CLAUDE.md) for per-provider auth and configuration details.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 1730bd9 — added bedrock (with --features bedrock note) to the inline backend list in CLAUDE.md.

Comment thread src/setup/wizard.rs Outdated

if is_known && confirm("Keep current provider?", true).map_err(SetupError::Io)? {
if current == "bedrock" {
return self.setup_bedrock().await;

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

When the user confirms "Keep current provider?" for a Bedrock provider at line 833, the code calls self.setup_bedrock().await which unconditionally re-runs the full setup flow: asking for region, auth method, and cross-region prefix. This is inconsistent with the stated intent of "keeping" the current provider — the user is forced to re-configure all settings even when keeping the same provider.

By contrast, the NEAR AI path calls run_provider_setup which has an early-return if the session is still valid (session.has_token().await check), and API key providers offer "Use this key?" confirmation before re-entering.

setup_bedrock() should check if a valid config already exists and offer to skip re-configuration (similar to how setup_nearai checks for an existing session, or how setup_api_key_provider checks for an existing env var with "Use this key?"). Without this, every --provider-only wizard re-run on a Bedrock setup forces the user to re-select region, auth, and cross-region even when nothing has changed.

Suggested change
return self.setup_bedrock().await;
// For Bedrock, keeping the current provider should not force re-running
// the entire setup flow (region, auth, cross-region prefix). We simply
// keep the existing configuration.
print_info("Keeping existing AWS Bedrock configuration.");
return Ok(());

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 1730bd9 — when user confirms "Keep current provider?" for Bedrock, we now early-return with "Keeping existing AWS Bedrock configuration" instead of re-running the full setup flow (region, auth, cross-region).

Comment thread src/setup/wizard.rs Outdated
// Named profile
let profile =
input("AWS profile name (from ~/.aws/config)").map_err(SetupError::Io)?;
if !profile.is_empty() {

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

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

When auth choice 1 (named profile) is selected but the user enters an empty string, self.settings.bedrock_profile is left unchanged. If a profile was previously configured, it will remain set and be written to .env as AWS_PROFILE by write_bootstrap_env. The user has no way to clear a previously configured profile by pressing Enter here. The empty-profile case should either return an error (requiring a non-empty profile name when "named profile" is selected) or clear the existing profile, consistent with what auth choice 0 does.

Suggested change
if !profile.is_empty() {
if profile.trim().is_empty() {
// Empty input clears any previously configured profile
self.settings.bedrock_profile = None;
print_info(
"AWS profile cleared; using default AWS credential chain instead.",
);
} else {

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 1730bd9 — empty profile input now clears bedrock_profile and informs the user ("AWS profile cleared; using default AWS credential chain instead"). Added regression test test_bedrock_empty_profile_clears_existing.

- Add `bedrock` to CLAUDE.md inline backend list (#10)
- Skip full setup re-run when keeping existing Bedrock config (#11)
- Clear stale bedrock_profile on empty named-profile input (#12)
- Add regression test for empty profile clearing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ilblackdragon
ilblackdragon merged commit d73e35c into main Mar 9, 2026
22 checks passed
@ilblackdragon
ilblackdragon deleted the takeover/345-aws-bedrock-provider branch March 9, 2026 07:10
This was referenced Mar 9, 2026
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
* feat: add AWS Bedrock LLM provider via native Converse API

* fix: use JSON parsing for tool result error detection instead of brittle substring matching

* refactor: extract duplicated inference config builder into helper function

* fix: address review feedback — safe casts, input validation, and tests

- Safe u32→i32 cast for max_tokens using try_from with clamp
- Remove brittle string-based error detection fallback for tool results
- Validate BEDROCK_CROSS_REGION against allowed values (us/eu/apac/global)
- Validate message list is non-empty before Converse API call
- Log when using default us-east-1 region
- Update llm_backend doc comment to list all backends
- Add tests for build_inference_config and empty message handling

* fix: persist AWS_PROFILE for Bedrock named profile auth

The wizard collected the profile name but only printed a hint to set
it manually. Now it saves to settings and writes AWS_PROFILE to the
bootstrap .env, consistent with how BEDROCK_REGION and other Bedrock
settings are persisted.

* feat: gate AWS Bedrock behind optional `bedrock` feature flag

The AWS SDK dependencies (aws-config, aws-sdk-bedrockruntime,
aws-smithy-types) require cmake and a C compiler to build aws-lc-sys.
Gate them behind an opt-in `bedrock` feature flag so default builds
are unaffected.

Build with: cargo build --features bedrock
All config, settings, and wizard code stays unconditional (no AWS deps)
so users can configure Bedrock even without the feature compiled — they
get a clear error at startup directing them to rebuild.

* fix: address review feedback and adapt Bedrock provider to registry architecture (takeover nearai#345)

- Resolve merge conflicts with main's registry-based provider system
- Add missing cache_creation_input_tokens/cache_read_input_tokens fields
- Add missing content_parts field in test ChatMessage
- Fix string literal type mismatches in wizard env_vars (.to_string())
- Remove non-functional bearer token auth (AWS_BEARER_TOKEN_BEDROCK) from
  wizard and documentation per reviewer feedback from @zmanian and @serrrfirat
- Remove stale BEDROCK_ACCESS_KEY proxy entry from provider table
- Update Bedrock provider to use is_bedrock string check (LlmBackend enum removed)
- Add bedrock_profile fallback from settings in config resolution

[skip-regression-check]

Co-Authored-By: cgorski <cgorski@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use main's Cargo.lock as base to preserve dependency versions

Regenerating Cargo.lock from scratch caused transitive dependency version
drift that broke the html_to_markdown fixture test in CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: bedrock config bugs — spurious warning, alias normalization, profile fallback

- Move is_bedrock check before unknown-backend warning to prevent
  spurious "unknown backend" log for bedrock users
- Normalize backend aliases ("aws", "aws_bedrock") to "bedrock" so
  the provider factory matches correctly
- Add settings.bedrock_profile fallback for AWS_PROFILE, consistent
  with region and cross_region resolution

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Copilot review feedback — bearer token cleanup, stop_sequences, model dedup

- Remove stale bearer token refs from setup README and CHANGELOG
- Remove dead bedrock_api_key secret injection mapping
- Pass stop_sequences through to Bedrock InferenceConfiguration
- Remove "API key" from wizard menu description (bearer token removed)
- Skip duplicate LLM_MODEL write for bedrock backend in wizard
- Fix cargo fmt formatting

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback — async new(), remove LiteLLM entry, wizard fixes

- Remove dead LiteLLM-based bedrock entry from providers.json (native
  Converse API intercepts before registry lookup)
- Make BedrockProvider::new() async to avoid block_in_place panic in
  current_thread runtimes; propagate async to create_llm_provider,
  build_provider_chain, and init_llm
- Document CMake build prerequisite in docs/LLM_PROVIDERS.md
- Clear bedrock_profile when user selects "default credentials" in wizard
- Fix selected_model clearing to match established pattern (conditional
  on provider switch, not unconditional)
- Add regression tests for bedrock model preservation and profile clearing

Addresses review feedback from @zmanian on PR nearai#713.
Streaming support tracked in nearai#741.

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address remaining review comments — CLAUDE.md backends, wizard UX

- Add `bedrock` to CLAUDE.md inline backend list (nearai#10)
- Skip full setup re-run when keeping existing Bedrock config (nearai#11)
- Clear stale bedrock_profile on empty named-profile input (nearai#12)
- Add regression test for empty profile clearing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Chris Gorski <cgorski@cgorski.org>
Co-authored-by: cgorski <cgorski@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
drchirag1991 pushed a commit to drchirag1991/ironclaw that referenced this pull request Apr 8, 2026
* feat: add AWS Bedrock LLM provider via native Converse API

* fix: use JSON parsing for tool result error detection instead of brittle substring matching

* refactor: extract duplicated inference config builder into helper function

* fix: address review feedback — safe casts, input validation, and tests

- Safe u32→i32 cast for max_tokens using try_from with clamp
- Remove brittle string-based error detection fallback for tool results
- Validate BEDROCK_CROSS_REGION against allowed values (us/eu/apac/global)
- Validate message list is non-empty before Converse API call
- Log when using default us-east-1 region
- Update llm_backend doc comment to list all backends
- Add tests for build_inference_config and empty message handling

* fix: persist AWS_PROFILE for Bedrock named profile auth

The wizard collected the profile name but only printed a hint to set
it manually. Now it saves to settings and writes AWS_PROFILE to the
bootstrap .env, consistent with how BEDROCK_REGION and other Bedrock
settings are persisted.

* feat: gate AWS Bedrock behind optional `bedrock` feature flag

The AWS SDK dependencies (aws-config, aws-sdk-bedrockruntime,
aws-smithy-types) require cmake and a C compiler to build aws-lc-sys.
Gate them behind an opt-in `bedrock` feature flag so default builds
are unaffected.

Build with: cargo build --features bedrock
All config, settings, and wizard code stays unconditional (no AWS deps)
so users can configure Bedrock even without the feature compiled — they
get a clear error at startup directing them to rebuild.

* fix: address review feedback and adapt Bedrock provider to registry architecture (takeover nearai#345)

- Resolve merge conflicts with main's registry-based provider system
- Add missing cache_creation_input_tokens/cache_read_input_tokens fields
- Add missing content_parts field in test ChatMessage
- Fix string literal type mismatches in wizard env_vars (.to_string())
- Remove non-functional bearer token auth (AWS_BEARER_TOKEN_BEDROCK) from
  wizard and documentation per reviewer feedback from @zmanian and @serrrfirat
- Remove stale BEDROCK_ACCESS_KEY proxy entry from provider table
- Update Bedrock provider to use is_bedrock string check (LlmBackend enum removed)
- Add bedrock_profile fallback from settings in config resolution

[skip-regression-check]

Co-Authored-By: cgorski <cgorski@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use main's Cargo.lock as base to preserve dependency versions

Regenerating Cargo.lock from scratch caused transitive dependency version
drift that broke the html_to_markdown fixture test in CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: bedrock config bugs — spurious warning, alias normalization, profile fallback

- Move is_bedrock check before unknown-backend warning to prevent
  spurious "unknown backend" log for bedrock users
- Normalize backend aliases ("aws", "aws_bedrock") to "bedrock" so
  the provider factory matches correctly
- Add settings.bedrock_profile fallback for AWS_PROFILE, consistent
  with region and cross_region resolution

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Copilot review feedback — bearer token cleanup, stop_sequences, model dedup

- Remove stale bearer token refs from setup README and CHANGELOG
- Remove dead bedrock_api_key secret injection mapping
- Pass stop_sequences through to Bedrock InferenceConfiguration
- Remove "API key" from wizard menu description (bearer token removed)
- Skip duplicate LLM_MODEL write for bedrock backend in wizard
- Fix cargo fmt formatting

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback — async new(), remove LiteLLM entry, wizard fixes

- Remove dead LiteLLM-based bedrock entry from providers.json (native
  Converse API intercepts before registry lookup)
- Make BedrockProvider::new() async to avoid block_in_place panic in
  current_thread runtimes; propagate async to create_llm_provider,
  build_provider_chain, and init_llm
- Document CMake build prerequisite in docs/LLM_PROVIDERS.md
- Clear bedrock_profile when user selects "default credentials" in wizard
- Fix selected_model clearing to match established pattern (conditional
  on provider switch, not unconditional)
- Add regression tests for bedrock model preservation and profile clearing

Addresses review feedback from @zmanian on PR nearai#713.
Streaming support tracked in nearai#741.

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address remaining review comments — CLAUDE.md backends, wizard UX

- Add `bedrock` to CLAUDE.md inline backend list (nearai#10)
- Skip full setup re-run when keeping existing Bedrock config (nearai#11)
- Clear stale bedrock_profile on empty named-profile input (nearai#12)
- Add regression test for empty profile clearing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Chris Gorski <cgorski@cgorski.org>
Co-authored-by: cgorski <cgorski@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: high Safety, secrets, auth, or critical infrastructure scope: config Configuration scope: dependencies Dependency updates scope: docs Documentation scope: llm LLM integration scope: setup Onboarding / setup size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants