From 9198f003398e21d5470eb60e344ec563cbd1d89d Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 27 May 2024 15:41:55 -0700 Subject: [PATCH 1/5] ui - show created at and updated at on models page --- .../src/components/model_dashboard.tsx | 153 ++++++++++++------ 1 file changed, 102 insertions(+), 51 deletions(-) diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 89061d916..18e29ebd8 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -86,6 +86,8 @@ import type { UploadProps } from "antd"; import { Upload } from "antd"; import TimeToFirstToken from "./model_metrics/time_to_first_token"; import DynamicFields from "./model_add/dynamic_form"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; + interface ModelDashboardProps { accessToken: string | null; token: string | null; @@ -269,6 +271,8 @@ const ModelDashboard: React.FC = ({ const [selectedProvider, setSelectedProvider] = useState("OpenAI"); const [healthCheckResponse, setHealthCheckResponse] = useState(""); const [editModalVisible, setEditModalVisible] = useState(false); + const [infoModalVisible, setInfoModalVisible] = useState(false); + const [selectedModel, setSelectedModel] = useState(null); const [availableModelGroups, setAvailableModelGroups] = useState< Array @@ -297,6 +301,15 @@ const ModelDashboard: React.FC = ({ useState(null); const [defaultRetry, setDefaultRetry] = useState(0); + function formatCreatedAt(createdAt: string | null) { + if (createdAt) { + const date = new Date(createdAt); + const options = { month: 'long', day: 'numeric', year: 'numeric' }; + return date.toLocaleDateString('en-US'); + } + return null; + } + const EditModelModal: React.FC = ({ visible, onCancel, @@ -423,11 +436,21 @@ const ModelDashboard: React.FC = ({ setEditModalVisible(true); }; + const handleInfoClick = (model: any) => { + setSelectedModel(model); + setInfoModalVisible(true); + }; + const handleEditCancel = () => { setEditModalVisible(false); setSelectedModel(null); }; + const handleInfoCancel = () => { + setInfoModalVisible(false); + setSelectedModel(null); + }; + const handleEditSubmit = async (formValues: Record) => { // Call API to update team with teamId and values @@ -1073,15 +1096,6 @@ const ModelDashboard: React.FC = ({ API Base )} - - Extra litellm Params - = ({ /1M Tokens ($)

+ - Max Tokens + Created At + + + Created By = ({ > Status + + + @@ -1140,12 +1167,14 @@ const ModelDashboard: React.FC = ({ - {model.model_name} +

+ {model.model_name} +

= ({ wordBreak: "break-word", }} > +

{model.provider} +

{userRole === "Admin" && ( - {model.api_base} - - )} - - - - Litellm params - - -
-                                  {JSON.stringify(
-                                    model.cleanedLitellmParams,
-                                    null,
-                                    2
-                                  )}
-                                
-
-
+ +
+                              {model && model.api_base ? model.api_base.slice(0, 20) : null}
+                            
+
+ + )} = ({ model.litellm_params.output_cost_per_token || null} - -

- Max Tokens: {model.max_tokens}

- Max Input Tokens: {model.max_input_tokens} + +

+ {formatCreatedAt(model.model_info.created_at)} +

+ +
+ +

+ {model.model_info.created_by}

= ({ size="xs" className="text-white" > -

DB Model

+

DB Model

) : ( = ({ size="xs" className="text-black" > -

Config Model

+

Config Model

)}
+ + + handleInfoClick(model)} + /> + + handleEditClick(model)} /> + + + + + + +
))} @@ -1277,6 +1314,20 @@ const ModelDashboard: React.FC = ({ model={selectedModel} onSubmit={handleEditSubmit} /> + + + Model Info + + {selectedModel && JSON.stringify(selectedModel, null, 2)} + + + Add new model From eccda76edc3c321c088aabe7628517f4c0e3b860 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 27 May 2024 15:53:16 -0700 Subject: [PATCH 2/5] router - include updated at and created at in model info --- litellm/proxy/proxy_server.py | 8 ++++++-- litellm/types/router.py | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 1bdb5edba..19e68965f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2790,6 +2790,11 @@ class ProxyConfig: model.model_info["id"] = _id model.model_info["db_model"] = True + model.model_info["created_at"] = getattr(model, "created_at", None) + model.model_info["updated_at"] = getattr(model, "updated_at", None) + model.model_info["created_by"] = getattr(model, "created_by", None) + model.model_info["updated_by"] = getattr(model, "updated_by", None) + if model.model_info is not None and isinstance(model.model_info, dict): if "id" not in model.model_info: model.model_info["id"] = model.model_id @@ -3075,10 +3080,9 @@ class ProxyConfig: try: if master_key is None or not isinstance(master_key, str): - raise Exception( + raise ValueError( f"Master key is not initialized or formatted. master_key={master_key}" ) - verbose_proxy_logger.debug(f"llm_router: {llm_router}") new_models = await prisma_client.db.litellm_proxymodeltable.find_many() # update llm router await self._update_llm_router( diff --git a/litellm/types/router.py b/litellm/types/router.py index 93c65a1cf..75e792f4c 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -1,9 +1,15 @@ +""" + litellm.Router Types - includes RouterConfig, UpdateRouterConfig, ModelInfo etc +""" + from typing import List, Optional, Union, Dict, Tuple, Literal, TypedDict +import uuid +import enum import httpx -from pydantic import BaseModel, validator, Field +from pydantic import BaseModel, Field +import datetime from .completion import CompletionRequest from .embedding import EmbeddingRequest -import uuid, enum class ModelConfig(BaseModel): @@ -76,6 +82,12 @@ class ModelInfo(BaseModel): db_model: bool = ( False # used for proxy - to separate models which are stored in the db vs. config. ) + updated_at: Optional[datetime.datetime] = None + updated_by: Optional[str] = None + + created_at: Optional[datetime.datetime] = None + created_by: Optional[str] = None + base_model: Optional[str] = ( None # specify if the base model is azure/gpt-3.5-turbo etc for accurate cost tracking ) From 5fe6fea9c8a7ee32034d57ed0d1ecfec4510c53a Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 27 May 2024 15:58:43 -0700 Subject: [PATCH 3/5] ui -clean up models default value --- .../src/components/model_dashboard.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 18e29ebd8..9f383f4a7 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1173,7 +1173,7 @@ const ModelDashboard: React.FC = ({ }} >

- {model.model_name} + {model.model_name || "-"}

= ({ }} >

- {model.provider} + {model.provider || "-"}

{userRole === "Admin" && ( @@ -1205,7 +1205,7 @@ const ModelDashboard: React.FC = ({ }} title={model && model.api_base ? model.api_base : ""} > - {model && model.api_base ? model.api_base.slice(0, 20) : null} + {model && model.api_base ? model.api_base.slice(0, 20) : "-"} @@ -1218,9 +1218,11 @@ const ModelDashboard: React.FC = ({ wordBreak: "break-word", }} > +
                             {model.input_cost ||
                               model.litellm_params.input_cost_per_token ||
-                              null}
+                              "-"}
+                            
= ({ wordBreak: "break-word", }} > +
                             {model.output_cost ||
                               model.litellm_params.output_cost_per_token ||
-                              null}
+                              "-"}
+                            

- {formatCreatedAt(model.model_info.created_at)} + {formatCreatedAt(model.model_info.created_at) || "-"}

- {model.model_info.created_by} + {model.model_info.created_by || "-"}

Date: Mon, 27 May 2024 16:03:21 -0700 Subject: [PATCH 4/5] fix clean up models page --- .../src/components/model_dashboard.tsx | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 9f383f4a7..a8a663733 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1062,7 +1062,6 @@ const ModelDashboard: React.FC = ({ @@ -1072,6 +1071,7 @@ const ModelDashboard: React.FC = ({ maxWidth: "150px", whiteSpace: "normal", wordBreak: "break-word", + fontSize: "11px" }} > Public Model Name @@ -1081,6 +1081,7 @@ const ModelDashboard: React.FC = ({ maxWidth: "100px", whiteSpace: "normal", wordBreak: "break-word", + fontSize: "11px" }} > Provider @@ -1091,6 +1092,7 @@ const ModelDashboard: React.FC = ({ maxWidth: "150px", whiteSpace: "normal", wordBreak: "break-word", + fontSize: "11px" }} > API Base @@ -1101,6 +1103,7 @@ const ModelDashboard: React.FC = ({ maxWidth: "85px", whiteSpace: "normal", wordBreak: "break-word", + fontSize: "11px" }} > Input Price{" "} @@ -1113,6 +1116,7 @@ const ModelDashboard: React.FC = ({ maxWidth: "85px", whiteSpace: "normal", wordBreak: "break-word", + fontSize: "11px" }} > Output Price{" "} @@ -1123,27 +1127,30 @@ const ModelDashboard: React.FC = ({ - Created At - - - Created By + ✨ Created At + + + ✨ Created By Status From ef7f3bc4febb18b9146c1ef12eabfca6690322eb Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 27 May 2024 16:29:51 -0700 Subject: [PATCH 5/5] backend - add audit logs for adding models --- docs/my-website/docs/proxy/enterprise.md | 1 + litellm/proxy/proxy_server.py | 10 +++++---- .../src/components/model_dashboard.tsx | 21 +++++++++++++------ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/my-website/docs/proxy/enterprise.md b/docs/my-website/docs/proxy/enterprise.md index c47589e8a..a8c84bf4f 100644 --- a/docs/my-website/docs/proxy/enterprise.md +++ b/docs/my-website/docs/proxy/enterprise.md @@ -21,6 +21,7 @@ Features: - ✅ Don't log/store specific requests to Langfuse, Sentry, etc. (eg confidential LLM requests) - ✅ Tracking Spend for Custom Tags - ✅ Custom Branding + Routes on Swagger Docs +- ✅ Audit Logs for `Created At, Created By` when Models Added ## Content Moderation diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 19e68965f..9eb0d2a15 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -2790,10 +2790,12 @@ class ProxyConfig: model.model_info["id"] = _id model.model_info["db_model"] = True - model.model_info["created_at"] = getattr(model, "created_at", None) - model.model_info["updated_at"] = getattr(model, "updated_at", None) - model.model_info["created_by"] = getattr(model, "created_by", None) - model.model_info["updated_by"] = getattr(model, "updated_by", None) + if premium_user is True: + # seeing "created_at", "updated_at", "created_by", "updated_by" is a LiteLLM Enterprise Feature + model.model_info["created_at"] = getattr(model, "created_at", None) + model.model_info["updated_at"] = getattr(model, "updated_at", None) + model.model_info["created_by"] = getattr(model, "created_by", None) + model.model_info["updated_by"] = getattr(model, "updated_by", None) if model.model_info is not None and isinstance(model.model_info, dict): if "id" not in model.model_info: diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index a8a663733..2cb1c2cf0 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1133,8 +1133,11 @@ const ModelDashboard: React.FC = ({ fontSize: "11px" }} > - ✨ Created At - + { + premiumUser ? "Created At" : ✨ Created At + } + + = ({ fontSize: "11px" }} > - ✨ Created By + { + premiumUser ? "Created By" : ✨ Created By + } = ({ selectedModelGroup === "" ) .map((model: any, index: number) => ( - + = ({

- {formatCreatedAt(model.model_info.created_at) || "-"} + { + premiumUser ? formatCreatedAt(model.model_info.created_at) || "-" : "-" + }

- {model.model_info.created_by || "-"} + { + premiumUser ? model.model_info.created_by || "-" : "-" + }