Spaces:
Configuration error
Configuration error
# What this tests ? | |
## Tests /models and /model/* endpoints | |
import pytest | |
import asyncio | |
import aiohttp | |
import os | |
import dotenv | |
from dotenv import load_dotenv | |
load_dotenv() | |
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() | |
async def get_models(session, key): | |
url = "http://0.0.0.0:4000/models" | |
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 /models") | |
print(response_text) | |
print() | |
if status != 200: | |
raise Exception(f"Request did not return a 200 status code: {status}") | |
return await response.json() | |
async def test_get_models(): | |
async with aiohttp.ClientSession() as session: | |
key_gen = await generate_key(session=session) | |
key = key_gen["key"] | |
await get_models(session=session, key=key) | |
async def add_models(session, model_id="123", model_name="azure-gpt-3.5", key="sk-1234", team_id=None): | |
url = "http://0.0.0.0:4000/model/new" | |
headers = { | |
"Authorization": f"Bearer {key}", | |
"Content-Type": "application/json", | |
} | |
data = { | |
"model_name": model_name, | |
"litellm_params": { | |
"model": "azure/chatgpt-v-3", | |
"api_key": "os.environ/AZURE_API_KEY", | |
"api_base": "https://openai-gpt-4-test-v-1.openai.azure.com/", | |
"api_version": "2023-05-15", | |
}, | |
"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 | |
async def update_model(session, model_id="123", model_name="azure-gpt-3.5", key="sk-1234"): | |
url = "http://0.0.0.0:4000/model/update" | |
headers = { | |
"Authorization": f"Bearer {key}", | |
"Content-Type": "application/json", | |
} | |
data = { | |
"model_name": model_name, | |
"litellm_params": { | |
"model": "azure/chatgpt-v-3", | |
"api_key": "os.environ/AZURE_API_KEY", | |
"api_base": "https://openai-gpt-4-test-v-1.openai.azure.com/", | |
"api_version": "2023-05-15", | |
}, | |
"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}") | |
response_json = await response.json() | |
return response_json | |
async def get_model_info(session, key, litellm_model_id=None): | |
""" | |
Make sure only models user has access to are returned | |
""" | |
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" | |
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() | |
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() | |
async def chat_completion(session, key, model="azure-gpt-3.5"): | |
url = "http://0.0.0.0:4000/chat/completions" | |
headers = { | |
"Authorization": f"Bearer {key}", | |
"Content-Type": "application/json", | |
} | |
data = { | |
"model": model, | |
"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}") | |
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" | |
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 | |
) | |
async def delete_model(session, model_id="123", key="sk-1234"): | |
""" | |
Make sure only models user has access to are returned | |
""" | |
url = "http://0.0.0.0:4000/model/delete" | |
headers = { | |
"Authorization": f"Bearer {key}", | |
"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() | |
async def test_add_and_delete_models(): | |
""" | |
- Add model | |
- Call new model -> expect to pass | |
- Delete model | |
- Call model -> expect to fail | |
""" | |
import uuid | |
async with aiohttp.ClientSession() as session: | |
key_gen = await generate_key(session=session) | |
key = key_gen["key"] | |
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 | |
await asyncio.sleep(10) | |
await chat_completion(session=session, key=key, model=model_name) | |
await delete_model(session=session, model_id=model_id) | |
try: | |
await chat_completion(session=session, key=key, model=model_name) | |
pytest.fail(f"Expected call to fail.") | |
except Exception: | |
pass | |
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": { | |
"model": "azure/chatgpt-v-3", | |
"api_key": os.getenv("AZURE_API_KEY"), | |
"api_base": "https://openai-gpt-4-test-v-1.openai.azure.com/", | |
"api_version": "2023-05-15", | |
}, | |
"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}") | |
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] | |
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 | |
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 | |
""" | |
import uuid | |
async with aiohttp.ClientSession() as session: | |
key_gen = await generate_key(session=session) | |
key = key_gen["key"] | |
master_key = "sk-1234" | |
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) | |
_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) | |
await asyncio.sleep(30) | |
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( | |
session=session, key=master_key, model_name=model_name | |
) | |
_healthy_endpooint = _health_info["healthy_endpoints"][0] | |
assert _health_info["healthy_count"] == 1 | |
assert ( | |
_healthy_endpooint["model"] == "azure/chatgpt-v-3" | |
) # this is the model that got added | |
# 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" | |
# cleanup | |
await delete_model(session=session, model_id=model_id) | |
async def test_get_personal_models_for_user(): | |
""" | |
Test /models endpoint with team | |
""" | |
from tests.test_users import new_user | |
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" | |
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) | |
expected_models = [ | |
"anthropic/claude-3-5-haiku-20241022", | |
"anthropic/claude-3-opus-20240229", | |
] | |
model_group_info = await get_model_group_info(session=session, key="sk-1234") | |
print(model_group_info) | |
has_anthropic_claude_3_5_haiku = False | |
has_anthropic_claude_3_opus = False | |
for model in model_group_info["data"]: | |
if model["model_group"] == "anthropic/claude-3-5-haiku-20241022": | |
has_anthropic_claude_3_5_haiku = True | |
if model["model_group"] == "anthropic/claude-3-opus-20240229": | |
has_anthropic_claude_3_opus = True | |
assert has_anthropic_claude_3_5_haiku and has_anthropic_claude_3_opus | |
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 | |
""" | |
from tests.test_users import new_user | |
from tests.test_team import new_team | |
import uuid | |
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 | |
model_data = await add_models(session=session, model_id=model_id, model_name=model_name, key=user_api_key, team_id=team_id) | |
model_id = model_data["model_id"] | |
# Update model | |
model_data = await update_model(session=session, model_id=model_id, model_name=model_name, key=user_api_key) | |
model_id = model_data["model_id"] | |
# Delete model | |
await delete_model(session=session, model_id=model_id, key=user_api_key) | |