mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-11 00:58:30 +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"
|
JSON_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ def create_llm(
|
|||||||
try:
|
try:
|
||||||
return LLM(model=llm_value)
|
return LLM(model=llm_value)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Failed to instantiate LLM with model='{llm_value}': {e}")
|
logger.error(f"Error instantiating LLM from string: {e}")
|
||||||
return None
|
raise e
|
||||||
|
|
||||||
if llm_value is None:
|
if llm_value is None:
|
||||||
return _llm_via_environment_or_fallback()
|
return _llm_via_environment_or_fallback()
|
||||||
@@ -62,8 +62,8 @@ def create_llm(
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Error instantiating LLM from unknown object type: {e}")
|
logger.error(f"Error instantiating LLM from unknown object type: {e}")
|
||||||
return None
|
raise e
|
||||||
|
|
||||||
|
|
||||||
UNACCEPTED_ATTRIBUTES: Final[list[str]] = [
|
UNACCEPTED_ATTRIBUTES: Final[list[str]] = [
|
||||||
@@ -176,10 +176,10 @@ def _llm_via_environment_or_fallback() -> LLM | None:
|
|||||||
try:
|
try:
|
||||||
return LLM(**llm_params)
|
return LLM(**llm_params)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(
|
logger.error(
|
||||||
f"Error instantiating LLM from environment/fallback: {type(e).__name__}: {e}"
|
f"Error instantiating LLM from environment/fallback: {type(e).__name__}: {e}"
|
||||||
)
|
)
|
||||||
return None
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def _normalize_key_name(key_name: str) -> str:
|
def _normalize_key_name(key_name: str) -> str:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from unittest import mock
|
|||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from crewai.agents.crew_agent_executor import AgentFinish, CrewAgentExecutor
|
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.event_bus import crewai_event_bus
|
||||||
from crewai.events.types.tool_usage_events import ToolUsageFinishedEvent
|
from crewai.events.types.tool_usage_events import ToolUsageFinishedEvent
|
||||||
from crewai.knowledge.knowledge import Knowledge
|
from crewai.knowledge.knowledge import Knowledge
|
||||||
@@ -135,7 +136,7 @@ def test_agent_with_missing_response_template():
|
|||||||
|
|
||||||
def test_agent_default_values():
|
def test_agent_default_values():
|
||||||
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
|
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
|
assert agent.allow_delegation is False
|
||||||
|
|
||||||
|
|
||||||
@@ -225,7 +226,7 @@ def test_logging_tool_usage():
|
|||||||
verbose=True,
|
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
|
assert agent.tools_handler.last_used_tool is None
|
||||||
task = Task(
|
task = Task(
|
||||||
description="What is 3 times 4?",
|
description="What is 3 times 4?",
|
||||||
|
|||||||
@@ -1,77 +1,79 @@
|
|||||||
import os
|
import os
|
||||||
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from crewai.cli.constants import DEFAULT_LLM_MODEL
|
||||||
from crewai.llm import LLM
|
from crewai.llm import LLM
|
||||||
from crewai.llms.base_llm import BaseLLM
|
from crewai.llms.base_llm import BaseLLM
|
||||||
from crewai.utilities.llm_utils import create_llm
|
from crewai.utilities.llm_utils import create_llm
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
try:
|
def test_create_llm_with_llm_instance() -> None:
|
||||||
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():
|
|
||||||
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
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)
|
llm = create_llm(llm_value=None)
|
||||||
assert isinstance(llm, BaseLLM)
|
assert isinstance(llm, BaseLLM)
|
||||||
assert llm.model == "gpt-4o-mini"
|
assert llm.model == DEFAULT_LLM_MODEL
|
||||||
|
|
||||||
|
|
||||||
def test_create_llm_with_unknown_object():
|
def test_create_llm_with_unknown_object() -> None:
|
||||||
class UnknownObject:
|
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||||
model_name = "gpt-4o"
|
class UnknownObject:
|
||||||
temperature = 0.7
|
model_name = "gpt-4o"
|
||||||
max_tokens = 1500
|
temperature = 0.7
|
||||||
|
max_tokens = 1500
|
||||||
|
|
||||||
unknown_obj = UnknownObject()
|
unknown_obj = UnknownObject()
|
||||||
llm = create_llm(llm_value=unknown_obj)
|
llm = create_llm(llm_value=unknown_obj)
|
||||||
assert isinstance(llm, BaseLLM)
|
assert isinstance(llm, BaseLLM)
|
||||||
assert llm.model == "gpt-4o"
|
assert llm.model == "gpt-4o"
|
||||||
assert llm.temperature == 0.7
|
assert llm.temperature == 0.7
|
||||||
assert llm.max_tokens == 1500
|
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(
|
with patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
{
|
{
|
||||||
@@ -90,25 +92,47 @@ def test_create_llm_from_env_with_unaccepted_attributes():
|
|||||||
assert not hasattr(llm, "AWS_REGION_NAME")
|
assert not hasattr(llm, "AWS_REGION_NAME")
|
||||||
|
|
||||||
|
|
||||||
def test_create_llm_with_partial_attributes():
|
def test_create_llm_with_partial_attributes() -> None:
|
||||||
class PartialAttributes:
|
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||||
model_name = "gpt-4o"
|
class PartialAttributes:
|
||||||
# temperature is missing
|
model_name = "gpt-4o"
|
||||||
|
# temperature is missing
|
||||||
|
|
||||||
obj = PartialAttributes()
|
obj = PartialAttributes()
|
||||||
llm = create_llm(llm_value=obj)
|
llm = create_llm(llm_value=obj)
|
||||||
assert isinstance(llm, BaseLLM)
|
assert isinstance(llm, BaseLLM)
|
||||||
assert llm.model == "gpt-4o"
|
assert llm.model == "gpt-4o"
|
||||||
assert llm.temperature is None # Should handle missing attributes gracefully
|
assert llm.temperature is None # Should handle missing attributes gracefully
|
||||||
|
|
||||||
|
|
||||||
def test_create_llm_with_invalid_type():
|
def test_create_llm_with_invalid_type() -> None:
|
||||||
# For integers, create_llm succeeds because str(42) becomes "42"
|
with patch.dict(os.environ, {"OPENAI_API_KEY": "fake-key"}, clear=True):
|
||||||
llm = create_llm(llm_value=42)
|
# For integers, create_llm succeeds because str(42) becomes "42"
|
||||||
assert llm is not None
|
llm = create_llm(llm_value=42)
|
||||||
assert isinstance(llm, BaseLLM)
|
assert llm is not None
|
||||||
assert llm.model == "42"
|
assert isinstance(llm, BaseLLM)
|
||||||
|
assert llm.model == "42"
|
||||||
|
|
||||||
# The error should occur when making the actual API call
|
# The error should occur when making the actual API call
|
||||||
with pytest.raises(Exception): # noqa: B017
|
with pytest.raises(Exception): # noqa: B017
|
||||||
llm.call(messages=[{"role": "user", "content": "Hello, world!"}])
|
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