diff --git a/lib/crewai-files/src/crewai_files/uploaders/anthropic.py b/lib/crewai-files/src/crewai_files/uploaders/anthropic.py index 9a975e64e..f1e8fd2ff 100644 --- a/lib/crewai-files/src/crewai_files/uploaders/anthropic.py +++ b/lib/crewai-files/src/crewai_files/uploaders/anthropic.py @@ -22,16 +22,23 @@ class AnthropicFileUploader(FileUploader): until explicitly deleted. """ - def __init__(self, api_key: str | None = None) -> None: + def __init__( + self, + api_key: str | None = None, + client: Any = None, + async_client: Any = None, + ) -> None: """Initialize the Anthropic uploader. Args: api_key: Optional Anthropic API key. If not provided, uses ANTHROPIC_API_KEY environment variable. + client: Optional pre-instantiated Anthropic client. + async_client: Optional pre-instantiated async Anthropic client. """ self._api_key = api_key or os.environ.get("ANTHROPIC_API_KEY") - self._client: Any = None - self._async_client: Any = None + self._client: Any = client + self._async_client: Any = async_client @property def provider_name(self) -> str: diff --git a/lib/crewai-files/src/crewai_files/uploaders/bedrock.py b/lib/crewai-files/src/crewai_files/uploaders/bedrock.py index e050df7e5..af0a0a538 100644 --- a/lib/crewai-files/src/crewai_files/uploaders/bedrock.py +++ b/lib/crewai-files/src/crewai_files/uploaders/bedrock.py @@ -110,6 +110,8 @@ class BedrockFileUploader(FileUploader): bucket_owner: str | None = None, prefix: str = "crewai-files", region: str | None = None, + client: Any = None, + async_client: Any = None, ) -> None: """Initialize the Bedrock S3 uploader. @@ -120,6 +122,8 @@ class BedrockFileUploader(FileUploader): Uses CREWAI_BEDROCK_S3_BUCKET_OWNER environment variable if not provided. prefix: S3 key prefix for uploaded files (default: "crewai-files"). region: AWS region. Uses AWS_REGION or AWS_DEFAULT_REGION if not provided. + client: Optional pre-instantiated boto3 S3 client. + async_client: Optional pre-instantiated aioboto3 S3 client. """ self._bucket_name = bucket_name or os.environ.get("CREWAI_BEDROCK_S3_BUCKET") self._bucket_owner = bucket_owner or os.environ.get( @@ -129,8 +133,8 @@ class BedrockFileUploader(FileUploader): self._region = region or os.environ.get( "AWS_REGION", os.environ.get("AWS_DEFAULT_REGION") ) - self._client: Any = None - self._async_client: Any = None + self._client: Any = client + self._async_client: Any = async_client @property def provider_name(self) -> str: diff --git a/lib/crewai-files/src/crewai_files/uploaders/factory.py b/lib/crewai-files/src/crewai_files/uploaders/factory.py index 9407d6dd2..455623f9e 100644 --- a/lib/crewai-files/src/crewai_files/uploaders/factory.py +++ b/lib/crewai-files/src/crewai_files/uploaders/factory.py @@ -36,10 +36,15 @@ ProviderType: TypeAlias = ( ) +from typing import Any as AnyType + + class _BaseOpts(TypedDict): """Kwargs for uploader factory.""" api_key: NotRequired[str | None] + client: NotRequired[AnyType] + async_client: NotRequired[AnyType] class OpenAIOpts(_BaseOpts): @@ -48,9 +53,12 @@ class OpenAIOpts(_BaseOpts): chunk_size: NotRequired[int] -class GeminiOpts(_BaseOpts): +class GeminiOpts(TypedDict): """Kwargs for gemini uploader factory.""" + api_key: NotRequired[str | None] + client: NotRequired[AnyType] + class AnthropicOpts(_BaseOpts): """Kwargs for anthropic uploader factory.""" @@ -63,6 +71,8 @@ class BedrockOpts(TypedDict): bucket_owner: NotRequired[str | None] prefix: NotRequired[str] region: NotRequired[str | None] + client: NotRequired[AnyType] + async_client: NotRequired[AnyType] class AllOptions(TypedDict): @@ -74,6 +84,8 @@ class AllOptions(TypedDict): bucket_owner: NotRequired[str | None] prefix: NotRequired[str] region: NotRequired[str | None] + client: NotRequired[AnyType] + async_client: NotRequired[AnyType] @overload @@ -133,7 +145,10 @@ def get_uploader( try: from crewai_files.uploaders.gemini import GeminiFileUploader - return GeminiFileUploader(api_key=kwargs.get("api_key")) + return GeminiFileUploader( + api_key=kwargs.get("api_key"), + client=kwargs.get("client"), + ) except ImportError: logger.warning( "google-genai not installed. Install with: pip install google-genai" @@ -144,7 +159,11 @@ def get_uploader( try: from crewai_files.uploaders.anthropic import AnthropicFileUploader - return AnthropicFileUploader(api_key=kwargs.get("api_key")) + return AnthropicFileUploader( + api_key=kwargs.get("api_key"), + client=kwargs.get("client"), + async_client=kwargs.get("async_client"), + ) except ImportError: logger.warning( "anthropic not installed. Install with: pip install anthropic" @@ -162,6 +181,8 @@ def get_uploader( return OpenAIFileUploader( api_key=kwargs.get("api_key"), chunk_size=kwargs.get("chunk_size", 67_108_864), + client=kwargs.get("client"), + async_client=kwargs.get("async_client"), ) except ImportError: logger.warning("openai not installed. Install with: pip install openai") @@ -187,6 +208,8 @@ def get_uploader( bucket_owner=kwargs.get("bucket_owner"), prefix=kwargs.get("prefix", "crewai-files"), region=kwargs.get("region"), + client=kwargs.get("client"), + async_client=kwargs.get("async_client"), ) except ImportError: logger.warning("boto3 not installed. Install with: pip install boto3") diff --git a/lib/crewai-files/src/crewai_files/uploaders/gemini.py b/lib/crewai-files/src/crewai_files/uploaders/gemini.py index e563093ee..156f8d96e 100644 --- a/lib/crewai-files/src/crewai_files/uploaders/gemini.py +++ b/lib/crewai-files/src/crewai_files/uploaders/gemini.py @@ -93,15 +93,20 @@ class GeminiFileUploader(FileUploader): Uses the google-genai SDK to upload files. Files are stored for 48 hours. """ - def __init__(self, api_key: str | None = None) -> None: + def __init__( + self, + api_key: str | None = None, + client: Any = None, + ) -> None: """Initialize the Gemini uploader. Args: api_key: Optional Google API key. If not provided, uses GOOGLE_API_KEY environment variable. + client: Optional pre-instantiated Gemini client. """ self._api_key = api_key or os.environ.get("GOOGLE_API_KEY") - self._client: Any = None + self._client: Any = client @property def provider_name(self) -> str: diff --git a/lib/crewai-files/src/crewai_files/uploaders/openai.py b/lib/crewai-files/src/crewai_files/uploaders/openai.py index ec18fcc43..8a6f976b5 100644 --- a/lib/crewai-files/src/crewai_files/uploaders/openai.py +++ b/lib/crewai-files/src/crewai_files/uploaders/openai.py @@ -100,6 +100,8 @@ class OpenAIFileUploader(FileUploader): self, api_key: str | None = None, chunk_size: int = DEFAULT_UPLOAD_CHUNK_SIZE, + client: Any = None, + async_client: Any = None, ) -> None: """Initialize the OpenAI uploader. @@ -107,11 +109,13 @@ class OpenAIFileUploader(FileUploader): api_key: Optional OpenAI API key. If not provided, uses OPENAI_API_KEY environment variable. chunk_size: Chunk size in bytes for multipart uploads (default 64MB). + client: Optional pre-instantiated OpenAI client. + async_client: Optional pre-instantiated async OpenAI client. """ self._api_key = api_key or os.environ.get("OPENAI_API_KEY") self._chunk_size = chunk_size - self._client: Any = None - self._async_client: Any = None + self._client: Any = client + self._async_client: Any = async_client @property def provider_name(self) -> str: diff --git a/lib/crewai/src/crewai/llms/base_llm.py b/lib/crewai/src/crewai/llms/base_llm.py index a122d1a30..862634b2a 100644 --- a/lib/crewai/src/crewai/llms/base_llm.py +++ b/lib/crewai/src/crewai/llms/base_llm.py @@ -310,6 +310,17 @@ class BaseLLM(ABC): """ return {"type": "text", "text": text} + def get_file_uploader(self) -> Any: + """Get a file uploader configured with this LLM's client. + + Returns an uploader instance that reuses this LLM's authenticated client, + avoiding the need to create a new connection for file uploads. + + Returns: + A FileUploader instance, or None if not supported by this provider. + """ + return None + # Common helper methods for native SDK implementations def _emit_call_started_event( diff --git a/lib/crewai/src/crewai/llms/providers/anthropic/completion.py b/lib/crewai/src/crewai/llms/providers/anthropic/completion.py index 571616561..b533415ad 100644 --- a/lib/crewai/src/crewai/llms/providers/anthropic/completion.py +++ b/lib/crewai/src/crewai/llms/providers/anthropic/completion.py @@ -1336,3 +1336,19 @@ class AnthropicCompletion(BaseLLM): if not self.supports_multimodal(): return [] return ["image/", "application/pdf"] + + def get_file_uploader(self) -> Any: + """Get an Anthropic file uploader using this LLM's clients. + + Returns: + AnthropicFileUploader instance with pre-configured sync and async clients. + """ + try: + from crewai_files.uploaders.anthropic import AnthropicFileUploader + + return AnthropicFileUploader( + client=self.client, + async_client=self.async_client, + ) + except ImportError: + return None diff --git a/lib/crewai/src/crewai/llms/providers/bedrock/completion.py b/lib/crewai/src/crewai/llms/providers/bedrock/completion.py index 8e7710f7a..77f400aeb 100644 --- a/lib/crewai/src/crewai/llms/providers/bedrock/completion.py +++ b/lib/crewai/src/crewai/llms/providers/bedrock/completion.py @@ -1665,6 +1665,34 @@ class BedrockCompletion(BaseLLM): return types + def get_file_uploader(self) -> Any: + """Get a Bedrock S3 file uploader using this LLM's AWS credentials. + + Creates an S3 client using the same AWS credentials configured for + this Bedrock LLM instance. + + Returns: + BedrockFileUploader instance with pre-configured S3 client, + or None if crewai_files is not installed. + """ + try: + import boto3 + from crewai_files.uploaders.bedrock import BedrockFileUploader + + s3_client = boto3.client( + "s3", + region_name=self.region_name, + aws_access_key_id=self.aws_access_key_id, + aws_secret_access_key=self.aws_secret_access_key, + aws_session_token=self.aws_session_token, + ) + return BedrockFileUploader( + region=self.region_name, + client=s3_client, + ) + except ImportError: + return None + def _get_document_format(self, content_type: str) -> str | None: """Map content type to Bedrock document format. diff --git a/lib/crewai/src/crewai/llms/providers/gemini/completion.py b/lib/crewai/src/crewai/llms/providers/gemini/completion.py index a8743e26b..284f00de9 100644 --- a/lib/crewai/src/crewai/llms/providers/gemini/completion.py +++ b/lib/crewai/src/crewai/llms/providers/gemini/completion.py @@ -1221,3 +1221,16 @@ class GeminiCompletion(BaseLLM): A content block in Gemini's expected format. """ return {"text": text} + + def get_file_uploader(self) -> Any: + """Get a Gemini file uploader using this LLM's client. + + Returns: + GeminiFileUploader instance with pre-configured client. + """ + try: + from crewai_files.uploaders.gemini import GeminiFileUploader + + return GeminiFileUploader(client=self.client) + except ImportError: + return None diff --git a/lib/crewai/src/crewai/llms/providers/openai/completion.py b/lib/crewai/src/crewai/llms/providers/openai/completion.py index e2deb868d..2788df84f 100644 --- a/lib/crewai/src/crewai/llms/providers/openai/completion.py +++ b/lib/crewai/src/crewai/llms/providers/openai/completion.py @@ -1103,3 +1103,19 @@ class OpenAICompletion(BaseLLM): if not self.supports_multimodal(): return [] return ["image/"] + + def get_file_uploader(self) -> Any: + """Get an OpenAI file uploader using this LLM's clients. + + Returns: + OpenAIFileUploader instance with pre-configured sync and async clients. + """ + try: + from crewai_files.uploaders.openai import OpenAIFileUploader + + return OpenAIFileUploader( + client=self.client, + async_client=self.async_client, + ) + except ImportError: + return None