Skip to content

fix(bedrock): resolve realtime AWS credentials from config, not just env - #32257

Closed
devin-ai-integration[bot] wants to merge 2 commits into
litellm_internal_stagingfrom
litellm_bedrock_realtime_config_creds
Closed

fix(bedrock): resolve realtime AWS credentials from config, not just env#32257
devin-ai-integration[bot] wants to merge 2 commits into
litellm_internal_stagingfrom
litellm_bedrock_realtime_config_creds

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Follow-up to #31924. The Nova Sonic realtime handler built its smithy client with EnvironmentCredentialsResolver, which only reads AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY from the process environment. Any aws_access_key_id, aws_secret_access_key, aws_session_token, aws_role_name, aws_profile_name, aws_web_identity_token, etc. set in the proxy config's litellm_params were silently ignored, so realtime sessions failed unless ambient env credentials happened to exist

This change routes the params (already plumbed through realtime_api/main.py into BedrockRealtime.async_realtime) through BaseAWSLLM.get_credentials, the same boto3-based resolution every other Bedrock endpoint uses (static keys, session tokens, role assumption, profiles, web identity), and adapts the resulting boto3 credentials to the smithy client via a small BotoCredentialsResolver. When no config params are set, get_credentials falls back to the standard boto3 chain, which still covers env vars, so existing env-based setups keep working. The resolver freezes credentials via run_in_executor so a refreshable-credentials STS call never blocks the event loop

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

No AWS credentials were available in this session, so a live Bedrock run wasn't possible. To verify against the real API, put keys only in the config (none in env) and run the same qa_client.py flow from #31924:

model_list:
  - model_name: "bedrock-sonic"
    litellm_params:
      model: bedrock/amazon.nova-sonic-v1:0
      aws_region_name: us-east-1
      aws_access_key_id: <key>
      aws_secret_access_key: <secret>
    model_info:
      mode: realtime

general_settings:
  master_key: "sk-qa-1234"
env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY .venv/bin/litellm --config qa_config.yaml --port 49781 --detailed_debug > proxy.log 2>&1 &
.venv/bin/python qa_client.py 49781

Before this PR the session dies with SmithyIdentityError: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required; after it, the client receives response.text.delta events and a final response.done

Regression tests pin both halves: BotoCredentialsResolver maps frozen boto3 credentials to a smithy AWSCredentialsIdentity, and async_realtime forwards all config aws_* params into get_credentials and wires the resulting resolver into the client config

Type

🐛 Bug Fix

Changes

litellm/llms/bedrock/realtime/handler.py: replace EnvironmentCredentialsResolver with BotoCredentialsResolver(self.get_credentials(aws_access_key_id=..., aws_role_name=..., ...))

tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_handler.py: regression tests for the credential path

Link to Devin session: https://app.devin.ai/sessions/9c6563e5a0834358aa1db302935bb36f
Requested by: @mateo-berri

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@mateo-berri mateo-berri self-assigned this Jul 6, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the EnvironmentCredentialsResolver in the Bedrock Nova Sonic realtime handler with a new BotoCredentialsResolver that reads credentials from the full get_credentials() boto3-based resolution chain, fixing silent failures when aws_access_key_id and related params were set in the proxy config rather than process environment variables.

  • handler.py: Adds BotoCredentialsResolver (adapts a frozen boto3 credentials object to the smithy AWSCredentialsIdentity interface) and wires all aws_* params through BaseAWSLLM.get_credentials() before constructing the smithy Config.
  • test_bedrock_realtime_handler.py: Two new tests — one unit-tests BotoCredentialsResolver.get_identity() with a fake boto3 credentials object, the other verifies that async_realtime forwards all config credentials into get_credentials() and stores the resulting resolver in the smithy Config.

Confidence Score: 4/5

Safe to merge; the credential fix is correct and all aws_* config params are properly forwarded through the existing boto3 resolution chain.

The core fix is sound: BotoCredentialsResolver correctly adapts the boto3 frozen credentials object to the smithy identity interface, and get_credentials() already handles every auth flow (static keys, session tokens, role assumption, web identity, profiles) with env-var fallback. The only concern is that get_frozen_credentials() inside the async get_identity() method is a synchronous botocore call that can block the event loop when refreshable credentials expire mid-session and a network round-trip to STS is needed.

litellm/llms/bedrock/realtime/handler.py — specifically the get_identity method in BotoCredentialsResolver

Important Files Changed

Filename Overview
litellm/llms/bedrock/realtime/handler.py Replaces EnvironmentCredentialsResolver with BotoCredentialsResolver; get_frozen_credentials() in get_identity() is a synchronous blocking call that could stall the event loop for refreshable credentials (role assumption, web identity) when mid-session token refresh occurs.
tests/test_litellm/llms/bedrock/realtime/test_bedrock_realtime_handler.py Adds two well-scoped regression tests: one for BotoCredentialsResolver mapping and one for end-to-end credential forwarding through async_realtime; all mocked, no real network calls.

Reviews (1): Last reviewed commit: "fix(bedrock): resolve realtime AWS crede..." | Re-trigger Greptile

Comment on lines +28 to +36
async def get_identity(self, *, properties: Any) -> Any:
from smithy_aws_core.identity import AWSCredentialsIdentity

frozen = self._credentials.get_frozen_credentials()
return AWSCredentialsIdentity(
access_key_id=frozen.access_key,
secret_access_key=frozen.secret_key,
session_token=frozen.token,
)

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.

P2 get_frozen_credentials() is a synchronous botocore call inside an async method. For RefreshableCredentials (produced by role assumption, web identity, or profile auth), an expired token triggers a synchronous STS HTTP request here, which will block the asyncio event loop for the duration of that network round-trip. Static key credentials are unaffected since get_frozen_credentials() is instant for them, but long-running WebSocket sessions with role-assumed credentials will hit this on every credential refresh cycle.

Suggested change
async def get_identity(self, *, properties: Any) -> Any:
from smithy_aws_core.identity import AWSCredentialsIdentity
frozen = self._credentials.get_frozen_credentials()
return AWSCredentialsIdentity(
access_key_id=frozen.access_key,
secret_access_key=frozen.secret_key,
session_token=frozen.token,
)
async def get_identity(self, *, properties: Any) -> Any:
import asyncio
from smithy_aws_core.identity import AWSCredentialsIdentity
loop = asyncio.get_event_loop()
frozen = await loop.run_in_executor(None, self._credentials.get_frozen_credentials)
return AWSCredentialsIdentity(
access_key_id=frozen.access_key,
secret_access_key=frozen.secret_key,
session_token=frozen.token,
)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…realtime resolver

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Closing: superseded by #32275, which landed the same config-credential fix

@mateo-berri mateo-berri closed this Jul 7, 2026
@mateo-berri
mateo-berri deleted the litellm_bedrock_realtime_config_creds branch July 7, 2026 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant