feat(provider): add dedicated AWS Bedrock Converse API provider

Replace the non-functional OpenAI-compatible stub with a purpose-built
Bedrock provider that implements AWS SigV4 signing from first principles
using hmac/sha2/hex crates — no AWS SDK dependency.

Key capabilities:
- SigV4 authentication (AKSK + optional session token)
- Converse API with native tool calling support
- Prompt caching via cachePoint heuristics
- Proper URI encoding for model IDs containing colons
- Resilient response parsing with unknown block type fallback

Also updates:
- Factory wiring and credential resolution bypass for AKSK auth
- Onboard wizard with Bedrock-specific model selection and guidance
- Provider reference docs with auth, region, and model ID details

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
KevinZhao 2026-02-19 09:38:02 +08:00 committed by Chummy
parent 9f94ad6db4
commit 0e4e0d590d
4 changed files with 1322 additions and 27 deletions

View file

@ -547,6 +547,7 @@ fn default_model_for_provider(provider: &str) -> String {
"ollama" => "llama3.2".into(),
"gemini" => "gemini-2.5-pro".into(),
"kimi-code" => "kimi-for-coding".into(),
"bedrock" | "aws-bedrock" => "anthropic.claude-sonnet-4-5-20250929-v1:0".into(),
"nvidia" => "meta/llama-3.3-70b-instruct".into(),
"astrai" => "anthropic/claude-sonnet-4.6".into(),
_ => "anthropic/claude-sonnet-4.6".into(),
@ -864,6 +865,24 @@ fn curated_models_for_provider(provider_name: &str) -> Vec<(String, String)> {
("codellama".to_string(), "Code Llama".to_string()),
("phi3".to_string(), "Phi-3 (small, fast)".to_string()),
],
"bedrock" => vec![
(
"anthropic.claude-sonnet-4-6".to_string(),
"Claude Sonnet 4.6 (latest, recommended)".to_string(),
),
(
"anthropic.claude-opus-4-6-v1".to_string(),
"Claude Opus 4.6 (strongest)".to_string(),
),
(
"anthropic.claude-haiku-4-5-20251001-v1:0".to_string(),
"Claude Haiku 4.5 (fastest, cheapest)".to_string(),
),
(
"anthropic.claude-sonnet-4-5-20250929-v1:0".to_string(),
"Claude Sonnet 4.5".to_string(),
),
],
"gemini" => vec![
(
"gemini-3-pro-preview".to_string(),
@ -1826,29 +1845,51 @@ fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, Optio
};
println!();
if !key_url.is_empty() {
if matches!(provider_name, "bedrock" | "aws-bedrock") {
// Bedrock uses AWS AKSK, not a single API key.
print_bullet("Bedrock uses AWS credentials (not a single API key).");
print_bullet(&format!(
"Get your API key at: {}",
style(key_url).cyan().underlined()
"Set {} and {} environment variables.",
style("AWS_ACCESS_KEY_ID").yellow(),
style("AWS_SECRET_ACCESS_KEY").yellow(),
));
}
print_bullet("You can also set it later via env var or config file.");
println!();
let key: String = Input::new()
.with_prompt(" Paste your API key (or press Enter to skip)")
.allow_empty(true)
.interact_text()?;
if key.is_empty() {
let env_var = provider_env_var(provider_name);
print_bullet(&format!(
"Skipped. Set {} or edit config.toml later.",
style(env_var).yellow()
"Optionally set {} for the region (default: us-east-1).",
style("AWS_REGION").yellow(),
));
}
if !key_url.is_empty() {
print_bullet(&format!(
"Manage IAM credentials at: {}",
style(key_url).cyan().underlined()
));
}
println!();
String::new()
} else {
if !key_url.is_empty() {
print_bullet(&format!(
"Get your API key at: {}",
style(key_url).cyan().underlined()
));
}
print_bullet("You can also set it later via env var or config file.");
println!();
key
let key: String = Input::new()
.with_prompt(" Paste your API key (or press Enter to skip)")
.allow_empty(true)
.interact_text()?;
if key.is_empty() {
let env_var = provider_env_var(provider_name);
print_bullet(&format!(
"Skipped. Set {} or edit config.toml later.",
style(env_var).yellow()
));
}
key
}
};
// ── Model selection ──