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:
parent
9f94ad6db4
commit
0e4e0d590d
4 changed files with 1322 additions and 27 deletions
|
|
@ -41,7 +41,7 @@ credential is not reused for fallback providers.
|
||||||
| `zai` | `z.ai` | No | `ZAI_API_KEY` |
|
| `zai` | `z.ai` | No | `ZAI_API_KEY` |
|
||||||
| `glm` | `zhipu` | No | `GLM_API_KEY` |
|
| `glm` | `zhipu` | No | `GLM_API_KEY` |
|
||||||
| `minimax` | `minimax-intl`, `minimax-io`, `minimax-global`, `minimax-cn`, `minimaxi`, `minimax-oauth`, `minimax-oauth-cn`, `minimax-portal`, `minimax-portal-cn` | No | `MINIMAX_OAUTH_TOKEN`, `MINIMAX_API_KEY` |
|
| `minimax` | `minimax-intl`, `minimax-io`, `minimax-global`, `minimax-cn`, `minimaxi`, `minimax-oauth`, `minimax-oauth-cn`, `minimax-portal`, `minimax-portal-cn` | No | `MINIMAX_OAUTH_TOKEN`, `MINIMAX_API_KEY` |
|
||||||
| `bedrock` | `aws-bedrock` | No | (use config/`API_KEY` fallback) |
|
| `bedrock` | `aws-bedrock` | No | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` + `AWS_REGION` |
|
||||||
| `qianfan` | `baidu` | No | `QIANFAN_API_KEY` |
|
| `qianfan` | `baidu` | No | `QIANFAN_API_KEY` |
|
||||||
| `qwen` | `dashscope`, `qwen-intl`, `dashscope-intl`, `qwen-us`, `dashscope-us` | No | `DASHSCOPE_API_KEY` |
|
| `qwen` | `dashscope`, `qwen-intl`, `dashscope-intl`, `qwen-us`, `dashscope-us` | No | `DASHSCOPE_API_KEY` |
|
||||||
| `groq` | — | No | `GROQ_API_KEY` |
|
| `groq` | — | No | `GROQ_API_KEY` |
|
||||||
|
|
@ -56,6 +56,17 @@ credential is not reused for fallback providers.
|
||||||
| `lmstudio` | `lm-studio` | Yes | (optional; local by default) |
|
| `lmstudio` | `lm-studio` | Yes | (optional; local by default) |
|
||||||
| `nvidia` | `nvidia-nim`, `build.nvidia.com` | No | `NVIDIA_API_KEY` |
|
| `nvidia` | `nvidia-nim`, `build.nvidia.com` | No | `NVIDIA_API_KEY` |
|
||||||
|
|
||||||
|
### Bedrock Notes
|
||||||
|
|
||||||
|
- Provider ID: `bedrock` (alias: `aws-bedrock`)
|
||||||
|
- API: [Converse API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html)
|
||||||
|
- Authentication: AWS AKSK (not a single API key). Set `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` environment variables.
|
||||||
|
- Optional: `AWS_SESSION_TOKEN` for temporary/STS credentials, `AWS_REGION` or `AWS_DEFAULT_REGION` (default: `us-east-1`).
|
||||||
|
- Default onboarding model: `anthropic.claude-sonnet-4-5-20250929-v1:0`
|
||||||
|
- Supports native tool calling and prompt caching (`cachePoint`).
|
||||||
|
- Cross-region inference profiles supported (e.g., `us.anthropic.claude-*`).
|
||||||
|
- Model IDs use Bedrock format: `anthropic.claude-sonnet-4-6`, `anthropic.claude-opus-4-6-v1`, etc.
|
||||||
|
|
||||||
### Kimi Code Notes
|
### Kimi Code Notes
|
||||||
|
|
||||||
- Provider ID: `kimi-code`
|
- Provider ID: `kimi-code`
|
||||||
|
|
|
||||||
|
|
@ -547,6 +547,7 @@ fn default_model_for_provider(provider: &str) -> String {
|
||||||
"ollama" => "llama3.2".into(),
|
"ollama" => "llama3.2".into(),
|
||||||
"gemini" => "gemini-2.5-pro".into(),
|
"gemini" => "gemini-2.5-pro".into(),
|
||||||
"kimi-code" => "kimi-for-coding".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(),
|
"nvidia" => "meta/llama-3.3-70b-instruct".into(),
|
||||||
"astrai" => "anthropic/claude-sonnet-4.6".into(),
|
"astrai" => "anthropic/claude-sonnet-4.6".into(),
|
||||||
_ => "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()),
|
("codellama".to_string(), "Code Llama".to_string()),
|
||||||
("phi3".to_string(), "Phi-3 (small, fast)".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" => vec![
|
||||||
(
|
(
|
||||||
"gemini-3-pro-preview".to_string(),
|
"gemini-3-pro-preview".to_string(),
|
||||||
|
|
@ -1826,29 +1845,51 @@ fn setup_provider(workspace_dir: &Path) -> Result<(String, String, String, Optio
|
||||||
};
|
};
|
||||||
|
|
||||||
println!();
|
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!(
|
print_bullet(&format!(
|
||||||
"Get your API key at: {}",
|
"Set {} and {} environment variables.",
|
||||||
style(key_url).cyan().underlined()
|
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!(
|
print_bullet(&format!(
|
||||||
"Skipped. Set {} or edit config.toml later.",
|
"Optionally set {} for the region (default: us-east-1).",
|
||||||
style(env_var).yellow()
|
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 ──
|
// ── Model selection ──
|
||||||
|
|
|
||||||
1241
src/providers/bedrock.rs
Normal file
1241
src/providers/bedrock.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod anthropic;
|
pub mod anthropic;
|
||||||
|
pub mod bedrock;
|
||||||
pub mod compatible;
|
pub mod compatible;
|
||||||
pub mod copilot;
|
pub mod copilot;
|
||||||
pub mod gemini;
|
pub mod gemini;
|
||||||
|
|
@ -502,6 +503,9 @@ fn resolve_provider_credential(name: &str, credential_override: Option<&str>) ->
|
||||||
}
|
}
|
||||||
name if is_glm_alias(name) => vec!["GLM_API_KEY"],
|
name if is_glm_alias(name) => vec!["GLM_API_KEY"],
|
||||||
name if is_minimax_alias(name) => vec![MINIMAX_OAUTH_TOKEN_ENV, MINIMAX_API_KEY_ENV],
|
name if is_minimax_alias(name) => vec![MINIMAX_OAUTH_TOKEN_ENV, MINIMAX_API_KEY_ENV],
|
||||||
|
// Bedrock uses AWS AKSK from env vars (AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY),
|
||||||
|
// not a single API key. Credential resolution happens inside BedrockProvider.
|
||||||
|
"bedrock" | "aws-bedrock" => return None,
|
||||||
name if is_qianfan_alias(name) => vec!["QIANFAN_API_KEY"],
|
name if is_qianfan_alias(name) => vec!["QIANFAN_API_KEY"],
|
||||||
name if is_qwen_alias(name) => vec!["DASHSCOPE_API_KEY"],
|
name if is_qwen_alias(name) => vec!["DASHSCOPE_API_KEY"],
|
||||||
name if is_zai_alias(name) => vec!["ZAI_API_KEY"],
|
name if is_zai_alias(name) => vec!["ZAI_API_KEY"],
|
||||||
|
|
@ -669,12 +673,7 @@ pub fn create_provider_with_url(
|
||||||
AuthStyle::Bearer,
|
AuthStyle::Bearer,
|
||||||
)
|
)
|
||||||
)),
|
)),
|
||||||
"bedrock" | "aws-bedrock" => Ok(Box::new(OpenAiCompatibleProvider::new(
|
"bedrock" | "aws-bedrock" => Ok(Box::new(bedrock::BedrockProvider::new())),
|
||||||
"Amazon Bedrock",
|
|
||||||
"https://bedrock-runtime.us-east-1.amazonaws.com",
|
|
||||||
key,
|
|
||||||
AuthStyle::Bearer,
|
|
||||||
))),
|
|
||||||
name if is_qianfan_alias(name) => Ok(Box::new(OpenAiCompatibleProvider::new(
|
name if is_qianfan_alias(name) => Ok(Box::new(OpenAiCompatibleProvider::new(
|
||||||
"Qianfan", "https://aip.baidubce.com", key, AuthStyle::Bearer,
|
"Qianfan", "https://aip.baidubce.com", key, AuthStyle::Bearer,
|
||||||
))),
|
))),
|
||||||
|
|
@ -1417,8 +1416,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn factory_bedrock() {
|
fn factory_bedrock() {
|
||||||
assert!(create_provider("bedrock", Some("key")).is_ok());
|
// Bedrock uses AWS env vars for credentials, not API key.
|
||||||
assert!(create_provider("aws-bedrock", Some("key")).is_ok());
|
assert!(create_provider("bedrock", None).is_ok());
|
||||||
|
assert!(create_provider("aws-bedrock", None).is_ok());
|
||||||
|
// Passing an api_key is harmless (ignored).
|
||||||
|
assert!(create_provider("bedrock", Some("ignored")).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue