Change litellm_model_cost_map to public route

This commit is contained in:
yuneng-jiang 2025-11-20 20:59:31 -08:00
parent b50790aaad
commit d672263fe3
9 changed files with 57 additions and 53 deletions

View file

@ -516,6 +516,7 @@ class LiteLLMRoutes(enum.Enum):
"/.well-known/litellm-ui-config",
"/public/model_hub",
"/public/agent_hub",
"/public/litellm_model_cost_map",
]
)
@ -538,7 +539,6 @@ class LiteLLMRoutes(enum.Enum):
"/global/predict/spend/logs",
"/global/activity",
"/health/services",
"/get/litellm_model_cost_map",
] + info_routes
internal_user_routes = (

View file

@ -9710,31 +9710,6 @@ async def config_yaml_endpoint(config_info: ConfigYAML):
return {"hello": "world"}
@router.get(
"/get/litellm_model_cost_map",
include_in_schema=False,
dependencies=[Depends(user_api_key_auth)],
)
async def get_litellm_model_cost_map(
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
# Check if user is admin
if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN:
raise HTTPException(
status_code=403,
detail=f"Access denied. Admin role required. Current role: {user_api_key_dict.user_role}",
)
try:
_model_cost_map = litellm.model_cost
return _model_cost_map
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Internal Server Error ({str(e)})",
)
@router.post(
"/reload/model_cost_map",
tags=["model management"],

View file

@ -116,3 +116,24 @@ async def get_provider_fields() -> List[ProviderCreateInfo]:
"""
return get_provider_create_metadata()
@router.get(
"/public/litellm_model_cost_map",
tags=["public", "model management"],
)
async def get_litellm_model_cost_map():
"""
Public endpoint to get the LiteLLM model cost map.
Returns pricing information for all supported models.
"""
import litellm
try:
_model_cost_map = litellm.model_cost
return _model_cost_map
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Internal Server Error ({str(e)})",
)

View file

@ -64,3 +64,28 @@ def test_get_provider_fields_returns_metadata():
}
assert {"api_base", "api_key"}.issubset(runway_credential_keys)
def test_get_litellm_model_cost_map_returns_cost_map():
app = FastAPI()
app.include_router(router)
client = TestClient(app)
response = client.get("/public/litellm_model_cost_map")
assert response.status_code == 200
payload = response.json()
assert isinstance(payload, dict)
assert len(payload) > 0, "Expected model cost map to contain at least one model"
# Verify the structure contains expected keys for at least one model
# Check for a common model like gpt-4 or gpt-3.5-turbo
model_keys = list(payload.keys())
assert len(model_keys) > 0
# Verify at least one model has expected cost fields
sample_model = model_keys[0]
sample_model_data = payload[sample_model]
assert isinstance(sample_model_data, dict)
# Check for common cost fields that should be present
assert "input_cost_per_token" in sample_model_data or "output_cost_per_token" in sample_model_data

View file

@ -1338,31 +1338,17 @@ class TestPriceDataReloadAPI:
assert "Access denied" in data["detail"]
assert "Admin role required" in data["detail"]
def test_get_model_cost_map_admin_access(self, client_with_auth):
"""Test that admin users can access the get model cost map endpoint"""
def test_get_model_cost_map_public_access(self, client_no_auth):
"""Test that the model cost map endpoint is publicly accessible"""
with patch(
"litellm.model_cost", {"gpt-3.5-turbo": {"input_cost_per_token": 0.001}}
):
response = client_with_auth.get("/get/litellm_model_cost_map")
response = client_no_auth.get("/public/litellm_model_cost_map")
assert response.status_code == 200
data = response.json()
assert "gpt-3.5-turbo" in data
def test_get_model_cost_map_non_admin_access(self, client_with_auth):
"""Test that non-admin users cannot access the get model cost map endpoint"""
# Mock non-admin user
mock_auth = MagicMock()
mock_auth.user_role = "user" # Non-admin role
app.dependency_overrides[user_api_key_auth] = lambda: mock_auth
response = client_with_auth.get("/get/litellm_model_cost_map")
assert response.status_code == 403
data = response.json()
assert "Access denied" in data["detail"]
assert "Admin role required" in data["detail"]
def test_reload_model_cost_map_error_handling(self, client_with_auth):
"""Test error handling in the reload endpoint"""
with patch(
@ -1572,7 +1558,7 @@ class TestPriceDataReloadIntegration:
assert response.status_code == 200
# Test get endpoint
response = client_with_auth.get("/get/litellm_model_cost_map")
response = client_with_auth.get("/public/litellm_model_cost_map")
assert response.status_code == 200
def test_distributed_reload_check_function(self):

View file

@ -419,7 +419,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
}
const fetchModelMap = async () => {
const data = await modelCostMap(accessToken);
const data = await modelCostMap();
console.log(`received model cost map data: ${Object.keys(data)}`);
setModelMap(data);
};

View file

@ -25,7 +25,7 @@ const PriceDataManagementTab = ({ setModelMap }: PriceDataManagementPanelProps)
onReloadSuccess={() => {
// Refresh the model map after successful reload
const fetchModelMap = async () => {
const data = await modelCostMap(accessToken);
const data = await modelCostMap();
setModelMap(data);
};
fetchModelMap();

View file

@ -306,13 +306,12 @@ export const getOpenAPISchema = async () => {
return jsonData;
};
export const modelCostMap = async (accessToken: string) => {
export const modelCostMap = async () => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/get/litellm_model_cost_map` : `/get/litellm_model_cost_map`;
const url = proxyBaseUrl ? `${proxyBaseUrl}/public/litellm_model_cost_map` : `/public/litellm_model_cost_map`;
const response = await fetch(url, {
method: "GET",
headers: {
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
@ -6677,7 +6676,6 @@ export const getGuardrailProviderSpecificParams = async (accessToken: string) =>
}
};
export const getAgentsList = async (accessToken: string) => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/v1/agents` : `/v1/agents`;
@ -6795,7 +6793,6 @@ export const patchAgentCall = async (
}
};
export const updateGuardrailCall = async (
accessToken: string,
guardrailId: string,

View file

@ -660,7 +660,7 @@ const OldModelDashboard: React.FC<ModelDashboardProps> = ({
}
const fetchModelMap = async () => {
const data = await modelCostMap(accessToken);
const data = await modelCostMap();
console.log(`received model cost map data: ${Object.keys(data)}`);
setModelMap(data);
};
@ -1734,7 +1734,7 @@ const OldModelDashboard: React.FC<ModelDashboardProps> = ({
onReloadSuccess={() => {
// Refresh the model map after successful reload
const fetchModelMap = async () => {
const data = await modelCostMap(accessToken);
const data = await modelCostMap();
setModelMap(data);
};
fetchModelMap();