2024-01-20 06:54:15 +08:00
# What this tests ?
## Tests /models and /model/* endpoints
import pytest
import asyncio
import aiohttp
2024-04-14 01:09:18 +08:00
import os
import dotenv
from dotenv import load_dotenv
load_dotenv ( )
2024-01-20 06:54:15 +08:00
2025-06-14 12:20:25 +08:00
2024-01-20 06:54:15 +08:00
async def generate_key ( session , models = [ ] ) :
url = " http://0.0.0.0:4000/key/generate "
headers = { " Authorization " : " Bearer sk-1234 " , " Content-Type " : " application/json " }
data = {
" models " : models ,
" duration " : None ,
}
async with session . post ( url , headers = headers , json = data ) as response :
status = response . status
response_text = await response . text ( )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
return await response . json ( )
2025-06-14 12:20:25 +08:00
async def get_models ( session , key , only_model_access_groups = False ) :
2024-01-20 06:54:15 +08:00
url = " http://0.0.0.0:4000/models "
2025-06-14 12:20:25 +08:00
if only_model_access_groups :
url + = " ?only_model_access_groups=True "
2024-01-20 06:54:15 +08:00
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
async with session . get ( url , headers = headers ) as response :
status = response . status
response_text = await response . text ( )
2024-04-14 01:49:41 +08:00
print ( " response from /models " )
2024-01-20 06:54:15 +08:00
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
2025-02-12 11:37:43 +08:00
return await response . json ( )
2024-01-20 06:54:15 +08:00
@pytest.mark.asyncio
2025-06-14 12:20:25 +08:00
async def test_get_models_multiple_tests ( ) :
2024-01-20 06:54:15 +08:00
async with aiohttp . ClientSession ( ) as session :
key_gen = await generate_key ( session = session )
key = key_gen [ " key " ]
2025-06-14 12:20:25 +08:00
models = await get_models ( session = session , key = key )
print ( f " \n \n models: { models } " )
assert len ( models [ " data " ] ) > 0
## Test only_model_access_groups
new_response = await get_models (
session = session , key = key , only_model_access_groups = True
)
print ( f " \n \n new_response: { new_response } " )
assert (
len ( new_response [ " data " ] ) == 0
) # no model access groups set on config.yaml
2024-01-20 06:54:15 +08:00
2025-06-14 12:20:25 +08:00
async def add_models (
session , model_id = " 123 " , model_name = " azure-gpt-3.5 " , key = " sk-1234 " , team_id = None
) :
2024-01-20 06:54:15 +08:00
url = " http://0.0.0.0:4000/model/new "
headers = {
2025-04-02 13:28:15 +08:00
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
data = {
" model_name " : model_name ,
" litellm_params " : {
2025-09-28 01:01:14 +08:00
" model " : " openai/gpt-4.1-nano " ,
" api_key " : " os.environ/OPENAI_API_KEY " ,
2025-04-02 13:28:15 +08:00
} ,
" model_info " : { " id " : model_id } ,
}
if team_id :
data [ " model_info " ] [ " team_id " ] = team_id
async with session . post ( url , headers = headers , json = data ) as response :
status = response . status
response_text = await response . text ( )
print ( f " Add models { response_text } " )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
response_json = await response . json ( )
return response_json
2025-06-14 12:20:25 +08:00
async def update_model (
session , model_id = " 123 " , model_name = " azure-gpt-3.5 " , key = " sk-1234 "
) :
2025-04-02 13:28:15 +08:00
url = " http://0.0.0.0:4000/model/update "
headers = {
" Authorization " : f " Bearer { key } " ,
2024-01-20 06:54:15 +08:00
" Content-Type " : " application/json " ,
}
data = {
2024-04-16 09:34:40 +08:00
" model_name " : model_name ,
2024-01-20 06:54:15 +08:00
" litellm_params " : {
2025-09-28 01:01:14 +08:00
" model " : " openai/gpt-4.1-nano " ,
" api_key " : " os.environ/OPENAI_API_KEY " ,
2024-01-20 06:54:15 +08:00
} ,
" model_info " : { " id " : model_id } ,
}
async with session . post ( url , headers = headers , json = data ) as response :
status = response . status
response_text = await response . text ( )
print ( f " Add models { response_text } " )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
2024-04-16 08:37:45 +08:00
response_json = await response . json ( )
return response_json
2024-01-20 06:54:15 +08:00
2025-01-22 00:19:07 +08:00
async def get_model_info ( session , key , litellm_model_id = None ) :
2024-01-20 06:54:15 +08:00
"""
Make sure only models user has access to are returned
"""
2025-01-22 00:19:07 +08:00
if litellm_model_id :
url = f " http://0.0.0.0:4000/model/info?litellm_model_id= { litellm_model_id } "
else :
url = " http://0.0.0.0:4000/model/info "
2024-01-20 06:54:15 +08:00
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
async with session . get ( url , headers = headers ) as response :
status = response . status
response_text = await response . text ( )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
return await response . json ( )
2025-02-12 11:37:43 +08:00
async def get_model_group_info ( session , key ) :
url = " http://0.0.0.0:4000/model_group/info "
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
async with session . get ( url , headers = headers ) as response :
status = response . status
response_text = await response . text ( )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
return await response . json ( )
2024-04-14 01:09:18 +08:00
async def chat_completion ( session , key , model = " azure-gpt-3.5 " ) :
2024-01-20 06:54:15 +08:00
url = " http://0.0.0.0:4000/chat/completions "
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
data = {
2024-04-14 01:09:18 +08:00
" model " : model ,
2024-01-20 06:54:15 +08:00
" messages " : [
{ " role " : " system " , " content " : " You are a helpful assistant. " } ,
{ " role " : " user " , " content " : " Hello! " } ,
] ,
}
async with session . post ( url , headers = headers , json = data ) as response :
status = response . status
response_text = await response . text ( )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
@pytest.mark.asyncio
async def test_get_models ( ) :
"""
Get models user has access to
"""
async with aiohttp . ClientSession ( ) as session :
key_gen = await generate_key ( session = session , models = [ " gpt-4 " ] )
key = key_gen [ " key " ]
response = await get_model_info ( session = session , key = key )
models = [ m [ " model_name " ] for m in response [ " data " ] ]
for m in models :
assert m == " gpt-4 "
2025-01-22 00:19:07 +08:00
@pytest.mark.asyncio
async def test_get_specific_model ( ) :
"""
Return specific model info
Ensure value of model_info is same as on ` / model / info ` ( no id set )
"""
async with aiohttp . ClientSession ( ) as session :
key_gen = await generate_key ( session = session , models = [ " gpt-4 " ] )
key = key_gen [ " key " ]
response = await get_model_info ( session = session , key = key )
models = [ m [ " model_name " ] for m in response [ " data " ] ]
model_specific_info = None
for idx , m in enumerate ( models ) :
assert m == " gpt-4 "
litellm_model_id = response [ " data " ] [ idx ] [ " model_info " ] [ " id " ]
model_specific_info = response [ " data " ] [ idx ]
assert litellm_model_id is not None
response = await get_model_info (
session = session , key = key , litellm_model_id = litellm_model_id
)
assert response [ " data " ] [ 0 ] [ " model_info " ] [ " id " ] == litellm_model_id
assert (
response [ " data " ] [ 0 ] == model_specific_info
) , " Model info is not the same. Got= {} , Expected= {} " . format (
response [ " data " ] [ 0 ] , model_specific_info
)
2025-04-02 13:28:15 +08:00
async def delete_model ( session , model_id = " 123 " , key = " sk-1234 " ) :
2024-01-20 06:54:15 +08:00
"""
Make sure only models user has access to are returned
"""
url = " http://0.0.0.0:4000/model/delete "
headers = {
2025-04-02 13:28:15 +08:00
" Authorization " : f " Bearer { key } " ,
2024-01-20 06:54:15 +08:00
" Content-Type " : " application/json " ,
}
data = { " id " : model_id }
async with session . post ( url , headers = headers , json = data ) as response :
status = response . status
response_text = await response . text ( )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
return await response . json ( )
2026-03-31 12:30:57 +08:00
@pytest.mark.skip (
reason = " Requires live proxy + OPENAI_API_KEY. Deterministic mock version in tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py::TestAddAndDeleteModelLifecycle "
)
2024-01-20 06:54:15 +08:00
@pytest.mark.asyncio
2024-04-09 04:29:03 +08:00
async def test_add_and_delete_models ( ) :
2024-01-20 06:54:15 +08:00
"""
2024-04-16 09:34:40 +08:00
- Add model
- Call new model - > expect to pass
- Delete model
- Call model - > expect to fail
2024-01-20 06:54:15 +08:00
"""
2025-09-26 06:47:01 +08:00
from litellm . _uuid import uuid
2024-04-16 09:34:40 +08:00
2024-01-20 06:54:15 +08:00
async with aiohttp . ClientSession ( ) as session :
key_gen = await generate_key ( session = session )
key = key_gen [ " key " ]
2024-04-16 09:34:40 +08:00
model_id = f " 12345_ { uuid . uuid4 ( ) } "
model_name = f " { uuid . uuid4 ( ) } "
response = await add_models (
session = session , model_id = model_id , model_name = model_name
)
assert response [ " model_id " ] == model_id
2024-04-16 08:37:45 +08:00
await asyncio . sleep ( 10 )
2024-04-16 09:34:40 +08:00
await chat_completion ( session = session , key = key , model = model_name )
2024-01-20 06:54:15 +08:00
await delete_model ( session = session , model_id = model_id )
2024-04-16 09:34:40 +08:00
try :
await chat_completion ( session = session , key = key , model = model_name )
pytest . fail ( f " Expected call to fail. " )
2024-10-02 07:44:20 +08:00
except Exception :
2024-04-16 09:34:40 +08:00
pass
2024-04-14 01:09:18 +08:00
async def add_model_for_health_checking ( session , model_id = " 123 " ) :
url = " http://0.0.0.0:4000/model/new "
headers = {
" Authorization " : f " Bearer sk-1234 " ,
" Content-Type " : " application/json " ,
}
data = {
" model_name " : f " azure-model-health-check- { model_id } " ,
" litellm_params " : {
2025-09-28 01:59:25 +08:00
" model " : " gpt-4.1-nano " ,
" api_key " : os . getenv ( " OPENAI_API_KEY " ) ,
2024-04-14 01:09:18 +08:00
} ,
" model_info " : { " id " : model_id } ,
}
async with session . post ( url , headers = headers , json = data ) as response :
status = response . status
response_text = await response . text ( )
print ( f " Add models { response_text } " )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
async def get_model_info_v2 ( session , key ) :
url = " http://0.0.0.0:4000/v2/model/info "
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
async with session . get ( url , headers = headers ) as response :
status = response . status
response_text = await response . text ( )
print ( " response from v2/model/info " )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
2024-05-11 04:43:19 +08:00
async def get_specific_model_info_v2 ( session , key , model_name ) :
url = " http://0.0.0.0:4000/v2/model/info?debug=True&model= " + model_name
print ( " running /model/info check for model= " , model_name )
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
async with session . get ( url , headers = headers ) as response :
status = response . status
response_text = await response . text ( )
print ( " response from v2/model/info " )
print ( response_text )
print ( )
_json_response = await response . json ( )
print ( " JSON response from /v2/model/info?model= " , model_name , _json_response )
_model_info = _json_response [ " data " ]
assert len ( _model_info ) == 1 , f " Expected 1 model, got { len ( _model_info ) } "
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
return _model_info [ 0 ]
2024-04-14 01:09:18 +08:00
async def get_model_health ( session , key , model_name ) :
url = " http://0.0.0.0:4000/health?model= " + model_name
headers = {
" Authorization " : f " Bearer { key } " ,
" Content-Type " : " application/json " ,
}
async with session . get ( url , headers = headers ) as response :
status = response . status
response_text = await response . json ( )
print ( " response from /health?model= " , model_name )
print ( response_text )
print ( )
if status != 200 :
raise Exception ( f " Request did not return a 200 status code: { status } " )
return response_text
@pytest.mark.asyncio
async def test_add_model_run_health ( ) :
"""
Add model
Call / model / info and v2 / model / info
- > Admin UI calls v2 / model / info
Call / chat / completions
Call / health
- > Ensure the health check for the endpoint is working as expected
"""
2025-09-26 06:47:01 +08:00
from litellm . _uuid import uuid
2024-04-14 01:09:18 +08:00
async with aiohttp . ClientSession ( ) as session :
key_gen = await generate_key ( session = session )
key = key_gen [ " key " ]
2024-04-17 09:13:40 +08:00
master_key = " sk-1234 "
2024-04-14 01:09:18 +08:00
model_id = str ( uuid . uuid4 ( ) )
model_name = f " azure-model-health-check- { model_id } "
print ( " adding model " , model_name )
await add_model_for_health_checking ( session = session , model_id = model_id )
2024-05-11 04:43:19 +08:00
_old_model_info = await get_specific_model_info_v2 (
session = session , key = key , model_name = model_name
)
print ( " model info before test " , _old_model_info )
2024-04-14 03:30:26 +08:00
await asyncio . sleep ( 30 )
2024-04-14 01:09:18 +08:00
print ( " calling /model/info " )
await get_model_info ( session = session , key = key )
print ( " calling v2/model/info " )
await get_model_info_v2 ( session = session , key = key )
print ( " calling /chat/completions -> expect to work " )
await chat_completion ( session = session , key = key , model = model_name )
print ( " calling /health?model= " , model_name )
_health_info = await get_model_health (
2024-04-17 09:13:40 +08:00
session = session , key = master_key , model_name = model_name
2024-04-14 01:09:18 +08:00
)
_healthy_endpooint = _health_info [ " healthy_endpoints " ] [ 0 ]
assert _health_info [ " healthy_count " ] == 1
assert (
2025-09-28 01:59:25 +08:00
_healthy_endpooint [ " model " ] == " gpt-4.1-nano "
2024-04-14 01:09:18 +08:00
) # this is the model that got added
2024-05-11 04:43:19 +08:00
# assert httpx client is is unchanges
await asyncio . sleep ( 10 )
_model_info_after_test = await get_specific_model_info_v2 (
session = session , key = key , model_name = model_name
)
print ( " model info after test " , _model_info_after_test )
old_openai_client = _old_model_info [ " openai_client " ]
new_openai_client = _model_info_after_test [ " openai_client " ]
print ( " old openai client " , old_openai_client )
print ( " new openai client " , new_openai_client )
"""
PROD TEST - This is extremly important
The OpenAI client used should be the same after 30 seconds
It is a serious bug if the openai client does not match here
"""
assert (
old_openai_client == new_openai_client
) , " OpenAI client does not match for the same model after 30 seconds "
2024-04-14 01:09:18 +08:00
# cleanup
await delete_model ( session = session , model_id = model_id )
2025-02-12 11:37:43 +08:00
2025-06-14 12:20:25 +08:00
2025-04-27 09:24:49 +08:00
@pytest.mark.asyncio
async def test_get_personal_models_for_user ( ) :
"""
Test / models endpoint with team
"""
2025-05-28 07:14:49 +08:00
from tests . test_users import new_user
2025-06-14 12:20:25 +08:00
2025-04-27 09:24:49 +08:00
async with aiohttp . ClientSession ( ) as session :
# Creat a user
user_data = await new_user ( session = session , i = 0 , models = [ " gpt-3.5-turbo " ] )
user_id = user_data [ " user_id " ]
user_api_key = user_data [ " key " ]
model_group_info = await get_model_group_info ( session = session , key = user_api_key )
print ( model_group_info )
assert len ( model_group_info [ " data " ] ) == 1
assert model_group_info [ " data " ] [ 0 ] [ " model_group " ] == " gpt-3.5-turbo "
2025-02-12 11:37:43 +08:00
2025-06-14 12:20:25 +08:00
2025-02-12 11:37:43 +08:00
@pytest.mark.asyncio
async def test_model_group_info_e2e ( ) :
"""
Test / model / group / info endpoint
"""
async with aiohttp . ClientSession ( ) as session :
models = await get_models ( session = session , key = " sk-1234 " )
print ( models )
model_group_info = await get_model_group_info ( session = session , key = " sk-1234 " )
print ( model_group_info )
2026-03-13 08:01:25 +08:00
# Check that the endpoint returns data and contains the wildcard
# anthropic model group from the proxy config
has_anthropic_wildcard = False
2025-02-12 11:37:43 +08:00
for model in model_group_info [ " data " ] :
2026-03-13 08:01:25 +08:00
if model [ " model_group " ] == " anthropic/* " :
has_anthropic_wildcard = True
2025-02-12 11:37:43 +08:00
2026-03-13 08:01:25 +08:00
assert has_anthropic_wildcard , (
f " Expected ' anthropic/* ' in model groups, got: "
f " { [ m [ ' model_group ' ] for m in model_group_info [ ' data ' ] ] } "
)
2025-04-02 13:28:15 +08:00
@pytest.mark.asyncio
async def test_team_model_e2e ( ) :
"""
Test team model e2e
- create team
- create user
- add user to team as admin
- add model to team
- update model
- delete model
"""
2025-05-28 07:14:49 +08:00
from tests . test_users import new_user
from tests . test_team import new_team
2025-09-26 06:47:01 +08:00
from litellm . _uuid import uuid
2025-06-14 12:20:25 +08:00
2025-04-02 13:28:15 +08:00
async with aiohttp . ClientSession ( ) as session :
# Creat a user
user_data = await new_user ( session = session , i = 0 )
user_id = user_data [ " user_id " ]
user_api_key = user_data [ " key " ]
# Create a team
member_list = [
{ " role " : " admin " , " user_id " : user_id } ,
]
team_data = await new_team ( session = session , member_list = member_list , i = 0 )
team_id = team_data [ " team_id " ]
model_id = str ( uuid . uuid4 ( ) )
model_name = " my-test-model "
# Add model to team
2025-06-14 12:20:25 +08:00
model_data = await add_models (
session = session ,
model_id = model_id ,
model_name = model_name ,
key = user_api_key ,
team_id = team_id ,
)
2025-04-02 13:28:15 +08:00
model_id = model_data [ " model_id " ]
# Update model
2025-06-14 12:20:25 +08:00
model_data = await update_model (
session = session , model_id = model_id , model_name = model_name , key = user_api_key
)
2025-04-02 13:28:15 +08:00
model_id = model_data [ " model_id " ]
2025-06-14 12:20:25 +08:00
2025-04-02 13:28:15 +08:00
# Delete model
await delete_model ( session = session , model_id = model_id , key = user_api_key )