diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index e6294baab8..6e242ddacb 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -292,6 +292,7 @@ class LiteLLMRoutes(enum.Enum): "/team/available", "/user/info", "/model/info", + "/v1/model/info", "/v2/model/info", "/v2/key/info", "/model_group/info", @@ -386,6 +387,7 @@ class LiteLLMRoutes(enum.Enum): "/global/predict/spend/logs", "/global/activity", "/health/services", + "/get/litellm_model_cost_map", ] + info_routes internal_user_routes = [ @@ -412,6 +414,8 @@ class LiteLLMRoutes(enum.Enum): "/team/member_add", "/team/member_delete", "/model/new", + "/model/update", + "/model/delete", ] # routes that manage their own allowed/disallowed logic ## Org Admin Routes ## diff --git a/litellm/proxy/management_endpoints/model_management_endpoints.py b/litellm/proxy/management_endpoints/model_management_endpoints.py index 2a2b7eae24..f83abe77be 100644 --- a/litellm/proxy/management_endpoints/model_management_endpoints.py +++ b/litellm/proxy/management_endpoints/model_management_endpoints.py @@ -13,7 +13,7 @@ model/{model_id}/update - PATCH endpoint for model update. import asyncio import json import uuid -from typing import Optional, cast +from typing import Literal, Optional, Union, cast from fastapi import APIRouter, Depends, HTTPException, Request, status from pydantic import BaseModel @@ -23,6 +23,7 @@ from litellm.constants import LITELLM_PROXY_ADMIN_NAME from litellm.proxy._types import ( CommonProxyErrors, LiteLLM_ProxyModelTable, + LiteLLM_TeamTable, LitellmTableNames, LitellmUserRoles, ModelInfoDelete, @@ -35,6 +36,7 @@ from litellm.proxy._types import ( ) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_value_helper +from litellm.proxy.management_endpoints.common_utils import _is_user_team_admin from litellm.proxy.management_endpoints.team_endpoints import ( team_model_add, update_team, @@ -317,22 +319,111 @@ async def _add_team_model_to_db( return model_response -def check_if_team_id_matches_key( - team_id: Optional[str], user_api_key_dict: UserAPIKeyAuth -) -> bool: - can_make_call = True - if ( - user_api_key_dict.user_role - and user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN - ): +class ModelManagementAuthChecks: + """ + Common auth checks for model management endpoints + """ + + @staticmethod + def can_user_make_team_model_call( + team_id: str, + user_api_key_dict: UserAPIKeyAuth, + team_obj: Optional[LiteLLM_TeamTable] = None, + premium_user: bool = False, + ) -> Literal[True]: + if premium_user is False: + raise HTTPException( + status_code=403, + detail={"error": CommonProxyErrors.not_premium_user.value}, + ) + if ( + user_api_key_dict.user_role + and user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN + ): + return True + elif team_obj is None or not _is_user_team_admin( + user_api_key_dict=user_api_key_dict, team_obj=team_obj + ): + raise HTTPException( + status_code=403, + detail={ + "error": "Team ID={} does not match the API key's team ID={}, OR you are not the admin for this team. Check `/user/info` to verify your team admin status.".format( + team_id, user_api_key_dict.team_id + ) + }, + ) + return True + + @staticmethod + async def allow_team_model_action( + model_params: Union[Deployment, updateDeployment], + user_api_key_dict: UserAPIKeyAuth, + prisma_client: PrismaClient, + premium_user: bool, + ) -> Literal[True]: + if model_params.model_info is None or model_params.model_info.team_id is None: + return True + if model_params.model_info.team_id is not None and premium_user is not True: + raise HTTPException( + status_code=403, + detail={"error": CommonProxyErrors.not_premium_user.value}, + ) + + _existing_team_row = await prisma_client.db.litellm_teamtable.find_unique( + where={"team_id": model_params.model_info.team_id} + ) + + if _existing_team_row is None: + raise HTTPException( + status_code=400, + detail={ + "error": "Team id={} does not exist in db".format( + model_params.model_info.team_id + ) + }, + ) + existing_team_row = LiteLLM_TeamTable(**_existing_team_row.model_dump()) + + ModelManagementAuthChecks.can_user_make_team_model_call( + team_id=model_params.model_info.team_id, + user_api_key_dict=user_api_key_dict, + team_obj=existing_team_row, + premium_user=premium_user, + ) + return True + + @staticmethod + async def can_user_make_model_call( + model_params: Union[Deployment, updateDeployment], + user_api_key_dict: UserAPIKeyAuth, + prisma_client: PrismaClient, + premium_user: bool, + ) -> Literal[True]: + + ## Check team model auth + if ( + model_params.model_info is not None + and model_params.model_info.team_id is not None + ): + return ModelManagementAuthChecks.can_user_make_team_model_call( + team_id=model_params.model_info.team_id, + user_api_key_dict=user_api_key_dict, + premium_user=premium_user, + ) + ## Check non-team model auth + elif user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: + raise HTTPException( + status_code=403, + detail={ + "error": "User does not have permission to make this model call. Your role={}. You can only make model calls if you are a PROXY_ADMIN or if you are a team admin, by specifying a team_id in the model_info.".format( + user_api_key_dict.user_role + ) + }, + ) + else: + return True + return True - if team_id is None: - if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN: - can_make_call = False - else: - if user_api_key_dict.team_id != team_id: - can_make_call = False - return can_make_call #### [BETA] - This is a beta endpoint, format might change based on user feedback. - https://github.com/BerriAI/litellm/issues/964 @@ -358,6 +449,7 @@ async def delete_model( from litellm.proxy.proxy_server import ( llm_router, + premium_user, prisma_client, store_model_in_db, ) @@ -370,6 +462,23 @@ async def delete_model( }, ) + model_in_db = await prisma_client.db.litellm_proxymodeltable.find_unique( + where={"model_id": model_info.id} + ) + if model_in_db is None: + raise HTTPException( + status_code=400, + detail={"error": f"Model with id={model_info.id} not found in db"}, + ) + + model_params = Deployment(**model_in_db.model_dump()) + await ModelManagementAuthChecks.can_user_make_model_call( + model_params=model_params, + user_api_key_dict=user_api_key_dict, + prisma_client=prisma_client, + premium_user=premium_user, + ) + # update DB if store_model_in_db is True: """ @@ -464,19 +573,13 @@ async def add_new_model( }, ) - if model_params.model_info.team_id is not None and premium_user is not True: - raise HTTPException( - status_code=403, - detail={"error": CommonProxyErrors.not_premium_user.value}, - ) - - if not check_if_team_id_matches_key( - team_id=model_params.model_info.team_id, user_api_key_dict=user_api_key_dict - ): - raise HTTPException( - status_code=403, - detail={"error": "Team ID does not match the API key's team ID"}, - ) + ## Auth check + await ModelManagementAuthChecks.can_user_make_model_call( + model_params=model_params, + user_api_key_dict=user_api_key_dict, + prisma_client=prisma_client, + premium_user=premium_user, + ) model_response: Optional[LiteLLM_ProxyModelTable] = None # update DB @@ -593,6 +696,7 @@ async def update_model( from litellm.proxy.proxy_server import ( LITELLM_PROXY_ADMIN_NAME, llm_router, + premium_user, prisma_client, store_model_in_db, ) @@ -606,6 +710,14 @@ async def update_model( "error": "No DB Connected. Here's how to do it - https://docs.litellm.ai/docs/proxy/virtual_keys" }, ) + + await ModelManagementAuthChecks.can_user_make_model_call( + model_params=model_params, + user_api_key_dict=user_api_key_dict, + prisma_client=prisma_client, + premium_user=premium_user, + ) + # update DB if store_model_in_db is True: _model_id = None diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index c2044ac1d6..5d7e92fd73 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -215,7 +215,6 @@ from litellm.proxy.management_endpoints.key_management_endpoints import ( from litellm.proxy.management_endpoints.model_management_endpoints import ( _add_model_to_db, _add_team_model_to_db, - check_if_team_id_matches_key, ) from litellm.proxy.management_endpoints.model_management_endpoints import ( router as model_management_router, @@ -5494,9 +5493,40 @@ async def transform_request(request: TransformRequestBody): return return_raw_request(endpoint=request.call_type, kwargs=request.request_body) +async def _check_if_model_is_user_added( + models: List[Dict], + user_api_key_dict: UserAPIKeyAuth, + prisma_client: Optional[PrismaClient], +) -> List[Dict]: + """ + Check if model is in db + + Check if db model is 'created_by' == user_api_key_dict.user_id + + Only return models that match + """ + if prisma_client is None: + raise HTTPException( + status_code=500, + detail={"error": CommonProxyErrors.db_not_connected_error.value}, + ) + filtered_models = [] + for model in models: + id = model.get("model_info", {}).get("id", None) + if id is None: + continue + db_model = await prisma_client.db.litellm_proxymodeltable.find_unique( + where={"model_id": id} + ) + if db_model is not None: + if db_model.created_by == user_api_key_dict.user_id: + filtered_models.append(model) + return filtered_models + + @router.get( "/v2/model/info", - description="v2 - returns all the models set on the config.yaml, shows 'user_access' = True if the user has access to the model. Provides more info about each model in /models, including config.yaml descriptions (except api key and api base)", + description="v2 - returns models available to the user based on their API key permissions. Shows model info from config.yaml (except api key and api base). Filter to just user-added models with ?user_models_only=true", tags=["model management"], dependencies=[Depends(user_api_key_auth)], include_in_schema=False, @@ -5506,6 +5536,9 @@ async def model_info_v2( model: Optional[str] = fastapi.Query( None, description="Specify the model name (optional)" ), + user_models_only: Optional[bool] = fastapi.Query( + False, description="Only return models added by this user" + ), debug: Optional[bool] = False, ): """ @@ -5536,6 +5569,20 @@ async def model_info_v2( if model is not None: all_models = [m for m in all_models if m["model_name"] == model] + if user_models_only is True: + """ + Check if model is in db + + Check if db model is 'created_by' == user_api_key_dict.user_id + + Only return models that match + """ + all_models = await _check_if_model_is_user_added( + models=all_models, + user_api_key_dict=user_api_key_dict, + prisma_client=prisma_client, + ) + # fill in model info based on config.yaml and litellm model_prices_and_context_window.json for _model in all_models: # provided model_info in config.yaml diff --git a/tests/litellm/proxy/management_endpoints/test_model_management_endpoints.py b/tests/litellm/proxy/management_endpoints/test_model_management_endpoints.py new file mode 100644 index 0000000000..49e3150e61 --- /dev/null +++ b/tests/litellm/proxy/management_endpoints/test_model_management_endpoints.py @@ -0,0 +1,196 @@ +import json +import os +import sys +from typing import Optional + +import pytest +from fastapi.testclient import TestClient + +sys.path.insert( + 0, os.path.abspath("../../../..") +) # Adds the parent directory to the system path +from litellm.proxy._types import ( + LiteLLM_TeamTable, + LitellmUserRoles, + Member, + UserAPIKeyAuth, +) +from litellm.proxy.management_endpoints.model_management_endpoints import ( + ModelManagementAuthChecks, +) +from litellm.proxy.utils import PrismaClient +from litellm.types.router import Deployment, LiteLLM_Params, updateDeployment + + +class MockPrismaClient: + def __init__(self, team_exists: bool = True): + self.team_exists = team_exists + self.db = self + + async def find_unique(self, where): + if self.team_exists: + return LiteLLM_TeamTable( + team_id=where["team_id"], + team_alias="test_team", + members_with_roles=[Member(user_id="test_user", role="admin")], + ) + return None + + @property + def litellm_teamtable(self): + return self + + +class TestModelManagementAuthChecks: + def setup_method(self): + """Setup test cases""" + self.admin_user = UserAPIKeyAuth( + user_id="test_admin", user_role=LitellmUserRoles.PROXY_ADMIN + ) + + self.normal_user = UserAPIKeyAuth( + user_id="test_user", user_role=LitellmUserRoles.INTERNAL_USER + ) + + self.team_admin_user = UserAPIKeyAuth( + user_id="test_user", + team_id="test_team", + user_role=LitellmUserRoles.INTERNAL_USER, + ) + + @pytest.mark.asyncio + async def test_can_user_make_team_model_call_admin_success(self): + """Test that admin users can make team model calls""" + result = ModelManagementAuthChecks.can_user_make_team_model_call( + team_id="test_team", user_api_key_dict=self.admin_user, premium_user=True + ) + assert result is True + + @pytest.mark.asyncio + async def test_can_user_make_team_model_call_non_premium_fails(self): + """Test that non-premium users cannot make team model calls""" + with pytest.raises(Exception) as exc_info: + ModelManagementAuthChecks.can_user_make_team_model_call( + team_id="test_team", + user_api_key_dict=self.admin_user, + premium_user=False, + ) + assert "403" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_can_user_make_team_model_call_team_admin_success(self): + """Test that team admins can make calls for their team""" + team_obj = LiteLLM_TeamTable( + team_id="test_team", + team_alias="test_team", + members_with_roles=[ + Member(user_id=self.team_admin_user.user_id, role="admin") + ], + ) + + result = ModelManagementAuthChecks.can_user_make_team_model_call( + team_id="test_team", + user_api_key_dict=self.team_admin_user, + team_obj=team_obj, + premium_user=True, + ) + assert result is True + + @pytest.mark.asyncio + async def test_allow_team_model_action_success(self): + """Test successful team model action""" + model_params = Deployment( + model_name="test_model", + litellm_params=LiteLLM_Params(model="test_model", team_id="test_team"), + model_info={"team_id": "test_team"}, + ) + prisma_client = MockPrismaClient(team_exists=True) + + result = await ModelManagementAuthChecks.allow_team_model_action( + model_params=model_params, + user_api_key_dict=self.admin_user, + prisma_client=prisma_client, + premium_user=True, + ) + assert result is True + + @pytest.mark.asyncio + async def test_allow_team_model_action_non_premium_fails(self): + """Test team model action fails for non-premium users""" + model_params = Deployment( + model_name="test_model", + litellm_params=LiteLLM_Params(model="test_model", team_id="test_team"), + model_info={"team_id": "test_team"}, + ) + prisma_client = MockPrismaClient(team_exists=True) + + with pytest.raises(Exception) as exc_info: + await ModelManagementAuthChecks.allow_team_model_action( + model_params=model_params, + user_api_key_dict=self.admin_user, + prisma_client=prisma_client, + premium_user=False, + ) + assert "403" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_allow_team_model_action_nonexistent_team_fails(self): + """Test team model action fails for non-existent team""" + model_params = Deployment( + model_name="test_model", + litellm_params=LiteLLM_Params( + model="test_model", + ), + model_info={"team_id": "nonexistent_team"}, + ) + prisma_client = MockPrismaClient(team_exists=False) + + with pytest.raises(Exception) as exc_info: + await ModelManagementAuthChecks.allow_team_model_action( + model_params=model_params, + user_api_key_dict=self.admin_user, + prisma_client=prisma_client, + premium_user=True, + ) + assert "400" in str(exc_info.value) + + @pytest.mark.asyncio + async def test_can_user_make_model_call_admin_success(self): + """Test that admin users can make any model call""" + model_params = Deployment( + model_name="test_model", + litellm_params=LiteLLM_Params( + model="test_model", + ), + model_info={"team_id": "test_team"}, + ) + prisma_client = MockPrismaClient() + + result = await ModelManagementAuthChecks.can_user_make_model_call( + model_params=model_params, + user_api_key_dict=self.admin_user, + prisma_client=prisma_client, + premium_user=True, + ) + assert result is True + + @pytest.mark.asyncio + async def test_can_user_make_model_call_normal_user_fails(self): + """Test that normal users cannot make model calls""" + model_params = Deployment( + model_name="test_model", + litellm_params=LiteLLM_Params( + model="test_model", + ), + model_info={"team_id": "test_team"}, + ) + prisma_client = MockPrismaClient() + + with pytest.raises(Exception) as exc_info: + await ModelManagementAuthChecks.can_user_make_model_call( + model_params=model_params, + user_api_key_dict=self.normal_user, + prisma_client=prisma_client, + premium_user=True, + ) + assert "403" in str(exc_info.value) diff --git a/tests/local_testing/test_add_update_models.py b/tests/local_testing/test_add_update_models.py index 1ea714d9b7..163687c1c2 100644 --- a/tests/local_testing/test_add_update_models.py +++ b/tests/local_testing/test_add_update_models.py @@ -110,49 +110,6 @@ async def test_add_new_model(prisma_client): assert _new_model_in_db is not None -@pytest.mark.parametrize( - "team_id, key_team_id, user_role, expected_result", - [ - ("1234", "1234", LitellmUserRoles.PROXY_ADMIN.value, True), - ( - "1234", - "1235", - LitellmUserRoles.PROXY_ADMIN.value, - True, - ), # proxy admin can add models for any team - (None, "1234", LitellmUserRoles.PROXY_ADMIN.value, True), - (None, None, LitellmUserRoles.PROXY_ADMIN.value, True), - ( - "1234", - "1234", - LitellmUserRoles.INTERNAL_USER.value, - True, - ), # internal users can add models for their team - ("1234", "1235", LitellmUserRoles.INTERNAL_USER.value, False), - (None, "1234", LitellmUserRoles.INTERNAL_USER.value, False), - ( - None, - None, - LitellmUserRoles.INTERNAL_USER.value, - False, - ), # internal users cannot add models by default - ], -) -def test_can_add_model(team_id, key_team_id, user_role, expected_result): - from litellm.proxy.proxy_server import check_if_team_id_matches_key - - args = { - "team_id": team_id, - "user_api_key_dict": UserAPIKeyAuth( - user_role=user_role, - api_key="sk-1234", - team_id=key_team_id, - ), - } - - assert check_if_team_id_matches_key(**args) is expected_result - - @pytest.mark.asyncio @pytest.mark.skip(reason="new feature, tests passing locally") async def test_add_update_model(prisma_client): diff --git a/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx index e2115e8467..1e0a2316df 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx @@ -13,6 +13,8 @@ import ConnectionErrorDisplay from "./model_connection_test"; import { TEST_MODES } from "./add_model_modes"; import { Row, Col } from "antd"; import { Text, TextInput } from "@tremor/react"; +import TeamDropdown from "../common_components/team_dropdown"; +import { all_admin_roles } from "@/utils/roles"; interface AddModelTabProps { form: FormInstance; @@ -28,6 +30,7 @@ interface AddModelTabProps { teams: Team[] | null; credentials: CredentialItem[]; accessToken: string; + userRole: string; } const { Title, Link } = Typography; @@ -46,6 +49,7 @@ const AddModelTab: React.FC = ({ teams, credentials, accessToken, + userRole, }) => { // State for test mode and connection testing const [testMode, setTestMode] = useState("chat"); @@ -64,6 +68,8 @@ const AddModelTab: React.FC = ({ setIsResultModalVisible(true); }; + const isAdmin = all_admin_roles.includes(userRole); + return ( <> Add new model @@ -217,6 +223,25 @@ const AddModelTab: React.FC = ({ ); }} +
+
+ Team Settings +
+
+ + + = ({
- - - }, { key: "3", page: "llm-playground", label: "Test Key", icon: , roles: rolesWithWriteAccess }, - { key: "2", page: "models", label: "Models", icon: , roles: all_admin_roles }, + { key: "2", page: "models", label: "Models", icon: , roles: rolesWithWriteAccess }, { key: "4", page: "usage", label: "Usage", icon: }, { key: "6", page: "teams", label: "Teams", icon: }, { key: "17", page: "organizations", label: "Organizations", icon: , roles: all_admin_roles }, diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 1e80e2250e..493f9ba426 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -111,6 +111,7 @@ import ModelInfoView from "./model_info_view"; import AddModelTab from "./add_model/add_model_tab"; import { ModelDataTable } from "./model_dashboard/table"; import { columns } from "./model_dashboard/columns"; +import { all_admin_roles } from "@/utils/roles"; interface ModelDashboardProps { accessToken: string | null; @@ -479,9 +480,6 @@ const ModelDashboard: React.FC = ({ } const fetchData = async () => { try { - const _providerSettings = await modelSettingsCall(accessToken); - setProviderSettings(_providerSettings); - // Replace with your actual API call for model data const modelDataResponse = await modelInfoCall( accessToken, @@ -490,6 +488,12 @@ const ModelDashboard: React.FC = ({ ); console.log("Model data response:", modelDataResponse.data); setModelData(modelDataResponse); + const _providerSettings = await modelSettingsCall(accessToken); + if (_providerSettings) { + setProviderSettings(_providerSettings); + } + + // loop through modelDataResponse and get all`model_name` values let all_model_groups: Set = new Set(); @@ -1001,7 +1005,7 @@ const ModelDashboard: React.FC = ({ ); let dynamicProviderForm: ProviderSettings | undefined = undefined; - if (providerKey) { + if (providerKey && providerSettings) { dynamicProviderForm = providerSettings.find( (provider) => provider.name === provider_map[providerKey] ); @@ -1055,16 +1059,17 @@ const ModelDashboard: React.FC = ({ /> ) : ( +
- All Models + {all_admin_roles.includes(userRole) ? All Models : Your Models} Add Model - LLM Credentials - + {all_admin_roles.includes(userRole) && LLM Credentials} + {all_admin_roles.includes(userRole) &&
/health Models
-
- Model Analytics - Model Retry Settings +
} + {all_admin_roles.includes(userRole) && Model Analytics} + {all_admin_roles.includes(userRole) && Model Retry Settings}
@@ -1081,26 +1086,25 @@ const ModelDashboard: React.FC = ({
- -
- Filter by Public Model Name +
+ {/* Left side - Title and description */} +
+ Model Management + {!all_admin_roles.includes(userRole) && + Add models for teams you are an admin for. + } +
+ + {/* Right side - Filter */} +
+ Filter by Public Model Name:
- - selectedModelGroup === "all" || - model.model_name === selectedModelGroup || - !selectedModelGroup - )} - isLoading={false} // Add loading state if needed - /> - - handleEditModelSubmit(data, accessToken, setEditModalVisible, setSelectedModel)} +
+ + selectedModelGroup === "all" || + model.model_name === selectedModelGroup || + !selectedModelGroup + )} + isLoading={false} // Add loading state if needed /> @@ -1153,6 +1151,7 @@ const ModelDashboard: React.FC = ({ teams={teams} credentials={credentialsList} accessToken={accessToken} + userRole={userRole} /> diff --git a/ui/litellm-dashboard/src/components/model_info_view.tsx b/ui/litellm-dashboard/src/components/model_info_view.tsx index 2d05e185c1..7a1c9a7abc 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.tsx @@ -58,7 +58,7 @@ export default function ModelInfoView({ const [isEditing, setIsEditing] = useState(false); const [existingCredential, setExistingCredential] = useState(null); - const canEditModel = userRole === "Admin"; + const canEditModel = userRole === "Admin" || modelData.model_info.created_by === userID; const isAdmin = userRole === "Admin"; const usingExistingCredential = modelData.litellm_params?.litellm_credential_name != null && modelData.litellm_params?.litellm_credential_name != undefined; @@ -209,8 +209,8 @@ export default function ModelInfoView({ Public Model Name: {getDisplayModelName(modelData)} {modelData.model_info.id}
- {isAdmin && ( -
+
+ {isAdmin && ( Re-use Credentials + )} + {canEditModel && ( Delete Model -
- )} + )} +
diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index cf0b9ec5dc..fbe0374e34 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -1,6 +1,7 @@ /** * Helper file for calls being made to proxy */ +import { all_admin_roles } from "@/utils/roles"; import { message } from "antd"; const isLocal = process.env.NODE_ENV === "development"; @@ -131,9 +132,9 @@ export const modelCreateCall = async ( }); if (!response.ok) { - const errorData = await response.json(); + const errorData = await response.text(); const errorMsg = - errorData.error?.message?.error || + errorData|| "Network response was not ok"; message.error(errorMsg); throw new Error(errorMsg); @@ -183,9 +184,8 @@ export const modelSettingsCall = async (accessToken: String) => { //message.info("Received model data"); return data; // Handle success - you might want to update some state or UI based on the created key - } catch (error) { - console.error("Failed to get callbacks:", error); - throw error; + } catch (error: any) { + console.error("Failed to get model settings:", error); } }; @@ -1213,8 +1213,12 @@ export const modelInfoCall = async ( * Get all models on proxy */ try { + console.log("modelInfoCall:", accessToken, userID, userRole); let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/model/info` : `/v2/model/info`; + if (!all_admin_roles.includes(userRole as string)) { // only show users models they've added + url += `?user_models_only=true`; + } //message.info("Requesting model data"); const response = await fetch(url, { method: "GET",