Spaces:
Configuration error
Configuration error
""" | |
Tests the following endpoints used by the UI | |
/global/spend/logs | |
/global/spend/keys | |
/global/spend/models | |
/global/activity | |
/global/activity/model | |
For all tests - test the following: | |
- Response is valid | |
- Response for Admin User is different from response from Internal User | |
""" | |
import os | |
import sys | |
import traceback | |
import uuid | |
from datetime import datetime | |
from dotenv import load_dotenv | |
from fastapi import Request | |
from fastapi.routing import APIRoute | |
load_dotenv() | |
import io | |
import os | |
import time | |
# this file is to test litellm/proxy | |
sys.path.insert( | |
0, os.path.abspath("../..") | |
) # Adds the parent directory to the system path | |
import asyncio | |
import logging | |
import pytest | |
import litellm | |
from litellm._logging import verbose_proxy_logger | |
from litellm.proxy.management_endpoints.internal_user_endpoints import ( | |
new_user, | |
user_info, | |
user_update, | |
) | |
from litellm.proxy.management_endpoints.key_management_endpoints import ( | |
delete_key_fn, | |
generate_key_fn, | |
generate_key_helper_fn, | |
info_key_fn, | |
regenerate_key_fn, | |
update_key_fn, | |
) | |
from litellm.proxy.management_endpoints.team_endpoints import ( | |
new_team, | |
team_info, | |
update_team, | |
) | |
from litellm.proxy.proxy_server import ( | |
LitellmUserRoles, | |
audio_transcriptions, | |
chat_completion, | |
completion, | |
embeddings, | |
model_list, | |
moderations, | |
user_api_key_auth, | |
) | |
from litellm.proxy.management_endpoints.customer_endpoints import ( | |
new_end_user, | |
) | |
from litellm.proxy.spend_tracking.spend_management_endpoints import ( | |
global_spend, | |
global_spend_logs, | |
global_spend_models, | |
global_spend_keys, | |
spend_key_fn, | |
spend_user_fn, | |
view_spend_logs, | |
) | |
from litellm.proxy.utils import PrismaClient, ProxyLogging, hash_token, update_spend | |
verbose_proxy_logger.setLevel(level=logging.DEBUG) | |
from starlette.datastructures import URL | |
from litellm.caching.caching import DualCache | |
from litellm.proxy._types import ( | |
DynamoDBArgs, | |
GenerateKeyRequest, | |
RegenerateKeyRequest, | |
KeyRequest, | |
LiteLLM_UpperboundKeyGenerateParams, | |
NewCustomerRequest, | |
NewTeamRequest, | |
NewUserRequest, | |
ProxyErrorTypes, | |
ProxyException, | |
UpdateKeyRequest, | |
UpdateTeamRequest, | |
UpdateUserRequest, | |
UserAPIKeyAuth, | |
) | |
proxy_logging_obj = ProxyLogging(user_api_key_cache=DualCache()) | |
def prisma_client(): | |
from litellm.proxy.proxy_cli import append_query_params | |
### add connection pool + pool timeout args | |
params = {"connection_limit": 100, "pool_timeout": 60} | |
database_url = os.getenv("DATABASE_URL") | |
modified_url = append_query_params(database_url, params) | |
os.environ["DATABASE_URL"] = modified_url | |
# Assuming PrismaClient is a class that needs to be instantiated | |
prisma_client = PrismaClient( | |
database_url=os.environ["DATABASE_URL"], proxy_logging_obj=proxy_logging_obj | |
) | |
# Reset litellm.proxy.proxy_server.prisma_client to None | |
litellm.proxy.proxy_server.litellm_proxy_budget_name = ( | |
f"litellm-proxy-budget-{time.time()}" | |
) | |
litellm.proxy.proxy_server.user_custom_key_generate = None | |
return prisma_client | |
async def test_view_daily_spend_ui(prisma_client): | |
print("prisma client=", prisma_client) | |
setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) | |
setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") | |
await litellm.proxy.proxy_server.prisma_client.connect() | |
from litellm.proxy.proxy_server import user_api_key_cache | |
spend_logs_for_admin = await global_spend_logs( | |
user_api_key_dict=UserAPIKeyAuth( | |
api_key="sk-1234", | |
user_role=LitellmUserRoles.PROXY_ADMIN, | |
), | |
api_key=None, | |
) | |
print("spend_logs_for_admin=", spend_logs_for_admin) | |
spend_logs_for_internal_user = await global_spend_logs( | |
user_api_key_dict=UserAPIKeyAuth( | |
api_key="sk-1234", user_role=LitellmUserRoles.INTERNAL_USER, user_id="1234" | |
), | |
api_key=None, | |
) | |
print("spend_logs_for_internal_user=", spend_logs_for_internal_user) | |
# Calculate total spend for admin | |
admin_total_spend = sum(log.get("spend", 0) for log in spend_logs_for_admin) | |
# Calculate total spend for internal user (0 in this case, but we'll keep it generic) | |
internal_user_total_spend = sum( | |
log.get("spend", 0) for log in spend_logs_for_internal_user | |
) | |
print("total_spend_for_admin=", admin_total_spend) | |
print("total_spend_for_internal_user=", internal_user_total_spend) | |
assert ( | |
admin_total_spend > internal_user_total_spend | |
), "Admin should have more spend than internal user" | |
async def test_global_spend_models(prisma_client): | |
print("prisma client=", prisma_client) | |
setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) | |
setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") | |
await litellm.proxy.proxy_server.prisma_client.connect() | |
# Test for admin user | |
models_spend_for_admin = await global_spend_models( | |
limit=10, | |
user_api_key_dict=UserAPIKeyAuth( | |
api_key="sk-1234", | |
user_role=LitellmUserRoles.PROXY_ADMIN, | |
), | |
) | |
print("models_spend_for_admin=", models_spend_for_admin) | |
# Test for internal user | |
models_spend_for_internal_user = await global_spend_models( | |
limit=10, | |
user_api_key_dict=UserAPIKeyAuth( | |
api_key="sk-1234", user_role=LitellmUserRoles.INTERNAL_USER, user_id="1234" | |
), | |
) | |
print("models_spend_for_internal_user=", models_spend_for_internal_user) | |
# Assertions | |
assert isinstance(models_spend_for_admin, list), "Admin response should be a list" | |
assert isinstance( | |
models_spend_for_internal_user, list | |
), "Internal user response should be a list" | |
# Check if the response has the expected shape for both admin and internal user | |
expected_keys = ["model", "total_spend"] | |
if len(models_spend_for_admin) > 0: | |
assert all( | |
key in models_spend_for_admin[0] for key in expected_keys | |
), f"Admin response should contain keys: {expected_keys}" | |
assert isinstance( | |
models_spend_for_admin[0]["model"], str | |
), "Model should be a string" | |
assert isinstance( | |
models_spend_for_admin[0]["total_spend"], (int, float) | |
), "Total spend should be a number" | |
if len(models_spend_for_internal_user) > 0: | |
assert all( | |
key in models_spend_for_internal_user[0] for key in expected_keys | |
), f"Internal user response should contain keys: {expected_keys}" | |
assert isinstance( | |
models_spend_for_internal_user[0]["model"], str | |
), "Model should be a string" | |
assert isinstance( | |
models_spend_for_internal_user[0]["total_spend"], (int, float) | |
), "Total spend should be a number" | |
# Check if the lists are sorted by total_spend in descending order | |
if len(models_spend_for_admin) > 1: | |
assert all( | |
models_spend_for_admin[i]["total_spend"] | |
>= models_spend_for_admin[i + 1]["total_spend"] | |
for i in range(len(models_spend_for_admin) - 1) | |
), "Admin response should be sorted by total_spend in descending order" | |
if len(models_spend_for_internal_user) > 1: | |
assert all( | |
models_spend_for_internal_user[i]["total_spend"] | |
>= models_spend_for_internal_user[i + 1]["total_spend"] | |
for i in range(len(models_spend_for_internal_user) - 1) | |
), "Internal user response should be sorted by total_spend in descending order" | |
# Check if admin has access to more or equal models compared to internal user | |
assert len(models_spend_for_admin) >= len( | |
models_spend_for_internal_user | |
), "Admin should have access to at least as many models as internal user" | |
# Check if the response contains expected fields | |
if len(models_spend_for_admin) > 0: | |
assert all( | |
key in models_spend_for_admin[0] for key in ["model", "total_spend"] | |
), "Admin response should contain model, total_spend, and total_tokens" | |
if len(models_spend_for_internal_user) > 0: | |
assert all( | |
key in models_spend_for_internal_user[0] for key in ["model", "total_spend"] | |
), "Internal user response should contain model, total_spend, and total_tokens" | |
async def test_global_spend_keys(prisma_client): | |
print("prisma client=", prisma_client) | |
setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) | |
setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") | |
await litellm.proxy.proxy_server.prisma_client.connect() | |
# Test for admin user | |
keys_spend_for_admin = await global_spend_keys( | |
limit=10, | |
user_api_key_dict=UserAPIKeyAuth( | |
api_key="sk-1234", | |
user_role=LitellmUserRoles.PROXY_ADMIN, | |
), | |
) | |
print("keys_spend_for_admin=", keys_spend_for_admin) | |
# Test for internal user | |
keys_spend_for_internal_user = await global_spend_keys( | |
limit=10, | |
user_api_key_dict=UserAPIKeyAuth( | |
api_key="sk-1234", user_role=LitellmUserRoles.INTERNAL_USER, user_id="1234" | |
), | |
) | |
print("keys_spend_for_internal_user=", keys_spend_for_internal_user) | |
# Assertions | |
assert isinstance(keys_spend_for_admin, list), "Admin response should be a list" | |
assert isinstance( | |
keys_spend_for_internal_user, list | |
), "Internal user response should be a list" | |
# Check if admin has access to more or equal keys compared to internal user | |
assert len(keys_spend_for_admin) >= len( | |
keys_spend_for_internal_user | |
), "Admin should have access to at least as many keys as internal user" | |
# Check if the response contains expected fields | |
if len(keys_spend_for_admin) > 0: | |
assert all( | |
key in keys_spend_for_admin[0] | |
for key in ["api_key", "total_spend", "key_alias", "key_name"] | |
), "Admin response should contain api_key, total_spend, key_alias, and key_name" | |
if len(keys_spend_for_internal_user) > 0: | |
assert all( | |
key in keys_spend_for_internal_user[0] | |
for key in ["api_key", "total_spend", "key_alias", "key_name"] | |
), "Internal user response should contain api_key, total_spend, key_alias, and key_name" | |