mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-03-28 06:38:19 +00:00
Compare commits
2 Commits
devin/1774
...
1.13.0a2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b2062009a | ||
|
|
886aa4ba8f |
@@ -4,6 +4,26 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="27 مارس 2026">
|
||||
## v1.13.0a2
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a2)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- تحديث تلقائي لمستودع اختبار النشر أثناء الإصدار
|
||||
- تحسين مرونة إصدار المؤسسات وتجربة المستخدم
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار للإصدار v1.13.0a1
|
||||
|
||||
## المساهمون
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="27 مارس 2026">
|
||||
## v1.13.0a1
|
||||
|
||||
|
||||
@@ -4,6 +4,26 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="Mar 27, 2026">
|
||||
## v1.13.0a2
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a2)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Auto-update deployment test repo during release
|
||||
- Improve enterprise release resilience and UX
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.13.0a1
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Mar 27, 2026">
|
||||
## v1.13.0a1
|
||||
|
||||
|
||||
@@ -4,6 +4,26 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 3월 27일">
|
||||
## v1.13.0a2
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a2)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- 릴리스 중 자동 업데이트 배포 테스트 리포지토리
|
||||
- 기업 릴리스의 복원력 및 사용자 경험 개선
|
||||
|
||||
### 문서
|
||||
- v1.13.0a1에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 3월 27일">
|
||||
## v1.13.0a1
|
||||
|
||||
|
||||
@@ -4,6 +4,26 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="27 mar 2026">
|
||||
## v1.13.0a2
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a2)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Repositório de teste de implantação de autoatualização durante o lançamento
|
||||
- Melhorar a resiliência e a experiência do usuário na versão empresarial
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.13.0a1
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@greysonlalonde
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="27 mar 2026">
|
||||
## v1.13.0a1
|
||||
|
||||
|
||||
@@ -152,4 +152,4 @@ __all__ = [
|
||||
"wrap_file_source",
|
||||
]
|
||||
|
||||
__version__ = "1.13.0a1"
|
||||
__version__ = "1.13.0a2"
|
||||
|
||||
@@ -11,7 +11,7 @@ dependencies = [
|
||||
"pytube~=15.0.0",
|
||||
"requests~=2.32.5",
|
||||
"docker~=7.1.0",
|
||||
"crewai==1.13.0a1",
|
||||
"crewai==1.13.0a2",
|
||||
"tiktoken~=0.8.0",
|
||||
"beautifulsoup4~=4.13.4",
|
||||
"python-docx~=1.2.0",
|
||||
|
||||
@@ -309,4 +309,4 @@ __all__ = [
|
||||
"ZapierActionTools",
|
||||
]
|
||||
|
||||
__version__ = "1.13.0a1"
|
||||
__version__ = "1.13.0a2"
|
||||
|
||||
@@ -25,7 +25,7 @@ class InvokeCrewAIAutomationTool(BaseTool):
|
||||
Basic usage:
|
||||
>>> tool = InvokeCrewAIAutomationTool(
|
||||
... crew_api_url="https://api.example.com",
|
||||
... crew_bearer_token=os.environ["CREWAI_BEARER_TOKEN"],
|
||||
... crew_bearer_token="your_token",
|
||||
... crew_name="My Crew",
|
||||
... crew_description="Description of what the crew does",
|
||||
... )
|
||||
@@ -39,7 +39,7 @@ class InvokeCrewAIAutomationTool(BaseTool):
|
||||
... }
|
||||
>>> tool = InvokeCrewAIAutomationTool(
|
||||
... crew_api_url="https://api.example.com",
|
||||
... crew_bearer_token=os.environ["CREWAI_BEARER_TOKEN"],
|
||||
... crew_bearer_token="your_token",
|
||||
... crew_name="My Crew",
|
||||
... crew_description="Description of what the crew does",
|
||||
... crew_inputs=custom_inputs,
|
||||
@@ -49,7 +49,7 @@ class InvokeCrewAIAutomationTool(BaseTool):
|
||||
>>> tools = [
|
||||
... InvokeCrewAIAutomationTool(
|
||||
... crew_api_url="https://canary-crew-[...].crewai.com",
|
||||
... crew_bearer_token=os.environ["CREWAI_BEARER_TOKEN"],
|
||||
... crew_bearer_token="[Your token: abcdef012345]",
|
||||
... crew_name="State of AI Report",
|
||||
... crew_description="Retrieves a report on state of AI for a given year.",
|
||||
... crew_inputs={
|
||||
|
||||
@@ -4,10 +4,9 @@ from crewai import Agent, Crew, Task
|
||||
from multion_tool import MultiOnTool # type: ignore[import-not-found]
|
||||
|
||||
|
||||
if not os.environ.get("OPENAI_API_KEY"):
|
||||
raise ValueError("Please set the OPENAI_API_KEY environment variable")
|
||||
os.environ["OPENAI_API_KEY"] = "Your Key"
|
||||
|
||||
multion_browse_tool = MultiOnTool(api_key=os.environ.get("MULTION_API_KEY", ""))
|
||||
multion_browse_tool = MultiOnTool(api_key="Your Key")
|
||||
|
||||
# Create a new agent
|
||||
Browser = Agent(
|
||||
|
||||
@@ -10317,7 +10317,7 @@
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"description": "A CrewAI tool for invoking external crew/flows APIs.\n\nThis tool provides CrewAI Platform API integration with external crew services, supporting:\n- Dynamic input schema configuration\n- Automatic polling for task completion\n- Bearer token authentication\n- Comprehensive error handling\n\nExample:\n Basic usage:\n >>> tool = InvokeCrewAIAutomationTool(\n ... crew_api_url=\"https://api.example.com\",\n ... crew_bearer_token=os.environ[\"CREWAI_BEARER_TOKEN\"],\n ... crew_name=\"My Crew\",\n ... crew_description=\"Description of what the crew does\",\n ... )\n\n With custom inputs:\n >>> custom_inputs = {\n ... \"param1\": Field(..., description=\"Description of param1\"),\n ... \"param2\": Field(\n ... default=\"default_value\", description=\"Description of param2\"\n ... ),\n ... }\n >>> tool = InvokeCrewAIAutomationTool(\n ... crew_api_url=\"https://api.example.com\",\n ... crew_bearer_token=os.environ[\"CREWAI_BEARER_TOKEN\"],\n ... crew_name=\"My Crew\",\n ... crew_description=\"Description of what the crew does\",\n ... crew_inputs=custom_inputs,\n ... )\n\nExample:\n >>> tools = [\n ... InvokeCrewAIAutomationTool(\n ... crew_api_url=\"https://canary-crew-[...].crewai.com\",\n ... crew_bearer_token=os.environ[\"CREWAI_BEARER_TOKEN\"],\n ... crew_name=\"State of AI Report\",\n ... crew_description=\"Retrieves a report on state of AI for a given year.\",\n ... crew_inputs={\n ... \"year\": Field(\n ... ..., description=\"Year to retrieve the report for (integer)\"\n ... )\n ... },\n ... )\n ... ]",
|
||||
"description": "A CrewAI tool for invoking external crew/flows APIs.\n\nThis tool provides CrewAI Platform API integration with external crew services, supporting:\n- Dynamic input schema configuration\n- Automatic polling for task completion\n- Bearer token authentication\n- Comprehensive error handling\n\nExample:\n Basic usage:\n >>> tool = InvokeCrewAIAutomationTool(\n ... crew_api_url=\"https://api.example.com\",\n ... crew_bearer_token=\"your_token\",\n ... crew_name=\"My Crew\",\n ... crew_description=\"Description of what the crew does\",\n ... )\n\n With custom inputs:\n >>> custom_inputs = {\n ... \"param1\": Field(..., description=\"Description of param1\"),\n ... \"param2\": Field(\n ... default=\"default_value\", description=\"Description of param2\"\n ... ),\n ... }\n >>> tool = InvokeCrewAIAutomationTool(\n ... crew_api_url=\"https://api.example.com\",\n ... crew_bearer_token=\"your_token\",\n ... crew_name=\"My Crew\",\n ... crew_description=\"Description of what the crew does\",\n ... crew_inputs=custom_inputs,\n ... )\n\nExample:\n >>> tools = [\n ... InvokeCrewAIAutomationTool(\n ... crew_api_url=\"https://canary-crew-[...].crewai.com\",\n ... crew_bearer_token=\"[Your token: abcdef012345]\",\n ... crew_name=\"State of AI Report\",\n ... crew_description=\"Retrieves a report on state of AI for a given year.\",\n ... crew_inputs={\n ... \"year\": Field(\n ... ..., description=\"Year to retrieve the report for (integer)\"\n ... )\n ... },\n ... )\n ... ]",
|
||||
"properties": {
|
||||
"crew_api_url": {
|
||||
"title": "Crew Api Url",
|
||||
|
||||
@@ -54,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tools = [
|
||||
"crewai-tools==1.13.0a1",
|
||||
"crewai-tools==1.13.0a2",
|
||||
]
|
||||
embeddings = [
|
||||
"tiktoken~=0.8.0"
|
||||
|
||||
@@ -42,7 +42,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
|
||||
|
||||
_suppress_pydantic_deprecation_warnings()
|
||||
|
||||
__version__ = "1.13.0a1"
|
||||
__version__ = "1.13.0a2"
|
||||
_telemetry_submitted = False
|
||||
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ def create_flow(name: str) -> None:
|
||||
(project_root / "src" / folder_name / "tools").mkdir(parents=True)
|
||||
(project_root / "tests").mkdir(exist_ok=True)
|
||||
|
||||
# Create .env file with placeholder
|
||||
# Create .env file
|
||||
with open(project_root / ".env", "w") as file:
|
||||
file.write("OPENAI_API_KEY=\n")
|
||||
file.write("OPENAI_API_KEY=YOUR_API_KEY")
|
||||
|
||||
package_dir = Path(__file__).parent
|
||||
templates_dir = package_dir / "templates" / "flow"
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.13.0a1"
|
||||
"crewai[tools]==1.13.0a2"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
|
||||
authors = [{ name = "Your Name", email = "you@example.com" }]
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.13.0a1"
|
||||
"crewai[tools]==1.13.0a2"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<3.14"
|
||||
dependencies = [
|
||||
"crewai[tools]==1.13.0a1"
|
||||
"crewai[tools]==1.13.0a2"
|
||||
]
|
||||
|
||||
[tool.crewai]
|
||||
|
||||
@@ -59,21 +59,21 @@ OPENAI_COMPATIBLE_PROVIDERS: dict[str, ProviderConfig] = {
|
||||
api_key_env="OLLAMA_API_KEY",
|
||||
base_url_env="OLLAMA_HOST",
|
||||
api_key_required=False,
|
||||
default_api_key=os.getenv("OLLAMA_DEFAULT_API_KEY", "ollama"),
|
||||
default_api_key="ollama",
|
||||
),
|
||||
"ollama_chat": ProviderConfig(
|
||||
base_url="http://localhost:11434/v1",
|
||||
api_key_env="OLLAMA_API_KEY",
|
||||
base_url_env="OLLAMA_HOST",
|
||||
api_key_required=False,
|
||||
default_api_key=os.getenv("OLLAMA_DEFAULT_API_KEY", "ollama"),
|
||||
default_api_key="ollama",
|
||||
),
|
||||
"hosted_vllm": ProviderConfig(
|
||||
base_url="http://localhost:8000/v1",
|
||||
api_key_env="VLLM_API_KEY",
|
||||
base_url_env="VLLM_BASE_URL",
|
||||
api_key_required=False,
|
||||
default_api_key=os.getenv("VLLM_DEFAULT_API_KEY", "no-key-required"),
|
||||
default_api_key="dummy",
|
||||
),
|
||||
"cerebras": ProviderConfig(
|
||||
base_url="https://api.cerebras.ai/v1",
|
||||
|
||||
@@ -363,11 +363,11 @@ def build_embedder(spec): # type: ignore[no-untyped-def]
|
||||
# From dictionary specification
|
||||
embedder = build_embedder({
|
||||
"provider": "openai",
|
||||
"config": {"api_key": os.environ["OPENAI_API_KEY"]}
|
||||
"config": {"api_key": "sk-..."}
|
||||
})
|
||||
|
||||
# From provider instance
|
||||
provider = OpenAIProvider(api_key=os.environ["OPENAI_API_KEY"])
|
||||
provider = OpenAIProvider(api_key="sk-...")
|
||||
embedder = build_embedder(provider)
|
||||
"""
|
||||
if isinstance(spec, BaseEmbeddingsProvider):
|
||||
|
||||
@@ -45,9 +45,9 @@ class GoogleGenAIVertexEmbeddingFunction(EmbeddingFunction[Documents]):
|
||||
model_name="gemini-embedding-001"
|
||||
)
|
||||
|
||||
# Using API key from environment variable (new SDK only)
|
||||
# Using API key (new SDK only)
|
||||
embedder = GoogleGenAIVertexEmbeddingFunction(
|
||||
api_key=os.environ["GOOGLE_API_KEY"],
|
||||
api_key="your-api-key",
|
||||
model_name="gemini-embedding-001"
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -49,9 +49,9 @@ class VertexAIProvider(BaseEmbeddingsProvider[GoogleGenAIVertexEmbeddingFunction
|
||||
model_name="gemini-embedding-001"
|
||||
)
|
||||
|
||||
# New model with API key (from environment variable)
|
||||
# New model with API key
|
||||
provider = VertexAIProvider(
|
||||
api_key=os.environ["GOOGLE_API_KEY"],
|
||||
api_key="your-api-key",
|
||||
model_name="gemini-embedding-001"
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -79,7 +79,7 @@ class TestProviderRegistry:
|
||||
assert config.base_url == "http://localhost:8000/v1"
|
||||
assert config.api_key_env == "VLLM_API_KEY"
|
||||
assert config.api_key_required is False
|
||||
assert config.default_api_key == "no-key-required"
|
||||
assert config.default_api_key == "dummy"
|
||||
|
||||
def test_cerebras_config(self):
|
||||
"""Test Cerebras provider configuration."""
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
"""Tests to detect and prevent hardcoded secrets in the codebase.
|
||||
|
||||
These tests scan source files for patterns that look like hardcoded secrets
|
||||
(API keys, tokens, passwords) to prevent accidental credential leaks.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from crewai.cli.create_flow import create_flow
|
||||
from crewai.llms.providers.openai_compatible.completion import (
|
||||
OPENAI_COMPATIBLE_PROVIDERS,
|
||||
)
|
||||
|
||||
# Root of the workspace
|
||||
WORKSPACE_ROOT = Path(__file__).resolve().parents[4]
|
||||
CREWAI_SRC = WORKSPACE_ROOT / "lib" / "crewai" / "src"
|
||||
CREWAI_TOOLS_SRC = WORKSPACE_ROOT / "lib" / "crewai-tools" / "src"
|
||||
|
||||
# Patterns that indicate hardcoded secrets in source code (not docs/tests)
|
||||
SECRET_PATTERNS = [
|
||||
# Actual API key formats
|
||||
re.compile(r'''["']sk-proj-[a-zA-Z0-9_-]{20,}["']'''),
|
||||
re.compile(r'''["']sk-ant-api[a-zA-Z0-9_-]{20,}["']'''),
|
||||
re.compile(r'''["']ghp_[a-zA-Z0-9]{36}["']'''),
|
||||
re.compile(r'''["']gho_[a-zA-Z0-9]{36}["']'''),
|
||||
re.compile(r'''["']xox[bpas]-[a-zA-Z0-9-]{10,}["']'''),
|
||||
re.compile(r'''["']AKIA[A-Z0-9]{16}["']'''),
|
||||
# os.environ assignment with hardcoded non-empty value
|
||||
re.compile(r'''os\.environ\[["'][A-Z_]*(?:KEY|TOKEN|SECRET|PASSWORD)["']\]\s*=\s*["'][^"']+["']'''),
|
||||
]
|
||||
|
||||
# Files/directories to skip (tests, docs, examples patterns in docstrings are OK)
|
||||
SKIP_DIRS = {
|
||||
"tests",
|
||||
"test",
|
||||
"__pycache__",
|
||||
".git",
|
||||
"cassettes",
|
||||
"node_modules",
|
||||
".venv",
|
||||
}
|
||||
|
||||
|
||||
def _get_python_source_files(root: Path) -> list[Path]:
|
||||
"""Get all Python source files, excluding test directories."""
|
||||
files = []
|
||||
for path in root.rglob("*.py"):
|
||||
parts = set(path.parts)
|
||||
if parts & SKIP_DIRS:
|
||||
continue
|
||||
files.append(path)
|
||||
return files
|
||||
|
||||
|
||||
class TestNoHardcodedSecrets:
|
||||
"""Test that source code does not contain hardcoded secrets."""
|
||||
|
||||
def test_no_real_api_keys_in_source(self):
|
||||
"""Verify no real API key patterns exist in source code."""
|
||||
violations = []
|
||||
|
||||
for src_root in [CREWAI_SRC, CREWAI_TOOLS_SRC]:
|
||||
if not src_root.exists():
|
||||
continue
|
||||
for filepath in _get_python_source_files(src_root):
|
||||
content = filepath.read_text(errors="ignore")
|
||||
for pattern in SECRET_PATTERNS:
|
||||
for match in pattern.finditer(content):
|
||||
# Get the line number
|
||||
line_num = content[: match.start()].count("\n") + 1
|
||||
violations.append(
|
||||
f"{filepath.relative_to(WORKSPACE_ROOT)}:{line_num}: {match.group()}"
|
||||
)
|
||||
|
||||
assert not violations, (
|
||||
f"Found {len(violations)} potential hardcoded secret(s):\n"
|
||||
+ "\n".join(violations)
|
||||
)
|
||||
|
||||
def test_no_env_assignment_with_hardcoded_keys(self):
|
||||
"""Verify no os.environ['KEY'] = 'hardcoded-value' patterns in source (non-test) code."""
|
||||
pattern = re.compile(
|
||||
r'''os\.environ\[["'](\w*(?:KEY|TOKEN|SECRET|PASSWORD)\w*)["']\]\s*=\s*["']([^"']+)["']'''
|
||||
)
|
||||
# Config flags that are not secrets
|
||||
ALLOWED_ENV_ASSIGNMENTS = {
|
||||
"TOKENIZERS_PARALLELISM",
|
||||
}
|
||||
|
||||
violations = []
|
||||
for src_root in [CREWAI_SRC, CREWAI_TOOLS_SRC]:
|
||||
if not src_root.exists():
|
||||
continue
|
||||
for filepath in _get_python_source_files(src_root):
|
||||
content = filepath.read_text(errors="ignore")
|
||||
for match in pattern.finditer(content):
|
||||
env_var_name = match.group(1)
|
||||
if env_var_name in ALLOWED_ENV_ASSIGNMENTS:
|
||||
continue
|
||||
line_num = content[: match.start()].count("\n") + 1
|
||||
violations.append(
|
||||
f"{filepath.relative_to(WORKSPACE_ROOT)}:{line_num}: "
|
||||
f"os.environ['{match.group(1)}'] = '{match.group(2)}'"
|
||||
)
|
||||
|
||||
assert not violations, (
|
||||
f"Found {len(violations)} hardcoded environment variable assignment(s):\n"
|
||||
+ "\n".join(violations)
|
||||
+ "\n\nUse os.environ.get() or read from .env files instead."
|
||||
)
|
||||
|
||||
|
||||
class TestCreateFlowEnvFile:
|
||||
"""Test that create_flow generates .env files without hardcoded secret values."""
|
||||
|
||||
def test_create_flow_env_file_has_no_hardcoded_api_key(self):
|
||||
"""Verify create_flow does not write a hardcoded API key value."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
original_cwd = os.getcwd()
|
||||
try:
|
||||
os.chdir(temp_dir)
|
||||
create_flow("test_flow")
|
||||
|
||||
env_file = Path(temp_dir) / "test_flow" / ".env"
|
||||
assert env_file.exists(), ".env file should be created"
|
||||
|
||||
content = env_file.read_text()
|
||||
assert "YOUR_API_KEY" not in content, (
|
||||
".env should not contain hardcoded placeholder 'YOUR_API_KEY'"
|
||||
)
|
||||
# The key name should be present but without a hardcoded value
|
||||
assert "OPENAI_API_KEY" in content, (
|
||||
".env should contain the OPENAI_API_KEY variable name"
|
||||
)
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
class TestProviderDefaultApiKeys:
|
||||
"""Test that provider default API keys use environment variable lookups."""
|
||||
|
||||
def test_ollama_default_api_key_from_env(self):
|
||||
"""Verify Ollama default API key can be overridden via environment variable."""
|
||||
with patch.dict(os.environ, {"OLLAMA_DEFAULT_API_KEY": "custom-ollama-key"}, clear=False):
|
||||
# Re-import to pick up new env var - but since module-level dict is already
|
||||
# evaluated, we test the env var pattern is used in the config
|
||||
config = OPENAI_COMPATIBLE_PROVIDERS["ollama"]
|
||||
# The default_api_key should be set (either from env or fallback)
|
||||
assert config.default_api_key is not None
|
||||
|
||||
def test_vllm_default_api_key_not_dummy(self):
|
||||
"""Verify hosted_vllm default API key is not the literal string 'dummy'."""
|
||||
config = OPENAI_COMPATIBLE_PROVIDERS["hosted_vllm"]
|
||||
assert config.default_api_key != "dummy", (
|
||||
"hosted_vllm should not use 'dummy' as a hardcoded default API key"
|
||||
)
|
||||
assert config.default_api_key is not None
|
||||
|
||||
def test_ollama_default_api_key_fallback(self):
|
||||
"""Verify Ollama uses 'ollama' as fallback when env var is not set."""
|
||||
# When OLLAMA_DEFAULT_API_KEY is not set, should fall back to "ollama"
|
||||
env = os.environ.copy()
|
||||
env.pop("OLLAMA_DEFAULT_API_KEY", None)
|
||||
with patch.dict(os.environ, env, clear=True):
|
||||
# The config was already created at module load time, so we check
|
||||
# the current value
|
||||
config = OPENAI_COMPATIBLE_PROVIDERS["ollama"]
|
||||
assert config.default_api_key is not None
|
||||
|
||||
def test_all_providers_have_valid_config(self):
|
||||
"""Verify all providers have properly configured API key settings."""
|
||||
for provider_name, config in OPENAI_COMPATIBLE_PROVIDERS.items():
|
||||
assert config.api_key_env, (
|
||||
f"Provider '{provider_name}' must have api_key_env configured"
|
||||
)
|
||||
if not config.api_key_required:
|
||||
assert config.default_api_key is not None, (
|
||||
f"Provider '{provider_name}' with api_key_required=False "
|
||||
"must have a default_api_key"
|
||||
)
|
||||
@@ -1,3 +1,3 @@
|
||||
"""CrewAI development tools."""
|
||||
|
||||
__version__ = "1.13.0a1"
|
||||
__version__ = "1.13.0a2"
|
||||
|
||||
Reference in New Issue
Block a user