From 28feeab36c74024de5c67bb8d166a91c8d625a9a Mon Sep 17 00:00:00 2001 From: Nate Armstrong Date: Mon, 19 Oct 2026 12:55:27 -0700 Subject: [PATCH] fix(gateway): bedrock chat uses the aws settings in the deployment config (#47365) The deployment's aws_* litellm_params (keys, region, session, role, aws_bedrock_runtime_endpoint) sit in their own group, not in extra, and only the Vertex group was copied into the call's params, so Bedrock chat signed with the environment's credentials and called the default regional endpoint. Copy the AWS group the same way, resolving os.environ/ references. --- .../gateway-inference/src/chat_completions.rs | 21 ++++++++++------ .../crates/gateway/tests/chat_translation.rs | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/litellm-rust/crates/gateway-inference/src/chat_completions.rs b/litellm-rust/crates/gateway-inference/src/chat_completions.rs index 41a3ac0fbc9..87380ead495 100644 --- a/litellm-rust/crates/gateway-inference/src/chat_completions.rs +++ b/litellm-rust/crates/gateway-inference/src/chat_completions.rs @@ -187,23 +187,30 @@ fn split(mut body: Map) -> (Value, Map) { /// A deployment's connection for a provider chat translates. Python's router reads each /// `os.environ/` setting from the environment when it loads the deployment, and `completion` -/// hands the Vertex AI settings to the call, where a request body cannot name them. +/// hands the Vertex AI and AWS settings to the call, where a request body cannot name them. async fn connection( gateway: &Gateway, deployment: &Deployment, optional_params: &mut Map, ) -> Result<(Option, Option), Error> { - let vertex = match serde_json::to_value(&deployment.litellm_params.vertex) { - Ok(Value::Object(fields)) => fields, - _ => Map::new(), - }; + // The Vertex AI and AWS settings both live in their own groups of `litellm_params`, not in + // `extra`, so each is copied into the call's params, where the provider config reads it. + let mut settings = Map::new(); + for group in [ + serde_json::to_value(&deployment.litellm_params.vertex), + serde_json::to_value(&deployment.litellm_params.aws), + ] { + if let Ok(Value::Object(fields)) = group { + settings.extend(fields); + } + } let references: Vec<&str> = [ deployment.api_key.as_deref(), deployment.api_base.as_deref(), ] .into_iter() .flatten() - .chain(vertex.values().filter_map(Value::as_str)) + .chain(settings.values().filter_map(Value::as_str)) .filter_map(|value| value.strip_prefix(ENV_REFERENCE)) .collect(); let secrets = match references.is_empty() { @@ -221,7 +228,7 @@ async fn connection( Some(name) => secrets.as_ref().and_then(|secrets| secrets.get(name)), None => Some(value.to_owned()), }; - for (name, value) in vertex { + for (name, value) in settings { let value = match value { Value::String(text) => resolve(&text).map(Value::String), Value::Null => None, diff --git a/litellm-rust/crates/gateway/tests/chat_translation.rs b/litellm-rust/crates/gateway/tests/chat_translation.rs index aa0c1da8f6f..3c7f7acf305 100644 --- a/litellm-rust/crates/gateway/tests/chat_translation.rs +++ b/litellm-rust/crates/gateway/tests/chat_translation.rs @@ -276,6 +276,13 @@ async fn harness() -> Harness { model: bedrock/{BEDROCK_MODEL} api_base: {upstream} api_key: bedrock-token + - model_name: bedrock-keys + litellm_params: + model: bedrock/{BEDROCK_MODEL} + aws_access_key_id: AKIACONFIGKEY + aws_secret_access_key: config-secret + aws_region_name: us-west-2 + aws_bedrock_runtime_endpoint: {upstream} - model_name: claude-dropping litellm_params: model: anthropic/{ANTHROPIC_MODEL} @@ -449,6 +456,24 @@ async fn anthropic_tool_round_trip() { ); } +#[tokio::test] +async fn bedrock_signs_with_the_keys_region_and_endpoint_in_the_config() { + let harness = harness().await; + harness + .json(json!({"model": "bedrock-keys", "messages": answered("tooluse_1"), "max_tokens": 16})) + .await; + + let sent = harness.sent(); + assert_eq!(sent.len(), 1, "the configured endpoint gets the call"); + assert!(sent[0].path.ends_with("/converse"), "{}", sent[0].path); + let authorization = sent[0].header("authorization").unwrap(); + assert!( + authorization.starts_with("AWS4-HMAC-SHA256 Credential=AKIACONFIGKEY/") + && authorization.contains("/us-west-2/bedrock/aws4_request"), + "{authorization}" + ); +} + #[tokio::test] async fn bedrock_tool_round_trip() { let harness = harness().await;