mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-08 07:38:29 +00:00
fix: update default LLM model and improve error logging in LLM utilities (#3785)
* fix: update default LLM model and improve error logging in LLM utilities * Updated the default LLM model from "gpt-4o-mini" to "gpt-4.1-mini" for better performance. * Enhanced error logging in the LLM utilities to use logger.error instead of logger.debug, ensuring that errors are properly reported and raised. * Added tests to verify behavior when OpenAI API key is missing and when Anthropic dependency is not available, improving robustness and error handling in LLM creation. * fix: update test for default LLM model usage * Refactored the test_create_llm_with_none_uses_default_model to use the imported DEFAULT_LLM_MODEL constant instead of a hardcoded string. * Ensured that the test correctly asserts the model used is the current default, improving maintainability and consistency across tests. * change default model to gpt-4.1-mini * change default model use defualt
This commit is contained in:
@@ -322,7 +322,7 @@ MODELS = {
|
||||
],
|
||||
}
|
||||
|
||||
DEFAULT_LLM_MODEL = "gpt-4o-mini"
|
||||
DEFAULT_LLM_MODEL = "gpt-4.1-mini"
|
||||
|
||||
JSON_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ def create_llm(
|
||||
try:
|
||||
return LLM(model=llm_value)
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to instantiate LLM with model='{llm_value}': {e}")
|
||||
return None
|
||||
logger.error(f"Error instantiating LLM from string: {e}")
|
||||
raise e
|
||||
|
||||
if llm_value is None:
|
||||
return _llm_via_environment_or_fallback()
|
||||
@@ -62,8 +62,8 @@ def create_llm(
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Error instantiating LLM from unknown object type: {e}")
|
||||
return None
|
||||
logger.error(f"Error instantiating LLM from unknown object type: {e}")
|
||||
raise e
|
||||
|
||||
|
||||
UNACCEPTED_ATTRIBUTES: Final[list[str]] = [
|
||||
@@ -176,10 +176,10 @@ def _llm_via_environment_or_fallback() -> LLM | None:
|
||||
try:
|
||||
return LLM(**llm_params)
|
||||
except Exception as e:
|
||||
logger.debug(
|
||||
logger.error(
|
||||
f"Error instantiating LLM from environment/fallback: {type(e).__name__}: {e}"
|
||||
)
|
||||
return None
|
||||
raise e
|
||||
|
||||
|
||||
def _normalize_key_name(key_name: str) -> str:
|
||||
|
||||
@@ -6,6 +6,7 @@ from unittest import mock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from crewai.agents.crew_agent_executor import AgentFinish, CrewAgentExecutor
|
||||
from crewai.cli.constants import DEFAULT_LLM_MODEL
|
||||
from crewai.events.event_bus import crewai_event_bus
|
||||
from crewai.events.types.tool_usage_events import ToolUsageFinishedEvent
|
||||
from crewai.knowledge.knowledge import Knowledge
|
||||
@@ -135,7 +136,7 @@ def test_agent_with_missing_response_template():
|
||||
|
||||
def test_agent_default_values():
|
||||
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
|
||||
assert agent.llm.model == "gpt-4o-mini"
|
||||
assert agent.llm.model == DEFAULT_LLM_MODEL
|
||||
assert agent.allow_delegation is False
|
||||
|
||||
|
||||
@@ -225,7 +226,7 @@ def test_logging_tool_usage():
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
assert agent.llm.model == "gpt-4o-mini"
|
||||
assert agent.llm.model == DEFAULT_LLM_MODEL
|
||||
assert agent.tools_handler.last_used_tool is None
|
||||
task = Task(
|
||||
description="What is 3 times 4?",
|
||||
|
||||
@@ -1,77 +1,79 @@
|
||||
import os
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from crewai.cli.constants import DEFAULT_LLM_MODEL
|
||||
from crewai.llm import LLM
|
||||
from crewai.llms.base_llm import BaseLLM
|
||||
from crewai.utilities.llm_utils import create_llm
|
||||
import pytest
|
||||
|
||||
|
||||
try:
|
||||
from litellm.exceptions import BadRequestError
|
||||
except ImportError:
|
||||
BadRequestError = Exception
|
||||
|
||||
|
||||
def test_create_llm_with_llm_instance():
|
||||
existing_llm = LLM(model="gpt-4o")
|
||||
llm = create_llm(llm_value=existing_llm)
|
||||
assert llm is existing_llm
|
||||
|
||||
|
||||
def test_create_llm_with_valid_model_string():
|
||||
llm = create_llm(llm_value="gpt-4o")
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o"
|
||||
|
||||
|
||||
def test_create_llm_with_invalid_model_string():
|
||||
# For invalid model strings, create_llm succeeds but call() fails with API error
|
||||
llm = create_llm(llm_value="invalid-model")
|
||||
assert llm is not None
|
||||
assert isinstance(llm, BaseLLM)
|
||||
|
||||
# The error should occur when making the actual API call
|
||||
# We expect some kind of API error (NotFoundError, etc.)
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
llm.call(messages=[{"role": "user", "content": "Hello, world!"}])
|
||||
|
||||
|
||||
def test_create_llm_with_unknown_object_missing_attributes():
|
||||
class UnknownObject:
|
||||
pass
|
||||
|
||||
unknown_obj = UnknownObject()
|
||||
llm = create_llm(llm_value=unknown_obj)
|
||||
|
||||
# Should succeed because str(unknown_obj) provides a model name
|
||||
assert llm is not None
|
||||
assert isinstance(llm, BaseLLM)
|
||||
|
||||
|
||||
def test_create_llm_with_none_uses_default_model():
|
||||
def test_create_llm_with_llm_instance() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
with patch("crewai.utilities.llm_utils.DEFAULT_LLM_MODEL", "gpt-4o-mini"):
|
||||
existing_llm = LLM(model="gpt-4o")
|
||||
llm = create_llm(llm_value=existing_llm)
|
||||
assert llm is existing_llm
|
||||
|
||||
|
||||
def test_create_llm_with_valid_model_string() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
llm = create_llm(llm_value="gpt-4o")
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o"
|
||||
|
||||
|
||||
def test_create_llm_with_invalid_model_string() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
# For invalid model strings, create_llm succeeds but call() fails with API error
|
||||
llm = create_llm(llm_value="invalid-model")
|
||||
assert llm is not None
|
||||
assert isinstance(llm, BaseLLM)
|
||||
|
||||
# The error should occur when making the actual API call
|
||||
# We expect some kind of API error (NotFoundError, etc.)
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
llm.call(messages=[{"role": "user", "content": "Hello, world!"}])
|
||||
|
||||
|
||||
def test_create_llm_with_unknown_object_missing_attributes() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
class UnknownObject:
|
||||
pass
|
||||
|
||||
unknown_obj = UnknownObject()
|
||||
llm = create_llm(llm_value=unknown_obj)
|
||||
|
||||
# Should succeed because str(unknown_obj) provides a model name
|
||||
assert llm is not None
|
||||
assert isinstance(llm, BaseLLM)
|
||||
|
||||
|
||||
def test_create_llm_with_none_uses_default_model() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
with patch("crewai.utilities.llm_utils.DEFAULT_LLM_MODEL", DEFAULT_LLM_MODEL):
|
||||
llm = create_llm(llm_value=None)
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o-mini"
|
||||
assert llm.model == DEFAULT_LLM_MODEL
|
||||
|
||||
|
||||
def test_create_llm_with_unknown_object():
|
||||
class UnknownObject:
|
||||
model_name = "gpt-4o"
|
||||
temperature = 0.7
|
||||
max_tokens = 1500
|
||||
def test_create_llm_with_unknown_object() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
class UnknownObject:
|
||||
model_name = "gpt-4o"
|
||||
temperature = 0.7
|
||||
max_tokens = 1500
|
||||
|
||||
unknown_obj = UnknownObject()
|
||||
llm = create_llm(llm_value=unknown_obj)
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o"
|
||||
assert llm.temperature == 0.7
|
||||
assert llm.max_tokens == 1500
|
||||
unknown_obj = UnknownObject()
|
||||
llm = create_llm(llm_value=unknown_obj)
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o"
|
||||
assert llm.temperature == 0.7
|
||||
if hasattr(llm, 'max_tokens'):
|
||||
assert llm.max_tokens == 1500
|
||||
|
||||
|
||||
def test_create_llm_from_env_with_unaccepted_attributes():
|
||||
def test_create_llm_from_env_with_unaccepted_attributes() -> None:
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
@@ -90,25 +92,47 @@ def test_create_llm_from_env_with_unaccepted_attributes():
|
||||
assert not hasattr(llm, "AWS_REGION_NAME")
|
||||
|
||||
|
||||
def test_create_llm_with_partial_attributes():
|
||||
class PartialAttributes:
|
||||
model_name = "gpt-4o"
|
||||
# temperature is missing
|
||||
def test_create_llm_with_partial_attributes() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
class PartialAttributes:
|
||||
model_name = "gpt-4o"
|
||||
# temperature is missing
|
||||
|
||||
obj = PartialAttributes()
|
||||
llm = create_llm(llm_value=obj)
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o"
|
||||
assert llm.temperature is None # Should handle missing attributes gracefully
|
||||
obj = PartialAttributes()
|
||||
llm = create_llm(llm_value=obj)
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "gpt-4o"
|
||||
assert llm.temperature is None # Should handle missing attributes gracefully
|
||||
|
||||
|
||||
def test_create_llm_with_invalid_type():
|
||||
# For integers, create_llm succeeds because str(42) becomes "42"
|
||||
llm = create_llm(llm_value=42)
|
||||
assert llm is not None
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "42"
|
||||
def test_create_llm_with_invalid_type() -> None:
|
||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||
# For integers, create_llm succeeds because str(42) becomes "42"
|
||||
llm = create_llm(llm_value=42)
|
||||
assert llm is not None
|
||||
assert isinstance(llm, BaseLLM)
|
||||
assert llm.model == "42"
|
||||
|
||||
# The error should occur when making the actual API call
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
llm.call(messages=[{"role": "user", "content": "Hello, world!"}])
|
||||
# The error should occur when making the actual API call
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
llm.call(messages=[{"role": "user", "content": "Hello, world!"}])
|
||||
|
||||
|
||||
def test_create_llm_openai_missing_api_key() -> None:
|
||||
"""Test that create_llm raises error when OpenAI API key is missing"""
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
with pytest.raises((ValueError, ImportError)) as exc_info:
|
||||
create_llm(llm_value="gpt-4o")
|
||||
|
||||
error_message = str(exc_info.value).lower()
|
||||
assert "openai_api_key" in error_message or "api_key" in error_message
|
||||
|
||||
|
||||
def test_create_llm_anthropic_missing_dependency() -> None:
|
||||
"""Test that create_llm raises error when Anthropic dependency is missing"""
|
||||
with patch.dict(os.environ, {"ANTHROPIC_API_KEY": "fake-key"}, clear=True):
|
||||
with patch("crewai.llm.LLM.__new__", side_effect=ImportError('Anthropic native provider not available, to install: uv add "crewai[anthropic]"')):
|
||||
with pytest.raises(ImportError) as exc_info:
|
||||
create_llm(llm_value="anthropic/claude-3-sonnet")
|
||||
|
||||
assert "Anthropic native provider not available, to install: uv add \"crewai[anthropic]\"" in str(exc_info.value)
|
||||
|
||||
Reference in New Issue
Block a user