Spaces:
Paused
Paused
| import datetime | |
| import json | |
| import os | |
| import sys | |
| import unittest | |
| from typing import List, Optional, Tuple | |
| from unittest.mock import ANY, AsyncMock, MagicMock, Mock, patch | |
| sys.path.insert( | |
| 0, os.path.abspath("../../..") | |
| ) # Adds the parent directory to the system-path | |
| import litellm | |
| from litellm.integrations.SlackAlerting.slack_alerting import SlackAlerting | |
| from litellm.proxy._types import CallInfo, Litellm_EntityType | |
| class TestSlackAlerting(unittest.TestCase): | |
| def setUp(self): | |
| self.slack_alerting = SlackAlerting() | |
| def test_get_percent_of_max_budget_left(self): | |
| # Test case 1: When max_budget is None | |
| user_info = CallInfo( | |
| max_budget=None, spend=50.0, event_group=Litellm_EntityType.KEY | |
| ) | |
| result = self.slack_alerting._get_percent_of_max_budget_left(user_info) | |
| self.assertEqual(result, 0.0) | |
| # Test case 2: When max_budget is 0 | |
| user_info = CallInfo( | |
| max_budget=0.0, spend=50.0, event_group=Litellm_EntityType.KEY | |
| ) | |
| result = self.slack_alerting._get_percent_of_max_budget_left(user_info) | |
| self.assertEqual(result, 0.0) | |
| # Test case 3: When spend is less than max_budget | |
| user_info = CallInfo( | |
| max_budget=100.0, spend=75.0, event_group=Litellm_EntityType.KEY | |
| ) | |
| result = self.slack_alerting._get_percent_of_max_budget_left(user_info) | |
| self.assertEqual(result, 0.25) | |
| # Test case 4: When spend equals max_budget | |
| user_info = CallInfo( | |
| max_budget=100.0, spend=100.0, event_group=Litellm_EntityType.KEY | |
| ) | |
| result = self.slack_alerting._get_percent_of_max_budget_left(user_info) | |
| self.assertEqual(result, 0.0) | |
| # Test case 5: When spend exceeds max_budget | |
| user_info = CallInfo( | |
| max_budget=100.0, spend=120.0, event_group=Litellm_EntityType.KEY | |
| ) | |
| result = self.slack_alerting._get_percent_of_max_budget_left(user_info) | |
| self.assertEqual(result, -0.2) | |
| def test_get_event_and_event_message_max_budget(self): | |
| # Initial setup with no event | |
| event = None | |
| event_message = "Test Message: " | |
| # Test case 1: When spend exceeds max_budget | |
| user_info = CallInfo( | |
| max_budget=100.0, | |
| spend=120.0, | |
| soft_budget=None, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=event, event_message=event_message | |
| ) | |
| self.assertEqual(event, "budget_crossed") | |
| self.assertTrue("Budget Crossed" in event_message) | |
| # Test case 2: When 5% of max_budget is left | |
| user_info = CallInfo( | |
| max_budget=100.0, | |
| spend=95.0, | |
| soft_budget=None, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=event, event_message=event_message | |
| ) | |
| self.assertEqual(event, "threshold_crossed") | |
| self.assertTrue("5% Threshold Crossed" in event_message) | |
| # Test case 3: When 15% of max_budget is left | |
| user_info = CallInfo( | |
| max_budget=100.0, | |
| spend=85.0, | |
| soft_budget=None, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=event, event_message=event_message | |
| ) | |
| self.assertEqual(event, "threshold_crossed") | |
| self.assertTrue("15% Threshold Crossed" in event_message) | |
| def test_get_event_and_event_message_soft_budget(self): | |
| # Initial setup with no event | |
| event = None | |
| event_message = "Test Message: " | |
| # Test case 1: When spend exceeds soft_budget | |
| user_info = CallInfo( | |
| max_budget=None, | |
| spend=120.0, | |
| soft_budget=100.0, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=event, event_message=event_message | |
| ) | |
| self.assertEqual(event, "soft_budget_crossed") | |
| self.assertTrue("Total Soft Budget" in event_message) | |
| # Test case 2: When spend is less than soft_budget | |
| user_info = CallInfo( | |
| max_budget=None, | |
| spend=90.0, | |
| soft_budget=100.0, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=None, event_message=event_message | |
| ) | |
| print("got event", event) | |
| print("got event_message", event_message) | |
| self.assertEqual(event, None) # No event should be triggered | |
| def test_get_event_and_event_message_both_budgets(self): | |
| # Initial setup with no event | |
| event = None | |
| event_message = "Test Message: " | |
| # Test case 1: When spend exceeds both max_budget and soft_budget | |
| user_info = CallInfo( | |
| max_budget=150.0, | |
| spend=160.0, | |
| soft_budget=100.0, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=event, event_message=event_message | |
| ) | |
| # budget_crossed has higher priority | |
| self.assertEqual(event, "budget_crossed") | |
| self.assertTrue("Budget Crossed" in event_message) | |
| # Test case 2: When spend exceeds soft_budget but not max_budget | |
| user_info = CallInfo( | |
| max_budget=150.0, | |
| spend=120.0, | |
| soft_budget=100.0, | |
| event_group=Litellm_EntityType.KEY, | |
| ) | |
| event, event_message = self.slack_alerting._get_event_and_event_message( | |
| user_info=user_info, event=event, event_message=event_message | |
| ) | |
| self.assertEqual(event, "soft_budget_crossed") | |
| self.assertTrue("Total Soft Budget" in event_message) | |
| # Calling update_values with alerting args should try to start the periodic task | |
| def test_update_values_starts_periodic_task(self, mock_create_task): | |
| # Make it do nothing (or return a dummy future) | |
| mock_create_task.return_value = AsyncMock() # prevents awaiting errors | |
| assert self.slack_alerting.periodic_started == False | |
| self.slack_alerting.update_values(alerting_args={"slack_alerting": "True"}) | |
| assert self.slack_alerting.periodic_started == True | |