mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-25 18:54:30 +00:00
* Add date picker to usage tab + Add reasoning_content token tracking across all providers on streaming (#9722) * feat(new_usage.tsx): add date picker for new usage tab allow user to look back on their usage data * feat(anthropic/chat/transformation.py): report reasoning tokens in completion token details allows usage tracking on how many reasoning tokens are actually being used * feat(streaming_chunk_builder.py): return reasoning_tokens in anthropic/openai streaming response allows tracking reasoning_token usage across providers * Fix update team metadata + fix bulk adding models on Ui (#9721) * fix(handle_add_model_submit.tsx): fix bulk adding models * fix(team_info.tsx): fix team metadata update Fixes https://github.com/BerriAI/litellm/issues/9689 * (v0) Unified file id - allow calling multiple providers with same file id (#9718) * feat(files_endpoints.py): initial commit adding 'target_model_names' support allow developer to specify all the models they want to call with the file * feat(files_endpoints.py): return unified files endpoint * test(test_files_endpoints.py): add validation test - if invalid purpose submitted * feat: more updates * feat: initial working commit of unified file id translation * fix: additional fixes * fix(router.py): remove model replace logic in jsonl on acreate_file enables file upload to work for chat completion requests as well * fix(files_endpoints.py): remove whitespace around model name * fix(azure/handler.py): return acreate_file with correct response type * fix: fix linting errors * test: fix mock test to run on github actions * fix: fix ruff errors * fix: fix file too large error * fix(utils.py): remove redundant var * test: modify test to work on github actions * test: update tests * test: more debug logs to understand ci/cd issue * test: fix test for respx * test: skip mock respx test fails on ci/cd - not clear why * fix: fix ruff check * fix: fix test * fix(model_connection_test.tsx): fix linting error * test: update unit tests
164 lines
5.5 KiB
Python
164 lines
5.5 KiB
Python
import importlib
|
|
import json
|
|
import os
|
|
import socket
|
|
import subprocess
|
|
import sys
|
|
from unittest.mock import AsyncMock, MagicMock, mock_open, patch
|
|
|
|
import click
|
|
import httpx
|
|
import pytest
|
|
import yaml
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
sys.path.insert(
|
|
0, os.path.abspath("../../..")
|
|
) # Adds the parent directory to the system-path
|
|
|
|
import litellm
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_initialize_scheduled_jobs_credentials(monkeypatch):
|
|
"""
|
|
Test that get_credentials is only called when store_model_in_db is True
|
|
"""
|
|
monkeypatch.delenv("DISABLE_PRISMA_SCHEMA_UPDATE", raising=False)
|
|
monkeypatch.delenv("STORE_MODEL_IN_DB", raising=False)
|
|
from litellm.proxy.proxy_server import ProxyStartupEvent
|
|
from litellm.proxy.utils import ProxyLogging
|
|
|
|
# Mock dependencies
|
|
mock_prisma_client = MagicMock()
|
|
mock_proxy_logging = MagicMock(spec=ProxyLogging)
|
|
mock_proxy_logging.slack_alerting_instance = MagicMock()
|
|
mock_proxy_config = AsyncMock()
|
|
|
|
with patch("litellm.proxy.proxy_server.proxy_config", mock_proxy_config), patch(
|
|
"litellm.proxy.proxy_server.store_model_in_db", False
|
|
): # set store_model_in_db to False
|
|
# Test when store_model_in_db is False
|
|
await ProxyStartupEvent.initialize_scheduled_background_jobs(
|
|
general_settings={},
|
|
prisma_client=mock_prisma_client,
|
|
proxy_budget_rescheduler_min_time=1,
|
|
proxy_budget_rescheduler_max_time=2,
|
|
proxy_batch_write_at=5,
|
|
proxy_logging_obj=mock_proxy_logging,
|
|
)
|
|
|
|
# Verify get_credentials was not called
|
|
mock_proxy_config.get_credentials.assert_not_called()
|
|
|
|
# Now test with store_model_in_db = True
|
|
with patch("litellm.proxy.proxy_server.proxy_config", mock_proxy_config), patch(
|
|
"litellm.proxy.proxy_server.store_model_in_db", True
|
|
), patch("litellm.proxy.proxy_server.get_secret_bool", return_value=True):
|
|
await ProxyStartupEvent.initialize_scheduled_background_jobs(
|
|
general_settings={},
|
|
prisma_client=mock_prisma_client,
|
|
proxy_budget_rescheduler_min_time=1,
|
|
proxy_budget_rescheduler_max_time=2,
|
|
proxy_batch_write_at=5,
|
|
proxy_logging_obj=mock_proxy_logging,
|
|
)
|
|
|
|
# Verify get_credentials was called both directly and scheduled
|
|
assert mock_proxy_config.get_credentials.call_count == 1 # Direct call
|
|
|
|
# Verify a scheduled job was added for get_credentials
|
|
mock_scheduler_calls = [
|
|
call[0] for call in mock_proxy_config.get_credentials.mock_calls
|
|
]
|
|
assert len(mock_scheduler_calls) > 0
|
|
|
|
|
|
# Mock Prisma
|
|
class MockPrisma:
|
|
def __init__(self, database_url=None, proxy_logging_obj=None, http_client=None):
|
|
self.database_url = database_url
|
|
self.proxy_logging_obj = proxy_logging_obj
|
|
self.http_client = http_client
|
|
|
|
async def connect(self):
|
|
pass
|
|
|
|
async def disconnect(self):
|
|
pass
|
|
|
|
|
|
mock_prisma = MockPrisma()
|
|
|
|
|
|
@patch(
|
|
"litellm.proxy.proxy_server.ProxyStartupEvent._setup_prisma_client",
|
|
return_value=mock_prisma,
|
|
)
|
|
@pytest.mark.asyncio
|
|
async def test_aaaproxy_startup_master_key(mock_prisma, monkeypatch, tmp_path):
|
|
"""
|
|
Test that master_key is correctly loaded from either config.yaml or environment variables
|
|
"""
|
|
import yaml
|
|
from fastapi import FastAPI
|
|
|
|
# Import happens here - this is when the module probably reads the config path
|
|
from litellm.proxy.proxy_server import proxy_startup_event
|
|
|
|
# Mock the Prisma import
|
|
monkeypatch.setattr("litellm.proxy.proxy_server.PrismaClient", MockPrisma)
|
|
|
|
# Create test app
|
|
app = FastAPI()
|
|
|
|
# Test Case 1: Master key from config.yaml
|
|
test_master_key = "sk-12345"
|
|
test_config = {"general_settings": {"master_key": test_master_key}}
|
|
|
|
# Create a temporary config file
|
|
config_path = tmp_path / "config.yaml"
|
|
with open(config_path, "w") as f:
|
|
yaml.dump(test_config, f)
|
|
|
|
print(f"SET ENV VARIABLE - CONFIG_FILE_PATH, str(config_path): {str(config_path)}")
|
|
# Second setting of CONFIG_FILE_PATH to a different value
|
|
monkeypatch.setenv("CONFIG_FILE_PATH", str(config_path))
|
|
print(f"config_path: {config_path}")
|
|
print(f"os.getenv('CONFIG_FILE_PATH'): {os.getenv('CONFIG_FILE_PATH')}")
|
|
async with proxy_startup_event(app):
|
|
from litellm.proxy.proxy_server import master_key
|
|
|
|
assert master_key == test_master_key
|
|
|
|
# Test Case 2: Master key from environment variable
|
|
test_env_master_key = "sk-67890"
|
|
|
|
# Create empty config
|
|
empty_config = {"general_settings": {}}
|
|
with open(config_path, "w") as f:
|
|
yaml.dump(empty_config, f)
|
|
|
|
monkeypatch.setenv("LITELLM_MASTER_KEY", test_env_master_key)
|
|
print("test_env_master_key: {}".format(test_env_master_key))
|
|
async with proxy_startup_event(app):
|
|
from litellm.proxy.proxy_server import master_key
|
|
|
|
assert master_key == test_env_master_key
|
|
|
|
# Test Case 3: Master key with os.environ prefix
|
|
test_resolved_key = "sk-resolved-key"
|
|
test_config_with_prefix = {
|
|
"general_settings": {"master_key": "os.environ/CUSTOM_MASTER_KEY"}
|
|
}
|
|
|
|
# Create config with os.environ prefix
|
|
with open(config_path, "w") as f:
|
|
yaml.dump(test_config_with_prefix, f)
|
|
|
|
monkeypatch.setenv("CUSTOM_MASTER_KEY", test_resolved_key)
|
|
async with proxy_startup_event(app):
|
|
from litellm.proxy.proxy_server import master_key
|
|
|
|
assert master_key == test_resolved_key
|