diff --git a/docs/ar/enterprise/integrations/salesforce.mdx b/docs/ar/enterprise/integrations/salesforce.mdx
index 4e7dc060d..3807a27ea 100644
--- a/docs/ar/enterprise/integrations/salesforce.mdx
+++ b/docs/ar/enterprise/integrations/salesforce.mdx
@@ -17,15 +17,62 @@ mode: "wide"
- حساب Salesforce بالصلاحيات المناسبة
- ربط حساب Salesforce الخاص بك عبر [صفحة التكاملات](https://app.crewai.com/integrations)
+
+ يتطلب Salesforce **تثبيتًا واحدًا يقوم به مسؤول النظام (admin)** لحزمة
+ CrewAI في مؤسستك قبل أن يتمكن أي مستخدم من الاتصال. هذا متطلب من منصة
+ Salesforce لجميع التكاملات المعتمدة على ExternalClientApp اعتبارًا من
+ إصدار Spring '26 — وليس خطوة خاصة بـ CrewAI. تدليلك خطوة Connect
+ Salesforce في CrewAI AMP خلال هذه العملية عند المحاولة الأولى.
+
+
## إعداد تكامل Salesforce
### 1. ربط حساب Salesforce الخاص بك
-1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/connectors)
-2. ابحث عن **Salesforce** في قسم تكاملات المصادقة
-3. انقر على **Connect** وأكمل عملية OAuth
-4. امنح الصلاحيات اللازمة لإدارة CRM والمبيعات
-5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations)
+1. انتقل إلى [تكاملات CrewAI AMP](https://app.crewai.com/crewai_plus/unified_tools).
+2. ابحث عن **Salesforce** في قسم تكاملات المصادقة.
+3. انقر على **Connect**.
+
+ما يحدث بعد ذلك يعتمد على ما إذا كان مسؤول Salesforce في مؤسستك قد ثبّت
+حزمة CrewAI بالفعل:
+
+- **الحزمة مثبتة بالفعل:** سيتم نقلك مباشرة إلى شاشة موافقة OAuth في
+ Salesforce — اعتمدها وسيكتمل الاتصال.
+- **الحزمة غير مثبتة بعد:** سترى صفحة **Install CrewAI in Salesforce**.
+ اتبع خطوات التثبيت لمرة واحدة أدناه، ثم عُد إلى CrewAI AMP وانقر على
+ **Connect** مرة أخرى.
+
+4. امنح الصلاحيات اللازمة لإدارة CRM والمبيعات.
+5. انسخ رمز المؤسسة من [إعدادات التكامل](https://app.crewai.com/crewai_plus/settings/integrations).
+
+#### تثبيت لمرة واحدة بواسطة المسؤول (مسؤول Salesforce فقط)
+
+عند أول نقرة على **Connect Salesforce** من أي مستخدم في مؤسستك، تقوم CrewAI
+بإعادة توجيهك إلى صفحة تثبيت تُشير إلى حزمة CrewAI المُدارة. يحتاج مسؤول
+Salesforce إلى تثبيتها مرة واحدة فقط لكامل المؤسسة.
+
+1. في صفحة التثبيت داخل CrewAI، انقر على **Install in Salesforce**. (يمكنك
+ أيضًا مشاركة عنوان URL لتلك الصفحة مع المسؤول — رابط التثبيت يعمل لأي
+ شخص يفتحه.)
+2. سجّل الدخول إلى Salesforce بصلاحيات مسؤول. لبيئات Sandbox، استبدل
+ `login.salesforce.com` بـ `test.salesforce.com` في الرابط قبل فتحه.
+3. اختر **Install for All Users**، ووافق على إشعار تطبيقات الجهات
+ الخارجية، ثم انقر **Install**.
+4. من Setup في Salesforce، ابحث عن **External Client App Manager** ←
+ **CrewAI App** ← افتح علامة التبويب **Policies** ← **Edit**، واضبط
+ القيم التالية:
+ - **Permitted Users:** All users may self-authorize
+ - **IP Relaxation:** Relax IP restrictions
+ - **Refresh Token Policy:** Refresh token is valid until revoked
+5. احفظ التغييرات.
+6. عُد إلى CrewAI AMP وانقر على **Connect Salesforce** مرة أخرى. سيكتمل
+ OAuth هذه المرة.
+
+
+ **لست مسؤول Salesforce؟** أعِد توجيه عنوان URL لصفحة التثبيت (أو رابط
+ التثبيت نفسه) إلى مسؤول Salesforce لديكم واطلب منه إكمال الخطوات أعلاه.
+ بمجرد انتهائه، عُد إلى CrewAI AMP وانقر على **Connect** مرة أخرى.
+
### 2. تثبيت الحزمة المطلوبة
diff --git a/docs/en/enterprise/integrations/salesforce.mdx b/docs/en/enterprise/integrations/salesforce.mdx
index c729a3a7e..5e4eb0e8b 100644
--- a/docs/en/enterprise/integrations/salesforce.mdx
+++ b/docs/en/enterprise/integrations/salesforce.mdx
@@ -17,15 +17,61 @@ Before using the Salesforce integration, ensure you have:
- A Salesforce account with appropriate permissions
- Connected your Salesforce account through the [Integrations page](https://app.crewai.com/integrations)
+
+ Salesforce requires a **one-time admin install** of the CrewAI package in
+ your org before any user can connect. This is a Salesforce platform
+ requirement for all ExternalClientApp-based integrations as of the Spring
+ '26 release — not a CrewAI-specific step. The Connect Salesforce flow in
+ CrewAI AMP walks you through it the first time.
+
+
## Setting Up Salesforce Integration
### 1. Connect Your Salesforce Account
-1. Navigate to [CrewAI AMP Integrations](https://app.crewai.com/crewai_plus/connectors)
-2. Find **Salesforce** in the Authentication Integrations section
-3. Click **Connect** and complete the OAuth flow
-4. Grant the necessary permissions for CRM and sales management
-5. Copy your Enterprise Token from [Integration Settings](https://app.crewai.com/crewai_plus/settings/integrations)
+1. Navigate to [CrewAI AMP Integrations](https://app.crewai.com/crewai_plus/unified_tools).
+2. Find **Salesforce** in the Authentication Integrations section.
+3. Click **Connect**.
+
+What happens next depends on whether a Salesforce admin in your org has
+already installed the CrewAI package:
+
+- **Package already installed:** you're taken straight to the Salesforce
+ OAuth consent screen — approve it and you're connected.
+- **Package not installed yet:** you'll see an **Install CrewAI in
+ Salesforce** page. Follow the one-time install steps below, then come
+ back to CrewAI AMP and click **Connect** again.
+
+4. Grant the necessary permissions for CRM and sales management.
+5. Copy your Enterprise Token from [Integration Settings](https://app.crewai.com/crewai_plus/settings/integrations).
+
+#### One-time admin install (Salesforce admin only)
+
+The first time anyone in your org clicks **Connect Salesforce**, CrewAI
+redirects them to an install page that points at the CrewAI managed package.
+A Salesforce admin needs to install it once for the whole org.
+
+1. On the install page in CrewAI, click **Install in Salesforce**. (You can
+ also share the page URL with your admin — the install link works for
+ anyone who opens it.)
+2. Sign in to Salesforce as an admin. For sandboxes, swap `login.salesforce.com`
+ for `test.salesforce.com` in the URL before opening it.
+3. Choose **Install for All Users**, acknowledge the third-party app prompt,
+ and click **Install**.
+4. In Salesforce Setup, search **External Client App Manager** → **CrewAI
+ App** → open the **Policies** tab → **Edit** and set:
+ - **Permitted Users:** All users may self-authorize
+ - **IP Relaxation:** Relax IP restrictions
+ - **Refresh Token Policy:** Refresh token is valid until revoked
+5. Save.
+6. Return to CrewAI AMP and click **Connect Salesforce** again. OAuth will
+ complete this time.
+
+
+ **Not a Salesforce admin?** Forward the install page URL (or the install
+ link itself) to your Salesforce admin and ask them to complete the steps
+ above. Once they're done, return to CrewAI AMP and click **Connect** again.
+
### 2. Install Required Package
diff --git a/docs/ko/enterprise/integrations/salesforce.mdx b/docs/ko/enterprise/integrations/salesforce.mdx
index 51675dff6..972e220f3 100644
--- a/docs/ko/enterprise/integrations/salesforce.mdx
+++ b/docs/ko/enterprise/integrations/salesforce.mdx
@@ -17,16 +17,60 @@ Salesforce 통합을 사용하기 전에 다음을 확인하세요:
- 적절한 권한이 있는 Salesforce 계정
- [통합 페이지](https://app.crewai.com/integrations)를 통해 Salesforce 계정 연결
+
+ Salesforce는 사용자가 연결하기 전에 **관리자가 CrewAI 패키지를 한 번 설치**
+ 해야 합니다. 이는 Spring '26 릴리스부터 모든 ExternalClientApp 기반 통합에
+ 적용되는 Salesforce 플랫폼의 요구 사항이며, CrewAI 고유의 단계가 아닙니다.
+ CrewAI AMP의 Connect Salesforce 플로우가 첫 연결 시 이 과정을 안내합니다.
+
+
## Salesforce 통합 설정
### 1. Salesforce 계정 연결
-1. [CrewAI AMP 통합](https://app.crewai.com/crewai_plus/connectors)으로 이동합니다.
+1. [CrewAI AMP 통합](https://app.crewai.com/crewai_plus/unified_tools)으로 이동합니다.
2. 인증 통합 섹션에서 **Salesforce**를 찾습니다.
-3. **연결**을 클릭하고 OAuth 과정을 완료합니다.
+3. **연결**을 클릭합니다.
+
+이후 동작은 관리자가 조직에 CrewAI 패키지를 이미 설치했는지에 따라 달라집니다:
+
+- **패키지가 이미 설치된 경우:** 곧바로 Salesforce OAuth 동의 화면으로
+ 이동합니다. 승인하면 연결이 완료됩니다.
+- **패키지가 아직 설치되지 않은 경우:** **Install CrewAI in Salesforce**
+ 페이지가 표시됩니다. 아래의 일회성 설치 단계를 따른 뒤, CrewAI AMP로
+ 돌아와 **연결**을 다시 클릭하세요.
+
4. CRM 및 영업 관리에 필요한 권한을 부여합니다.
5. [통합 설정](https://app.crewai.com/crewai_plus/settings/integrations)에서 Enterprise Token을 복사합니다.
+#### 일회성 관리자 설치 (Salesforce 관리자 전용)
+
+조직 내 누군가 **Connect Salesforce**를 처음 클릭하면, CrewAI는 CrewAI
+관리형 패키지의 설치 페이지로 리디렉션합니다. Salesforce 관리자가 조직
+전체를 위해 한 번만 설치하면 됩니다.
+
+1. CrewAI 내 설치 페이지에서 **Install in Salesforce**를 클릭합니다.
+ (해당 페이지 URL을 관리자에게 공유해도 됩니다. 설치 링크는 누구든 열 수
+ 있도록 동작합니다.)
+2. 관리자 권한으로 Salesforce에 로그인합니다. 샌드박스 환경에서는 URL의
+ `login.salesforce.com`을 `test.salesforce.com`으로 바꾼 뒤 엽니다.
+3. **Install for All Users**를 선택하고, 서드파티 앱 동의 항목을 확인한 뒤
+ **Install**을 클릭합니다.
+4. Salesforce Setup에서 **External Client App Manager** → **CrewAI App** →
+ **Policies** 탭 → **Edit**로 이동하여 다음과 같이 설정합니다:
+ - **Permitted Users:** All users may self-authorize
+ - **IP Relaxation:** Relax IP restrictions
+ - **Refresh Token Policy:** Refresh token is valid until revoked
+5. 저장합니다.
+6. CrewAI AMP로 돌아가 **Connect Salesforce**를 다시 클릭합니다. 이번에는
+ OAuth가 정상적으로 완료됩니다.
+
+
+ **Salesforce 관리자가 아니신가요?** 설치 페이지의 URL(또는 설치 링크 자체)
+ 을 Salesforce 관리자에게 전달하고 위 단계를 진행해 달라고 요청하세요.
+ 관리자가 완료하면 CrewAI AMP로 돌아와 **연결**을 다시 클릭하면 됩니다.
+
+
### 2. 필수 패키지 설치
```bash
diff --git a/docs/pt-BR/enterprise/integrations/salesforce.mdx b/docs/pt-BR/enterprise/integrations/salesforce.mdx
index 8697577e4..4bbda3ece 100644
--- a/docs/pt-BR/enterprise/integrations/salesforce.mdx
+++ b/docs/pt-BR/enterprise/integrations/salesforce.mdx
@@ -17,15 +17,65 @@ Antes de usar a integração Salesforce, certifique-se de que você possui:
- Uma conta Salesforce com permissões apropriadas
- Sua conta Salesforce conectada via a [página de Integrações](https://app.crewai.com/integrations)
+
+ O Salesforce exige uma **instalação única feita por um administrador** do
+ pacote CrewAI na sua organização antes que qualquer usuário possa se
+ conectar. Isso é uma exigência da plataforma Salesforce para todas as
+ integrações baseadas em ExternalClientApp a partir da release Spring '26 —
+ não é uma etapa específica da CrewAI. O fluxo Connect Salesforce na CrewAI
+ AMP guia você por esta etapa na primeira vez.
+
+
## Configurando a Integração Salesforce
### 1. Conecte sua Conta Salesforce
-1. Acesse [CrewAI AMP Integrações](https://app.crewai.com/crewai_plus/connectors)
-2. Encontre **Salesforce** na seção Integrações de Autenticação
-3. Clique em **Conectar** e complete o fluxo OAuth
-4. Conceda as permissões necessárias para gerenciamento de CRM e vendas
-5. Copie seu Token Enterprise em [Configurações de Integração](https://app.crewai.com/crewai_plus/settings/integrations)
+1. Acesse [CrewAI AMP Integrações](https://app.crewai.com/crewai_plus/unified_tools).
+2. Encontre **Salesforce** na seção Integrações de Autenticação.
+3. Clique em **Conectar**.
+
+O que acontece em seguida depende de o administrador Salesforce já ter
+instalado o pacote CrewAI na sua organização:
+
+- **Pacote já instalado:** você será levado diretamente à tela de consentimento
+ OAuth do Salesforce — aprove e a conexão estará feita.
+- **Pacote ainda não instalado:** você verá uma página **Install CrewAI in
+ Salesforce**. Siga as etapas de instalação única abaixo e, depois, volte à
+ CrewAI AMP e clique em **Conectar** novamente.
+
+4. Conceda as permissões necessárias para gerenciamento de CRM e vendas.
+5. Copie seu Token Enterprise em [Configurações de Integração](https://app.crewai.com/crewai_plus/settings/integrations).
+
+#### Instalação única pelo administrador (apenas admin Salesforce)
+
+Na primeira vez que alguém na sua organização clica em **Connect Salesforce**,
+a CrewAI redireciona para uma página de instalação que aponta para o pacote
+gerenciado CrewAI. Um administrador Salesforce precisa instalá-lo uma única
+vez para toda a organização.
+
+1. Na página de instalação dentro da CrewAI, clique em **Install in
+ Salesforce**. (Você também pode compartilhar a URL dessa página com seu
+ administrador — o link de instalação funciona para qualquer pessoa que o
+ abra.)
+2. Entre no Salesforce como administrador. Para sandboxes, troque
+ `login.salesforce.com` por `test.salesforce.com` na URL antes de abrir.
+3. Escolha **Install for All Users**, confirme o aviso sobre aplicativos de
+ terceiros e clique em **Install**.
+4. No Setup do Salesforce, busque **External Client App Manager** → **CrewAI
+ App** → abra a aba **Policies** → **Edit** e configure:
+ - **Permitted Users:** All users may self-authorize
+ - **IP Relaxation:** Relax IP restrictions
+ - **Refresh Token Policy:** Refresh token is valid until revoked
+5. Salve.
+6. Volte à CrewAI AMP e clique em **Connect Salesforce** novamente. Desta vez
+ o OAuth será concluído.
+
+
+ **Não é administrador Salesforce?** Encaminhe a URL da página de instalação
+ (ou o link de instalação em si) para o seu administrador e peça que ele
+ conclua as etapas acima. Quando ele terminar, volte à CrewAI AMP e clique
+ em **Conectar** novamente.
+
### 2. Instale o Pacote Necessário
diff --git a/lib/crewai-files/src/crewai_files/formatting/api.py b/lib/crewai-files/src/crewai_files/formatting/api.py
index 1d846b18a..b056f014e 100644
--- a/lib/crewai-files/src/crewai_files/formatting/api.py
+++ b/lib/crewai-files/src/crewai_files/formatting/api.py
@@ -114,14 +114,12 @@ def format_multimodal_content(
content_blocks: list[dict[str, Any]] = []
provider_type = _normalize_provider(provider)
- # Add text block first if provided
if text:
content_blocks.append(_format_text_block(text, provider_type, api))
if not files:
return content_blocks
- # Use API-specific constraints for OpenAI
constraints_key: str = provider_type
if api == "responses" and "openai" in provider_type.lower():
constraints_key = "openai_responses"
@@ -186,7 +184,6 @@ async def aformat_multimodal_content(
if not files:
return content_blocks
- # Use API-specific constraints for OpenAI
constraints_key: str = provider_type
if api == "responses" and "openai" in provider_type.lower():
constraints_key = "openai_responses"
diff --git a/lib/crewai-files/src/crewai_files/resolution/resolver.py b/lib/crewai-files/src/crewai_files/resolution/resolver.py
index 31c54c55a..0359c6253 100644
--- a/lib/crewai-files/src/crewai_files/resolution/resolver.py
+++ b/lib/crewai-files/src/crewai_files/resolution/resolver.py
@@ -245,7 +245,6 @@ class FileResolver:
type_constraint = self._get_type_constraint(content_type, constraints)
if type_constraint is not None:
- # Check if file exceeds type-specific inline limit
if file_size > type_constraint.max_size_bytes:
logger.debug(
f"File {file.filename} ({file_size}B) exceeds {content_type} "
diff --git a/lib/crewai-files/tests/processing/test_processor.py b/lib/crewai-files/tests/processing/test_processor.py
index 1648b6aeb..01700e045 100644
--- a/lib/crewai-files/tests/processing/test_processor.py
+++ b/lib/crewai-files/tests/processing/test_processor.py
@@ -162,7 +162,6 @@ class TestFileProcessorValidate:
image=ImageConstraints(max_size_bytes=10),
)
processor = FileProcessor(constraints=constraints)
- # Set mode to strict on the file
file = ImageFile(
source=FileBytes(data=MINIMAL_PNG, filename="test.png"), mode="strict"
)
@@ -199,7 +198,6 @@ class TestFileProcessorProcess:
image=ImageConstraints(max_size_bytes=10),
)
processor = FileProcessor(constraints=constraints)
- # Set mode to strict on the file
file = ImageFile(
source=FileBytes(data=MINIMAL_PNG, filename="test.png"), mode="strict"
)
@@ -214,7 +212,6 @@ class TestFileProcessorProcess:
image=ImageConstraints(max_size_bytes=10),
)
processor = FileProcessor(constraints=constraints)
- # Set mode to warn on the file
file = ImageFile(
source=FileBytes(data=MINIMAL_PNG, filename="test.png"), mode="warn"
)
diff --git a/lib/crewai-files/tests/test_resolver.py b/lib/crewai-files/tests/test_resolver.py
index 095eb4329..d0f9e3e40 100644
--- a/lib/crewai-files/tests/test_resolver.py
+++ b/lib/crewai-files/tests/test_resolver.py
@@ -93,14 +93,11 @@ class TestFileResolver:
resolver = FileResolver(upload_cache=cache)
file = ImageFile(source=FileBytes(data=MINIMAL_PNG, filename="test.png"))
- # First resolution
resolved1 = resolver.resolve(file, "openai")
- # Second resolution (should use same base64 encoding)
resolved2 = resolver.resolve(file, "openai")
assert isinstance(resolved1, InlineBase64)
assert isinstance(resolved2, InlineBase64)
- # Data should be identical
assert resolved1.data == resolved2.data
def test_clear_cache(self):
@@ -108,7 +105,6 @@ class TestFileResolver:
cache = UploadCache()
file = ImageFile(source=FileBytes(data=MINIMAL_PNG, filename="test.png"))
- # Add something to cache manually
cache.set(file=file, provider="gemini", file_id="test")
resolver = FileResolver(upload_cache=cache)
diff --git a/lib/crewai-files/tests/test_upload_cache.py b/lib/crewai-files/tests/test_upload_cache.py
index 5b2bb6a47..f7d9f86a3 100644
--- a/lib/crewai-files/tests/test_upload_cache.py
+++ b/lib/crewai-files/tests/test_upload_cache.py
@@ -162,7 +162,6 @@ class TestUploadCache:
source=FileBytes(data=MINIMAL_PNG + b"x", filename="test2.png")
)
- # Add one expired and one valid entry
past = datetime.now(timezone.utc) - timedelta(hours=1)
future = datetime.now(timezone.utc) + timedelta(hours=24)
diff --git a/lib/crewai-tools/src/crewai_tools/adapters/crewai_rag_adapter.py b/lib/crewai-tools/src/crewai_tools/adapters/crewai_rag_adapter.py
index be2039c51..b0a655830 100644
--- a/lib/crewai-tools/src/crewai_tools/adapters/crewai_rag_adapter.py
+++ b/lib/crewai-tools/src/crewai_tools/adapters/crewai_rag_adapter.py
@@ -252,12 +252,10 @@ class CrewAIRagAdapter(Adapter):
if filename.startswith("."):
continue
- # Skip binary files based on extension
file_ext = os.path.splitext(filename)[1].lower()
if file_ext in binary_extensions:
continue
- # Skip __pycache__ directories
if "__pycache__" in root:
continue
diff --git a/lib/crewai-tools/src/crewai_tools/adapters/enterprise_adapter.py b/lib/crewai-tools/src/crewai_tools/adapters/enterprise_adapter.py
index 157459f1f..1bb4b4aae 100644
--- a/lib/crewai-tools/src/crewai_tools/adapters/enterprise_adapter.py
+++ b/lib/crewai-tools/src/crewai_tools/adapters/enterprise_adapter.py
@@ -46,7 +46,6 @@ class EnterpriseActionTool(BaseTool):
schema_props, required = self._extract_schema_info(action_schema)
- # Define field definitions for the model
field_definitions = {}
for param_name, param_details in schema_props.items():
param_desc = param_details.get("description", "")
@@ -59,12 +58,10 @@ class EnterpriseActionTool(BaseTool):
except Exception:
field_type = str
- # Create field definition based on requirement
field_definitions[param_name] = self._create_field_definition(
field_type, is_required, param_desc
)
- # Create the model
if field_definitions:
try:
args_schema = create_model( # type: ignore[call-overload]
diff --git a/lib/crewai-tools/src/crewai_tools/adapters/rag_adapter.py b/lib/crewai-tools/src/crewai_tools/adapters/rag_adapter.py
index 99a9105a7..61f25b1a9 100644
--- a/lib/crewai-tools/src/crewai_tools/adapters/rag_adapter.py
+++ b/lib/crewai-tools/src/crewai_tools/adapters/rag_adapter.py
@@ -16,7 +16,6 @@ class RAGAdapter(Adapter):
):
super().__init__()
- # Prepare embedding configuration
embedding_config = {"api_key": embedding_api_key, **embedding_kwargs}
self._adapter = RAG(
diff --git a/lib/crewai-tools/src/crewai_tools/aws/bedrock/agents/invoke_agent_tool.py b/lib/crewai-tools/src/crewai_tools/aws/bedrock/agents/invoke_agent_tool.py
index 20043dfda..949a5765b 100644
--- a/lib/crewai-tools/src/crewai_tools/aws/bedrock/agents/invoke_agent_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/aws/bedrock/agents/invoke_agent_tool.py
@@ -14,7 +14,6 @@ from crewai_tools.aws.bedrock.exceptions import (
)
-# Load environment variables from .env file
load_dotenv()
@@ -66,29 +65,24 @@ class BedrockInvokeAgentTool(BaseTool):
self.enable_trace = enable_trace
self.end_session = end_session
- # Update the description if provided
if description:
self.description = description
- # Validate parameters
self._validate_parameters()
def _validate_parameters(self) -> None:
"""Validate the parameters according to AWS API requirements."""
try:
- # Validate agent_id
if not self.agent_id:
raise BedrockValidationError("agent_id cannot be empty")
if not isinstance(self.agent_id, str):
raise BedrockValidationError("agent_id must be a string")
- # Validate agent_alias_id
if not self.agent_alias_id:
raise BedrockValidationError("agent_alias_id cannot be empty")
if not isinstance(self.agent_alias_id, str):
raise BedrockValidationError("agent_alias_id must be a string")
- # Validate session_id if provided
if self.session_id and not isinstance(self.session_id, str):
raise BedrockValidationError("session_id must be a string")
@@ -113,7 +107,6 @@ class BedrockInvokeAgentTool(BaseTool):
),
)
- # Format the prompt with current time
current_utc = datetime.now(timezone.utc)
prompt = f"""
The current time is: {current_utc}
@@ -132,12 +125,9 @@ Below is the users query or task. Complete it and answer it consicely and to the
endSession=self.end_session,
)
- # Process the response
completion = ""
- # Check if response contains a completion field
if "completion" in response:
- # Process streaming response format
for event in response.get("completion", []):
if "chunk" in event and "bytes" in event["chunk"]:
chunk_bytes = event["chunk"]["bytes"]
@@ -161,7 +151,6 @@ Below is the users query or task. Complete it and answer it consicely and to the
"response_keys": list(response.keys()),
}
- # Add more debug info
if "chunk" in response:
debug_info["chunk_keys"] = list(response["chunk"].keys())
diff --git a/lib/crewai-tools/src/crewai_tools/aws/bedrock/browser/browser_toolkit.py b/lib/crewai-tools/src/crewai_tools/aws/bedrock/browser/browser_toolkit.py
index c3bda7af0..a7b6a3d05 100644
--- a/lib/crewai-tools/src/crewai_tools/aws/bedrock/browser/browser_toolkit.py
+++ b/lib/crewai-tools/src/crewai_tools/aws/bedrock/browser/browser_toolkit.py
@@ -135,10 +135,8 @@ class NavigateTool(BrowserBaseTool):
def _run(self, url: str, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Get page for this thread
page = self.get_sync_page(thread_id)
- # Validate URL scheme
parsed_url = urlparse(url)
if parsed_url.scheme not in ("http", "https"):
raise ValueError("URL scheme must be 'http' or 'https'")
@@ -153,10 +151,8 @@ class NavigateTool(BrowserBaseTool):
async def _arun(self, url: str, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the async tool."""
try:
- # Get page for this thread
page = await self.get_async_page(thread_id)
- # Validate URL scheme
parsed_url = urlparse(url)
if parsed_url.scheme not in ("http", "https"):
raise ValueError("URL scheme must be 'http' or 'https'")
@@ -191,7 +187,6 @@ class ClickTool(BrowserBaseTool):
def _run(self, selector: str, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Get the current page
page = self.get_sync_page(thread_id)
# Click on the element
@@ -218,7 +213,6 @@ class ClickTool(BrowserBaseTool):
) -> str:
"""Use the async tool."""
try:
- # Get the current page
page = await self.get_async_page(thread_id)
# Click on the element
@@ -251,7 +245,6 @@ class NavigateBackTool(BrowserBaseTool):
def _run(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Get the current page
page = self.get_sync_page(thread_id)
# Navigate back
@@ -266,7 +259,6 @@ class NavigateBackTool(BrowserBaseTool):
async def _arun(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the async tool."""
try:
- # Get the current page
page = await self.get_async_page(thread_id)
# Navigate back
@@ -289,7 +281,6 @@ class ExtractTextTool(BrowserBaseTool):
def _run(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Import BeautifulSoup
try:
from bs4 import BeautifulSoup
except ImportError:
@@ -298,10 +289,8 @@ class ExtractTextTool(BrowserBaseTool):
" Please install it with 'pip install beautifulsoup4'."
)
- # Get the current page
page = self.get_sync_page(thread_id)
- # Extract text
content = page.content()
soup = BeautifulSoup(content, "html.parser")
return soup.get_text(separator="\n").strip()
@@ -311,7 +300,6 @@ class ExtractTextTool(BrowserBaseTool):
async def _arun(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the async tool."""
try:
- # Import BeautifulSoup
try:
from bs4 import BeautifulSoup
except ImportError:
@@ -320,10 +308,8 @@ class ExtractTextTool(BrowserBaseTool):
" Please install it with 'pip install beautifulsoup4'."
)
- # Get the current page
page = await self.get_async_page(thread_id)
- # Extract text
content = await page.content()
soup = BeautifulSoup(content, "html.parser")
return soup.get_text(separator="\n").strip()
@@ -341,7 +327,6 @@ class ExtractHyperlinksTool(BrowserBaseTool):
def _run(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Import BeautifulSoup
try:
from bs4 import BeautifulSoup, Tag
except ImportError:
@@ -350,10 +335,8 @@ class ExtractHyperlinksTool(BrowserBaseTool):
" Please install it with 'pip install beautifulsoup4'."
)
- # Get the current page
page = self.get_sync_page(thread_id)
- # Extract hyperlinks
content = page.content()
soup = BeautifulSoup(content, "html.parser")
links = []
@@ -374,7 +357,6 @@ class ExtractHyperlinksTool(BrowserBaseTool):
async def _arun(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the async tool."""
try:
- # Import BeautifulSoup
try:
from bs4 import BeautifulSoup, Tag
except ImportError:
@@ -383,10 +365,8 @@ class ExtractHyperlinksTool(BrowserBaseTool):
" Please install it with 'pip install beautifulsoup4'."
)
- # Get the current page
page = await self.get_async_page(thread_id)
- # Extract hyperlinks
content = await page.content()
soup = BeautifulSoup(content, "html.parser")
links = []
@@ -415,10 +395,8 @@ class GetElementsTool(BrowserBaseTool):
def _run(self, selector: str, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Get the current page
page = self.get_sync_page(thread_id)
- # Get elements
elements = page.query_selector_all(selector)
if not elements:
return f"No elements found with selector '{selector}'"
@@ -437,10 +415,8 @@ class GetElementsTool(BrowserBaseTool):
) -> str:
"""Use the async tool."""
try:
- # Get the current page
page = await self.get_async_page(thread_id)
- # Get elements
elements = await page.query_selector_all(selector)
if not elements:
return f"No elements found with selector '{selector}'"
@@ -465,10 +441,8 @@ class CurrentWebPageTool(BrowserBaseTool):
def _run(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the sync tool."""
try:
- # Get the current page
page = self.get_sync_page(thread_id)
- # Get information
url = page.url
title = page.title()
return f"URL: {url}\nTitle: {title}"
@@ -478,10 +452,8 @@ class CurrentWebPageTool(BrowserBaseTool):
async def _arun(self, thread_id: str = "default", **kwargs: Any) -> str:
"""Use the async tool."""
try:
- # Get the current page
page = await self.get_async_page(thread_id)
- # Get information
url = page.url
title = await page.title()
return f"URL: {url}\nTitle: {title}"
diff --git a/lib/crewai-tools/src/crewai_tools/aws/bedrock/code_interpreter/code_interpreter_toolkit.py b/lib/crewai-tools/src/crewai_tools/aws/bedrock/code_interpreter/code_interpreter_toolkit.py
index 151f74eb7..bbe02fb54 100644
--- a/lib/crewai-tools/src/crewai_tools/aws/bedrock/code_interpreter/code_interpreter_toolkit.py
+++ b/lib/crewai-tools/src/crewai_tools/aws/bedrock/code_interpreter/code_interpreter_toolkit.py
@@ -155,12 +155,10 @@ class ExecuteCodeTool(BaseTool):
thread_id: str = "default",
) -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Execute code
response = code_interpreter.invoke(
method="executeCode",
params={
@@ -204,12 +202,10 @@ class ExecuteCommandTool(BaseTool):
def _run(self, command: str, thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Execute command
response = code_interpreter.invoke(
method="executeCommand", params={"command": command}
)
@@ -237,12 +233,10 @@ class ReadFilesTool(BaseTool):
def _run(self, paths: list[str], thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Read files
response = code_interpreter.invoke(
method="readFiles", params={"paths": paths}
)
@@ -270,7 +264,6 @@ class ListFilesTool(BaseTool):
def _run(self, directory_path: str = "", thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
@@ -303,12 +296,10 @@ class DeleteFilesTool(BaseTool):
def _run(self, paths: list[str], thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Remove files
response = code_interpreter.invoke(
method="removeFiles", params={"paths": paths}
)
@@ -336,12 +327,10 @@ class WriteFilesTool(BaseTool):
def _run(self, files: list[dict[str, str]], thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Write files
response = code_interpreter.invoke(
method="writeFiles", params={"content": files}
)
@@ -371,12 +360,10 @@ class StartCommandTool(BaseTool):
def _run(self, command: str, thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Start command execution
response = code_interpreter.invoke(
method="startCommandExecution", params={"command": command}
)
@@ -404,12 +391,10 @@ class GetTaskTool(BaseTool):
def _run(self, task_id: str, thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Get task status
response = code_interpreter.invoke(
method="getTask", params={"taskId": task_id}
)
@@ -437,12 +422,10 @@ class StopTaskTool(BaseTool):
def _run(self, task_id: str, thread_id: str = "default") -> str:
try:
- # Get or create code interpreter
code_interpreter = self.toolkit._get_or_create_interpreter(
thread_id=thread_id
)
- # Stop task
response = code_interpreter.invoke(
method="stopTask", params={"taskId": task_id}
)
@@ -555,7 +538,6 @@ class CodeInterpreterToolkit:
f"Started code interpreter with session_id:{code_interpreter.session_id} for thread:{thread_id}"
)
- # Store the interpreter
self._code_interpreters[thread_id] = code_interpreter
return code_interpreter
@@ -582,7 +564,6 @@ class CodeInterpreterToolkit:
thread_id: Optional thread ID to clean up. If None, cleans up all sessions.
"""
if thread_id:
- # Clean up a specific thread's session
if thread_id in self._code_interpreters:
try:
self._code_interpreters[thread_id].stop()
@@ -595,7 +576,6 @@ class CodeInterpreterToolkit:
f"Error stopping code interpreter for thread {thread_id}: {e}"
)
else:
- # Clean up all sessions
thread_ids = list(self._code_interpreters.keys())
for tid in thread_ids:
try:
diff --git a/lib/crewai-tools/src/crewai_tools/aws/bedrock/knowledge_base/retriever_tool.py b/lib/crewai-tools/src/crewai_tools/aws/bedrock/knowledge_base/retriever_tool.py
index 1cac4e6fd..6ab1d6418 100644
--- a/lib/crewai-tools/src/crewai_tools/aws/bedrock/knowledge_base/retriever_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/aws/bedrock/knowledge_base/retriever_tool.py
@@ -12,7 +12,6 @@ from crewai_tools.aws.bedrock.exceptions import (
)
-# Load environment variables from .env file
load_dotenv()
@@ -69,7 +68,6 @@ class BedrockKBRetrieverTool(BaseTool):
else:
self.retrieval_configuration = retrieval_configuration
- # Validate parameters
self._validate_parameters()
# Update the description to include the knowledge base details
@@ -83,7 +81,6 @@ class BedrockKBRetrieverTool(BaseTool):
"""
vector_search_config = {}
- # Add number of results if provided
if self.number_of_results is not None:
vector_search_config["numberOfResults"] = self.number_of_results
@@ -92,7 +89,6 @@ class BedrockKBRetrieverTool(BaseTool):
def _validate_parameters(self) -> None:
"""Validate the parameters according to AWS API requirements."""
try:
- # Validate knowledge_base_id
if not self.knowledge_base_id:
raise BedrockValidationError("knowledge_base_id cannot be empty")
if not isinstance(self.knowledge_base_id, str):
@@ -106,7 +102,6 @@ class BedrockKBRetrieverTool(BaseTool):
"knowledge_base_id must contain only alphanumeric characters"
)
- # Validate next_token if provided
if self.next_token:
if not isinstance(self.next_token, str):
raise BedrockValidationError("next_token must be a string")
@@ -117,7 +112,6 @@ class BedrockKBRetrieverTool(BaseTool):
if " " in self.next_token:
raise BedrockValidationError("next_token cannot contain spaces")
- # Validate number_of_results if provided
if self.number_of_results is not None:
if not isinstance(self.number_of_results, int):
raise BedrockValidationError("number_of_results must be an integer")
@@ -138,12 +132,10 @@ class BedrockKBRetrieverTool(BaseTool):
Returns:
Dict[str, Any]: Processed result with standardized format
"""
- # Extract content
content_obj = result.get("content", {})
content = content_obj.get("text", "")
content_type = content_obj.get("type", "text")
- # Extract location information
location = result.get("location", {})
location_type = location.get("type", "unknown")
source_uri = None
@@ -160,7 +152,6 @@ class BedrockKBRetrieverTool(BaseTool):
"sqlLocation": {"field": "query", "type": "SQL"},
}
- # Extract the URI based on location type
for loc_key, config in location_mapping.items():
if loc_key in location:
source_uri = location[loc_key].get(config["field"])
@@ -168,7 +159,6 @@ class BedrockKBRetrieverTool(BaseTool):
location_type = config["type"]
break
- # Create result object
result_object = {
"content": content,
"content_type": content_type,
@@ -176,18 +166,15 @@ class BedrockKBRetrieverTool(BaseTool):
"source_uri": source_uri,
}
- # Add optional fields if available
if "score" in result:
result_object["score"] = result["score"]
if "metadata" in result:
result_object["metadata"] = result["metadata"]
- # Handle byte content if present
if "byteContent" in content_obj:
result_object["byte_content"] = content_obj["byteContent"]
- # Handle row content if present
if "row" in content_obj:
result_object["row_content"] = content_obj["row"]
@@ -212,13 +199,11 @@ class BedrockKBRetrieverTool(BaseTool):
# AWS SDK will automatically use AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from environment
)
- # Prepare the request parameters
retrieve_params = {
"knowledgeBaseId": self.knowledge_base_id,
"retrievalQuery": {"text": query},
}
- # Add optional parameters if provided
if self.retrieval_configuration:
retrieve_params["retrievalConfiguration"] = self.retrieval_configuration
@@ -228,16 +213,13 @@ class BedrockKBRetrieverTool(BaseTool):
if self.next_token:
retrieve_params["nextToken"] = self.next_token
- # Make the retrieve API call
response = bedrock_agent_runtime.retrieve(**retrieve_params)
- # Process the response
results = []
for result in response.get("retrievalResults", []):
processed_result = self._process_retrieval_result(result)
results.append(processed_result)
- # Build the response object
response_object = {}
if results:
response_object["results"] = results
@@ -250,7 +232,6 @@ class BedrockKBRetrieverTool(BaseTool):
if "guardrailAction" in response:
response_object["guardrailAction"] = response["guardrailAction"]
- # Return the results as a JSON string
return json.dumps(response_object, indent=2)
except ClientError as e:
diff --git a/lib/crewai-tools/src/crewai_tools/aws/s3/reader_tool.py b/lib/crewai-tools/src/crewai_tools/aws/s3/reader_tool.py
index d5c8f7b51..356fcc42e 100644
--- a/lib/crewai-tools/src/crewai_tools/aws/s3/reader_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/aws/s3/reader_tool.py
@@ -37,7 +37,6 @@ class S3ReaderTool(BaseTool):
aws_secret_access_key=os.getenv("CREW_AWS_SEC_ACCESS_KEY"),
)
- # Read file content from S3
response = s3.get_object(Bucket=bucket_name, Key=object_key)
result: str = response["Body"].read().decode("utf-8")
return result
diff --git a/lib/crewai-tools/src/crewai_tools/rag/chunkers/text_chunker.py b/lib/crewai-tools/src/crewai_tools/rag/chunkers/text_chunker.py
index 7b9aae5b0..0a20e5592 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/chunkers/text_chunker.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/chunkers/text_chunker.py
@@ -12,15 +12,15 @@ class TextChunker(BaseChunker):
if separators is None:
separators = [
"\n\n\n", # Multiple line breaks (sections)
- "\n\n", # Paragraph breaks
- "\n", # Line breaks
- ". ", # Sentence endings
- "! ", # Exclamation endings
- "? ", # Question endings
- "; ", # Semicolon breaks
- ", ", # Comma breaks
- " ", # Word breaks
- "", # Character level
+ "\n\n",
+ "\n",
+ ". ",
+ "! ",
+ "? ",
+ "; ",
+ ", ",
+ " ",
+ "",
]
super().__init__(chunk_size, chunk_overlap, separators, keep_separator)
@@ -36,15 +36,15 @@ class DocxChunker(BaseChunker):
if separators is None:
separators = [
"\n\n\n", # Multiple line breaks (major sections)
- "\n\n", # Paragraph breaks
- "\n", # Line breaks
- ". ", # Sentence endings
- "! ", # Exclamation endings
- "? ", # Question endings
- "; ", # Semicolon breaks
- ", ", # Comma breaks
- " ", # Word breaks
- "", # Character level
+ "\n\n",
+ "\n",
+ ". ",
+ "! ",
+ "? ",
+ "; ",
+ ", ",
+ " ",
+ "",
]
super().__init__(chunk_size, chunk_overlap, separators, keep_separator)
@@ -62,15 +62,15 @@ class MdxChunker(BaseChunker):
"\n## ", # H2 headers (major sections)
"\n### ", # H3 headers (subsections)
"\n#### ", # H4 headers (sub-subsections)
- "\n\n", # Paragraph breaks
- "\n```", # Code block boundaries
- "\n", # Line breaks
- ". ", # Sentence endings
- "! ", # Exclamation endings
- "? ", # Question endings
- "; ", # Semicolon breaks
- ", ", # Comma breaks
- " ", # Word breaks
- "", # Character level
+ "\n\n",
+ "\n```",
+ "\n",
+ ". ",
+ "! ",
+ "? ",
+ "; ",
+ ", ",
+ " ",
+ "",
]
super().__init__(chunk_size, chunk_overlap, separators, keep_separator)
diff --git a/lib/crewai-tools/src/crewai_tools/rag/chunkers/web_chunker.py b/lib/crewai-tools/src/crewai_tools/rag/chunkers/web_chunker.py
index cc1a514d3..b8112b39b 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/chunkers/web_chunker.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/chunkers/web_chunker.py
@@ -11,15 +11,15 @@ class WebsiteChunker(BaseChunker):
):
if separators is None:
separators = [
- "\n\n\n", # Major section breaks
- "\n\n", # Paragraph breaks
- "\n", # Line breaks
- ". ", # Sentence endings
- "! ", # Exclamation endings
- "? ", # Question endings
- "; ", # Semicolon breaks
- ", ", # Comma breaks
- " ", # Word breaks
- "", # Character level
+ "\n\n\n",
+ "\n\n",
+ "\n",
+ ". ",
+ "! ",
+ "? ",
+ "; ",
+ ", ",
+ " ",
+ "",
]
super().__init__(chunk_size, chunk_overlap, separators, keep_separator)
diff --git a/lib/crewai-tools/src/crewai_tools/rag/core.py b/lib/crewai-tools/src/crewai_tools/rag/core.py
index a3e5acf7d..6d460968e 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/core.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/core.py
@@ -191,7 +191,6 @@ class RAG(Adapter):
metadatas = results.get("metadatas", [None])[0] or []
distances = results.get("distances", [None])[0] or []
- # Return sources with relevance scores
formatted_results = []
for i, doc in enumerate(documents):
metadata = metadatas[i] if i < len(metadatas) else {}
diff --git a/lib/crewai-tools/src/crewai_tools/rag/data_types.py b/lib/crewai-tools/src/crewai_tools/rag/data_types.py
index 2ab62f20f..27ee48abb 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/data_types.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/data_types.py
@@ -37,7 +37,6 @@ class DataType(str, Enum):
DataType.TEXT: ("text_chunker", "TextChunker"),
DataType.DOCX: ("text_chunker", "DocxChunker"),
DataType.MDX: ("text_chunker", "MdxChunker"),
- # Structured formats
DataType.CSV: ("structured_chunker", "CsvChunker"),
DataType.JSON: ("structured_chunker", "JsonChunker"),
DataType.XML: ("structured_chunker", "XmlChunker"),
diff --git a/lib/crewai-tools/src/crewai_tools/rag/embedding_service.py b/lib/crewai-tools/src/crewai_tools/rag/embedding_service.py
index 9ac1b66e8..d7b395cef 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/embedding_service.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/embedding_service.py
@@ -113,10 +113,8 @@ class EmbeddingService:
try:
from crewai.rag.embeddings.factory import build_embedder
- # Build the configuration for CrewAI's factory
config = self._build_provider_config()
- # Create the embedding function
self._embedding_function = build_embedder(config)
logger.info(
@@ -287,7 +285,6 @@ class EmbeddingService:
if not texts:
return []
- # Filter out empty texts
valid_texts = [text for text in texts if text and text.strip()]
if not valid_texts:
logger.warning("No valid texts provided for batch embedding")
diff --git a/lib/crewai-tools/src/crewai_tools/rag/loaders/mdx_loader.py b/lib/crewai-tools/src/crewai_tools/rag/loaders/mdx_loader.py
index 6efcd7310..91c3b3da3 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/loaders/mdx_loader.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/loaders/mdx_loader.py
@@ -39,16 +39,12 @@ class MDXLoader(BaseLoader):
def _parse_mdx(self, content: str, source_ref: str) -> LoaderResult:
cleaned_content = content
- # Remove import statements
cleaned_content = _IMPORT_PATTERN.sub("", cleaned_content)
- # Remove export statements
cleaned_content = _EXPORT_PATTERN.sub("", cleaned_content)
- # Remove JSX tags (simple approach)
cleaned_content = _JSX_TAG_PATTERN.sub("", cleaned_content)
- # Clean up extra whitespace
cleaned_content = _EXTRA_NEWLINES_PATTERN.sub("\n\n", cleaned_content)
cleaned_content = cleaned_content.strip()
diff --git a/lib/crewai-tools/src/crewai_tools/rag/misc.py b/lib/crewai-tools/src/crewai_tools/rag/misc.py
index 2f906b8f5..dabe1599f 100644
--- a/lib/crewai-tools/src/crewai_tools/rag/misc.py
+++ b/lib/crewai-tools/src/crewai_tools/rag/misc.py
@@ -31,9 +31,7 @@ def sanitize_metadata_for_chromadb(metadata: dict[str, Any]) -> dict[str, Any]:
if isinstance(value, (str, int, float, bool)) or value is None:
sanitized[key] = value
elif isinstance(value, (list, tuple)):
- # Convert lists/tuples to pipe-separated strings
sanitized[key] = " | ".join(str(v) for v in value)
else:
- # Convert other types to string
sanitized[key] = str(value)
return sanitized
diff --git a/lib/crewai-tools/src/crewai_tools/security/safe_path.py b/lib/crewai-tools/src/crewai_tools/security/safe_path.py
index 4dde68e12..997366e94 100644
--- a/lib/crewai-tools/src/crewai_tools/security/safe_path.py
+++ b/lib/crewai-tools/src/crewai_tools/security/safe_path.py
@@ -27,11 +27,6 @@ def _is_escape_hatch_enabled() -> bool:
return os.environ.get(_UNSAFE_PATHS_ENV, "").lower() in ("true", "1", "yes")
-# ---------------------------------------------------------------------------
-# File path validation
-# ---------------------------------------------------------------------------
-
-
def validate_file_path(path: str, base_dir: str | None = None) -> str:
"""Validate that a file path is safe to read.
@@ -101,10 +96,6 @@ def validate_directory_path(path: str, base_dir: str | None = None) -> str:
return validated
-# ---------------------------------------------------------------------------
-# URL validation
-# ---------------------------------------------------------------------------
-
# Private and reserved IP ranges that should not be accessed
_BLOCKED_IPV4_NETWORKS = [
ipaddress.ip_network("10.0.0.0/8"),
@@ -185,7 +176,6 @@ def validate_url(url: str) -> str:
if not parsed.hostname:
raise ValueError(f"URL has no hostname: '{url}'")
- # Resolve DNS and check IPs
try:
addrinfos = socket.getaddrinfo(
parsed.hostname, parsed.port or (443 if parsed.scheme == "https" else 80)
diff --git a/lib/crewai-tools/src/crewai_tools/tools/ai_mind_tool/ai_mind_tool.py b/lib/crewai-tools/src/crewai_tools/tools/ai_mind_tool/ai_mind_tool.py
index b82673167..8b9add627 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/ai_mind_tool/ai_mind_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/ai_mind_tool/ai_mind_tool.py
@@ -62,7 +62,6 @@ class AIMindTool(BaseTool):
minds_client = Client(api_key=self.api_key)
- # Convert the datasources to DatabaseConfig objects.
datasources = []
for datasource in self.datasources:
config = DatabaseConfig(
@@ -74,7 +73,6 @@ class AIMindTool(BaseTool):
)
datasources.append(config)
- # Generate a random name for the Mind.
name = f"{AIMindToolConstants.MIND_NAME_PREFIX}_{secrets.token_hex(5)}"
mind = minds_client.minds.create(
@@ -84,7 +82,6 @@ class AIMindTool(BaseTool):
self.mind_name = mind.name
def _run(self, query: str) -> str | None:
- # Run the query on the AI-Mind.
# The Minds API is OpenAI compatible and therefore, the OpenAI client can be used.
openai_client = OpenAI(
base_url=AIMindToolConstants.MINDS_API_BASE_URL, api_key=self.api_key
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/base.py b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/base.py
index cc6c4de96..b9ed3551d 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/base.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/base.py
@@ -186,7 +186,6 @@ class BraveSearchToolBase(BaseTool, ABC):
for attempt in range(_max_retries):
self._rate_limit()
- # Make the request
try:
resp = requests.get(
self.search_url,
@@ -203,7 +202,6 @@ class BraveSearchToolBase(BaseTool, ABC):
f"Brave Search API request timed out after {self._timeout}s: {exc}"
) from exc
- # Log the rate limit headers and request details
logger.debug(
"Brave Search API request: %s %s -> %d",
"GET",
@@ -251,7 +249,6 @@ class BraveSearchToolBase(BaseTool, ABC):
params = self._common_payload_refinement(params)
- # Validate only schema fields
schema_keys = self.args_schema.model_fields
payload_in = {k: v for k, v in params.items() if k in schema_keys}
@@ -301,7 +298,6 @@ class BraveSearchToolBase(BaseTool, ABC):
if k not in fields or fields[k].is_required() or v not in self._EMPTY_VALUES
}
- # Make sure params has "q" for query instead of "query" or "search_query"
query = params.get("query") or params.get("search_query")
if query is not None and "q" not in params:
params["q"] = query
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_image_tool.py b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_image_tool.py
index 99aed4235..695e7dea0 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_image_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_image_tool.py
@@ -27,7 +27,6 @@ class BraveImageSearchTool(BraveSearchToolBase):
return params
def _refine_response(self, response: dict[str, Any]) -> list[dict[str, Any]]:
- # Make the response more concise, and easier to consume
results = response.get("results", [])
return [
{
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_news_tool.py b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_news_tool.py
index 80872433c..a82dc2dec 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_news_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_news_tool.py
@@ -27,7 +27,6 @@ class BraveNewsSearchTool(BraveSearchToolBase):
return params
def _refine_response(self, response: dict[str, Any]) -> list[dict[str, Any]]:
- # Make the response more concise, and easier to consume
results = response.get("results", [])
return [
{
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_search_tool.py
index 3e4c1d623..e91d21a96 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_search_tool.py
@@ -68,7 +68,6 @@ class BraveSearchTool(BaseTool):
)
BraveSearchTool._last_request_time = time.time()
- # Construct and send the request
try:
# Fallback to "query" or "search_query" for backwards compatibility
query = kwargs.get("q") or kwargs.get("query") or kwargs.get("search_query")
@@ -123,11 +122,9 @@ class BraveSearchTool(BaseTool):
payload["operators"] = operators
# Limit the result types to "web" since there is presently no
- # handling of other types like "discussions", "faq", "infobox",
# "news", "videos", or "locations".
payload["result_filter"] = "web"
- # Setup Request Headers
headers = {
"X-Subscription-Token": os.environ["BRAVE_API_KEY"],
"Accept": "application/json",
@@ -136,7 +133,7 @@ class BraveSearchTool(BaseTool):
response = requests.get(
self.search_url, headers=headers, params=payload, timeout=30
)
- response.raise_for_status() # Handle non-200 responses
+ response.raise_for_status()
results = response.json()
# TODO: Handle other result types like "discussions", "faq", etc.
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_video_tool.py b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_video_tool.py
index c69cfc7fc..841ffd4d3 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_video_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brave_search_tool/brave_video_tool.py
@@ -27,7 +27,6 @@ class BraveVideoSearchTool(BraveSearchToolBase):
return params
def _refine_response(self, response: dict[str, Any]) -> list[dict[str, Any]]:
- # Make the response more concise, and easier to consume
results = response.get("results", [])
return [
{
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_dataset.py b/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_dataset.py
index 5da2d623b..f5ad0be34 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_dataset.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_dataset.py
@@ -496,7 +496,6 @@ class BrightDataDatasetTool(BaseTool):
)
async with aiohttp.ClientSession() as session:
- # Step 1: Trigger job
async with session.post(
f"{BRIGHTDATA_API_URL}/datasets/v3/trigger",
params={"dataset_id": dataset_id, "include_errors": "true"},
@@ -511,7 +510,6 @@ class BrightDataDatasetTool(BaseTool):
trigger_data = await trigger_response.json()
snapshot_id = trigger_data.get("snapshot_id")
- # Step 2: Poll for completion
elapsed = 0
while elapsed < timeout:
await asyncio.sleep(polling_interval)
@@ -536,7 +534,6 @@ class BrightDataDatasetTool(BaseTool):
else:
raise TimeoutError("Polling timed out before job completed.")
- # Step 3: Retrieve result
async with session.get(
f"{BRIGHTDATA_API_URL}/datasets/v3/snapshot/{snapshot_id}",
params={"format": output_format},
diff --git a/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py b/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py
index 884a8ac8e..83b5c4c30 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_serp.py
@@ -173,15 +173,12 @@ class BrightDataSearchTool(BaseTool):
)
results_count = kwargs.get("results_count", "10")
- # Validate required parameters
if not query:
raise ValueError("query is required either in constructor or method call")
- # Build the search URL
query = urllib.parse.quote(query)
url = self.get_search_url(search_engine, query)
- # Add parameters to the URL
params = []
if country:
@@ -214,7 +211,6 @@ class BrightDataSearchTool(BaseTool):
if params:
url += "&" + "&".join(params)
- # Set up the API request parameters
request_params = {"zone": self.zone, "url": url, "format": "raw"}
request_params = {k: v for k, v in request_params.items() if v is not None}
diff --git a/lib/crewai-tools/src/crewai_tools/tools/contextualai_create_agent_tool/contextual_create_agent_tool.py b/lib/crewai-tools/src/crewai_tools/tools/contextualai_create_agent_tool/contextual_create_agent_tool.py
index 59bc0d443..c3ac80608 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/contextualai_create_agent_tool/contextual_create_agent_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/contextualai_create_agent_tool/contextual_create_agent_tool.py
@@ -53,7 +53,6 @@ class ContextualAICreateAgentTool(BaseTool):
try:
import os
- # Create datastore
datastore = self.contextual_client.datastores.create(name=datastore_name)
datastore_id = datastore.id
@@ -71,7 +70,6 @@ class ContextualAICreateAgentTool(BaseTool):
)
document_ids.append(ingestion_result.id)
- # Create agent
agent = self.contextual_client.agents.create(
name=agent_name,
description=agent_description,
diff --git a/lib/crewai-tools/src/crewai_tools/tools/contextualai_parse_tool/contextual_parse_tool.py b/lib/crewai-tools/src/crewai_tools/tools/contextualai_parse_tool/contextual_parse_tool.py
index 99ef71514..4feac0663 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/contextualai_parse_tool/contextual_parse_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/contextualai_parse_tool/contextual_parse_tool.py
@@ -96,7 +96,6 @@ class ContextualAIParseTool(BaseTool):
sleep(5)
- # Get parse results
results_url = f"{base_url}/parse/jobs/{job_id}/results"
result = requests.get(
results_url,
diff --git a/lib/crewai-tools/src/crewai_tools/tools/couchbase_tool/couchbase_tool.py b/lib/crewai-tools/src/crewai_tools/tools/couchbase_tool/couchbase_tool.py
index 936c30ed8..32bb03521 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/couchbase_tool/couchbase_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/couchbase_tool/couchbase_tool.py
@@ -84,22 +84,18 @@ class CouchbaseFTSVectorSearchTool(BaseTool):
"""
scope_collection_map: dict[str, Any] = {}
- # Get a list of all scopes in the bucket
for scope in self._bucket.collections().get_all_scopes():
scope_collection_map[scope.name] = []
- # Get a list of all the collections in the scope
for collection in scope.collections:
scope_collection_map[scope.name].append(collection.name)
- # Check if the scope exists
if self.scope_name not in scope_collection_map:
raise ValueError(
f"Scope {self.scope_name} not found in Couchbase "
f"bucket {self.bucket_name}"
)
- # Check if the collection exists in the scope
if self.collection_name not in scope_collection_map[self.scope_name]:
raise ValueError(
f"Collection {self.collection_name} not found in scope "
@@ -162,7 +158,6 @@ class CouchbaseFTSVectorSearchTool(BaseTool):
"Please check the connection and credentials"
) from e
- # check if bucket exists
if not self._check_bucket_exists():
raise ValueError(
f"Bucket {self.bucket_name} does not exist. "
diff --git a/lib/crewai-tools/src/crewai_tools/tools/databricks_query_tool/databricks_query_tool.py b/lib/crewai-tools/src/crewai_tools/tools/databricks_query_tool/databricks_query_tool.py
index 2ce9338ab..dd058655a 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/databricks_query_tool/databricks_query_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/databricks_query_tool/databricks_query_tool.py
@@ -172,13 +172,11 @@ class DatabricksQueryTool(BaseTool):
if not results:
return "Query returned no results."
- # Get column names from the first row
if not results[0]:
return "Query returned empty rows with no columns."
columns = list(results[0].keys())
- # If we have rows but they're all empty, handle that case
if not columns:
return "Query returned rows but with no column data."
@@ -186,19 +184,14 @@ class DatabricksQueryTool(BaseTool):
col_widths = {col: len(col) for col in columns}
for row in results:
for col in columns:
- # Convert value to string and get its length
- # Handle None values gracefully
value_str = str(row[col]) if row[col] is not None else "NULL"
col_widths[col] = max(col_widths[col], len(value_str))
- # Create header row
header = " | ".join(f"{col:{col_widths[col]}}" for col in columns)
separator = "-+-".join("-" * col_widths[col] for col in columns)
- # Format data rows
data_rows = []
for row in results:
- # Handle None values by displaying "NULL"
row_values = {
col: str(row[col]) if row[col] is not None else "NULL"
for col in columns
@@ -208,7 +201,6 @@ class DatabricksQueryTool(BaseTool):
)
data_rows.append(data_row)
- # Add row count information
result_info = f"({len(results)} row{'s' if len(results) != 1 else ''} returned)"
# Combine all parts
@@ -231,14 +223,12 @@ class DatabricksQueryTool(BaseTool):
str: Formatted query results
"""
try:
- # Get parameters with fallbacks to default values
query = kwargs.get("query")
catalog = kwargs.get("catalog") or self.default_catalog
db_schema = kwargs.get("db_schema") or self.default_schema
warehouse_id = kwargs.get("warehouse_id") or self.default_warehouse_id
row_limit = kwargs.get("row_limit", 1000)
- # Validate schema and query
validated_input = DatabricksQueryToolSchema(
query=query,
catalog=catalog,
@@ -247,7 +237,6 @@ class DatabricksQueryTool(BaseTool):
row_limit=row_limit,
)
- # Extract validated parameters
query = validated_input.query
catalog = validated_input.catalog
db_schema = validated_input.db_schema
@@ -256,26 +245,21 @@ class DatabricksQueryTool(BaseTool):
if warehouse_id is None:
return "SQL warehouse ID must be provided either as a parameter or as a default."
- # Setup SQL context with catalog/schema if provided
-
context: ExecutionContext = {}
if catalog:
context["catalog"] = catalog
if db_schema:
context["schema"] = db_schema
- # Execute query
statement = self.workspace_client.statement_execution
try:
- # Execute the statement
execution = statement.execute_statement(
warehouse_id=warehouse_id, statement=query, **context
)
statement_id = execution.statement_id
except Exception as execute_error:
- # Handle immediate execution errors
return f"Error starting query execution: {execute_error!s}"
# Poll for results with better error handling
@@ -284,7 +268,7 @@ class DatabricksQueryTool(BaseTool):
timeout = 300 # 5 minutes timeout
start_time = time.time()
poll_count = 0
- previous_state = None # Track previous state to detect changes
+ previous_state = None
if statement_id is None:
return "Failed to retrieve statement ID after execution."
@@ -292,27 +276,21 @@ class DatabricksQueryTool(BaseTool):
while time.time() - start_time < timeout:
poll_count += 1
try:
- # Get statement status
result = statement.get_statement(statement_id)
- # Check if finished - be very explicit about state checking
if hasattr(result, "status") and hasattr(result.status, "state"):
state_value = str(
result.status.state # type: ignore[union-attr]
) # Convert to string to handle both string and enum
- # Track state changes for debugging
if previous_state != state_value:
previous_state = state_value
- # Check if state indicates completion
if "SUCCEEDED" in state_value:
break
if "FAILED" in state_value:
- # Extract error message with more robust handling
error_info = "No detailed error info"
try:
- # First try direct access to error.message
if (
hasattr(result.status, "error")
and result.status.error # type: ignore[union-attr]
@@ -322,16 +300,13 @@ class DatabricksQueryTool(BaseTool):
# Some APIs may have a different structure
elif hasattr(result.status.error, "error_message"): # type: ignore[union-attr]
error_info = result.status.error.error_message # type: ignore[union-attr]
- # Last resort, try to convert the whole error object to string
else:
error_info = str(result.status.error) # type: ignore[union-attr]
except Exception as err_extract_error:
- # If all else fails, try to get any info we can
error_info = (
f"Error details unavailable: {err_extract_error!s}"
)
- # Return immediately on first FAILED state detection
return f"Query execution failed: {error_info}"
if "CANCELED" in state_value:
return "Query was canceled"
@@ -341,17 +316,14 @@ class DatabricksQueryTool(BaseTool):
if poll_count > 3:
return f"Error checking query status: {poll_error!s}"
- # Wait before polling again
time.sleep(2)
- # Check if we timed out
if result is None:
return "Query returned no result (likely timed out or failed)"
if not hasattr(result, "status") or not hasattr(result.status, "state"):
return "Query completed but returned an invalid result structure"
- # Convert state to string for comparison
state_value = str(result.status.state) # type: ignore[union-attr]
if not any(
state in state_value for state in ["SUCCEEDED", "FAILED", "CANCELED"]
@@ -372,7 +344,6 @@ class DatabricksQueryTool(BaseTool):
if has_schema and has_result:
try:
- # Get schema for column names
columns = [col.name for col in result.manifest.schema.columns] # type: ignore[union-attr]
# Debug info for schema
@@ -382,16 +353,13 @@ class DatabricksQueryTool(BaseTool):
# Dump the raw structure of result data to help troubleshoot
if _has_data_array(result):
- # Add defensive check for None data_array
if result.result.data_array is None:
- # Return empty result handling rather than trying to process null data
return "Query executed successfully (no data returned)"
# IMPROVED DETECTION LOGIC: Check if we're possibly dealing with rows where each item
# contains a single value or character (which could indicate incorrect row structure)
is_likely_incorrect_row_structure = False
- # Only try to analyze sample if data_array exists and has content
if (
_has_data_array(result)
and len(result.result.data_array) > 0
@@ -421,7 +389,6 @@ class DatabricksQueryTool(BaseTool):
single_digit_count += 1
# If a significant portion of the first values are single characters or digits,
- # this likely indicates data is being incorrectly structured
if (
total_items > 0
and (single_char_count + single_digit_count)
@@ -465,14 +432,12 @@ class DatabricksQueryTool(BaseTool):
else:
needs_special_string_handling = False
- # Process results differently based on detection
if (
"needs_special_string_handling" in locals()
and needs_special_string_handling
):
# We're dealing with data where the rows may be incorrectly structured
- # Collect all values into a flat list
all_values: list[Any] = []
if (
hasattr(result.result, "data_array")
@@ -486,10 +451,8 @@ class DatabricksQueryTool(BaseTool):
else:
all_values.append(item)
- # Get the expected column count from schema
expected_column_count = len(columns)
- # Try to reconstruct rows using pattern recognition
reconstructed_rows = []
# PATTERN RECOGNITION APPROACH
@@ -509,7 +472,6 @@ class DatabricksQueryTool(BaseTool):
# This value looks like an ID, might be the start of a row
if i < len(all_values) - 1:
next_few_values = all_values[i + 1 : i + 5]
- # If following values look like they could be part of a title
if any(
isinstance(v, str) and len(v) > 1
for v in next_few_values
@@ -517,7 +479,6 @@ class DatabricksQueryTool(BaseTool):
id_indices.append(i)
if id_indices:
- # If we found potential row starts, use them to extract rows
for i in range(len(id_indices)):
start_idx = id_indices[i]
end_idx = (
@@ -526,7 +487,6 @@ class DatabricksQueryTool(BaseTool):
else len(all_values)
)
- # Extract values for this row
row_values = all_values[start_idx:end_idx]
# Special handling for Netflix title data
@@ -535,9 +495,7 @@ class DatabricksQueryTool(BaseTool):
"Title" in columns
and len(row_values) > expected_column_count
):
- # Try to reconstruct by looking for patterns
# We know ID is first, then Title (which may be split)
- # Then other fields like Genre, etc.
# Take first value as ID
row_dict = {columns[0]: row_values[0]}
@@ -546,7 +504,6 @@ class DatabricksQueryTool(BaseTool):
title_end_idx = 1
for j in range(2, min(100, len(row_values))):
val = row_values[j]
- # Check for common genres or non-title markers
if isinstance(val, str) and val in [
"Comedy",
"Drama",
@@ -562,7 +519,6 @@ class DatabricksQueryTool(BaseTool):
# Reconstruct title from individual characters
if title_end_idx > 1:
title_chars = row_values[1:title_end_idx]
- # Check if they're individual characters
if all(
isinstance(c, str) and len(c) == 1
for c in title_chars
@@ -607,24 +563,21 @@ class DatabricksQueryTool(BaseTool):
)
if title_idx >= 0:
- # Try to detect if title is split across multiple values
i = 0
while i < len(all_values):
- # Check if this could be an ID (start of a row)
if isinstance(
all_values[i], str
) and id_pattern.match(all_values[i]):
row_dict = {columns[0]: all_values[i]}
i += 1
- # Try to reconstruct title if it appears to be split
title_chars = []
while (
i < len(all_values)
and isinstance(all_values[i], str)
and len(all_values[i]) <= 1
and len(title_chars) < 100
- ): # Cap title length
+ ):
title_chars.append(all_values[i])
i += 1
@@ -633,7 +586,6 @@ class DatabricksQueryTool(BaseTool):
title_chars
)
- # Add remaining fields
for j in range(title_idx + 1, len(columns)):
if i < len(all_values):
row_dict[columns[j]] = all_values[i]
@@ -655,7 +607,6 @@ class DatabricksQueryTool(BaseTool):
]
for chunk in chunks:
- # Skip chunks that seem to be partial/incomplete rows
if (
len(chunk) < expected_column_count * 0.75
): # Allow for some missing values
@@ -663,7 +614,6 @@ class DatabricksQueryTool(BaseTool):
row_dict = {}
- # Map values to column names
for i, col in enumerate(columns):
if i < len(chunk):
row_dict[col] = chunk[i]
@@ -672,7 +622,6 @@ class DatabricksQueryTool(BaseTool):
reconstructed_rows.append(row_dict)
- # Apply post-processing to fix known issues
if reconstructed_rows and "Title" in columns:
for row in reconstructed_rows:
# Fix titles that might still have issues
@@ -680,7 +629,6 @@ class DatabricksQueryTool(BaseTool):
isinstance(row.get("Title"), str)
and len(row.get("Title")) <= 1 # type: ignore[arg-type]
):
- # This is likely still a fragmented title - mark as potentially incomplete
row["Title"] = f"[INCOMPLETE] {row.get('Title')}"
# Ensure we respect the row limit
@@ -689,18 +637,13 @@ class DatabricksQueryTool(BaseTool):
chunk_results = reconstructed_rows
else:
- # Process normal result structure as before
-
- # Check different result structures
if (
hasattr(result.result, "data_array")
and result.result.data_array # type: ignore[union-attr]
):
- # Check if data appears to be malformed within chunks
for _chunk_idx, chunk in enumerate(
result.result.data_array # type: ignore[union-attr]
):
- # Check if chunk might actually contain individual columns of a single row
# This is another way data might be malformed - check the first few values
if len(chunk) > 0 and len(columns) > 1:
# If there seems to be a mismatch between chunk structure and expected columns
@@ -714,10 +657,9 @@ class DatabricksQueryTool(BaseTool):
len(chunk) > len(columns) * 3
): # Heuristic: if chunk has way more items than columns
# This chunk might actually be values of multiple rows - try to reconstruct
- values = chunk # All values in this chunk
+ values = chunk
reconstructed_rows = []
- # Try to create rows based on expected column count
for i in range(
0, len(values), len(columns)
):
@@ -739,7 +681,7 @@ class DatabricksQueryTool(BaseTool):
if reconstructed_rows:
chunk_results.extend(reconstructed_rows)
- continue # Skip normal processing for this chunk
+ continue
# Special case: when chunk contains exactly the right number of values for a single row
# This handles the case where instead of a list of rows, we just got all values in a flat list
@@ -752,12 +694,9 @@ class DatabricksQueryTool(BaseTool):
len(chunk) > 0
and len(chunk) % len(columns) == 0
):
- # Process flat list of values as rows
for i in range(0, len(chunk), len(columns)):
row_values = chunk[i : i + len(columns)]
- if len(row_values) == len(
- columns
- ): # Only process complete rows
+ if len(row_values) == len(columns):
row_dict = {
col: val
for col, val in zip(
@@ -768,25 +707,19 @@ class DatabricksQueryTool(BaseTool):
}
chunk_results.append(row_dict)
- # Skip regular row processing for this chunk
continue
# Normal processing for typical row structure
for _row_idx, row in enumerate(chunk):
# Ensure row is actually a collection of values
if not isinstance(row, (list, tuple, dict)):
- # This might be a single value; skip it or handle specially
continue
- # Convert each row to a dictionary with column names as keys
row_dict = {}
- # Handle dict rows directly
if isinstance(row, dict):
- # Use the existing column mapping
row_dict = dict(row)
elif isinstance(row, (list, tuple)):
- # Map list of values to columns
for i, val in enumerate(row):
if (
i < len(columns)
@@ -798,7 +731,6 @@ class DatabricksQueryTool(BaseTool):
row_dict[dynamic_col] = val
all_columns.add(dynamic_col)
- # If we have fewer values than columns, set missing values to None
for col in columns:
if col not in row_dict:
row_dict[col] = None
@@ -824,7 +756,6 @@ class DatabricksQueryTool(BaseTool):
row_dict[dynamic_col] = val
all_columns.add(dynamic_col)
- # If we have fewer values than columns, set missing values to None
for i, col in enumerate(columns):
if i >= len(row):
row_dict[col] = None
@@ -840,7 +771,6 @@ class DatabricksQueryTool(BaseTool):
}
normalized_results.append(normalized_row)
- # Replace the original results with normalized ones
chunk_results = normalized_results
except Exception as results_error:
@@ -856,7 +786,6 @@ class DatabricksQueryTool(BaseTool):
if "SUCCEEDED" in state_value:
return "Query executed successfully (no results to display)"
- # Format and return results
return self._format_results(chunk_results) # type: ignore[arg-type]
except Exception as e:
diff --git a/lib/crewai-tools/src/crewai_tools/tools/file_writer_tool/file_writer_tool.py b/lib/crewai-tools/src/crewai_tools/tools/file_writer_tool/file_writer_tool.py
index b7ac38fe8..002c9ab0a 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/file_writer_tool/file_writer_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/file_writer_tool/file_writer_tool.py
@@ -37,11 +37,9 @@ class FileWriterTool(BaseTool):
filepath = os.path.join(directory, filename)
# Prevent path traversal: the resolved path must be strictly inside
- # the resolved directory. This blocks ../sequences, absolute paths in
# filename, and symlink escapes regardless of how directory is set.
# is_relative_to() does a proper path-component comparison that is
# safe on case-insensitive filesystems and avoids the "// " edge case
- # that plagues startswith(real_directory + os.sep).
# We also reject the case where filepath resolves to the directory
# itself, since that is not a valid file target.
real_directory = Path(directory).resolve()
diff --git a/lib/crewai-tools/src/crewai_tools/tools/files_compressor_tool/files_compressor_tool.py b/lib/crewai-tools/src/crewai_tools/tools/files_compressor_tool/files_compressor_tool.py
index 8a759263a..379d5c247 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/files_compressor_tool/files_compressor_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/files_compressor_tool/files_compressor_tool.py
@@ -93,11 +93,9 @@ class FileCompressorTool(BaseTool):
def _generate_output_path(input_path: str, format: str) -> str:
"""Generates output path based on input path and format."""
if os.path.isfile(input_path):
- base_name = os.path.splitext(os.path.basename(input_path))[
- 0
- ] # Remove extension
+ base_name = os.path.splitext(os.path.basename(input_path))[0]
else:
- base_name = os.path.basename(os.path.normpath(input_path)) # Directory name
+ base_name = os.path.basename(os.path.normpath(input_path))
return os.path.join(os.getcwd(), f"{base_name}.{format}")
@staticmethod
diff --git a/lib/crewai-tools/src/crewai_tools/tools/firecrawl_scrape_website_tool/firecrawl_scrape_website_tool.py b/lib/crewai-tools/src/crewai_tools/tools/firecrawl_scrape_website_tool/firecrawl_scrape_website_tool.py
index 35b002961..81df69ef8 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/firecrawl_scrape_website_tool/firecrawl_scrape_website_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/firecrawl_scrape_website_tool/firecrawl_scrape_website_tool.py
@@ -57,7 +57,7 @@ class FirecrawlScrapeWebsiteTool(BaseTool):
"only_main_content": True,
"include_tags": [],
"exclude_tags": [],
- "max_age": 172800000, # 2 days cache
+ "max_age": 172800000,
"headers": {},
"wait_for": 0,
"mobile": False,
diff --git a/lib/crewai-tools/src/crewai_tools/tools/invoke_crewai_automation_tool/invoke_crewai_automation_tool.py b/lib/crewai-tools/src/crewai_tools/tools/invoke_crewai_automation_tool/invoke_crewai_automation_tool.py
index 2398ddd41..2f1e7868e 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/invoke_crewai_automation_tool/invoke_crewai_automation_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/invoke_crewai_automation_tool/invoke_crewai_automation_tool.py
@@ -67,7 +67,7 @@ class InvokeCrewAIAutomationTool(BaseTool):
crew_api_url: str
crew_bearer_token: str
- max_polling_time: int = 10 * 60 # 10 minutes
+ max_polling_time: int = 10 * 60
def __init__(
self,
@@ -88,12 +88,9 @@ class InvokeCrewAIAutomationTool(BaseTool):
max_polling_time: Maximum time in seconds to wait for task completion (default: 600 seconds = 10 minutes)
crew_inputs: Optional dictionary defining custom input schema fields
"""
- # Create dynamic args_schema if custom inputs provided
if crew_inputs:
- # Start with the base prompt field
fields = {}
- # Add custom fields
for field_name, field_def in crew_inputs.items():
if isinstance(field_def, tuple):
fields[field_name] = field_def
@@ -101,12 +98,10 @@ class InvokeCrewAIAutomationTool(BaseTool):
# Assume it's a Field object, extract type from annotation if available
fields[field_name] = (str, field_def)
- # Create dynamic model
args_schema = create_model("DynamicInvokeCrewAIAutomationInput", **fields) # type: ignore[call-overload]
else:
args_schema = InvokeCrewAIAutomationInput
- # Initialize the parent class with proper field values
super().__init__(
name=crew_name,
description=crew_description,
@@ -162,7 +157,6 @@ class InvokeCrewAIAutomationTool(BaseTool):
if kwargs is None:
kwargs = {}
- # Start the crew
response = self._kickoff_crew(inputs=kwargs)
kickoff_id: str | None = response.get("kickoff_id")
@@ -178,7 +172,7 @@ class InvokeCrewAIAutomationTool(BaseTool):
if status_response.get("state", "").lower() == "failed":
return f"Error: Crew task failed. Response: {status_response}"
except Exception as e:
- if i == self.max_polling_time - 1: # Last attempt
+ if i == self.max_polling_time - 1:
return f"Error: Failed to get crew status after {self.max_polling_time} attempts. Last error: {e}"
time.sleep(1)
diff --git a/lib/crewai-tools/src/crewai_tools/tools/merge_agent_handler_tool/merge_agent_handler_tool.py b/lib/crewai-tools/src/crewai_tools/tools/merge_agent_handler_tool/merge_agent_handler_tool.py
index c28474618..980f07dd9 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/merge_agent_handler_tool/merge_agent_handler_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/merge_agent_handler_tool/merge_agent_handler_tool.py
@@ -91,7 +91,6 @@ class MergeAgentHandlerTool(BaseTool):
if params:
payload["params"] = params
- # Log the full payload for debugging
logger.debug(f"MCP Request to {url}: {json.dumps(payload, indent=2)}")
try:
@@ -99,7 +98,6 @@ class MergeAgentHandlerTool(BaseTool):
response.raise_for_status()
result = response.json()
- # Handle JSON-RPC error responses
if "error" in result:
error_msg = result["error"].get("message", "Unknown error")
error_code = result["error"].get("code", -1)
@@ -119,20 +117,16 @@ class MergeAgentHandlerTool(BaseTool):
def _run(self, **kwargs: Any) -> Any:
"""Execute the Agent Handler tool with the given arguments."""
try:
- # Log what we're about to send
logger.info(f"Executing {self.tool_name} with arguments: {kwargs}")
- # Make the tool call via MCP
result = self._make_mcp_request(
method="tools/call",
params={"name": self.tool_name, "arguments": kwargs},
)
- # Extract the actual result from the MCP response
if "result" in result and "content" in result["result"]:
content = result["result"]["content"]
if content and len(content) > 0:
- # Parse the text content (it's JSON-encoded)
text_content = content[0].get("text", "")
try:
return json.loads(text_content)
@@ -176,10 +170,8 @@ class MergeAgentHandlerTool(BaseTool):
... registered_user_id="91b2b905-e866-40c8-8be2-efe53827a0aa",
... )
"""
- # Create an empty args schema model (proper BaseModel subclass)
empty_args_schema = create_model(f"{tool_name.replace('__', '_').title()}Args")
- # Initialize session and get tool schema
instance = cls(
name=tool_name,
description=f"Execute {tool_name} via Agent Handler",
@@ -191,7 +183,6 @@ class MergeAgentHandlerTool(BaseTool):
**kwargs,
)
- # Try to fetch the actual tool schema from Agent Handler
try:
result = instance._make_mcp_request(method="tools/list")
if "result" in result and "tools" in result["result"]:
@@ -222,7 +213,6 @@ class MergeAgentHandlerTool(BaseTool):
field_type: Any = Any
field_default: Any = ...
- # Map JSON schema types to Python types
json_type = field_schema.get("type", "string")
if json_type == "string":
field_type = str
@@ -237,7 +227,6 @@ class MergeAgentHandlerTool(BaseTool):
elif json_type == "object":
field_type = dict[str, Any]
- # Make field optional if not required
if field_name not in required:
field_type = field_type | None
field_default = None
@@ -303,7 +292,6 @@ class MergeAgentHandlerTool(BaseTool):
... tool_names=["linear__create_issue", "linear__get_issues"],
... )
"""
- # Create a temporary instance to fetch the tool list
temp_instance = cls(
name="temp",
description="temp",
@@ -315,7 +303,6 @@ class MergeAgentHandlerTool(BaseTool):
)
try:
- # Fetch available tools
result = temp_instance._make_mcp_request(method="tools/list")
if "result" not in result or "tools" not in result["result"]:
@@ -325,13 +312,11 @@ class MergeAgentHandlerTool(BaseTool):
available_tools = result["result"]["tools"]
- # Filter tools if specific names were requested
if tool_names:
available_tools = [
t for t in available_tools if t.get("name") in tool_names
]
- # Check if all requested tools were found
found_names = {t.get("name") for t in available_tools}
missing_names = set(tool_names) - found_names
if missing_names:
@@ -339,7 +324,6 @@ class MergeAgentHandlerTool(BaseTool):
f"The following tools were not found in the Tool Pack: {missing_names}"
)
- # Create tool instances
tools = []
for tool_schema in available_tools:
tool_name = tool_schema.get("name")
diff --git a/lib/crewai-tools/src/crewai_tools/tools/mongodb_vector_search_tool/vector_search.py b/lib/crewai-tools/src/crewai_tools/tools/mongodb_vector_search_tool/vector_search.py
index 382576d55..3ab37fdd2 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/mongodb_vector_search_tool/vector_search.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/mongodb_vector_search_tool/vector_search.py
@@ -260,7 +260,6 @@ class MongoDBVectorSearchTool(BaseTool):
)
]
operations = [ReplaceOne({"_id": doc["_id"]}, doc, upsert=True) for doc in docs]
- # insert the documents in MongoDB Atlas
result = self._coll.bulk_write(operations)
if result.upserted_ids is None:
raise ValueError("No documents were inserted.")
@@ -277,7 +276,6 @@ class MongoDBVectorSearchTool(BaseTool):
include_embeddings = query_config.include_embeddings
post_filter_pipeline = query_config.post_filter_pipeline
- # Create the embedding for the query
query_vector = self._embed_texts([query])[0]
# Atlas Vector Search, potentially with filter
@@ -296,7 +294,6 @@ class MongoDBVectorSearchTool(BaseTool):
{"$set": {"score": {"$meta": "vectorSearchScore"}}},
]
- # Remove embeddings unless requested
if not include_embeddings:
pipeline.append({"$project": {self.embedding_key: 0}})
@@ -308,7 +305,6 @@ class MongoDBVectorSearchTool(BaseTool):
cursor = self._coll.aggregate(pipeline) # type: ignore[arg-type]
docs = []
- # Format
for doc in cursor:
docs.append(doc) # noqa: PERF402
return json_util.dumps(docs)
diff --git a/lib/crewai-tools/src/crewai_tools/tools/multion_tool/example.py b/lib/crewai-tools/src/crewai_tools/tools/multion_tool/example.py
index 00646a0d4..3e55146b5 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/multion_tool/example.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/multion_tool/example.py
@@ -8,7 +8,6 @@ os.environ["OPENAI_API_KEY"] = "Your Key"
multion_browse_tool = MultiOnTool(api_key="Your Key")
-# Create a new agent
Browser = Agent(
role="Browser Agent",
goal="control web browsers using natural language ",
@@ -17,7 +16,6 @@ Browser = Agent(
verbose=True,
)
-# Define tasks
browse = Task(
description="Summarize the top 3 trending AI News headlines",
expected_output="A summary of the top 3 trending AI News headlines",
diff --git a/lib/crewai-tools/src/crewai_tools/tools/nl2sql/nl2sql_tool.py b/lib/crewai-tools/src/crewai_tools/tools/nl2sql/nl2sql_tool.py
index 4e20b4354..818c61dd2 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/nl2sql/nl2sql_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/nl2sql/nl2sql_tool.py
@@ -80,7 +80,6 @@ _AS_PAREN_RE = re.compile(r"\bAS\s*\(", re.IGNORECASE)
def _iter_as_paren_matches(stmt: str) -> Iterator[re.Match[str]]:
"""Yield regex matches for ``AS\\s*(`` outside of string literals."""
- # Build a set of character positions that are inside string literals.
in_string: set[int] = set()
i = 0
while i < len(stmt):
@@ -124,7 +123,6 @@ def _skip_string_literal(stmt: str, pos: int) -> int:
i = pos + 1
while i < len(stmt):
if stmt[i] == quote_char:
- # Check for escaped quote ('')
if i + 1 < len(stmt) and stmt[i + 1] == quote_char:
i += 2
continue
@@ -290,9 +288,7 @@ class NL2SQLTool(BaseTool):
self.tables = tables
self.columns = data
- # ------------------------------------------------------------------
# Query validation
- # ------------------------------------------------------------------
def _validate_query(self, sql_query: str) -> None:
"""Raise ValueError if *sql_query* is not permitted under the current config.
@@ -323,7 +319,6 @@ class NL2SQLTool(BaseTool):
# EXPLAIN ANALYZE / EXPLAIN ANALYSE actually *executes* the underlying
# query. Resolve the real command so write operations are caught.
- # Handles both space-separated ("EXPLAIN ANALYZE DELETE …") and
# parenthesized ("EXPLAIN (ANALYZE) DELETE …", "EXPLAIN (ANALYZE, VERBOSE) DELETE …").
# EXPLAIN ANALYZE actually executes the underlying query — resolve the
# real command so write operations are caught.
@@ -332,10 +327,8 @@ class NL2SQLTool(BaseTool):
if resolved:
command = resolved
- # WITH starts a CTE. Read-only CTEs are fine; writable CTEs
# (e.g. WITH d AS (DELETE …) SELECT …) must be blocked in read-only mode.
if command == "WITH":
- # Check for write commands inside CTE bodies.
write_found = _detect_writable_cte(stmt)
if write_found:
found = write_found
@@ -352,7 +345,6 @@ class NL2SQLTool(BaseTool):
)
return
- # Check the main query after the CTE definitions.
main_query = _extract_main_query_after_cte(stmt)
if main_query:
main_cmd = main_query.split()[0].upper().rstrip(";")
@@ -404,9 +396,7 @@ class NL2SQLTool(BaseTool):
first_token = stripped.split()[0] if stripped.split() else ""
return first_token.upper().rstrip(";")
- # ------------------------------------------------------------------
# Schema introspection helpers
- # ------------------------------------------------------------------
def _fetch_available_tables(self) -> list[dict[str, Any]] | str:
return self.execute_sql(
@@ -428,9 +418,7 @@ class NL2SQLTool(BaseTool):
params={"table_name": table_name},
)
- # ------------------------------------------------------------------
# Core execution
- # ------------------------------------------------------------------
def _run(self, sql_query: str) -> list[dict[str, Any]] | str:
try:
@@ -497,7 +485,6 @@ class NL2SQLTool(BaseTool):
try:
result = session.execute(text(sql_query), params or {})
- # Only commit when the operation actually mutates state
if self.allow_dml and is_write:
session.commit()
diff --git a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_product_scraper_tool/oxylabs_amazon_product_scraper_tool.py b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_product_scraper_tool/oxylabs_amazon_product_scraper_tool.py
index ba574d039..915b462ca 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_product_scraper_tool/oxylabs_amazon_product_scraper_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_product_scraper_tool/oxylabs_amazon_product_scraper_tool.py
@@ -107,7 +107,6 @@ class OxylabsAmazonProductScraperTool(BaseTool):
username, password = self._get_credentials_from_env()
if OXYLABS_AVAILABLE:
- # import RealtimeClient to make it accessible for the current scope
from oxylabs import RealtimeClient
kwargs["oxylabs_api"] = RealtimeClient(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_search_scraper_tool/oxylabs_amazon_search_scraper_tool.py b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_search_scraper_tool/oxylabs_amazon_search_scraper_tool.py
index b1176383c..2f693283b 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_search_scraper_tool/oxylabs_amazon_search_scraper_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_amazon_search_scraper_tool/oxylabs_amazon_search_scraper_tool.py
@@ -109,7 +109,6 @@ class OxylabsAmazonSearchScraperTool(BaseTool):
username, password = self._get_credentials_from_env()
if OXYLABS_AVAILABLE:
- # import RealtimeClient to make it accessible for the current scope
from oxylabs import RealtimeClient
kwargs["oxylabs_api"] = RealtimeClient(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_google_search_scraper_tool/oxylabs_google_search_scraper_tool.py b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_google_search_scraper_tool/oxylabs_google_search_scraper_tool.py
index 06b1e2f07..1d29fcd3e 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_google_search_scraper_tool/oxylabs_google_search_scraper_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_google_search_scraper_tool/oxylabs_google_search_scraper_tool.py
@@ -112,7 +112,6 @@ class OxylabsGoogleSearchScraperTool(BaseTool):
username, password = self._get_credentials_from_env()
if OXYLABS_AVAILABLE:
- # import RealtimeClient to make it accessible for the current scope
from oxylabs import RealtimeClient
kwargs["oxylabs_api"] = RealtimeClient(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_universal_scraper_tool/oxylabs_universal_scraper_tool.py b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_universal_scraper_tool/oxylabs_universal_scraper_tool.py
index 59baef944..adb52fbcc 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/oxylabs_universal_scraper_tool/oxylabs_universal_scraper_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/oxylabs_universal_scraper_tool/oxylabs_universal_scraper_tool.py
@@ -103,7 +103,6 @@ class OxylabsUniversalScraperTool(BaseTool):
username, password = self._get_credentials_from_env()
if OXYLABS_AVAILABLE:
- # import RealtimeClient to make it accessible for the current scope
from oxylabs import RealtimeClient
kwargs["oxylabs_api"] = RealtimeClient(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/example.py b/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/example.py
index 942e0b765..b863e0d21 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/example.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/example.py
@@ -11,7 +11,6 @@ from patronus_local_evaluator_tool import ( # type: ignore[import-not-found]
)
-# Test the PatronusLocalEvaluatorTool where agent uses the local evaluator
client = Client()
@@ -41,7 +40,6 @@ patronus_eval_tool = PatronusLocalEvaluatorTool(
evaluated_model_gold_answer="example label",
)
-# Create a new agent
coding_agent = Agent(
role="Coding Agent",
goal="Generate high quality code and verify that the output is code by using Patronus AI's evaluation tool.",
@@ -50,7 +48,6 @@ coding_agent = Agent(
verbose=True,
)
-# Define tasks
generate_code = Task(
description="Create a simple program to generate the first N numbers in the Fibonacci sequence. Select the most appropriate evaluator and criteria for evaluating your output.",
expected_output="Program that generates the first N numbers in the Fibonacci sequence.",
diff --git a/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_eval_tool.py b/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_eval_tool.py
index 5d2663b83..427d37e4a 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_eval_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_eval_tool.py
@@ -119,7 +119,6 @@ class PatronusEvalTool(BaseTool):
evaluated_model_retrieved_context: str | None,
evaluators: list[dict[str, str]],
) -> Any:
- # Assert correct format of evaluators
evals = []
for ev in evaluators:
evals.append( # noqa: PERF401
diff --git a/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_local_evaluator_tool.py b/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_local_evaluator_tool.py
index 632dcf3f5..7b9bc8849 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_local_evaluator_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/patronus_eval_tool/patronus_local_evaluator_tool.py
@@ -103,7 +103,6 @@ class PatronusLocalEvaluatorTool(BaseTool):
try:
- # Only rebuild if the class hasn't been initialized yet
if not hasattr(PatronusLocalEvaluatorTool, "_model_rebuilt"):
PatronusLocalEvaluatorTool.model_rebuild()
PatronusLocalEvaluatorTool._model_rebuilt = True # type: ignore[attr-defined]
diff --git a/lib/crewai-tools/src/crewai_tools/tools/qdrant_vector_search_tool/qdrant_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/qdrant_vector_search_tool/qdrant_search_tool.py
index 57ce9738e..c50fcabbf 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/qdrant_vector_search_tool/qdrant_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/qdrant_vector_search_tool/qdrant_search_tool.py
@@ -43,7 +43,6 @@ class QdrantVectorSearchTool(BaseTool):
model_config = ConfigDict(arbitrary_types_allowed=True)
- # --- Metadata ---
name: str = "QdrantVectorSearchTool"
description: str = "Search Qdrant vector DB for relevant documents."
args_schema: type[BaseModel] = QdrantToolSchema
@@ -68,7 +67,6 @@ class QdrantVectorSearchTool(BaseTool):
@model_validator(mode="after")
def _setup_qdrant(self) -> QdrantVectorSearchTool:
- # Import the qdrant_package if it's a string
if isinstance(self.qdrant_package, str):
self.qdrant_package = importlib.import_module(self.qdrant_package)
diff --git a/lib/crewai-tools/src/crewai_tools/tools/scrapegraph_scrape_tool/scrapegraph_scrape_tool.py b/lib/crewai-tools/src/crewai_tools/tools/scrapegraph_scrape_tool/scrapegraph_scrape_tool.py
index be41e0602..ccccd0294 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/scrapegraph_scrape_tool/scrapegraph_scrape_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/scrapegraph_scrape_tool/scrapegraph_scrape_tool.py
@@ -125,7 +125,6 @@ class ScrapegraphScrapeTool(BaseTool):
if user_prompt is not None:
self.user_prompt = user_prompt
- # Configure logging only if enabled
if self.enable_logging:
sgai_logger.set_logging(level="INFO")
@@ -170,11 +169,9 @@ class ScrapegraphScrapeTool(BaseTool):
if not website_url:
raise ValueError("website_url is required")
- # Validate URL format
self._validate_url(website_url)
try:
- # Make the SmartScraper request
if self._client is None:
raise RuntimeError("Client not initialized")
return self._client.smartscraper(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/selenium_scraping_tool/selenium_scraping_tool.py b/lib/crewai-tools/src/crewai_tools/tools/selenium_scraping_tool/selenium_scraping_tool.py
index f4d3bceeb..41ca86671 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/selenium_scraping_tool/selenium_scraping_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/selenium_scraping_tool/selenium_scraping_tool.py
@@ -192,7 +192,6 @@ class SeleniumScrapingTool(BaseTool):
if not url:
raise ValueError("URL cannot be empty")
- # Validate URL format
if not re.match(r"^https?://", url):
raise ValueError("URL must start with http:// or https://")
diff --git a/lib/crewai-tools/src/crewai_tools/tools/serper_scrape_website_tool/serper_scrape_website_tool.py b/lib/crewai-tools/src/crewai_tools/tools/serper_scrape_website_tool/serper_scrape_website_tool.py
index 55521104b..729f979ef 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/serper_scrape_website_tool/serper_scrape_website_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/serper_scrape_website_tool/serper_scrape_website_tool.py
@@ -49,16 +49,13 @@ class SerperScrapeWebsiteTool(BaseTool):
# Serper API endpoint
api_url = "https://scrape.serper.dev"
- # Get API key from environment variable for security
api_key = os.getenv("SERPER_API_KEY")
- # Prepare the payload
payload = json.dumps({"url": url, "includeMarkdown": include_markdown})
# Set headers
headers = {"X-API-KEY": api_key or "", "Content-Type": "application/json"}
- # Make the API request
response = requests.post(
api_url,
headers=headers,
@@ -66,11 +63,9 @@ class SerperScrapeWebsiteTool(BaseTool):
timeout=30,
)
- # Check if request was successful
if response.status_code == 200:
result = response.json()
- # Extract the scraped content
if "text" in result:
return str(result["text"])
return f"Successfully scraped {url}, but no text content found in response: {response.text}"
diff --git a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_job_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_job_search_tool.py
index b635eaba0..eed08a611 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_job_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_job_search_tool.py
@@ -61,7 +61,6 @@ class SerplyJobSearchTool(RagTool):
elif search_query is not None:
query_payload["q"] = search_query
- # build the url
url = f"{self.request_url}{urlencode(query_payload)}"
response = requests.request("GET", url, headers=self.headers, timeout=30)
diff --git a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_news_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_news_search_tool.py
index e1cb19b11..95a129014 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_news_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_news_search_tool.py
@@ -53,7 +53,6 @@ class SerplyNewsSearchTool(BaseTool):
self,
**kwargs: Any,
) -> Any:
- # build query parameters
query_payload = {}
if "query" in kwargs:
@@ -61,7 +60,6 @@ class SerplyNewsSearchTool(BaseTool):
elif "search_query" in kwargs:
query_payload["q"] = kwargs["search_query"]
- # build the url
url = f"{self.search_url}{urlencode(query_payload)}"
response = requests.request(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_scholar_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_scholar_search_tool.py
index f7d9a6b8d..1b43c45bb 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_scholar_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_scholar_search_tool.py
@@ -64,7 +64,6 @@ class SerplyScholarSearchTool(BaseTool):
elif "search_query" in kwargs:
query_payload["q"] = kwargs["search_query"]
- # build the url
url = f"{self.search_url}{urlencode(query_payload)}"
response = requests.request(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_web_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_web_search_tool.py
index a79f03b1b..bd95ab8d7 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_web_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/serply_api_tool/serply_web_search_tool.py
@@ -58,7 +58,6 @@ class SerplyWebSearchTool(BaseTool):
self.device_type = device_type
self.proxy_location = proxy_location
- # build query parameters
self.query_payload = {
"num": limit,
"gl": proxy_location.upper(),
@@ -80,7 +79,6 @@ class SerplyWebSearchTool(BaseTool):
elif "search_query" in kwargs:
self.query_payload["q"] = kwargs["search_query"] # type: ignore[index]
- # build the url
url = f"{self.search_url}{urlencode(self.query_payload)}" # type: ignore[arg-type]
response = requests.request(
diff --git a/lib/crewai-tools/src/crewai_tools/tools/singlestore_search_tool/singlestore_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/singlestore_search_tool/singlestore_search_tool.py
index b941dbf0d..e367738c5 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/singlestore_search_tool/singlestore_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/singlestore_search_tool/singlestore_search_tool.py
@@ -123,7 +123,6 @@ class SingleStoreSearchTool(BaseTool):
def __init__(
self,
tables: list[str] | None = None,
- # Basic connection parameters
host: str | None = None,
user: str | None = None,
password: str | None = None,
@@ -147,7 +146,6 @@ class SingleStoreSearchTool(BaseTool):
conv: dict[int, Callable[..., Any]] | None = None,
credential_type: str | None = None,
autocommit: bool | None = None,
- # Result formatting options
results_type: str | None = None,
buffered: bool | None = None,
results_format: str | None = None,
@@ -210,13 +208,10 @@ class SingleStoreSearchTool(BaseTool):
"`singlestore` package not found, please run `uv add crewai-tools[singlestore]`"
)
- # Set the data type for the parent class
kwargs["data_type"] = "singlestore"
super().__init__(**kwargs)
- # Build connection arguments dictionary with sensible defaults
self.connection_args = {
- # Basic connection parameters
"host": host,
"user": user,
"password": password,
@@ -240,7 +235,6 @@ class SingleStoreSearchTool(BaseTool):
"conv": conv or {},
"credential_type": credential_type,
"autocommit": autocommit,
- # Result formatting
"results_type": results_type,
"buffered": buffered,
"results_format": results_format,
@@ -266,13 +260,11 @@ class SingleStoreSearchTool(BaseTool):
):
self.connection_args["conn_attrs"] = dict()
- # Add tool identification to connection attributes
self.connection_args["conn_attrs"]["_connector_name"] = (
"crewAI SingleStore Tool"
)
self.connection_args["conn_attrs"]["_connector_version"] = "1.0"
- # Initialize connection pool for efficient connection management
self.connection_pool = QueuePool(
creator=self._create_connection,
pool_size=pool_size or 5,
@@ -280,7 +272,6 @@ class SingleStoreSearchTool(BaseTool):
timeout=timeout or 30.0,
)
- # Validate database schema and initialize table information
self._initialize_tables(tables)
def _initialize_tables(self, tables: list[str]) -> None:
@@ -295,22 +286,18 @@ class SingleStoreSearchTool(BaseTool):
conn = self._get_connection()
try:
with conn.cursor() as cursor:
- # Get all existing tables in the database
cursor.execute("SHOW TABLES")
existing_tables = {table[0] for table in cursor.fetchall()}
- # Validate that the database has tables
if not existing_tables or len(existing_tables) == 0:
raise ValueError(
"No tables found in the database. "
"Please ensure the database is initialized with the required tables."
)
- # Use all tables if none specified
if not tables or len(tables) == 0:
tables = list(existing_tables)
- # Build table definitions for description
table_definitions = []
for table in tables:
if table not in existing_tables:
@@ -319,7 +306,6 @@ class SingleStoreSearchTool(BaseTool):
f"Please ensure the table is created."
)
- # Get column information for each table
cursor.execute(f"SHOW COLUMNS FROM {table}")
columns = cursor.fetchall()
column_info = ", ".join(f"{row[0]} {row[1]}" for row in columns)
@@ -328,7 +314,6 @@ class SingleStoreSearchTool(BaseTool):
# Ensure the connection is returned to the pool
conn.close()
- # Update the tool description with actual table information
self.description = (
f"A tool that can be used to semantic search a query from a SingleStore "
f"database's {', '.join(table_definitions)} table(s) content."
@@ -379,11 +364,9 @@ class SingleStoreSearchTool(BaseTool):
Returns:
tuple: (is_valid: bool, message: str)
"""
- # Check if the input is a string
if not isinstance(search_query, str):
return False, "Search query must be a string."
- # Remove leading/trailing whitespace and convert to lowercase for checking
query_lower = search_query.strip().lower()
# Allow only SELECT and SHOW statements
@@ -405,25 +388,20 @@ class SingleStoreSearchTool(BaseTool):
Returns:
str: Formatted search results or error message
"""
- # Validate the query before execution
valid, message = self._validate_query(search_query)
if not valid:
return f"Invalid search query: {message}"
- # Execute the query using a connection from the pool
conn = self._get_connection()
try:
with conn.cursor() as cursor:
try:
- # Execute the validated search query
cursor.execute(search_query)
results = cursor.fetchall()
- # Handle empty results
if not results:
return "No results found."
- # Format the results for readable output
formatted_results = "\n".join(
[", ".join([str(item) for item in row]) for row in results]
)
diff --git a/lib/crewai-tools/src/crewai_tools/tools/snowflake_search_tool/snowflake_search_tool.py b/lib/crewai-tools/src/crewai_tools/tools/snowflake_search_tool/snowflake_search_tool.py
index b68dab109..fb293319c 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/snowflake_search_tool/snowflake_search_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/snowflake_search_tool/snowflake_search_tool.py
@@ -11,7 +11,6 @@ from pydantic import BaseModel, ConfigDict, Field, SecretStr
if TYPE_CHECKING:
- # Import types for type checking only
from snowflake.connector.connection import (
SnowflakeConnection,
)
@@ -29,7 +28,6 @@ try:
except ImportError:
SNOWFLAKE_AVAILABLE = False
-# Configure logging
logger = logging.getLogger(__name__)
# Cache for query results
@@ -257,7 +255,6 @@ class SnowflakeSearchTool(BaseTool):
) -> Any:
"""Execute the search query."""
try:
- # Override database/schema if provided
if database:
await self._execute_query(f"USE DATABASE {database}")
if snowflake_schema:
@@ -284,7 +281,6 @@ class SnowflakeSearchTool(BaseTool):
try:
- # Only rebuild if the class hasn't been initialized yet
if not hasattr(SnowflakeSearchTool, "_model_rebuilt"):
SnowflakeSearchTool.model_rebuild()
SnowflakeSearchTool._model_rebuilt = True
diff --git a/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/example.py b/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/example.py
index d0996c9c2..83f23a93a 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/example.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/example.py
@@ -28,23 +28,19 @@ from crewai_tools import StagehandTool
_printer = Printer()
-# Load environment variables from .env file
load_dotenv()
-# Get API keys from environment variables
# You can set these in your shell or in a .env file
browserbase_api_key = os.environ.get("BROWSERBASE_API_KEY")
browserbase_project_id = os.environ.get("BROWSERBASE_PROJECT_ID")
-model_api_key = os.environ.get("OPENAI_API_KEY") # or OPENAI_API_KEY
+model_api_key = os.environ.get("OPENAI_API_KEY")
-# Initialize the StagehandTool with your credentials and use context manager
with StagehandTool(
- api_key=browserbase_api_key, # New parameter naming
- project_id=browserbase_project_id, # New parameter naming
+ api_key=browserbase_api_key,
+ project_id=browserbase_project_id,
model_api_key=model_api_key,
- model_name=AvailableModel.GPT_4O, # Using the enum from schemas
+ model_name=AvailableModel.GPT_4O,
) as stagehand_tool:
- # Create a web researcher agent with the StagehandTool
researcher = Agent(
role="Web Researcher",
goal="Find and extract information from websites using different Stagehand primitives",
@@ -74,7 +70,6 @@ with StagehandTool(
tools=[stagehand_tool],
)
- # Define a research task that demonstrates all three primitives
research_task = Task(
description=(
"Demonstrate Stagehand capabilities by performing the following steps:\n"
@@ -104,7 +99,6 @@ with StagehandTool(
agent=researcher,
)
- # Set up the crew
crew = Crew(
agents=[researcher],
tasks=[research_task], # You can switch this to web_research_task if you prefer
@@ -112,7 +106,6 @@ with StagehandTool(
process=Process.sequential,
)
- # Run the crew and get the result
result = crew.kickoff()
_printer.print("\n==== RESULTS ====\n", color="cyan")
diff --git a/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/stagehand_tool.py b/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/stagehand_tool.py
index fe584f338..eba4a156d 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/stagehand_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/stagehand_tool/stagehand_tool.py
@@ -11,7 +11,6 @@ from crewai.tools import BaseTool, EnvVar
from pydantic import BaseModel, Field
-# Define a flag to track whether stagehand is available
_HAS_STAGEHAND = False
try:
@@ -37,7 +36,6 @@ except ImportError:
ExtractOptions = Any
ObserveOptions = Any
- # Mock configure_logging function
def configure_logging(
level: str | None = None,
remove_logger_name: bool | None = None,
@@ -45,7 +43,6 @@ except ImportError:
) -> None:
pass
- # Define only what's needed for class defaults
class AvailableModel: # type: ignore[no-redef]
CLAUDE_3_7_SONNET_LATEST = "anthropic.claude-3-7-sonnet-20240607"
@@ -203,7 +200,6 @@ class StagehandTool(BaseTool):
self._testing = _testing
super().__init__(**kwargs)
- # Set up logger
import logging
self._logger = logging.getLogger(__name__)
@@ -231,7 +227,6 @@ class StagehandTool(BaseTool):
self._session_id = session_id
- # Configure logging based on verbosity level
if not self._testing:
log_level = {1: "INFO", 2: "WARNING", 3: "DEBUG"}.get(self.verbose, "ERROR")
configure_logging(
@@ -263,7 +258,6 @@ class StagehandTool(BaseTool):
def _get_model_api_key(self) -> str | None:
"""Get the appropriate API key based on the model being used."""
- # Check model type and get appropriate key
model_str = str(self.model_name)
if "gpt" in model_str.lower():
return self.model_api_key or os.getenv("OPENAI_API_KEY")
@@ -280,10 +274,9 @@ class StagehandTool(BaseTool):
async def _setup_stagehand(self, session_id: str | None = None) -> tuple[Any, Any]:
"""Initialize Stagehand if not already set up."""
- # If we're in testing mode, return mock objects
if self._testing:
if not self._stagehand:
- # Create mock objects for testing
+
class MockPage:
async def act(self, options: Any) -> Any:
mock_result = type("MockResult", (), {})()
@@ -331,7 +324,6 @@ class StagehandTool(BaseTool):
# Normal initialization for non-testing mode
if not self._stagehand:
- # Get the appropriate API key based on model type
model_api_key = self._get_model_api_key()
if not model_api_key:
@@ -339,7 +331,6 @@ class StagehandTool(BaseTool):
"No appropriate API key found for model. Please set OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY"
)
- # Build the StagehandConfig with proper parameter names
config = StagehandConfig(
env="BROWSERBASE",
apiKey=self.api_key, # Browserbase API key (camelCase)
@@ -356,10 +347,8 @@ class StagehandTool(BaseTool):
browserbaseSessionID=session_id or self._session_id,
)
- # Initialize Stagehand with config
self._stagehand = Stagehand(config=config) # type: ignore[call-arg]
- # Initialize the Stagehand instance
await self._stagehand.init()
self._page = self._stagehand.page
self._session_id = self._stagehand.session_id
@@ -368,7 +357,6 @@ class StagehandTool(BaseTool):
def _extract_steps(self, instruction: str) -> list[str]:
"""Extract individual steps from multi-step instructions."""
- # Check for numbered steps (Step 1:, Step 2:, etc.)
if re.search(r"Step \d+:", instruction, re.IGNORECASE):
steps = re.findall(
r"Step \d+:\s*([^;]+?)(?=Step \d+:|$)",
@@ -376,14 +364,12 @@ class StagehandTool(BaseTool):
re.IGNORECASE | re.DOTALL,
)
return [step.strip() for step in steps if step.strip()]
- # Check for semicolon-separated instructions
if ";" in instruction:
return [step.strip() for step in instruction.split(";") if step.strip()]
return [instruction]
def _simplify_instruction(self, instruction: str) -> str:
"""Simplify complex instructions to basic actions."""
- # Extract the core action from complex instructions
instruction_lower = instruction.lower()
if "search" in instruction_lower and "click" in instruction_lower:
@@ -392,7 +378,6 @@ class StagehandTool(BaseTool):
return "click on the search input field"
return "search for content on the page"
if "click" in instruction_lower:
- # Extract what to click
if "button" in instruction_lower:
return "click the button"
if "link" in instruction_lower:
@@ -402,7 +387,7 @@ class StagehandTool(BaseTool):
return "click on the element"
if "type" in instruction_lower or "enter" in instruction_lower:
return "type in the input field"
- return instruction # Return as-is if can't simplify
+ return instruction
async def _async_run(
self,
@@ -411,7 +396,6 @@ class StagehandTool(BaseTool):
command_type: str = "act",
) -> StagehandResult:
"""Override _async_run with improved atomic action handling."""
- # Handle missing instruction based on command type
if not instruction:
if command_type == "navigate" and url:
instruction = f"Navigate to {url}"
@@ -439,7 +423,6 @@ class StagehandTool(BaseTool):
f"Executing {command_type} with instruction: {instruction}"
)
- # Get the API key to pass to model operations
model_api_key = self._get_model_api_key()
model_client_options: dict[str, Any] = {"apiKey": model_api_key}
@@ -451,9 +434,7 @@ class StagehandTool(BaseTool):
# Small delay to ensure page is fully loaded
await asyncio.sleep(1)
- # Process according to command type
if command_type.lower() == "act":
- # Extract steps from complex instructions
steps = self._extract_steps(instruction)
self._logger.info(f"Extracted {len(steps)} steps: {steps}")
@@ -462,7 +443,6 @@ class StagehandTool(BaseTool):
self._logger.info(f"Executing step {i + 1}/{len(steps)}: {step}")
try:
- # Create act options with API key for each step
from stagehand.schemas import ActOptions
act_options = ActOptions(
@@ -483,7 +463,6 @@ class StagehandTool(BaseTool):
error_msg = f"Step failed: {step_error}"
self._logger.warning(f"Step {i + 1} failed: {error_msg}")
- # Try with simplified instruction
try:
simplified = self._simplify_instruction(step)
if simplified != step:
@@ -501,13 +480,11 @@ class StagehandTool(BaseTool):
result = await page.act(act_options)
results.append(result.model_dump())
else:
- # If we can't simplify or retry fails, record the error
results.append({"error": error_msg, "step": step})
except Exception as retry_error:
self._logger.error(f"Retry also failed: {retry_error}")
results.append({"error": str(retry_error), "step": step})
- # Return combined results
if len(results) == 1:
# Single step, return as-is
if "error" in results[0]:
@@ -537,7 +514,6 @@ class StagehandTool(BaseTool):
)
if command_type.lower() == "extract":
- # Create extract options with API key
from stagehand.schemas import ExtractOptions
extract_options = ExtractOptions(
@@ -545,7 +521,7 @@ class StagehandTool(BaseTool):
modelName=self.model_name,
domSettleTimeoutMs=self.dom_settle_timeout_ms,
useTextExtract=True,
- modelClientOptions=model_client_options, # Add API key here
+ modelClientOptions=model_client_options,
)
result = await page.extract(extract_options)
@@ -553,7 +529,6 @@ class StagehandTool(BaseTool):
return self._format_result(True, result.model_dump())
if command_type.lower() == "observe":
- # Create observe options with API key
from stagehand.schemas import ObserveOptions
observe_options = ObserveOptions(
@@ -561,12 +536,11 @@ class StagehandTool(BaseTool):
modelName=self.model_name,
onlyVisible=True,
domSettleTimeoutMs=self.dom_settle_timeout_ms,
- modelClientOptions=model_client_options, # Add API key here
+ modelClientOptions=model_client_options,
)
observe_results = await page.observe(observe_options)
- # Format the observation results
formatted_results: list[dict[str, Any]] = []
for i, obs_result in enumerate(observe_results):
formatted_results.append(
@@ -616,7 +590,6 @@ class StagehandTool(BaseTool):
Returns:
The result of the browser automation task
"""
- # Handle missing instruction based on command type
if not instruction:
if command_type == "navigate" and url:
instruction = f"Navigate to {url}"
@@ -626,7 +599,6 @@ class StagehandTool(BaseTool):
instruction = "Extract information from the page"
else:
instruction = "Perform the requested action"
- # Create an event loop if we're not already in one
try:
loop = asyncio.get_event_loop()
if loop.is_running():
@@ -647,7 +619,6 @@ class StagehandTool(BaseTool):
self._async_run(instruction, url, command_type)
)
- # Format the result for output
if result.success:
if command_type.lower() == "act":
if isinstance(result.data, dict) and "steps" in result.data:
@@ -696,7 +667,6 @@ class StagehandTool(BaseTool):
async def _async_close(self) -> None:
"""Asynchronously clean up Stagehand resources."""
- # Skip for test mode
if self._testing:
self._stagehand = None
self._page = None
@@ -710,7 +680,6 @@ class StagehandTool(BaseTool):
def close(self) -> None:
"""Clean up Stagehand resources."""
- # Skip actual closing for testing mode
if self._testing:
self._stagehand = None
self._page = None
@@ -741,7 +710,6 @@ class StagehandTool(BaseTool):
else:
close_method()
except Exception: # noqa: S110
- # Log but don't raise - we're cleaning up
pass
self._stagehand = None
diff --git a/lib/crewai-tools/src/crewai_tools/tools/vision_tool/vision_tool.py b/lib/crewai-tools/src/crewai_tools/tools/vision_tool/vision_tool.py
index 24904c0f6..bc6d6b464 100644
--- a/lib/crewai-tools/src/crewai_tools/tools/vision_tool/vision_tool.py
+++ b/lib/crewai-tools/src/crewai_tools/tools/vision_tool/vision_tool.py
@@ -25,7 +25,6 @@ class ImagePromptSchema(BaseModel):
if not path.exists():
raise ValueError(f"Image file does not exist: {v}")
- # Validate supported formats
valid_extensions = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
if path.suffix.lower() not in valid_extensions:
raise ValueError(
diff --git a/lib/crewai-tools/tests/adapters/mcp_adapter_test.py b/lib/crewai-tools/tests/adapters/mcp_adapter_test.py
index 188f86699..6f67878dd 100644
--- a/lib/crewai-tools/tests/adapters/mcp_adapter_test.py
+++ b/lib/crewai-tools/tests/adapters/mcp_adapter_test.py
@@ -137,7 +137,6 @@ def test_context_manager_with_filtered_tools(echo_server_script):
assert len(tools) == 1
assert tools[0].name == "echo_tool"
assert tools[0].run(text="hello") == "Echo: hello"
- # Check that calc_tool is not present
with pytest.raises(IndexError):
_ = tools[1]
with pytest.raises(KeyError):
@@ -152,7 +151,6 @@ def test_context_manager_sse_with_filtered_tools(echo_sse_server):
assert len(tools) == 1
assert tools[0].name == "calc_tool"
assert tools[0].run(a=10, b=5) == "15"
- # Check that echo_tool is not present
with pytest.raises(IndexError):
_ = tools[1]
with pytest.raises(KeyError):
diff --git a/lib/crewai-tools/tests/base_tool_test.py b/lib/crewai-tools/tests/base_tool_test.py
index 6b7c5e6af..a4702acf0 100644
--- a/lib/crewai-tools/tests/base_tool_test.py
+++ b/lib/crewai-tools/tests/base_tool_test.py
@@ -10,7 +10,6 @@ def test_creating_a_tool_using_annotation():
"""Clear description for what this tool is useful for, you agent will need this information to use it."""
return question
- # Assert all the right attributes were defined
assert my_tool.name == "Name of my tool"
assert (
my_tool.description
@@ -48,7 +47,6 @@ def test_creating_a_tool_using_baseclass():
return question
my_tool = MyCustomTool()
- # Assert all the right attributes were defined
assert my_tool.name == "Name of my tool"
assert (
my_tool.description
@@ -87,7 +85,6 @@ def test_setting_cache_function():
return question
my_tool = MyCustomTool()
- # Assert all the right attributes were defined
assert not my_tool.cache_function()
@@ -100,5 +97,4 @@ def test_default_cache_function_is_true():
return question
my_tool = MyCustomTool()
- # Assert all the right attributes were defined
assert my_tool.cache_function()
diff --git a/lib/crewai-tools/tests/file_read_tool_test.py b/lib/crewai-tools/tests/file_read_tool_test.py
index 174b32229..e3c4f9c9b 100644
--- a/lib/crewai-tools/tests/file_read_tool_test.py
+++ b/lib/crewai-tools/tests/file_read_tool_test.py
@@ -6,18 +6,15 @@ from crewai_tools import FileReadTool
def test_file_read_tool_constructor():
"""Test FileReadTool initialization with file_path."""
- # Create a temporary test file
test_file = "/tmp/test_file.txt"
test_content = "Hello, World!"
with open(test_file, "w") as f:
f.write(test_content)
- # Test initialization with file_path
tool = FileReadTool(file_path=test_file)
assert tool.file_path == test_file
assert "test_file.txt" in tool.description
- # Clean up
os.remove(test_file)
@@ -28,7 +25,6 @@ def test_file_read_tool_run():
# Use mock_open to mock file operations
with patch("builtins.open", mock_open(read_data=test_content)):
- # Test reading file with runtime file_path
tool = FileReadTool()
result = tool._run(file_path=test_file)
assert result == test_content
@@ -36,16 +32,13 @@ def test_file_read_tool_run():
def test_file_read_tool_error_handling():
"""Test FileReadTool error handling."""
- # Test missing file path
tool = FileReadTool()
result = tool._run()
assert "Error: No file path provided" in result
- # Test non-existent file
result = tool._run(file_path="/nonexistent/file.txt")
assert "Error: File not found at path:" in result
- # Test permission error
with patch("builtins.open", side_effect=PermissionError()):
result = tool._run(file_path="/tmp/no_permission.txt")
assert "Error: Permission denied" in result
@@ -58,7 +51,6 @@ def test_file_read_tool_constructor_and_run():
content1 = "File 1 content"
content2 = "File 2 content"
- # First test with content1
with patch("builtins.open", mock_open(read_data=content1)):
tool = FileReadTool(file_path=test_file1)
result = tool._run()
@@ -90,7 +82,6 @@ def test_file_read_tool_chunk_reading():
with patch("builtins.open", mock_open(read_data=file_content)):
tool = FileReadTool()
- # Test reading a specific chunk (lines 3-5)
result = tool._run(file_path=test_file, start_line=3, line_count=3)
expected = "".join(lines[2:5]) # Lines are 0-indexed in the array
assert result == expected
@@ -120,7 +111,6 @@ def test_file_read_tool_chunk_error_handling():
with patch("builtins.open", mock_open(read_data=file_content)):
tool = FileReadTool()
- # Test start_line exceeding file length
result = tool._run(file_path=test_file, start_line=10)
assert "Error: Start line 10 exceeds the number of lines in the file" in result
@@ -139,12 +129,10 @@ def test_file_read_tool_zero_or_negative_start_line():
with patch("builtins.open", mock_open(read_data=file_content)):
tool = FileReadTool()
- # Test with start_line = None
result = tool._run(file_path=test_file, start_line=None)
expected = "".join(lines) # Should read the entire file
assert result == expected
- # Test with start_line = 0
result = tool._run(file_path=test_file, start_line=0)
expected = "".join(lines) # Should read the entire file
assert result == expected
@@ -154,7 +142,6 @@ def test_file_read_tool_zero_or_negative_start_line():
expected = "".join(lines[0:3]) # Should read first 3 lines
assert result == expected
- # Test with negative start_line
result = tool._run(file_path=test_file, start_line=-5)
expected = "".join(lines) # Should read the entire file
assert result == expected
diff --git a/lib/crewai-tools/tests/rag/test_docx_loader.py b/lib/crewai-tools/tests/rag/test_docx_loader.py
index 51222c829..f1f11d978 100644
--- a/lib/crewai-tools/tests/rag/test_docx_loader.py
+++ b/lib/crewai-tools/tests/rag/test_docx_loader.py
@@ -14,7 +14,7 @@ class TestDOCXLoader:
mock_doc.paragraphs = [
Mock(text="First paragraph"),
Mock(text="Second paragraph"),
- Mock(text=" "), # Blank paragraph
+ Mock(text=" "),
]
mock_doc.tables = []
mock_docx_class.return_value = mock_doc
diff --git a/lib/crewai-tools/tests/rag/test_embedding_service.py b/lib/crewai-tools/tests/rag/test_embedding_service.py
index c6c74fdf1..6ba56748a 100644
--- a/lib/crewai-tools/tests/rag/test_embedding_service.py
+++ b/lib/crewai-tools/tests/rag/test_embedding_service.py
@@ -65,24 +65,20 @@ class TestEmbeddingService:
"""Test getting default API keys from environment."""
service = EmbeddingService.__new__(EmbeddingService) # Create without __init__
- # Test with environment variable set
with patch.dict(os.environ, {"OPENAI_API_KEY": "test-openai-key"}):
api_key = service._get_default_api_key("openai")
assert api_key == "test-openai-key"
- # Test with no environment variable
with patch.dict(os.environ, {}, clear=True):
api_key = service._get_default_api_key("openai")
assert api_key is None
- # Test unknown provider
api_key = service._get_default_api_key("unknown-provider")
assert api_key is None
@patch('crewai.rag.embeddings.factory.build_embedder')
def test_initialization_success(self, mock_build_embedder):
"""Test successful initialization."""
- # Mock the embedding function
mock_embedding_function = Mock()
mock_build_embedder.return_value = mock_embedding_function
@@ -97,7 +93,6 @@ class TestEmbeddingService:
assert service.config.api_key == "test-key"
assert service._embedding_function == mock_embedding_function
- # Verify build_embedder was called with correct config
mock_build_embedder.assert_called_once()
call_args = mock_build_embedder.call_args[0][0]
assert call_args["provider"] == "openai"
@@ -115,7 +110,6 @@ class TestEmbeddingService:
@patch('crewai.rag.embeddings.factory.build_embedder')
def test_embed_text_success(self, mock_build_embedder):
"""Test successful text embedding."""
- # Mock the embedding function
mock_embedding_function = Mock()
mock_embedding_function.return_value = [[0.1, 0.2, 0.3]]
mock_build_embedder.return_value = mock_embedding_function
@@ -147,7 +141,6 @@ class TestEmbeddingService:
@patch('crewai.rag.embeddings.factory.build_embedder')
def test_embed_batch_success(self, mock_build_embedder):
"""Test successful batch embedding."""
- # Mock the embedding function
mock_embedding_function = Mock()
mock_embedding_function.return_value = [[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]
mock_build_embedder.return_value = mock_embedding_function
@@ -182,7 +175,6 @@ class TestEmbeddingService:
@patch('crewai.rag.embeddings.factory.build_embedder')
def test_validate_connection(self, mock_build_embedder):
"""Test connection validation."""
- # Mock successful embedding
mock_embedding_function = Mock()
mock_embedding_function.return_value = [[0.1, 0.2, 0.3]]
mock_build_embedder.return_value = mock_embedding_function
@@ -191,14 +183,12 @@ class TestEmbeddingService:
assert service.validate_connection() is True
- # Mock failed embedding
mock_embedding_function.side_effect = Exception("Connection failed")
assert service.validate_connection() is False
@patch('crewai.rag.embeddings.factory.build_embedder')
def test_get_service_info(self, mock_build_embedder):
"""Test getting service information."""
- # Mock the embedding function
mock_embedding_function = Mock()
mock_embedding_function.return_value = [[0.1, 0.2, 0.3]]
mock_build_embedder.return_value = mock_embedding_function
@@ -277,7 +267,6 @@ class TestProviderConfigurations:
extra_config={"dimensions": 1024}
)
- # Check the configuration passed to build_embedder
call_args = mock_build_embedder.call_args[0][0]
assert call_args["provider"] == "openai"
assert call_args["config"]["api_key"] == "test-key"
@@ -298,7 +287,6 @@ class TestProviderConfigurations:
extra_config={"input_type": "document"}
)
- # Check the configuration passed to build_embedder
call_args = mock_build_embedder.call_args[0][0]
assert call_args["provider"] == "voyageai"
assert call_args["config"]["api_key"] == "test-key"
@@ -318,7 +306,6 @@ class TestProviderConfigurations:
api_key="test-key"
)
- # Check the configuration passed to build_embedder
call_args = mock_build_embedder.call_args[0][0]
assert call_args["provider"] == "cohere"
assert call_args["config"]["api_key"] == "test-key"
@@ -335,7 +322,6 @@ class TestProviderConfigurations:
api_key="test-key"
)
- # Check the configuration passed to build_embedder
call_args = mock_build_embedder.call_args[0][0]
assert call_args["provider"] == "google-generativeai"
assert call_args["config"]["api_key"] == "test-key"
diff --git a/lib/crewai-tools/tests/rag/test_json_loader.py b/lib/crewai-tools/tests/rag/test_json_loader.py
index 1787b029a..3ec0aa2c7 100644
--- a/lib/crewai-tools/tests/rag/test_json_loader.py
+++ b/lib/crewai-tools/tests/rag/test_json_loader.py
@@ -126,9 +126,6 @@ class TestJSONLoader:
finally:
os.unlink(path)
- # ------------------------------
- # URL-based tests
- # ------------------------------
@patch("requests.get")
def test_url_response_valid_json(self, mock_get):
diff --git a/lib/crewai-tools/tests/test_generate_tool_specs.py b/lib/crewai-tools/tests/test_generate_tool_specs.py
index 0841eeda6..8868df96a 100644
--- a/lib/crewai-tools/tests/test_generate_tool_specs.py
+++ b/lib/crewai-tools/tests/test_generate_tool_specs.py
@@ -45,7 +45,6 @@ class MockTool(BaseTool):
)
-# --- Intermediate base class (like RagTool, BraveSearchToolBase) ---
class MockIntermediateBase(BaseTool):
"""Simulates an intermediate tool base class (e.g. RagTool, BraveSearchToolBase)."""
diff --git a/lib/crewai-tools/tests/tools/brave_search_tool_test.py b/lib/crewai-tools/tests/tools/brave_search_tool_test.py
index 52ef88f47..642bc3544 100644
--- a/lib/crewai-tools/tests/tools/brave_search_tool_test.py
+++ b/lib/crewai-tools/tests/tools/brave_search_tool_test.py
@@ -51,9 +51,6 @@ def _mock_response(
return resp
-# Fixtures
-
-
@pytest.fixture(autouse=True)
def _brave_env_and_rate_limit():
"""Set BRAVE_API_KEY for every test. Rate limiting is per-instance (each tool starts with a fresh clock)."""
@@ -81,8 +78,6 @@ def video_tool():
return BraveVideoSearchTool()
-# Initialization
-
ALL_TOOL_CLASSES = [
BraveWebSearchTool,
BraveImageSearchTool,
@@ -343,7 +338,6 @@ def test_refine_request_payload_passes_multiple_goggles_as_multiple_params(web_t
# Null-like / empty value stripping
-#
# crewAI's ensure_all_properties_required (pydantic_schema_utils.py) marks
# every schema property as required for OpenAI strict-mode compatibility.
# Because optional Brave API parameters look required to the LLM, it fills
diff --git a/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py b/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py
index 11ca018e8..8a4ca4775 100644
--- a/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py
+++ b/lib/crewai-tools/tests/tools/brightdata_serp_tool_test.py
@@ -20,7 +20,6 @@ class TestBrightDataSearchTool(unittest.TestCase):
mock_response.text = "mock response text"
mock_post.return_value = mock_response
- # Define search input
input_data = {
"query": "latest AI news",
"search_engine": "google",
@@ -46,7 +45,6 @@ class TestBrightDataSearchTool(unittest.TestCase):
self.assertIn("Error", result)
def tearDown(self):
- # Clean up env vars
pass
diff --git a/lib/crewai-tools/tests/tools/couchbase_tool_test.py b/lib/crewai-tools/tests/tools/couchbase_tool_test.py
index 851f17580..d8291697e 100644
--- a/lib/crewai-tools/tests/tools/couchbase_tool_test.py
+++ b/lib/crewai-tools/tests/tools/couchbase_tool_test.py
@@ -42,17 +42,14 @@ sys.modules["couchbase.options"] = mock_couchbase.options
sys.modules["couchbase.vector_search"] = mock_couchbase.vector_search
sys.modules["couchbase.exceptions"] = mock_couchbase.exceptions
-# Now import the tool
from crewai_tools.tools.couchbase_tool.couchbase_tool import (
CouchbaseFTSVectorSearchTool,
)
-# --- Test Fixtures ---
@pytest.fixture(autouse=True)
def reset_global_mocks():
"""Reset call counts for globally defined mocks before each test."""
- # Reset the specific mock causing the issue
mock_couchbase.vector_search.VectorQuery.reset_mock()
# It's good practice to also reset other related global mocks
# that might be called in your tests to prevent similar issues:
@@ -67,7 +64,6 @@ def ensure_couchbase_mocks():
# This fixture ensures our mocks are in place regardless of import order
original_modules = {}
- # Store any existing modules
for module_name in [
"couchbase",
"couchbase.search",
@@ -105,7 +101,6 @@ def mock_cluster():
collection = MagicMock()
scope_search_index_manager = MagicMock()
- # Setup mock return values for checks
cluster.buckets.return_value = bucket_manager
cluster.search_indexes.return_value = search_index_manager
cluster.bucket.return_value = bucket
@@ -113,10 +108,8 @@ def mock_cluster():
scope.collection.return_value = collection
scope.search_indexes.return_value = scope_search_index_manager
- # Mock bucket existence check
bucket_manager.get_bucket.return_value = True
- # Mock scope/collection existence check
mock_scope_spec = MagicMock()
mock_scope_spec.name = "test_scope"
mock_collection_spec = MagicMock()
@@ -124,7 +117,6 @@ def mock_cluster():
mock_scope_spec.collections = [mock_collection_spec]
bucket.collections.return_value.get_all_scopes.return_value = [mock_scope_spec]
- # Mock index existence check
mock_index_def = MagicMock()
mock_index_def.name = "test_index"
scope_search_index_manager.get_all_indexes.return_value = [mock_index_def]
@@ -157,7 +149,6 @@ def tool_config(mock_cluster, mock_embedding_function):
@pytest.fixture
def couchbase_tool(tool_config):
- # Patch COUCHBASE_AVAILABLE to True for these tests
with patch(
"crewai_tools.tools.couchbase_tool.couchbase_tool.COUCHBASE_AVAILABLE", True
):
@@ -177,9 +168,6 @@ def mock_search_iter():
return mock_iter
-# --- Test Cases ---
-
-
def test_initialization_success(couchbase_tool, tool_config):
"""Test successful initialization with valid config."""
assert couchbase_tool.cluster == tool_config["cluster"]
@@ -247,7 +235,6 @@ def test_run_success_scoped_index(
query = "find relevant documents"
# expected_embedding = mock_embedding_function(query)
- # Mock the scope search method
couchbase_tool._scope.search = MagicMock(return_value=mock_search_iter)
# Mock the VectorQuery/VectorSearch/SearchRequest creation using runtime patching
with (
@@ -277,28 +264,21 @@ def test_run_success_scoped_index(
result = couchbase_tool._run(query=query)
- # Check embedding function call
tool_config["embedding_function"].assert_called_once_with(query)
- # Check VectorQuery call
mock_vq.assert_called_once_with(
tool_config["embedding_key"],
mock_embedding_function.return_value,
tool_config["limit"],
)
- # Check VectorSearch call
mock_vs.from_vector_query.assert_called_once_with(mock_vector_query)
- # Check SearchRequest creation
mock_sr.create.assert_called_once_with(mock_vector_search)
- # Check SearchOptions creation
mock_so.assert_called_once_with(limit=tool_config["limit"], fields=["*"])
- # Check that scope search was called correctly
couchbase_tool._scope.search.assert_called_once_with(
tool_config["index_name"], mock_search_req, mock_search_options
)
- # Check cluster search was NOT called
couchbase_tool.cluster.search.assert_not_called()
# Check result format (simple check for JSON structure)
@@ -320,7 +300,6 @@ def test_run_success_global_index(
query = "find global documents"
# expected_embedding = mock_embedding_function(query)
- # Mock the cluster search method
couchbase_tool.cluster.search = MagicMock(return_value=mock_search_iter)
# Mock the VectorQuery/VectorSearch/SearchRequest creation using runtime patching
with (
@@ -350,28 +329,22 @@ def test_run_success_global_index(
result = couchbase_tool._run(query=query)
- # Check embedding function call
tool_config["embedding_function"].assert_called_once_with(query)
- # Check VectorQuery/Search call
mock_vq.assert_called_once_with(
tool_config["embedding_key"],
mock_embedding_function.return_value,
tool_config["limit"],
)
mock_sr.create.assert_called_once_with(mock_vector_search)
- # Check SearchOptions creation
mock_so.assert_called_once_with(limit=tool_config["limit"], fields=["*"])
- # Check that cluster search was called correctly
couchbase_tool.cluster.search.assert_called_once_with(
tool_config["index_name"], mock_search_req, mock_search_options
)
- # Check scope search was NOT called
couchbase_tool._scope.search.assert_not_called()
- # Check result format
assert '"id": "doc1"' in result
assert '"id": "doc2"' in result
diff --git a/lib/crewai-tools/tests/tools/files_compressor_tool_test.py b/lib/crewai-tools/tests/tools/files_compressor_tool_test.py
index 4fb38a13a..9c4a4228f 100644
--- a/lib/crewai-tools/tests/tools/files_compressor_tool_test.py
+++ b/lib/crewai-tools/tests/tools/files_compressor_tool_test.py
@@ -17,7 +17,7 @@ def test_input_path_does_not_exist(mock_exists, tool):
@patch("os.path.exists", return_value=True)
@patch("os.getcwd", return_value="/mocked/cwd")
-@patch.object(FileCompressorTool, "_compress_zip") # Mock actual compression
+@patch.object(FileCompressorTool, "_compress_zip")
@patch.object(FileCompressorTool, "_prepare_output", return_value=True)
def test_generate_output_path_default(
mock_prepare, mock_compress, mock_cwd, mock_exists, tool
diff --git a/lib/crewai-tools/tests/tools/merge_agent_handler_tool_test.py b/lib/crewai-tools/tests/tools/merge_agent_handler_tool_test.py
index 17bdd79fb..5ffc634da 100644
--- a/lib/crewai-tools/tests/tools/merge_agent_handler_tool_test.py
+++ b/lib/crewai-tools/tests/tools/merge_agent_handler_tool_test.py
@@ -409,26 +409,20 @@ def test_tool_parameters_are_passed_in_request(mock_post):
tool_name="linear__update_issue",
)
- # Execute tool with specific parameters
tool._run(id="issue-123", title="New Title", priority=1)
- # Verify the request was made
mock_post.assert_called_once()
- # Get the JSON payload that was sent
payload = mock_post.call_args.kwargs["json"]
- # Verify MCP structure
assert payload["jsonrpc"] == "2.0"
assert payload["method"] == "tools/call"
assert "id" in payload
- # Verify parameters are in the request
assert "params" in payload
assert payload["params"]["name"] == "linear__update_issue"
assert "arguments" in payload["params"]
- # Verify the actual arguments were passed
arguments = payload["params"]["arguments"]
assert arguments["id"] == "issue-123"
assert arguments["title"] == "New Title"
@@ -438,12 +432,9 @@ def test_tool_parameters_are_passed_in_request(mock_post):
@patch("requests.post")
def test_tool_run_method_passes_parameters(mock_post, mock_tool_pack_response):
"""Test that parameters are passed when using the .run() method (how CrewAI calls it)."""
- # Mock the tools/list response
mock_response = Mock()
mock_response.status_code = 200
- # First call: tools/list
- # Second call: tools/call
mock_response.json.side_effect = [
mock_tool_pack_response, # tools/list response
{
@@ -454,7 +445,6 @@ def test_tool_run_method_passes_parameters(mock_post, mock_tool_pack_response):
]
mock_post.return_value = mock_response
- # Create tool using from_tool_name (which fetches schema)
tool = MergeAgentHandlerTool.from_tool_name(
tool_name="linear__create_issue",
tool_pack_id="test-pack-id",
@@ -467,21 +457,17 @@ def test_tool_run_method_passes_parameters(mock_post, mock_tool_pack_response):
# Verify two calls were made: tools/list and tools/call
assert mock_post.call_count == 2
- # Get the second call (tools/call)
second_call = mock_post.call_args_list[1]
payload = second_call.kwargs["json"]
- # Verify it's a tools/call request
assert payload["method"] == "tools/call"
assert payload["params"]["name"] == "linear__create_issue"
- # Verify parameters were passed
arguments = payload["params"]["arguments"]
assert arguments["title"] == "Test Issue"
assert arguments["description"] == "Test description"
assert arguments["priority"] == 2
- # Verify result was returned
assert result["success"] is True
assert result["id"] == "issue-123"
diff --git a/lib/crewai-tools/tests/tools/rag/rag_tool_test.py b/lib/crewai-tools/tests/tools/rag/rag_tool_test.py
index 93896e8b2..69ad26b1e 100644
--- a/lib/crewai-tools/tests/tools/rag/rag_tool_test.py
+++ b/lib/crewai-tools/tests/tools/rag/rag_tool_test.py
@@ -66,7 +66,6 @@ def test_rag_tool_add_and_query(
tool.add("The sky is blue on a clear day.")
tool.add("Machine learning is a subset of artificial intelligence.")
- # Verify documents were added
assert mock_client.add_documents.call_count == 2
result = tool._run(query="What color is the sky?")
diff --git a/lib/crewai-tools/tests/tools/rag/test_rag_tool_add_data_type.py b/lib/crewai-tools/tests/tools/rag/test_rag_tool_add_data_type.py
index d8304ee0f..b1cf60bd9 100644
--- a/lib/crewai-tools/tests/tools/rag/test_rag_tool_add_data_type.py
+++ b/lib/crewai-tools/tests/tools/rag/test_rag_tool_add_data_type.py
@@ -100,7 +100,6 @@ class TestDataTypeStringValues:
) -> None:
"""Test data_type='pdf_file' with existing PDF file."""
with TemporaryDirectory() as tmpdir:
- # Create a minimal valid PDF file
test_file = Path(tmpdir) / "test.pdf"
test_file.write_bytes(
b"%PDF-1.4\n1 0 obj\n<<\n/Type /Catalog\n>>\nendobj\ntrailer\n"
@@ -184,7 +183,6 @@ class TestDataTypeStringValues:
) -> None:
"""Test data_type='directory' with existing directory."""
with TemporaryDirectory() as tmpdir:
- # Create some files in the directory
(Path(tmpdir) / "file1.txt").write_text("Content 1")
(Path(tmpdir) / "file2.txt").write_text("Content 2")
diff --git a/lib/crewai-tools/tests/tools/rag/test_rag_tool_path_validation.py b/lib/crewai-tools/tests/tools/rag/test_rag_tool_path_validation.py
index a58cccde3..9c24bcd72 100644
--- a/lib/crewai-tools/tests/tools/rag/test_rag_tool_path_validation.py
+++ b/lib/crewai-tools/tests/tools/rag/test_rag_tool_path_validation.py
@@ -27,9 +27,7 @@ def tool(mock_rag_client: MagicMock) -> RagTool:
return RagTool()
-# ---------------------------------------------------------------------------
# Positional arg validation (existing behaviour, regression guard)
-# ---------------------------------------------------------------------------
class TestPositionalArgValidation:
def test_blocks_traversal_in_positional_arg(self, tool):
@@ -41,10 +39,6 @@ class TestPositionalArgValidation:
tool.add("file:///etc/passwd")
-# ---------------------------------------------------------------------------
-# Keyword arg validation (the newly fixed gap)
-# ---------------------------------------------------------------------------
-
class TestKwargPathValidation:
def test_blocks_traversal_via_path_kwarg(self, tool):
with pytest.raises(ValueError, match="Blocked unsafe path"):
diff --git a/lib/crewai-tools/tests/tools/singlestore_search_tool_test.py b/lib/crewai-tools/tests/tools/singlestore_search_tool_test.py
index 18b1584db..ca301f187 100644
--- a/lib/crewai-tools/tests/tools/singlestore_search_tool_test.py
+++ b/lib/crewai-tools/tests/tools/singlestore_search_tool_test.py
@@ -38,7 +38,6 @@ def clean_db_url(docker_server_url) -> Generator[str, None, None]:
curr.close()
conn.close()
except Exception:
- # Ignore cleanup errors
pass
@@ -48,7 +47,6 @@ def sample_table_setup(clean_db_url):
conn = connect(host=clean_db_url, database="test_crewai")
curr = conn.cursor()
- # Create sample tables
curr.execute(
"""
CREATE TABLE employees (
@@ -98,7 +96,6 @@ class TestSingleStoreSearchTool:
def test_tool_creation_with_connection_params(self, sample_table_setup):
"""Test tool creation with individual connection parameters."""
- # Parse URL components for individual parameters
url_parts = sample_table_setup.split("@")[1].split(":")
host = url_parts[0]
port = int(url_parts[1].split("/")[0])
@@ -141,7 +138,6 @@ class TestSingleStoreSearchTool:
database="test_crewai",
)
- # Check that description includes specific tables
assert "employees" in tool.description
assert "departments" not in tool.description
@@ -166,7 +162,6 @@ class TestSingleStoreSearchTool:
tool = SingleStoreSearchTool(host=sample_table_setup, database="test_crewai")
- # Check description contains table definitions
assert "employees(" in tool.description
assert "departments(" in tool.description
assert "id int" in tool.description.lower()
@@ -300,7 +295,6 @@ class TestSingleStoreSearchTool:
pool_size=2,
)
- # Execute multiple queries to test pool usage
results = []
for _ in range(5):
result = tool._run("SELECT COUNT(*) FROM employees")
@@ -317,7 +311,6 @@ class TestSingleStoreSearchTool:
valid_input = SingleStoreSearchToolSchema(search_query="SELECT * FROM test")
assert valid_input.search_query == "SELECT * FROM test"
- # Test that description is present
schema_dict = SingleStoreSearchToolSchema.model_json_schema()
assert "search_query" in schema_dict["properties"]
assert "description" in schema_dict["properties"]["search_query"]
diff --git a/lib/crewai-tools/tests/tools/snowflake_search_tool_test.py b/lib/crewai-tools/tests/tools/snowflake_search_tool_test.py
index fe827d5df..873a8f941 100644
--- a/lib/crewai-tools/tests/tools/snowflake_search_tool_test.py
+++ b/lib/crewai-tools/tests/tools/snowflake_search_tool_test.py
@@ -57,7 +57,6 @@ async def test_connection_pooling(snowflake_tool, mock_snowflake_connection):
with patch.object(snowflake_tool, "_create_connection") as mock_create_conn:
mock_create_conn.return_value = mock_snowflake_connection
- # Execute multiple queries
await asyncio.gather(
snowflake_tool._run("SELECT 1"),
snowflake_tool._run("SELECT 2"),
@@ -73,10 +72,8 @@ async def test_cleanup_on_deletion(snowflake_tool, mock_snowflake_connection):
with patch.object(snowflake_tool, "_create_connection") as mock_create_conn:
mock_create_conn.return_value = mock_snowflake_connection
- # Add connection to pool
await snowflake_tool._get_connection()
- # Return connection to pool
async with snowflake_tool._pool_lock:
snowflake_tool._connection_pool.append(mock_snowflake_connection)
@@ -91,12 +88,10 @@ def test_config_validation():
with pytest.raises(ValueError):
SnowflakeConfig()
- # Test invalid account format
with pytest.raises(ValueError):
SnowflakeConfig(
account="invalid//account", user="test_user", password="test_pass"
)
- # Test missing authentication
with pytest.raises(ValueError):
SnowflakeConfig(account="test_account", user="test_user")
diff --git a/lib/crewai-tools/tests/tools/stagehand_tool_test.py b/lib/crewai-tools/tests/tools/stagehand_tool_test.py
index bed0cd311..508b1357e 100644
--- a/lib/crewai-tools/tests/tools/stagehand_tool_test.py
+++ b/lib/crewai-tools/tests/tools/stagehand_tool_test.py
@@ -28,13 +28,11 @@ class MockStagehandUtils:
@pytest.fixture(scope="module", autouse=True)
def mock_stagehand_modules():
"""Mock stagehand modules at the start of this test module."""
- # Store original modules if they exist
original_modules = {}
for module_name in ["stagehand", "stagehand.schemas", "stagehand.utils"]:
if module_name in sys.modules:
original_modules[module_name] = sys.modules[module_name]
- # Create and inject mock modules
mock_stagehand = MockStagehandModule()
mock_stagehand_schemas = MockStagehandSchemas()
mock_stagehand_utils = MockStagehandUtils()
@@ -43,7 +41,6 @@ def mock_stagehand_modules():
sys.modules["stagehand.schemas"] = mock_stagehand_schemas
sys.modules["stagehand.utils"] = mock_stagehand_utils
- # Import after mocking
from crewai_tools.tools.stagehand_tool.stagehand_tool import (
StagehandResult,
StagehandTool,
@@ -142,10 +139,8 @@ def test_stagehand_tool_initialization():
)
def test_act_command(mock_run, stagehand_tool):
"""Test the 'act' command functionality."""
- # Setup mock
mock_run.return_value = "Action result: Action completed successfully"
- # Run the tool
result = stagehand_tool._run(
instruction="Click the submit button", command_type="act"
)
@@ -160,10 +155,8 @@ def test_act_command(mock_run, stagehand_tool):
)
def test_navigate_command(mock_run, stagehand_tool):
"""Test the 'navigate' command functionality."""
- # Setup mock
mock_run.return_value = "Successfully navigated to https://example.com"
- # Run the tool
result = stagehand_tool._run(
instruction="Go to example.com",
url="https://example.com",
@@ -179,12 +172,10 @@ def test_navigate_command(mock_run, stagehand_tool):
)
def test_extract_command(mock_run, stagehand_tool):
"""Test the 'extract' command functionality."""
- # Setup mock
mock_run.return_value = (
'Extracted data: {"data": "Extracted content", "metadata": {"source": "test"}}'
)
- # Run the tool
result = stagehand_tool._run(
instruction="Extract all product names and prices", command_type="extract"
)
@@ -199,10 +190,8 @@ def test_extract_command(mock_run, stagehand_tool):
)
def test_observe_command(mock_run, stagehand_tool):
"""Test the 'observe' command functionality."""
- # Setup mock
mock_run.return_value = "Element 1: Button element\nSuggested action: click\nElement 2: Input field\nSuggested action: type"
- # Run the tool
result = stagehand_tool._run(
instruction="Find all interactive elements", command_type="observe"
)
@@ -219,10 +208,8 @@ def test_observe_command(mock_run, stagehand_tool):
)
def test_error_handling(mock_run, stagehand_tool):
"""Test error handling in the tool."""
- # Setup mock
mock_run.return_value = "Error: Browser automation error"
- # Run the tool
result = stagehand_tool._run(
instruction="Click a non-existent button", command_type="act"
)
@@ -234,7 +221,6 @@ def test_error_handling(mock_run, stagehand_tool):
def test_initialization_parameters():
"""Test that the StagehandTool initializes with the correct parameters."""
- # Create tool with custom parameters
tool = StagehandTool(
api_key="custom_api_key",
project_id="custom_project_id",
@@ -260,7 +246,6 @@ def test_initialization_parameters():
def test_close_method():
"""Test that the close method cleans up resources correctly."""
- # Create the tool with testing mode
tool = StagehandTool(
api_key="test_api_key",
project_id="test_project_id",
@@ -268,14 +253,11 @@ def test_close_method():
_testing=True,
)
- # Setup mock stagehand instance
tool._stagehand = MagicMock()
tool._stagehand.close = MagicMock() # Non-async mock
tool._page = MagicMock()
- # Call the close method
tool.close()
- # Verify resources were cleaned up
assert tool._stagehand is None
assert tool._page is None
diff --git a/lib/crewai-tools/tests/tools/test_file_writer_tool.py b/lib/crewai-tools/tests/tools/test_file_writer_tool.py
index eb816ee38..88dae9cf8 100644
--- a/lib/crewai-tools/tests/tools/test_file_writer_tool.py
+++ b/lib/crewai-tools/tests/tools/test_file_writer_tool.py
@@ -137,7 +137,6 @@ def test_file_exists_error_handling(tool, temp_env, overwrite):
assert read_file(path) == "Pre-existing content"
-# --- Path traversal prevention ---
def test_blocks_traversal_in_filename(tool, temp_env):
# Create a sibling "outside" directory so we can assert nothing was written there.
diff --git a/lib/crewai-tools/tests/tools/test_mongodb_vector_search_tool.py b/lib/crewai-tools/tests/tools/test_mongodb_vector_search_tool.py
index d5f27249e..3bede48a3 100644
--- a/lib/crewai-tools/tests/tools/test_mongodb_vector_search_tool.py
+++ b/lib/crewai-tools/tests/tools/test_mongodb_vector_search_tool.py
@@ -5,7 +5,6 @@ from crewai_tools import MongoDBVectorSearchConfig, MongoDBVectorSearchTool
import pytest
-# Unit Test Fixtures
@pytest.fixture
def mongodb_vector_search_tool():
tool = MongoDBVectorSearchTool(
@@ -15,9 +14,7 @@ def mongodb_vector_search_tool():
yield tool
-# Unit Tests
def test_successful_query_execution(mongodb_vector_search_tool):
- # Enable embedding
with patch.object(mongodb_vector_search_tool._coll, "aggregate") as mock_aggregate:
mock_aggregate.return_value = [dict(text="foo", score=0.1, _id=1)]
@@ -50,7 +47,6 @@ def test_provide_config():
def test_cleanup_on_deletion(mongodb_vector_search_tool):
with patch.object(mongodb_vector_search_tool, "_client") as mock_client:
- # Trigger cleanup
mongodb_vector_search_tool.__del__()
mock_client.close.assert_called_once()
diff --git a/lib/crewai-tools/tests/tools/test_nl2sql_security.py b/lib/crewai-tools/tests/tools/test_nl2sql_security.py
index abef973ff..aedfad281 100644
--- a/lib/crewai-tools/tests/tools/test_nl2sql_security.py
+++ b/lib/crewai-tools/tests/tools/test_nl2sql_security.py
@@ -16,9 +16,6 @@ from sqlalchemy import create_engine, text # noqa: E402
from crewai_tools.tools.nl2sql.nl2sql_tool import NL2SQLTool # noqa: E402
-# ---------------------------------------------------------------------------
-# Helpers
-# ---------------------------------------------------------------------------
SQLITE_URI = "sqlite://" # in-memory
@@ -36,11 +33,6 @@ def _make_tool(allow_dml: bool = False, **kwargs) -> NL2SQLTool:
return NL2SQLTool(db_uri=SQLITE_URI, allow_dml=allow_dml, **kwargs)
-# ---------------------------------------------------------------------------
-# Read-only enforcement (allow_dml=False)
-# ---------------------------------------------------------------------------
-
-
class TestReadOnlyMode:
def test_select_allowed_by_default(self):
tool = _make_tool()
@@ -88,11 +80,6 @@ class TestReadOnlyMode:
tool._validate_query("DESCRIBE users")
-# ---------------------------------------------------------------------------
-# DML enabled (allow_dml=True)
-# ---------------------------------------------------------------------------
-
-
class TestDMLEnabled:
def test_insert_allowed_when_dml_enabled(self):
tool = _make_tool(allow_dml=True)
@@ -132,9 +119,7 @@ class TestDMLEnabled:
os.unlink(db_path)
-# ---------------------------------------------------------------------------
# Parameterised query — SQL injection prevention
-# ---------------------------------------------------------------------------
class TestParameterisedQueries:
@@ -178,11 +163,6 @@ class TestParameterisedQueries:
assert captured["params"]["table_name"] == injection
-# ---------------------------------------------------------------------------
-# session.commit() not called for read-only queries
-# ---------------------------------------------------------------------------
-
-
class TestNoCommitForReadOnly:
def test_select_does_not_commit(self):
tool = _make_tool(allow_dml=False)
@@ -229,11 +209,6 @@ class TestNoCommitForReadOnly:
mock_session.commit.assert_called_once()
-# ---------------------------------------------------------------------------
-# Environment-variable escape hatch
-# ---------------------------------------------------------------------------
-
-
class TestEnvVarEscapeHatch:
def test_env_var_enables_dml(self):
with patch.dict(os.environ, {"CREWAI_NL2SQL_ALLOW_DML": "true"}):
@@ -264,11 +239,6 @@ class TestEnvVarEscapeHatch:
tool._validate_query("DROP TABLE sensitive_data")
-# ---------------------------------------------------------------------------
-# _run() propagates ValueError from _validate_query
-# ---------------------------------------------------------------------------
-
-
class TestRunValidation:
def test_run_raises_on_blocked_query(self):
tool = _make_tool(allow_dml=False)
@@ -281,9 +251,7 @@ class TestRunValidation:
assert result == [{"n": 1}]
-# ---------------------------------------------------------------------------
# Multi-statement / semicolon injection prevention
-# ---------------------------------------------------------------------------
class TestSemicolonInjection:
@@ -318,11 +286,6 @@ class TestSemicolonInjection:
tool._validate_query("DROP TABLE users")
-# ---------------------------------------------------------------------------
-# Writable CTEs (WITH … DELETE/INSERT/UPDATE)
-# ---------------------------------------------------------------------------
-
-
class TestWritableCTE:
def test_writable_cte_delete_blocked_in_read_only(self):
"""WITH d AS (DELETE FROM users RETURNING *) SELECT * FROM d — blocked."""
@@ -374,11 +337,6 @@ class TestWritableCTE:
)
-# ---------------------------------------------------------------------------
-# EXPLAIN ANALYZE executes the underlying query
-# ---------------------------------------------------------------------------
-
-
def test_cte_with_write_main_query_blocked(self):
"""WITH cte AS (SELECT 1) DELETE FROM users — main query must be caught."""
tool = _make_tool(allow_dml=False)
@@ -460,11 +418,6 @@ class TestExplainAnalyze:
tool._validate_query("EXPLAIN (VERBOSE) SELECT * FROM users")
-# ---------------------------------------------------------------------------
-# Multi-statement commit covers ALL statements (not just the first)
-# ---------------------------------------------------------------------------
-
-
class TestMultiStatementCommit:
def test_select_then_insert_triggers_commit(self):
"""SELECT 1; INSERT … — commit must happen because INSERT is a write."""
@@ -533,11 +486,6 @@ class TestMultiStatementCommit:
mock_session.commit.assert_called_once()
-# ---------------------------------------------------------------------------
-# Extended _WRITE_COMMANDS coverage
-# ---------------------------------------------------------------------------
-
-
class TestExtendedWriteCommands:
@pytest.mark.parametrize(
"stmt",
@@ -562,11 +510,6 @@ class TestExtendedWriteCommands:
tool._validate_query(stmt)
-# ---------------------------------------------------------------------------
-# EXPLAIN ANALYZE VERBOSE handling
-# ---------------------------------------------------------------------------
-
-
class TestExplainAnalyzeVerbose:
def test_explain_analyze_verbose_select_allowed(self):
"""EXPLAIN ANALYZE VERBOSE SELECT should be allowed (read-only)."""
@@ -585,11 +528,6 @@ class TestExplainAnalyzeVerbose:
tool._validate_query("EXPLAIN VERBOSE SELECT * FROM users")
-# ---------------------------------------------------------------------------
-# CTE with string literal parens
-# ---------------------------------------------------------------------------
-
-
class TestCTEStringLiteralParens:
def test_cte_string_paren_does_not_bypass(self):
"""Parens inside string literals should not confuse the paren walker."""
@@ -607,11 +545,6 @@ class TestCTEStringLiteralParens:
)
-# ---------------------------------------------------------------------------
-# EXPLAIN ANALYZE commit logic
-# ---------------------------------------------------------------------------
-
-
class TestExplainAnalyzeCommit:
def test_explain_analyze_delete_triggers_commit(self):
"""EXPLAIN ANALYZE DELETE should trigger commit when allow_dml=True."""
@@ -636,9 +569,7 @@ class TestExplainAnalyzeCommit:
mock_session.commit.assert_called_once()
-# ---------------------------------------------------------------------------
# AS( inside string literals must not confuse CTE detection
-# ---------------------------------------------------------------------------
class TestCTEStringLiteralAS:
@@ -658,9 +589,7 @@ class TestCTEStringLiteralAS:
)
-# ---------------------------------------------------------------------------
# Unknown command after CTE should be blocked
-# ---------------------------------------------------------------------------
class TestCTEUnknownCommand:
diff --git a/lib/crewai-tools/tests/tools/test_oxylabs_tools.py b/lib/crewai-tools/tests/tools/test_oxylabs_tools.py
index 2b0bef76f..f135e421c 100644
--- a/lib/crewai-tools/tests/tools/test_oxylabs_tools.py
+++ b/lib/crewai-tools/tests/tools/test_oxylabs_tools.py
@@ -113,7 +113,6 @@ def test_tool_initialization_with_env_vars(tool_class: type[BaseTool]):
],
)
def test_tool_initialization_failure(tool_class: type[BaseTool]):
- # making sure env vars are not set
for key in ["OXYLABS_USERNAME", "OXYLABS_PASSWORD"]:
if key in os.environ:
del os.environ[key]
@@ -150,12 +149,10 @@ def test_tool_invocation(
# setting via __dict__ to bypass pydantic validation
tool.__dict__["oxylabs_api"] = oxylabs_api
- # verifying parsed job returns json content
result = tool.run("Scraping Query 1")
assert isinstance(result, str)
assert isinstance(json.loads(result), dict)
- # verifying raw job returns str
result = tool.run("Scraping Query 2")
assert isinstance(result, str)
assert "" in result
diff --git a/lib/crewai-tools/tests/utilities/test_safe_path.py b/lib/crewai-tools/tests/utilities/test_safe_path.py
index 4fb5d1ec7..48fcc9f38 100644
--- a/lib/crewai-tools/tests/utilities/test_safe_path.py
+++ b/lib/crewai-tools/tests/utilities/test_safe_path.py
@@ -13,10 +13,6 @@ from crewai_tools.security.safe_path import (
)
-# ---------------------------------------------------------------------------
-# File path validation
-# ---------------------------------------------------------------------------
-
class TestValidateFilePath:
"""Tests for validate_file_path."""
@@ -52,7 +48,6 @@ class TestValidateFilePath:
def test_rejects_symlink_escape(self, tmp_path):
"""Reject symlinks that point outside base_dir."""
link = tmp_path / "sneaky_link"
- # Create a symlink pointing to /etc/passwd
os.symlink("/etc/passwd", str(link))
with pytest.raises(ValueError, match="outside the allowed directory"):
validate_file_path("sneaky_link", str(tmp_path))
@@ -90,10 +85,6 @@ class TestValidateDirectoryPath:
validate_directory_path("../../", str(tmp_path))
-# ---------------------------------------------------------------------------
-# URL validation
-# ---------------------------------------------------------------------------
-
class TestValidateUrl:
"""Tests for validate_url."""
diff --git a/lib/devtools/src/crewai_devtools/docs_check.py b/lib/devtools/src/crewai_devtools/docs_check.py
index 8a4432fb5..a58f2c5c8 100644
--- a/lib/devtools/src/crewai_devtools/docs_check.py
+++ b/lib/devtools/src/crewai_devtools/docs_check.py
@@ -34,9 +34,6 @@ _LANGUAGE_NAMES: Final[dict[DocLang, str]] = {
}
-# --- Structured output models ---
-
-
class DocAction(BaseModel):
"""A single documentation action to take."""
@@ -66,8 +63,6 @@ class DocsAnalysis(BaseModel):
)
-# --- Prompts ---
-
_ANALYZE_SYSTEM: Final[str] = """\
You are a documentation analyst for the CrewAI open-source framework.
diff --git a/lib/devtools/tests/test_toml_updates.py b/lib/devtools/tests/test_toml_updates.py
index a13eb0f96..80b18648d 100644
--- a/lib/devtools/tests/test_toml_updates.py
+++ b/lib/devtools/tests/test_toml_updates.py
@@ -13,9 +13,6 @@ from crewai_devtools.cli import (
)
-# --- update_pyproject_version ---
-
-
class TestUpdatePyprojectVersion:
def test_updates_version(self, tmp_path: Path) -> None:
pyproject = tmp_path / "pyproject.toml"
@@ -82,9 +79,6 @@ class TestUpdatePyprojectVersion:
assert 'description = "A package"' in result
-# --- _pin_crewai_deps ---
-
-
class TestPinCrewaiDeps:
def test_pins_exact_version(self) -> None:
content = dedent("""\
@@ -195,9 +189,6 @@ class TestPinCrewaiDeps:
assert "==" not in result
-# --- _repin_crewai_install ---
-
-
class TestRepinCrewaiInstall:
def test_repins_a2a_extra(self) -> None:
result = _repin_crewai_install('uv pip install "crewai[a2a]==1.14.0"', "2.0.0")
@@ -228,9 +219,6 @@ class TestRepinCrewaiInstall:
assert _repin_crewai_install(cmd, "2.0.0") == cmd
-# --- update_pyproject_dependencies ---
-
-
class TestUpdatePyprojectDependencies:
def test_default_packages_cover_all_workspace_members(self) -> None:
"""Every workspace member must be in the default rewrite list.
@@ -320,9 +308,6 @@ class TestUpdatePyprojectDependencies:
assert '"crewai-core==2.0.0"' in result
-# --- update_template_dependencies ---
-
-
class TestUpdateTemplateDependencies:
def test_updates_jinja_template(self, tmp_path: Path) -> None:
"""Template pyproject.toml files with Jinja placeholders should not break."""