From dd4a1062913375cfb4782ba0936a32d9ebbfcb3d Mon Sep 17 00:00:00 2001 From: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com> Date: Fri, 25 Sep 2026 09:37:31 +0530 Subject: [PATCH] fix(bedrock): fall back to sync calls from acall (#7680) --- .../llms/providers/bedrock/completion.py | 19 +++++++-- lib/crewai/tests/llms/bedrock/test_bedrock.py | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/crewai/src/crewai/llms/providers/bedrock/completion.py b/lib/crewai/src/crewai/llms/providers/bedrock/completion.py index 792c66ca1..2528aa204 100644 --- a/lib/crewai/src/crewai/llms/providers/bedrock/completion.py +++ b/lib/crewai/src/crewai/llms/providers/bedrock/completion.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio from collections.abc import Mapping, Sequence from contextlib import AsyncExitStack import json @@ -485,15 +486,25 @@ class BedrockCompletion(BaseLLM): Generated text response or structured output. Raises: - NotImplementedError: If aiobotocore is not installed. LLMContextLengthExceededError: If context window is exceeded. """ effective_response_model = response_model or self.response_format if not AIOBOTOCORE_AVAILABLE: - raise NotImplementedError( - "Async support for AWS Bedrock requires aiobotocore. " - 'Install with: uv add "crewai[bedrock]"' + logging.warning( + "aiobotocore is not installed; falling back to synchronous AWS " + "Bedrock calls in a worker thread. Install `crewai[bedrock]` " + "for native async support." + ) + return await asyncio.to_thread( + self.call, + messages, + tools=tools, + callbacks=callbacks, + available_functions=available_functions, + from_task=from_task, + from_agent=from_agent, + response_model=effective_response_model, ) with llm_call_context(): diff --git a/lib/crewai/tests/llms/bedrock/test_bedrock.py b/lib/crewai/tests/llms/bedrock/test_bedrock.py index fd79198fc..8659393ee 100644 --- a/lib/crewai/tests/llms/bedrock/test_bedrock.py +++ b/lib/crewai/tests/llms/bedrock/test_bedrock.py @@ -1,4 +1,6 @@ +import logging import os +import threading from unittest.mock import patch, MagicMock import pytest @@ -210,6 +212,45 @@ def test_bedrock_completion_call(): mock_call.assert_called_once_with("Hello, how are you?") +@pytest.mark.asyncio +async def test_bedrock_acall_falls_back_to_sync_call_without_aiobotocore(caplog): + """Async Bedrock calls remain usable when only the sync SDK is installed.""" + llm = LLM(model="bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0") + callbacks = [MagicMock()] + available_functions = {"lookup": MagicMock()} + call_thread_id: int | None = None + + def sync_call(*args, **kwargs): + nonlocal call_thread_id + call_thread_id = threading.get_ident() + return "fallback response" + + with caplog.at_level(logging.WARNING): + with ( + patch.object(bedrock_completion, "AIOBOTOCORE_AVAILABLE", False), + patch.object(llm, "call", side_effect=sync_call) as mock_call, + ): + event_loop_thread_id = threading.get_ident() + result = await llm.acall( + "Hello, how are you?", + callbacks=callbacks, + available_functions=available_functions, + ) + + assert result == "fallback response" + assert call_thread_id != event_loop_thread_id + assert "falling back to synchronous AWS Bedrock calls" in caplog.text + mock_call.assert_called_once_with( + "Hello, how are you?", + tools=None, + callbacks=callbacks, + available_functions=available_functions, + from_task=None, + from_agent=None, + response_model=None, + ) + + def test_bedrock_completion_called_during_crew_execution(): """ Test that BedrockCompletion.call is actually invoked when running a crew