Compare commits

...

3 Commits

Author SHA1 Message Date
Devin AI
2892cf98f1 Address PR feedback: Add logging, refactor callback removal, and add type hints
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-23 17:13:01 +00:00
Devin AI
90f508da12 Fix CI issues: Fix import sorting in tests/llm_test.py
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-23 17:10:41 +00:00
Devin AI
6161e6893e Fix 'list.remove(x): x not in list' error in crewai chat command
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-03-23 17:08:37 +00:00
2 changed files with 67 additions and 3 deletions

View File

@@ -955,20 +955,37 @@ class LLM:
self.context_window_size = int(value * CONTEXT_WINDOW_USAGE_RATIO) self.context_window_size = int(value * CONTEXT_WINDOW_USAGE_RATIO)
return self.context_window_size return self.context_window_size
def set_callbacks(self, callbacks: List[Any]): def _safe_remove_callback(self, callback_list: List[Any], callback: Any) -> None:
"""
Safely remove a callback from a list, handling the case where it doesn't exist.
Args:
callback_list: The list of callbacks to remove from
callback: The callback to remove
"""
try:
callback_list.remove(callback)
except ValueError as e:
logging.debug(f"Callback {callback} not found in callback list: {e}")
pass
def set_callbacks(self, callbacks: List[Any]) -> None:
""" """
Attempt to keep a single set of callbacks in litellm by removing old Attempt to keep a single set of callbacks in litellm by removing old
duplicates and adding new ones. duplicates and adding new ones.
Args:
callbacks: List of callback functions to set
""" """
with suppress_warnings(): with suppress_warnings():
callback_types = [type(callback) for callback in callbacks] callback_types = [type(callback) for callback in callbacks]
for callback in litellm.success_callback[:]: for callback in litellm.success_callback[:]:
if type(callback) in callback_types: if type(callback) in callback_types:
litellm.success_callback.remove(callback) self._safe_remove_callback(litellm.success_callback, callback)
for callback in litellm._async_success_callback[:]: for callback in litellm._async_success_callback[:]:
if type(callback) in callback_types: if type(callback) in callback_types:
litellm._async_success_callback.remove(callback) self._safe_remove_callback(litellm._async_success_callback, callback)
litellm.callbacks = callbacks litellm.callbacks = callbacks

View File

@@ -2,6 +2,7 @@ import os
from time import sleep from time import sleep
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import litellm
import pytest import pytest
from pydantic import BaseModel from pydantic import BaseModel
@@ -443,3 +444,49 @@ def test_tool_execution_error_event():
assert event.tool_args == {"param": "test"} assert event.tool_args == {"param": "test"}
assert event.tool_class == failing_tool assert event.tool_class == failing_tool
assert "Tool execution failed!" in event.error assert "Tool execution failed!" in event.error
def test_set_callbacks_with_nonexistent_callback():
"""Test that set_callbacks handles the case where a callback doesn't exist in the list."""
# Create a mock callback
class MockCallback:
def __init__(self):
self.called = False
def __call__(self, *args, **kwargs):
self.called = True
# Create a test callback
test_callback = MockCallback()
# Make sure the callback lists are empty
original_success_callbacks = litellm.success_callback.copy()
original_async_callbacks = litellm._async_success_callback.copy()
try:
# Clear the callback lists to ensure clean state
litellm.success_callback.clear()
litellm._async_success_callback.clear()
# Create an LLM instance
llm = LLM(model="gpt-4o-mini")
# Call set_callbacks with our test callback - this should work without error
llm.set_callbacks([test_callback])
# Now call set_callbacks again - this should also work without error
# even though the callback is already in the list
llm.set_callbacks([test_callback])
# Now remove the callback and try to remove it again - this should not raise an error
litellm.success_callback.clear()
litellm._async_success_callback.clear()
# This would previously fail with "list.remove(x): x not in list"
llm.set_callbacks([test_callback])
assert True # If we get here, no exception was raised
finally:
# Restore the original callbacks
litellm.success_callback = original_success_callbacks
litellm._async_success_callback = original_async_callbacks