diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 4927f3db4ca..3faadfe5122 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -691,6 +691,7 @@ async def user_api_key_auth( "/key", "/spend", "/user", + "/model/info", ] # check if the current route startswith any of the allowed routes if ( @@ -3953,7 +3954,6 @@ async def add_new_model(model_params: ModelParams): ) -#### [BETA] - This is a beta endpoint, format might change based on user feedback https://github.com/BerriAI/litellm/issues/933. If you need a stable endpoint use /model/info @router.get( "/model/info", description="Provides more info about each model in /models, including config.yaml descriptions (except api key and api base)", @@ -3986,6 +3986,14 @@ async def model_info_v1( # read litellm model_prices_and_context_window.json to get the following: # input_cost_per_token, output_cost_per_token, max_tokens litellm_model_info = get_litellm_model_info(model=model) + if litellm_model_info == {}: + # use litellm_param model_name to get model_info + litellm_params = model.get("litellm_params", {}) + litellm_model = litellm_params.get("model", None) + try: + litellm_model_info = litellm.get_model_info(model=litellm_model) + except: + litellm_model_info = {} for k, v in litellm_model_info.items(): if k not in model_info: model_info[k] = v diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 80a3b6940ab..02e72b57971 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -3,6 +3,7 @@ import React, { Suspense, useEffect, useState } from "react"; import { useSearchParams } from "next/navigation"; import Navbar from "../components/navbar"; import UserDashboard from "../components/user_dashboard"; +import ModelDashboard from "@/components/model_dashboard"; import Sidebar from "../components/leftnav"; import Usage from "../components/usage"; import { jwtDecode } from "jwt-decode"; @@ -80,7 +81,15 @@ const CreateKeyPage = () => { userEmail={userEmail} setUserEmail={setUserEmail} /> - ) : ( + ) : page == "models" ? ( + + ) + : ( = ({ setPage }) => { setPage("api-keys")}> API Keys - setPage("usage")}> + setPage("models")}> + Models + + setPage("usage")}> Usage diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx new file mode 100644 index 00000000000..a454399f09b --- /dev/null +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -0,0 +1,122 @@ +import React, { useState, useEffect } from "react"; +import { Card, Title, Subtitle, Table, TableHead, TableRow, TableCell, TableBody, Metric, Grid } from "@tremor/react"; +import { modelInfoCall } from "./networking"; + +interface ModelDashboardProps { + accessToken: string; + token: string | null; + userRole: string | null; + userID: string | null; +} + +const ModelDashboard: React.FC = ({ + accessToken, + token, + userRole, + userID, +}) => { + const [modelData, setModelData] = useState({ data: [] }); + + useEffect(() => { + const fetchData = async () => { + try { + // Replace with your actual API call for model data + const modelDataResponse = await modelInfoCall(accessToken, userID, userRole); + console.log("Model data response:", modelDataResponse.data); + setModelData(modelDataResponse); + } catch (error) { + console.error("There was an error fetching the model data", error); + } + }; + + if (accessToken && token && userRole && userID) { + fetchData(); + } + }, [accessToken, token, userRole, userID]); + + if (!modelData) { + return
Loading...
; + } + + // loop through model data and edit each row + for (let i = 0; i < modelData.data.length; i++) { + let curr_model = modelData.data[i]; + let litellm_model_name = curr_model?.litellm_params?.model; + + let model_info = curr_model?.model_info; + + let defaultProvider = "openai"; + let provider = ""; + let input_cost = "Undefined" + let output_cost = "Undefined" + let max_tokens = "Undefined" + + // Check if litellm_model_name is null or undefined + if (litellm_model_name) { + // Split litellm_model_name based on "/" + let splitModel = litellm_model_name.split("/"); + + // Get the first element in the split + let firstElement = splitModel[0]; + + // If there is only one element, default provider to openai + provider = splitModel.length === 1 ? defaultProvider : firstElement; + + console.log("Provider:", provider); + } else { + // litellm_model_name is null or undefined, default provider to openai + provider = defaultProvider; + console.log("Provider:", provider); + } + + if (model_info) { + input_cost = model_info?.input_cost_per_token; + output_cost = model_info?.output_cost_per_token; + max_tokens = model_info?.max_tokens; + + } + modelData.data[i].provider = provider + modelData.data[i].input_cost = input_cost + modelData.data[i].output_cost = output_cost + modelData.data[i].max_tokens = max_tokens + + } + + + return ( +
+ + + Available Models + + + + Model Name + Provider + Input Price per token ($) + Output Price per token ($) + Max Tokens + + + + {modelData.data.map((model: any) => ( + + +

{model.model_name}

+ {model.provider} + {model.input_cost} + {model.output_cost} + {model.max_tokens} + + +
+ ))} +
+
+
+
+
+ ); +}; + +export default ModelDashboard; diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 4521ca44661..7905ed7dbcd 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -137,6 +137,41 @@ export const userInfoCall = async ( } }; + + +export const modelInfoCall = async ( + accessToken: String, + userID: String, + userRole: String +) => { + try { + let url = proxyBaseUrl ? `${proxyBaseUrl}/model/info` : `/model/info`; + + message.info("Requesting model data"); + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData = await response.text(); + message.error(errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + 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 create key:", error); + throw error; + } +}; + export const keySpendLogsCall = async (accessToken: String, token: String) => { try { const url = proxyBaseUrl ? `${proxyBaseUrl}/spend/logs` : `/spend/logs`;