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 || "-" : "-"
+ }