2023-12-12 08:40:23 +08:00
### What this tests ####
## This test asserts the type of data passed into each method of the custom callback handler
2024-09-02 04:31:42 +08:00
import asyncio
import inspect
import os
import sys
import time
import traceback
2023-12-12 08:40:23 +08:00
from datetime import datetime
2024-09-02 04:31:42 +08:00
2023-12-12 08:40:23 +08:00
import pytest
2023-12-25 16:40:38 +08:00
sys . path . insert ( 0 , os . path . abspath ( " ../.. " ) )
2024-09-02 04:31:42 +08:00
from typing import List , Literal , Optional
from unittest . mock import AsyncMock , MagicMock , patch
2023-12-12 08:40:23 +08:00
import litellm
2024-09-02 04:31:42 +08:00
from litellm import Cache , Router
2023-12-12 08:40:23 +08:00
from litellm . integrations . custom_logger import CustomLogger
# Test Scenarios (test across completion, streaming, embedding)
## 1: Pre-API-Call
## 2: Post-API-Call
## 3: On LiteLLM Call success
## 4: On LiteLLM Call failure
2023-12-25 16:40:38 +08:00
## fallbacks
## retries
2023-12-12 08:40:23 +08:00
2023-12-25 16:40:38 +08:00
# Test cases
## 1. Simple Azure OpenAI acompletion + streaming call
## 2. Simple Azure OpenAI aembedding call
2023-12-12 08:40:23 +08:00
## 3. Azure OpenAI acompletion + streaming call with retries
## 4. Azure OpenAI aembedding call with retries
## 5. Azure OpenAI acompletion + streaming call with fallbacks
## 6. Azure OpenAI aembedding call with fallbacks
2025-08-24 04:13:23 +08:00
## Test interfaces
2023-12-25 16:40:38 +08:00
## 1. router.completion() + router.embeddings()
## 2. proxy.completions + proxy.embeddings
2024-09-12 13:36:06 +08:00
litellm . num_retries = 0
2023-12-12 08:40:23 +08:00
2023-12-25 16:40:38 +08:00
class CompletionCustomHandler (
CustomLogger
) : # https://docs.litellm.ai/docs/observability/custom_callback#callback-class
2023-12-12 08:40:23 +08:00
"""
2023-12-25 16:40:38 +08:00
The set of expected inputs to a custom handler for a
2023-12-12 08:40:23 +08:00
"""
2023-12-25 16:40:38 +08:00
2023-12-12 08:40:23 +08:00
# Class variables or attributes
def __init__ ( self ) :
self . errors = [ ]
2023-12-25 16:40:38 +08:00
self . states : Optional [
List [
Literal [
" sync_pre_api_call " ,
" async_pre_api_call " ,
" post_api_call " ,
" sync_stream " ,
" async_stream " ,
" sync_success " ,
" async_success " ,
" sync_failure " ,
" async_failure " ,
]
]
] = [ ]
2023-12-12 08:40:23 +08:00
2023-12-25 16:40:38 +08:00
def log_pre_api_call ( self , model , messages , kwargs ) :
try :
print ( f " received kwargs in pre-input: { kwargs } " )
2023-12-12 08:40:23 +08:00
self . states . append ( " sync_pre_api_call " )
## MODEL
assert isinstance ( model , str )
## MESSAGES
assert isinstance ( messages , list )
## KWARGS
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " model " ] , str )
assert isinstance ( kwargs [ " messages " ] , list )
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
2023-12-12 08:40:23 +08:00
### ROUTER-SPECIFIC KWARGS
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] [ " model_group " ] , str )
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] [ " deployment " ] , str )
assert isinstance ( kwargs [ " litellm_params " ] [ " model_info " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] [ " model_info " ] [ " id " ] , str )
2023-12-25 16:40:38 +08:00
assert isinstance (
kwargs [ " litellm_params " ] [ " proxy_server_request " ] , ( str , type ( None ) )
)
assert isinstance (
kwargs [ " litellm_params " ] [ " preset_cache_key " ] , ( str , type ( None ) )
)
2023-12-12 08:40:23 +08:00
assert isinstance ( kwargs [ " litellm_params " ] [ " stream_response " ] , dict )
2023-12-25 16:40:38 +08:00
except Exception as e :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
2023-12-25 16:40:38 +08:00
def log_post_api_call ( self , kwargs , response_obj , start_time , end_time ) :
2023-12-12 08:40:23 +08:00
try :
self . states . append ( " post_api_call " )
2023-12-25 16:40:38 +08:00
## START TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( start_time , datetime )
2023-12-25 16:40:38 +08:00
## END TIME
2023-12-12 08:40:23 +08:00
assert end_time == None
2023-12-25 16:40:38 +08:00
## RESPONSE OBJECT
2023-12-12 08:40:23 +08:00
assert response_obj == None
2023-12-25 16:40:38 +08:00
## KWARGS
assert isinstance ( kwargs [ " model " ] , str )
assert isinstance ( kwargs [ " messages " ] , list )
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
assert isinstance ( kwargs [ " input " ] , ( list , dict , str ) )
assert isinstance ( kwargs [ " api_key " ] , ( str , type ( None ) ) )
assert (
isinstance (
kwargs [ " original_response " ] , ( str , litellm . CustomStreamWrapper )
)
or inspect . iscoroutine ( kwargs [ " original_response " ] )
or inspect . isasyncgen ( kwargs [ " original_response " ] )
)
assert isinstance ( kwargs [ " additional_args " ] , ( dict , type ( None ) ) )
assert isinstance ( kwargs [ " log_event_type " ] , str )
2023-12-12 08:40:23 +08:00
### ROUTER-SPECIFIC KWARGS
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] [ " model_group " ] , str )
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] [ " deployment " ] , str )
assert isinstance ( kwargs [ " litellm_params " ] [ " model_info " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] [ " model_info " ] [ " id " ] , str )
2023-12-25 16:40:38 +08:00
assert isinstance (
kwargs [ " litellm_params " ] [ " proxy_server_request " ] , ( str , type ( None ) )
)
assert isinstance (
kwargs [ " litellm_params " ] [ " preset_cache_key " ] , ( str , type ( None ) )
)
2023-12-12 08:40:23 +08:00
assert isinstance ( kwargs [ " litellm_params " ] [ " stream_response " ] , dict )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
2023-12-25 16:40:38 +08:00
2023-12-12 08:40:23 +08:00
async def async_log_stream_event ( self , kwargs , response_obj , start_time , end_time ) :
try :
self . states . append ( " async_stream " )
2023-12-25 16:40:38 +08:00
## START TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( start_time , datetime )
2023-12-25 16:40:38 +08:00
## END TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( end_time , datetime )
2023-12-25 16:40:38 +08:00
## RESPONSE OBJECT
2024-12-22 23:21:25 +08:00
assert isinstance ( response_obj , litellm . ModelResponseStream )
2023-12-12 08:40:23 +08:00
## KWARGS
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " model " ] , str )
assert isinstance ( kwargs [ " messages " ] , list ) and isinstance (
kwargs [ " messages " ] [ 0 ] , dict
)
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
assert (
isinstance ( kwargs [ " input " ] , list )
and isinstance ( kwargs [ " input " ] [ 0 ] , dict )
) or isinstance ( kwargs [ " input " ] , ( dict , str ) )
assert isinstance ( kwargs [ " api_key " ] , ( str , type ( None ) ) )
assert (
isinstance (
kwargs [ " original_response " ] , ( str , litellm . CustomStreamWrapper )
)
or inspect . isasyncgen ( kwargs [ " original_response " ] )
or inspect . iscoroutine ( kwargs [ " original_response " ] )
)
assert isinstance ( kwargs [ " additional_args " ] , ( dict , type ( None ) ) )
assert isinstance ( kwargs [ " log_event_type " ] , str )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
2023-12-25 16:40:38 +08:00
def log_success_event ( self , kwargs , response_obj , start_time , end_time ) :
2023-12-12 08:40:23 +08:00
try :
self . states . append ( " sync_success " )
2023-12-25 16:40:38 +08:00
## START TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( start_time , datetime )
2023-12-25 16:40:38 +08:00
## END TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( end_time , datetime )
2023-12-25 16:40:38 +08:00
## RESPONSE OBJECT
2023-12-12 08:40:23 +08:00
assert isinstance ( response_obj , litellm . ModelResponse )
## KWARGS
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " model " ] , str )
assert isinstance ( kwargs [ " messages " ] , list ) and isinstance (
kwargs [ " messages " ] [ 0 ] , dict
)
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
assert (
isinstance ( kwargs [ " input " ] , list )
and isinstance ( kwargs [ " input " ] [ 0 ] , dict )
) or isinstance ( kwargs [ " input " ] , ( dict , str ) )
assert isinstance ( kwargs [ " api_key " ] , ( str , type ( None ) ) )
assert isinstance (
kwargs [ " original_response " ] , ( str , litellm . CustomStreamWrapper )
)
assert isinstance ( kwargs [ " additional_args " ] , ( dict , type ( None ) ) )
assert isinstance ( kwargs [ " log_event_type " ] , str )
2023-12-14 13:05:07 +08:00
assert kwargs [ " cache_hit " ] is None or isinstance ( kwargs [ " cache_hit " ] , bool )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
2023-12-25 16:40:38 +08:00
def log_failure_event ( self , kwargs , response_obj , start_time , end_time ) :
2023-12-12 08:40:23 +08:00
try :
self . states . append ( " sync_failure " )
2023-12-25 16:40:38 +08:00
## START TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( start_time , datetime )
2023-12-25 16:40:38 +08:00
## END TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( end_time , datetime )
2023-12-25 16:40:38 +08:00
## RESPONSE OBJECT
2023-12-12 08:40:23 +08:00
assert response_obj == None
## KWARGS
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " model " ] , str )
assert isinstance ( kwargs [ " messages " ] , list ) and isinstance (
kwargs [ " messages " ] [ 0 ] , dict
)
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
assert (
isinstance ( kwargs [ " input " ] , list )
and isinstance ( kwargs [ " input " ] [ 0 ] , dict )
) or isinstance ( kwargs [ " input " ] , ( dict , str ) )
assert isinstance ( kwargs [ " api_key " ] , ( str , type ( None ) ) )
assert (
isinstance (
kwargs [ " original_response " ] , ( str , litellm . CustomStreamWrapper )
)
or kwargs [ " original_response " ] == None
)
assert isinstance ( kwargs [ " additional_args " ] , ( dict , type ( None ) ) )
assert isinstance ( kwargs [ " log_event_type " ] , str )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
2023-12-25 16:40:38 +08:00
2023-12-12 08:40:23 +08:00
async def async_log_pre_api_call ( self , model , messages , kwargs ) :
2023-12-25 16:40:38 +08:00
try :
2023-12-12 08:40:23 +08:00
"""
2023-12-25 16:40:38 +08:00
No - op .
Not implemented yet .
2023-12-12 08:40:23 +08:00
"""
pass
2023-12-25 16:40:38 +08:00
except Exception as e :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
async def async_log_success_event ( self , kwargs , response_obj , start_time , end_time ) :
2023-12-25 16:40:38 +08:00
try :
2025-08-24 04:13:23 +08:00
print ( " CompletionCustomHandler.async_log_success_event, kwargs: " , kwargs )
2023-12-12 08:40:23 +08:00
self . states . append ( " async_success " )
2025-08-24 04:13:23 +08:00
print ( " ############### CompletionCustomHandler async success, kwargs: " , kwargs )
2023-12-25 16:40:38 +08:00
## START TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( start_time , datetime )
2023-12-25 16:40:38 +08:00
## END TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( end_time , datetime )
2023-12-25 16:40:38 +08:00
## RESPONSE OBJECT
assert isinstance (
response_obj , ( litellm . ModelResponse , l itellm . EmbeddingResponse )
)
2023-12-12 08:40:23 +08:00
## KWARGS
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " model " ] , str )
2024-02-08 08:30:58 +08:00
# checking we use base_model for azure cost calculation
2024-02-08 10:18:15 +08:00
base_model = litellm . utils . _get_base_model_from_metadata (
model_call_details = kwargs
2024-02-08 08:30:58 +08:00
)
2024-02-08 10:18:15 +08:00
if (
2025-04-19 05:22:12 +08:00
kwargs [ " model " ] == " chatgpt-v-3 "
2024-02-08 10:18:15 +08:00
and base_model is not None
and kwargs [ " stream " ] != True
) :
2024-02-08 08:30:58 +08:00
# when base_model is set for azure, we should use pricing for the base_model
# this checks response_cost == litellm.cost_per_token(model=base_model)
assert isinstance ( kwargs [ " response_cost " ] , float )
response_cost = kwargs [ " response_cost " ]
print (
f " response_cost: { response_cost } , for model: { kwargs [ ' model ' ] } and base_model: { base_model } "
)
prompt_tokens = response_obj . usage . prompt_tokens
completion_tokens = response_obj . usage . completion_tokens
# ensure the pricing is based on the base_model here
prompt_price , completion_price = litellm . cost_per_token (
model = base_model ,
prompt_tokens = prompt_tokens ,
completion_tokens = completion_tokens ,
)
expected_price = prompt_price + completion_price
print ( f " expected price: { expected_price } " )
assert (
response_cost == expected_price
) , f " response_cost: { response_cost } != expected_price: { expected_price } . For model: { kwargs [ ' model ' ] } and base_model: { base_model } . should have used base_model for price "
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " messages " ] , list )
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
assert isinstance ( kwargs [ " input " ] , ( list , dict , str ) )
assert isinstance ( kwargs [ " api_key " ] , ( str , type ( None ) ) )
assert (
isinstance (
kwargs [ " original_response " ] , ( str , litellm . CustomStreamWrapper )
)
or inspect . isasyncgen ( kwargs [ " original_response " ] )
or inspect . iscoroutine ( kwargs [ " original_response " ] )
)
assert isinstance ( kwargs [ " additional_args " ] , ( dict , type ( None ) ) )
assert isinstance ( kwargs [ " log_event_type " ] , str )
2023-12-14 11:06:02 +08:00
assert kwargs [ " cache_hit " ] is None or isinstance ( kwargs [ " cache_hit " ] , bool )
2023-12-12 08:40:23 +08:00
### ROUTER-SPECIFIC KWARGS
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] [ " model_group " ] , str )
assert isinstance ( kwargs [ " litellm_params " ] [ " metadata " ] [ " deployment " ] , str )
assert isinstance ( kwargs [ " litellm_params " ] [ " model_info " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] [ " model_info " ] [ " id " ] , str )
2023-12-25 16:40:38 +08:00
assert isinstance (
kwargs [ " litellm_params " ] [ " proxy_server_request " ] , ( str , type ( None ) )
)
assert isinstance (
kwargs [ " litellm_params " ] [ " preset_cache_key " ] , ( str , type ( None ) )
)
2023-12-12 08:40:23 +08:00
assert isinstance ( kwargs [ " litellm_params " ] [ " stream_response " ] , dict )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
async def async_log_failure_event ( self , kwargs , response_obj , start_time , end_time ) :
try :
print ( f " received original response: { kwargs [ ' original_response ' ] } " )
self . states . append ( " async_failure " )
2023-12-25 16:40:38 +08:00
## START TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( start_time , datetime )
2023-12-25 16:40:38 +08:00
## END TIME
2023-12-12 08:40:23 +08:00
assert isinstance ( end_time , datetime )
2023-12-25 16:40:38 +08:00
## RESPONSE OBJECT
2023-12-12 08:40:23 +08:00
assert response_obj == None
## KWARGS
2023-12-25 16:40:38 +08:00
assert isinstance ( kwargs [ " model " ] , str )
assert isinstance ( kwargs [ " messages " ] , list )
assert isinstance ( kwargs [ " optional_params " ] , dict )
assert isinstance ( kwargs [ " litellm_params " ] , dict )
assert isinstance ( kwargs [ " start_time " ] , ( datetime , type ( None ) ) )
assert isinstance ( kwargs [ " stream " ] , bool )
assert isinstance ( kwargs [ " user " ] , ( str , type ( None ) ) )
assert isinstance ( kwargs [ " input " ] , ( list , str , dict ) )
assert isinstance ( kwargs [ " api_key " ] , ( str , type ( None ) ) )
assert (
isinstance (
kwargs [ " original_response " ] , ( str , litellm . CustomStreamWrapper )
)
or inspect . isasyncgen ( kwargs [ " original_response " ] )
or inspect . iscoroutine ( kwargs [ " original_response " ] )
or kwargs [ " original_response " ] == None
)
assert isinstance ( kwargs [ " additional_args " ] , ( dict , type ( None ) ) )
assert isinstance ( kwargs [ " log_event_type " ] , str )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
self . errors . append ( traceback . format_exc ( ) )
2023-12-25 16:40:38 +08:00
# Simple Azure OpenAI call
2023-12-12 08:40:23 +08:00
## COMPLETION
2025-02-01 07:06:37 +08:00
# @pytest.mark.flaky(retries=5, delay=1)
2023-12-12 08:40:23 +08:00
@pytest.mark.asyncio
async def test_async_chat_azure ( ) :
2023-12-25 16:40:38 +08:00
try :
2023-12-12 09:41:47 +08:00
customHandler_completion_azure_router = CompletionCustomHandler ( )
customHandler_streaming_azure_router = CompletionCustomHandler ( )
2023-12-12 08:40:23 +08:00
customHandler_failure = CompletionCustomHandler ( )
2023-12-12 09:41:47 +08:00
litellm . callbacks = [ customHandler_completion_azure_router ]
2024-02-08 08:30:58 +08:00
litellm . set_verbose = True
2023-12-12 08:40:23 +08:00
model_list = [
2023-12-25 16:40:38 +08:00
{
2025-09-28 00:07:44 +08:00
" model_name " : " gpt-4.1-nano " , # openai model name
2023-12-25 16:40:38 +08:00
" litellm_params " : { # params for litellm completion/embedding call
2025-10-26 01:19:24 +08:00
" model " : " azure/gpt-4.1-mini " ,
2023-12-25 16:40:38 +08:00
" api_key " : os . getenv ( " AZURE_API_KEY " ) ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
2025-10-26 01:19:24 +08:00
" model_info " : { " base_model " : " azure/gpt-4.1-mini " } ,
2023-12-25 16:40:38 +08:00
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
2024-09-12 13:36:06 +08:00
router = Router ( model_list = model_list , num_retries = 0 ) # type: ignore
2023-12-25 16:40:38 +08:00
response = await router . acompletion (
2025-09-28 00:12:37 +08:00
model = " gpt-4.1-nano " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " Hi 👋 - i ' m openai " } ] ,
)
2025-08-24 04:13:23 +08:00
print ( " got response, sleeping 5 seconds.... " )
await asyncio . sleep ( 5 )
2023-12-12 09:41:47 +08:00
assert len ( customHandler_completion_azure_router . errors ) == 0
2023-12-25 16:40:38 +08:00
assert (
len ( customHandler_completion_azure_router . states ) == 3
) # pre, post, success
# streaming
2025-01-31 11:35:50 +08:00
litellm . logging_callback_manager . _reset_all_callbacks ( )
2023-12-12 09:41:47 +08:00
litellm . callbacks = [ customHandler_streaming_azure_router ]
2024-09-12 13:36:06 +08:00
router2 = Router ( model_list = model_list , num_retries = 0 ) # type: ignore
2023-12-25 16:40:38 +08:00
response = await router2 . acompletion (
2025-09-28 00:12:37 +08:00
model = " gpt-4.1-nano " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " Hi 👋 - i ' m openai " } ] ,
stream = True ,
)
async for chunk in response :
2023-12-12 09:41:47 +08:00
print ( f " async azure router chunk: { chunk } " )
2023-12-12 08:40:23 +08:00
continue
2025-08-24 04:13:23 +08:00
await asyncio . sleep ( 5 )
2023-12-12 09:41:47 +08:00
print ( f " customHandler.states: { customHandler_streaming_azure_router . states } " )
assert len ( customHandler_streaming_azure_router . errors ) == 0
2023-12-25 16:40:38 +08:00
assert (
2025-02-01 07:06:37 +08:00
len ( customHandler_streaming_azure_router . states ) > = 3
2023-12-25 16:40:38 +08:00
) # pre, post, stream (multiple times), success
# failure
2023-12-12 08:40:23 +08:00
model_list = [
2023-12-25 16:40:38 +08:00
{
" model_name " : " gpt-3.5-turbo " , # openai model name
" litellm_params " : { # params for litellm completion/embedding call
2025-05-01 13:11:12 +08:00
" model " : " azure/gpt-4o-new-test " ,
2023-12-25 16:40:38 +08:00
" api_key " : " my-bad-key " ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
2025-01-31 11:35:50 +08:00
litellm . logging_callback_manager . _reset_all_callbacks ( )
2023-12-12 08:40:23 +08:00
litellm . callbacks = [ customHandler_failure ]
2024-09-12 13:36:06 +08:00
router3 = Router ( model_list = model_list , num_retries = 0 ) # type: ignore
2023-12-25 16:40:38 +08:00
try :
response = await router3 . acompletion (
model = " gpt-3.5-turbo " ,
messages = [ { " role " : " user " , " content " : " Hi 👋 - i ' m openai " } ] ,
)
2023-12-12 08:40:23 +08:00
print ( f " response in router3 acompletion: { response } " )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
pass
2025-08-24 04:13:23 +08:00
await asyncio . sleep ( 5 )
2023-12-12 08:40:23 +08:00
print ( f " customHandler.states: { customHandler_failure . states } " )
assert len ( customHandler_failure . errors ) == 0
2023-12-25 16:40:38 +08:00
assert len ( customHandler_failure . states ) == 3 # pre, post, failure
2023-12-12 08:40:23 +08:00
assert " async_failure " in customHandler_failure . states
2023-12-25 16:40:38 +08:00
except Exception as e :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
pytest . fail ( f " An exception occurred - { str ( e ) } " )
2023-12-25 16:40:38 +08:00
2023-12-12 08:40:23 +08:00
## EMBEDDING
2023-12-12 08:58:57 +08:00
@pytest.mark.asyncio
2023-12-12 08:40:23 +08:00
async def test_async_embedding_azure ( ) :
2023-12-25 16:40:38 +08:00
try :
2023-12-12 08:40:23 +08:00
customHandler = CompletionCustomHandler ( )
customHandler_failure = CompletionCustomHandler ( )
litellm . callbacks = [ customHandler ]
model_list = [
2023-12-25 16:40:38 +08:00
{
" model_name " : " azure-embedding-model " , # openai model name
" litellm_params " : { # params for litellm completion/embedding call
2025-09-28 00:07:44 +08:00
" model " : " azure/text-embedding-ada-002 " ,
2023-12-25 16:40:38 +08:00
" api_key " : os . getenv ( " AZURE_API_KEY " ) ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
router = Router ( model_list = model_list ) # type: ignore
response = await router . aembedding (
model = " azure-embedding-model " , input = [ " hello from litellm! " ]
)
2023-12-12 08:40:23 +08:00
await asyncio . sleep ( 2 )
assert len ( customHandler . errors ) == 0
2023-12-25 16:40:38 +08:00
assert len ( customHandler . states ) == 3 # pre, post, success
# failure
2023-12-12 08:40:23 +08:00
model_list = [
2023-12-25 16:40:38 +08:00
{
" model_name " : " azure-embedding-model " , # openai model name
" litellm_params " : { # params for litellm completion/embedding call
2025-09-28 03:41:35 +08:00
" model " : " azure/text-embedding-ada-002 " ,
2023-12-25 16:40:38 +08:00
" api_key " : " my-bad-key " ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
2025-01-31 11:35:50 +08:00
litellm . logging_callback_manager . _reset_all_callbacks ( )
2023-12-12 08:40:23 +08:00
litellm . callbacks = [ customHandler_failure ]
2024-09-12 13:36:06 +08:00
router3 = Router ( model_list = model_list , num_retries = 0 ) # type: ignore
2023-12-25 16:40:38 +08:00
try :
response = await router3 . aembedding (
model = " azure-embedding-model " , input = [ " hello from litellm! " ]
)
2023-12-12 08:40:23 +08:00
print ( f " response in router3 aembedding: { response } " )
2024-10-02 07:44:20 +08:00
except Exception :
2023-12-12 08:40:23 +08:00
pass
await asyncio . sleep ( 1 )
print ( f " customHandler.states: { customHandler_failure . states } " )
assert len ( customHandler_failure . errors ) == 0
2023-12-25 16:40:38 +08:00
assert len ( customHandler_failure . states ) == 3 # pre, post, failure
2023-12-12 08:40:23 +08:00
assert " async_failure " in customHandler_failure . states
2023-12-25 16:40:38 +08:00
except Exception as e :
2023-12-12 08:40:23 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
pytest . fail ( f " An exception occurred - { str ( e ) } " )
2023-12-25 16:40:38 +08:00
2023-12-12 08:58:57 +08:00
# asyncio.run(test_async_embedding_azure())
2023-12-12 09:11:34 +08:00
# Azure OpenAI call w/ Fallbacks
2023-12-12 08:40:23 +08:00
## COMPLETION
2023-12-12 08:58:57 +08:00
@pytest.mark.asyncio
2023-12-25 16:40:38 +08:00
async def test_async_chat_azure_with_fallbacks ( ) :
try :
2023-12-12 08:58:57 +08:00
customHandler_fallbacks = CompletionCustomHandler ( )
2023-12-12 09:41:47 +08:00
litellm . callbacks = [ customHandler_fallbacks ]
2024-09-13 10:14:58 +08:00
litellm . set_verbose = True
2023-12-25 16:40:38 +08:00
# with fallbacks
2023-12-12 08:58:57 +08:00
model_list = [
2023-12-25 16:40:38 +08:00
{
" model_name " : " gpt-3.5-turbo " , # openai model name
" litellm_params " : { # params for litellm completion/embedding call
2025-10-26 01:19:24 +08:00
" model " : " azure/gpt-4.1-mini " ,
2023-12-25 16:40:38 +08:00
" api_key " : " my-bad-key " ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
{
" model_name " : " gpt-3.5-turbo-16k " ,
" litellm_params " : {
" model " : " gpt-3.5-turbo-16k " ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
2024-09-13 10:14:58 +08:00
router = Router (
model_list = model_list ,
fallbacks = [ { " gpt-3.5-turbo " : [ " gpt-3.5-turbo-16k " ] } ] ,
retry_policy = litellm . router . RetryPolicy (
AuthenticationErrorRetries = 0 ,
) ,
) # type: ignore
2023-12-25 16:40:38 +08:00
response = await router . acompletion (
model = " gpt-3.5-turbo " ,
messages = [ { " role " : " user " , " content " : " Hi 👋 - i ' m openai " } ] ,
)
2023-12-12 08:58:57 +08:00
await asyncio . sleep ( 2 )
print ( f " customHandler_fallbacks.states: { customHandler_fallbacks . states } " )
assert len ( customHandler_fallbacks . errors ) == 0
2023-12-25 16:40:38 +08:00
assert (
len ( customHandler_fallbacks . states ) == 6
) # pre, post, failure, pre, post, success
2023-12-12 09:41:47 +08:00
litellm . callbacks = [ ]
2023-12-25 16:40:38 +08:00
except Exception as e :
2023-12-12 08:58:57 +08:00
print ( f " Assertion Error: { traceback . format_exc ( ) } " )
pytest . fail ( f " An exception occurred - { str ( e ) } " )
2023-12-25 16:40:38 +08:00
2023-12-14 11:11:43 +08:00
# asyncio.run(test_async_chat_azure_with_fallbacks())
2023-12-25 16:40:38 +08:00
# CACHING
2023-12-14 11:11:43 +08:00
## Test Azure - completion, embedding
@pytest.mark.asyncio
2024-09-22 02:41:25 +08:00
@pytest.mark.flaky ( retries = 3 , delay = 1 )
2023-12-14 11:11:43 +08:00
async def test_async_completion_azure_caching ( ) :
customHandler_caching = CompletionCustomHandler ( )
2023-12-25 16:40:38 +08:00
litellm . cache = Cache (
type = " redis " ,
host = os . environ [ " REDIS_HOST " ] ,
port = os . environ [ " REDIS_PORT " ] ,
password = os . environ [ " REDIS_PASSWORD " ] ,
)
2023-12-14 11:11:43 +08:00
litellm . callbacks = [ customHandler_caching ]
unique_time = time . time ( )
model_list = [
2023-12-25 16:40:38 +08:00
{
2025-09-28 00:23:49 +08:00
" model_name " : " gpt-4.1-nano " , # openai model name
2023-12-25 16:40:38 +08:00
" litellm_params " : { # params for litellm completion/embedding call
2025-10-26 01:19:24 +08:00
" model " : " azure/gpt-4.1-mini " ,
2023-12-25 16:40:38 +08:00
" api_key " : os . getenv ( " AZURE_API_KEY " ) ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
{
" model_name " : " gpt-3.5-turbo-16k " ,
" litellm_params " : {
" model " : " gpt-3.5-turbo-16k " ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
router = Router ( model_list = model_list ) # type: ignore
response1 = await router . acompletion (
2025-09-28 00:23:49 +08:00
model = " gpt-4.1-nano " ,
2024-09-02 04:31:42 +08:00
messages = [
{ " role " : " user " , " content " : f " Hi 👋 - i ' m async azure { unique_time } " }
] ,
2023-12-25 16:40:38 +08:00
caching = True ,
)
2023-12-14 11:11:43 +08:00
await asyncio . sleep ( 1 )
print ( f " customHandler_caching.states pre-cache hit: { customHandler_caching . states } " )
2023-12-25 16:40:38 +08:00
response2 = await router . acompletion (
2025-09-28 00:23:49 +08:00
model = " gpt-4.1-nano " ,
2024-09-02 04:31:42 +08:00
messages = [
{ " role " : " user " , " content " : f " Hi 👋 - i ' m async azure { unique_time } " }
] ,
2023-12-25 16:40:38 +08:00
caching = True ,
)
await asyncio . sleep ( 1 ) # success callbacks are done in parallel
print (
f " customHandler_caching.states post-cache hit: { customHandler_caching . states } "
)
2023-12-14 11:11:43 +08:00
assert len ( customHandler_caching . errors ) == 0
2023-12-25 16:40:38 +08:00
assert len ( customHandler_caching . states ) == 4 # pre, post, success, success
2024-09-02 04:31:42 +08:00
2025-09-28 00:11:43 +08:00
@pytest.mark.asyncio
async def test_async_completion_azure_caching_streaming ( ) :
import copy
2025-09-28 00:23:49 +08:00
import uuid
2025-09-28 00:11:43 +08:00
litellm . set_verbose = True
customHandler_caching = CompletionCustomHandler ( )
litellm . cache = Cache (
type = " redis " ,
host = os . environ [ " REDIS_HOST " ] ,
port = os . environ [ " REDIS_PORT " ] ,
password = os . environ [ " REDIS_PASSWORD " ] ,
)
litellm . callbacks = [ customHandler_caching ]
unique_time = uuid . uuid4 ( )
2025-09-28 00:48:16 +08:00
# Use Router instead of direct litellm.acompletion to get router-specific metadata
model_list = [
{
" model_name " : " gpt-4.1-nano " ,
" litellm_params " : {
2025-10-26 01:19:24 +08:00
" model " : " azure/gpt-4.1-mini " ,
2025-09-28 00:48:16 +08:00
" api_key " : os . getenv ( " AZURE_API_KEY " ) ,
" api_version " : os . getenv ( " AZURE_API_VERSION " ) ,
" api_base " : os . getenv ( " AZURE_API_BASE " ) ,
} ,
" tpm " : 240000 ,
" rpm " : 1800 ,
} ,
]
router = Router ( model_list = model_list )
response1 = await router . acompletion (
model = " gpt-4.1-nano " ,
2025-09-28 00:11:43 +08:00
messages = [
{ " role " : " user " , " content " : f " Hi 👋 - i ' m async azure { unique_time } " }
] ,
caching = True ,
stream = True ,
)
async for chunk in response1 :
print ( f " chunk in response1: { chunk } " )
await asyncio . sleep ( 1 )
initial_customhandler_caching_states = len ( customHandler_caching . states )
print ( f " customHandler_caching.states pre-cache hit: { customHandler_caching . states } " )
2025-09-28 00:48:16 +08:00
response2 = await router . acompletion (
model = " gpt-4.1-nano " ,
2025-09-28 00:11:43 +08:00
messages = [
{ " role " : " user " , " content " : f " Hi 👋 - i ' m async azure { unique_time } " }
] ,
caching = True ,
stream = True ,
)
async for chunk in response2 :
print ( f " chunk in response2: { chunk } " )
await asyncio . sleep ( 1 ) # success callbacks are done in parallel
print (
f " customHandler_caching.states post-cache hit: { customHandler_caching . states } "
)
assert len ( customHandler_caching . errors ) == 0
assert (
len ( customHandler_caching . states ) > initial_customhandler_caching_states
) # pre, post, streaming .., success, success
@pytest.mark.asyncio
@pytest.mark.flaky ( retries = 3 , delay = 2 )
async def test_async_embedding_azure_caching ( ) :
print ( " Testing custom callback input - Azure Caching " )
customHandler_caching = CompletionCustomHandler ( )
litellm . cache = Cache (
type = " redis " ,
host = os . environ [ " REDIS_HOST " ] ,
port = os . environ [ " REDIS_PORT " ] ,
password = os . environ [ " REDIS_PASSWORD " ] ,
)
2025-09-28 00:54:54 +08:00
router = Router ( model_list = [ {
" model_name " : " text-embedding-ada-002 " ,
" litellm_params " : {
" model " : " openai/text-embedding-ada-002 " ,
} ,
} ] )
2025-09-28 00:11:43 +08:00
litellm . callbacks = [ customHandler_caching ]
unique_time = time . time ( )
2025-09-28 00:54:54 +08:00
response1 = await router . aembedding (
model = " text-embedding-ada-002 " ,
2025-09-28 00:11:43 +08:00
input = [ f " good morning from litellm1 { unique_time } " ] ,
caching = True ,
)
await asyncio . sleep ( 1 ) # set cache is async for aembedding()
2025-09-28 00:54:54 +08:00
response2 = await router . aembedding (
model = " text-embedding-ada-002 " ,
2025-09-28 00:11:43 +08:00
input = [ f " good morning from litellm1 { unique_time } " ] ,
caching = True ,
)
await asyncio . sleep ( 1 ) # success callbacks are done in parallel
print ( customHandler_caching . states )
print ( customHandler_caching . errors )
assert len ( customHandler_caching . errors ) == 0
assert len ( customHandler_caching . states ) == 4 # pre, post, success, success
2024-09-02 04:31:42 +08:00
@pytest.mark.asyncio
async def test_rate_limit_error_callback ( ) :
"""
Assert a callback is hit , if a model group starts hitting rate limit errors
Relevant issue : https : / / github . com / BerriAI / litellm / issues / 4096
"""
from litellm . litellm_core_utils . litellm_logging import Logging as LiteLLMLogging
customHandler = CompletionCustomHandler ( )
litellm . callbacks = [ customHandler ]
litellm . success_callback = [ ]
router = Router (
model_list = [
{
" model_name " : " my-test-gpt " ,
" litellm_params " : {
" model " : " gpt-3.5-turbo " ,
" mock_response " : " litellm.RateLimitError " ,
} ,
}
] ,
allowed_fails = 2 ,
num_retries = 0 ,
)
litellm_logging_obj = LiteLLMLogging (
model = " my-test-gpt " ,
messages = [ { " role " : " user " , " content " : " hi " } ] ,
stream = False ,
call_type = " acompletion " ,
litellm_call_id = " 1234 " ,
start_time = datetime . now ( ) ,
function_id = " 1234 " ,
)
try :
_ = await router . acompletion (
model = " my-test-gpt " ,
messages = [ { " role " : " user " , " content " : " Hey, how ' s it going? " } ] ,
)
except Exception :
pass
with patch . object (
2024-09-12 13:36:06 +08:00
customHandler , " log_model_group_rate_limit_error " , new = AsyncMock ( )
2024-09-02 04:31:42 +08:00
) as mock_client :
print (
f " customHandler.log_model_group_rate_limit_error: { customHandler . log_model_group_rate_limit_error } "
)
2024-09-12 13:36:06 +08:00
try :
_ = await router . acompletion (
model = " my-test-gpt " ,
messages = [ { " role " : " user " , " content " : " Hey, how ' s it going? " } ] ,
litellm_logging_obj = litellm_logging_obj ,
)
except ( litellm . RateLimitError , ValueError ) :
pass
2024-09-02 04:31:42 +08:00
await asyncio . sleep ( 3 )
mock_client . assert_called_once ( )
assert " original_model_group " in mock_client . call_args . kwargs
assert mock_client . call_args . kwargs [ " original_model_group " ] == " my-test-gpt "
2025-09-28 00:11:43 +08:00