Merge pull request #3868 from BerriAI/litellm_show_updated_created_models

[Feat] Show Created at, Created by on `Models` Page
This commit is contained in:
Ishaan Jaff 2024-05-27 16:32:29 -07:00 committed by GitHub
commit d71bb96047
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 149 additions and 59 deletions

View file

@ -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

View file

@ -2790,6 +2790,13 @@ class ProxyConfig:
model.model_info["id"] = _id
model.model_info["db_model"] = True
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:
model.model_info["id"] = model.model_id
@ -3075,10 +3082,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(

View file

@ -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
)

View file

@ -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<ModelDashboardProps> = ({
const [selectedProvider, setSelectedProvider] = useState<String>("OpenAI");
const [healthCheckResponse, setHealthCheckResponse] = useState<string>("");
const [editModalVisible, setEditModalVisible] = useState<boolean>(false);
const [infoModalVisible, setInfoModalVisible] = useState<boolean>(false);
const [selectedModel, setSelectedModel] = useState<any>(null);
const [availableModelGroups, setAvailableModelGroups] = useState<
Array<string>
@ -297,6 +301,15 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
useState<RetryPolicyObject | null>(null);
const [defaultRetry, setDefaultRetry] = useState<number>(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<EditModelModalProps> = ({
visible,
onCancel,
@ -423,11 +436,21 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
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<string, any>) => {
// Call API to update team with teamId and values
@ -1039,7 +1062,6 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
</div>
<Card>
<Table
className="mt-5"
style={{ maxWidth: "1500px", width: "100%" }}
>
<TableHead>
@ -1049,6 +1071,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
maxWidth: "150px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
Public Model Name
@ -1058,6 +1081,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
maxWidth: "100px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
Provider
@ -1068,25 +1092,18 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
maxWidth: "150px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
API Base
</TableHeaderCell>
)}
<TableHeaderCell
style={{
maxWidth: "200px",
whiteSpace: "normal",
wordBreak: "break-word",
}}
>
Extra litellm Params
</TableHeaderCell>
<TableHeaderCell
style={{
maxWidth: "85px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
Input Price{" "}
@ -1099,6 +1116,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
maxWidth: "85px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
Output Price{" "}
@ -1106,24 +1124,45 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
/1M Tokens ($)
</p>
</TableHeaderCell>
<TableHeaderCell
style={{
maxWidth: "120px",
maxWidth: "100px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
Max Tokens
{
premiumUser ? "Created At" : <a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank" style={{color: "#72bcd4" }}> Created At</a>
}
</TableHeaderCell>
<TableHeaderCell
style={{
maxWidth: "100px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
{
premiumUser ? "Created By" : <a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank" style={{color: "#72bcd4" }}> Created By</a>
}
</TableHeaderCell>
<TableHeaderCell
style={{
maxWidth: "50px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "11px"
}}
>
Status
</TableHeaderCell>
<TableHeaderCell>
</TableHeaderCell>
</TableRow>
</TableHead>
<TableBody>
@ -1137,15 +1176,17 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
selectedModelGroup === ""
)
.map((model: any, index: number) => (
<TableRow key={index}>
<TableRow key={index} style={{ maxHeight: "1px", minHeight: "1px" }}>
<TableCell
style={{
maxWidth: "150px",
maxWidth: "100px",
whiteSpace: "normal",
wordBreak: "break-word",
}}
>
<Text>{model.model_name}</Text>
<p style={{ fontSize: "10px" }}>
{model.model_name || "-"}
</p>
</TableCell>
<TableCell
style={{
@ -1154,41 +1195,34 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
wordBreak: "break-word",
}}
>
{model.provider}
<p style={{ fontSize: "10px" }}>
{model.provider || "-"}
</p>
</TableCell>
{userRole === "Admin" && (
<TableCell
style={{
maxWidth: "150px",
whiteSpace: "normal",
wordBreak: "break-word",
}}
>
<Tooltip title={model && model.api_base}>
<pre
style={{
maxWidth: "150px",
whiteSpace: "normal",
wordBreak: "break-word",
fontSize: "10px",
}}
title={model && model.api_base ? model.api_base : ""}
>
{model.api_base}
</TableCell>
{model && model.api_base ? model.api_base.slice(0, 20) : "-"}
</pre>
</Tooltip>
</TableCell>
)}
<TableCell
style={{
maxWidth: "200px",
whiteSpace: "normal",
wordBreak: "break-word",
}}
>
<Accordion>
<AccordionHeader>
<Text>Litellm params</Text>
</AccordionHeader>
<AccordionBody>
<pre>
{JSON.stringify(
model.cleanedLitellmParams,
null,
2
)}
</pre>
</AccordionBody>
</Accordion>
</TableCell>
<TableCell
style={{
maxWidth: "80px",
@ -1196,9 +1230,11 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
wordBreak: "break-word",
}}
>
<pre style={{ fontSize: "10px" }}>
{model.input_cost ||
model.litellm_params.input_cost_per_token ||
null}
"-"}
</pre>
</TableCell>
<TableCell
style={{
@ -1207,20 +1243,25 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
wordBreak: "break-word",
}}
>
<pre style={{ fontSize: "10px" }}>
{model.output_cost ||
model.litellm_params.output_cost_per_token ||
null}
"-"}
</pre>
</TableCell>
<TableCell
style={{
maxWidth: "120px",
whiteSpace: "normal",
wordBreak: "break-word",
}}
>
<p style={{ fontSize: "10px" }}>
Max Tokens: {model.max_tokens} <br></br>
Max Input Tokens: {model.max_input_tokens}
<TableCell>
<p style={{ fontSize: "10px" }}>
{
premiumUser ? formatCreatedAt(model.model_info.created_at) || "-" : "-"
}
</p>
</TableCell>
<TableCell>
<p style={{ fontSize: "10px" }}>
{
premiumUser ? model.model_info.created_by || "-" : "-"
}
</p>
</TableCell>
<TableCell
@ -1236,7 +1277,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
size="xs"
className="text-white"
>
<p style={{ fontSize: "10px" }}>DB Model</p>
<p style={{ fontSize: "8px" }}>DB Model</p>
</Badge>
) : (
<Badge
@ -1244,26 +1285,42 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
size="xs"
className="text-black"
>
<p style={{ fontSize: "10px" }}>Config Model</p>
<p style={{ fontSize: "8px" }}>Config Model</p>
</Badge>
)}
</TableCell>
<TableCell
style={{
maxWidth: "100px",
maxWidth: "150px",
whiteSpace: "normal",
wordBreak: "break-word",
}}
>
<Grid numItems={3}>
<Col>
<Icon
icon={InformationCircleIcon}
size="sm"
onClick={() => handleInfoClick(model)}
/>
</Col>
<Col>
<Icon
icon={PencilAltIcon}
size="sm"
onClick={() => handleEditClick(model)}
/>
</Col>
<Col>
<DeleteModelButton
modelID={model.model_info.id}
accessToken={accessToken}
/>
</Col>
</Grid>
</TableCell>
</TableRow>
))}
@ -1277,6 +1334,20 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
model={selectedModel}
onSubmit={handleEditSubmit}
/>
<Modal
title={selectedModel && selectedModel.model_name}
visible={infoModalVisible}
width={800}
footer={null}
onCancel={handleInfoCancel}
>
<Title>Model Info</Title>
<SyntaxHighlighter language="json" >
{selectedModel && JSON.stringify(selectedModel, null, 2)}
</SyntaxHighlighter>
</Modal>
</TabPanel>
<TabPanel className="h-full">
<Title2 level={2}>Add new model</Title2>