mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-09 02:24:40 +00:00
(UI) - Adding new models enhancement - show provider logo (#8033)
* ui allow wildcard models * ui show model dashboard * add advanced settings in card * fix button * ui - add provider logos on admin ui
This commit is contained in:
@@ -1,16 +1,4 @@
|
||||
model_list:
|
||||
- model_name: openai/*
|
||||
litellm_params:
|
||||
model: openai/*
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
- model_name: bedrock/*
|
||||
litellm_params:
|
||||
model: bedrock/*
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
- model_name: text-embedding-ada-002
|
||||
litellm_params:
|
||||
model: openai/text-embedding-ada-002
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
- model_name: fake-openai-endpoint
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
@@ -19,11 +7,6 @@ model_list:
|
||||
|
||||
|
||||
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["langfuse"]
|
||||
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["prometheus"]
|
||||
prometheus_initialize_budget_metrics: true
|
||||
|
||||
@@ -83,6 +83,8 @@ import {
|
||||
CheckCircleIcon,
|
||||
XCircleIcon,
|
||||
FilterIcon,
|
||||
ChevronUpIcon,
|
||||
ChevronDownIcon,
|
||||
} from "@heroicons/react/outline";
|
||||
import DeleteModelButton from "./delete_model_button";
|
||||
const { Title: Title2, Link } = Typography;
|
||||
@@ -92,6 +94,7 @@ 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";
|
||||
import { Providers, provider_map, providerLogoMap, getProviderLogoAndName, getPlaceholder } from "./provider_info_helpers";
|
||||
|
||||
interface ModelDashboardProps {
|
||||
accessToken: string | null;
|
||||
@@ -136,42 +139,6 @@ interface ProviderSettings {
|
||||
fields: ProviderFields[];
|
||||
}
|
||||
|
||||
enum Providers {
|
||||
OpenAI = "OpenAI",
|
||||
Azure = "Azure",
|
||||
Azure_AI_Studio = "Azure AI Studio",
|
||||
Anthropic = "Anthropic",
|
||||
Google_AI_Studio = "Google AI Studio",
|
||||
Bedrock = "Amazon Bedrock",
|
||||
Groq = "Groq",
|
||||
MistralAI = "Mistral AI",
|
||||
Deepseek = "Deepseek",
|
||||
OpenAI_Compatible = "OpenAI-Compatible Endpoints (Together AI, etc.)",
|
||||
Vertex_AI = "Vertex AI (Anthropic, Gemini, etc.)",
|
||||
Cohere = "Cohere",
|
||||
Databricks = "Databricks",
|
||||
Ollama = "Ollama",
|
||||
xAI = "xAI",
|
||||
}
|
||||
|
||||
const provider_map: Record<string, string> = {
|
||||
OpenAI: "openai",
|
||||
Azure: "azure",
|
||||
Azure_AI_Studio: "azure_ai",
|
||||
Anthropic: "anthropic",
|
||||
Google_AI_Studio: "gemini",
|
||||
Bedrock: "bedrock",
|
||||
Groq: "groq",
|
||||
MistralAI: "mistral",
|
||||
Cohere: "cohere_chat",
|
||||
OpenAI_Compatible: "openai",
|
||||
Vertex_AI: "vertex_ai",
|
||||
Databricks: "databricks",
|
||||
xAI: "xai",
|
||||
Deepseek: "deepseek",
|
||||
Ollama: "ollama",
|
||||
|
||||
};
|
||||
|
||||
const retry_policy_map: Record<string, string> = {
|
||||
"BadRequestError (400)": "BadRequestErrorRetries",
|
||||
@@ -182,12 +149,22 @@ const retry_policy_map: Record<string, string> = {
|
||||
"InternalServerError (500)": "InternalServerErrorRetries",
|
||||
};
|
||||
|
||||
const handleSubmit = async (
|
||||
export const handleSubmit = async (
|
||||
formValues: Record<string, any>,
|
||||
accessToken: string,
|
||||
form: any
|
||||
) => {
|
||||
try {
|
||||
// If model_name is not provided, use provider.toLowerCase() + "/*"
|
||||
if (!formValues["model_name"]) {
|
||||
formValues["model_name"] = formValues["custom_llm_provider"].toLowerCase() + "/*";
|
||||
}
|
||||
|
||||
// If model is not provided, use provider.toLowerCase() + "/*"
|
||||
if (!formValues["model"]) {
|
||||
formValues["model"] = [formValues["custom_llm_provider"].toLowerCase() + "/*"];
|
||||
}
|
||||
|
||||
/**
|
||||
* For multiple litellm model names - create a separate deployment for each
|
||||
* - get the list
|
||||
@@ -362,6 +339,8 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
|
||||
const [allEndUsers, setAllEndUsers] = useState<any[]>([]);
|
||||
|
||||
// Add state for advanced settings visibility
|
||||
const [showAdvancedSettings, setShowAdvancedSettings] = useState<boolean>(false);
|
||||
|
||||
const updateModelMetrics = async (
|
||||
modelGroup: string | null,
|
||||
@@ -1332,23 +1311,6 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const getPlaceholder = (selectedProvider: string): string => {
|
||||
if (selectedProvider === Providers.Vertex_AI) {
|
||||
return "gemini-pro";
|
||||
} else if (selectedProvider == Providers.Anthropic) {
|
||||
return "claude-3-opus";
|
||||
} else if (selectedProvider == Providers.Bedrock) {
|
||||
return "claude-3-opus";
|
||||
} else if (selectedProvider == Providers.Google_AI_Studio) {
|
||||
return "gemini-pro";
|
||||
} else if (selectedProvider == Providers.Azure_AI_Studio) {
|
||||
return "azure_ai/command-r-plus";
|
||||
} else if (selectedProvider == Providers.Azure) {
|
||||
return "azure/my-deployment";
|
||||
} else {
|
||||
return "gpt-3.5-turbo";
|
||||
}
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
form
|
||||
@@ -1587,14 +1549,35 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
<p className="text-xs">{model.model_name || "-"}</p>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
style={{
|
||||
maxWidth: "100px",
|
||||
whiteSpace: "normal",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
<p className="text-xs">{model.provider || "-"}</p>
|
||||
style={{
|
||||
maxWidth: "100px",
|
||||
whiteSpace: "normal",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
{model.provider && (
|
||||
<img
|
||||
src={getProviderLogoAndName(model.provider).logo}
|
||||
alt={`${model.provider} logo`}
|
||||
className="w-4 h-4"
|
||||
onError={(e) => {
|
||||
// Create a div with provider initial as fallback
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement('div');
|
||||
fallbackDiv.className = 'w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs';
|
||||
fallbackDiv.textContent = model.provider?.charAt(0) || '-';
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<p className="text-xs">{model.provider || "-"}</p>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
style={{
|
||||
maxWidth: "100px",
|
||||
@@ -1792,6 +1775,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
labelAlign="left"
|
||||
>
|
||||
<>
|
||||
|
||||
<Form.Item
|
||||
rules={[{ required: true, message: "Required" }]}
|
||||
label="Provider:"
|
||||
@@ -1810,97 +1794,29 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
setSelectedProvider(provider);
|
||||
}}
|
||||
>
|
||||
{provider}
|
||||
<div className="flex items-center space-x-2">
|
||||
<img
|
||||
src={providerLogoMap[provider]}
|
||||
alt={`${provider} logo`}
|
||||
className="w-5 h-5"
|
||||
onError={(e) => {
|
||||
// Create a div with provider initial as fallback
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement('div');
|
||||
fallbackDiv.className = 'w-5 h-5 rounded-full bg-gray-200 flex items-center justify-center text-xs';
|
||||
fallbackDiv.textContent = provider.charAt(0);
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>{provider}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
rules={[{ required: true, message: "Required" }]}
|
||||
label="Public Model Name"
|
||||
name="model_name"
|
||||
tooltip="Model name your users will pass in. Also used for load-balancing, LiteLLM will load balance between all models with this public name."
|
||||
className="mb-0"
|
||||
>
|
||||
<TextInput
|
||||
|
||||
/>
|
||||
</Form.Item>
|
||||
<Row>
|
||||
<Col span={10}></Col>
|
||||
<Col span={10}>
|
||||
<Text className="mb-3 mt-1">
|
||||
Model name your users will pass in.
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
label="LiteLLM Model Name(s)"
|
||||
tooltip="Actual model name used for making litellm.completion() / litellm.embedding() call."
|
||||
className="mb-0"
|
||||
>
|
||||
<Form.Item
|
||||
name="model"
|
||||
rules={[{ required: true, message: "Required" }]}
|
||||
noStyle
|
||||
>
|
||||
{ (selectedProvider === Providers.Azure) || (selectedProvider === Providers.OpenAI_Compatible) || (selectedProvider === Providers.Ollama) ? (
|
||||
<TextInput placeholder={getPlaceholder(selectedProvider.toString())} />
|
||||
) : providerModels.length > 0 ? (
|
||||
<MultiSelect>
|
||||
<MultiSelectItem value="custom">Custom Model Name (Enter below)</MultiSelectItem>
|
||||
{providerModels.map((model, index) => (
|
||||
<MultiSelectItem key={index} value={model}>
|
||||
{model}
|
||||
</MultiSelectItem>
|
||||
))}
|
||||
</MultiSelect>
|
||||
) : (
|
||||
<TextInput placeholder={getPlaceholder(selectedProvider.toString())} />
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, currentValues) => prevValues.model !== currentValues.model}
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const selectedModels = getFieldValue('model') || [];
|
||||
return selectedModels.includes('custom') && (
|
||||
<Form.Item
|
||||
name="custom_model_name"
|
||||
rules={[{ required: true, message: "Please enter a custom model name" }]}
|
||||
className="mt-2"
|
||||
>
|
||||
<TextInput placeholder="Enter custom model name" />
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
<Row>
|
||||
<Col span={10}></Col>
|
||||
<Col span={10}>
|
||||
<Text className="mb-3 mt-1">
|
||||
Actual model name used for making{" "}
|
||||
<Link
|
||||
href="https://docs.litellm.ai/docs/providers"
|
||||
target="_blank"
|
||||
>
|
||||
litellm.completion() call
|
||||
</Link>
|
||||
. We'll{" "}
|
||||
<Link
|
||||
href="https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config"
|
||||
target="_blank"
|
||||
>
|
||||
loadbalance
|
||||
</Link>{" "}
|
||||
models with the same 'public name'
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
{dynamicProviderForm !== undefined &&
|
||||
dynamicProviderForm.fields.length > 0 && (
|
||||
<DynamicFields
|
||||
@@ -1917,6 +1833,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
rules={[{ required: true, message: "Required" }]}
|
||||
label="API Key"
|
||||
name="api_key"
|
||||
tooltip="LLM API Credentials"
|
||||
>
|
||||
<TextInput placeholder="sk-" type="password" />
|
||||
</Form.Item>
|
||||
@@ -2044,7 +1961,111 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
<TextInput placeholder="us-east-1" />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
|
||||
<Card
|
||||
className="mb-4"
|
||||
onClick={() => setShowAdvancedSettings(!showAdvancedSettings)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<div className="flex justify-between items-center">
|
||||
<Text>Advanced Settings</Text>
|
||||
<Icon
|
||||
icon={showAdvancedSettings ? ChevronUpIcon : ChevronDownIcon}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{showAdvancedSettings && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Public Model Name"
|
||||
name="model_name"
|
||||
tooltip="Model name your users will pass in. Also used for load-balancing, LiteLLM will load balance between all models with this public name."
|
||||
className="mb-0"
|
||||
rules={[{ required: false }]}
|
||||
>
|
||||
<TextInput />
|
||||
</Form.Item>
|
||||
<Row>
|
||||
<Col span={10}></Col>
|
||||
<Col span={10}>
|
||||
<Text className="mb-3 mt-1">
|
||||
Model name your users will pass in.
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<Form.Item
|
||||
label="LiteLLM Model Name(s)"
|
||||
tooltip="Actual model name used for making litellm.completion() / litellm.embedding() call."
|
||||
className="mb-0"
|
||||
>
|
||||
<Form.Item
|
||||
name="model"
|
||||
rules={[{ required: false }]}
|
||||
noStyle
|
||||
>
|
||||
{(selectedProvider === Providers.Azure) || (selectedProvider === Providers.OpenAI_Compatible) || (selectedProvider === Providers.Ollama) ? (
|
||||
<TextInput placeholder={getPlaceholder(selectedProvider.toString())} />
|
||||
) : providerModels.length > 0 ? (
|
||||
<MultiSelect>
|
||||
<MultiSelectItem value="custom">Custom Model Name (Enter below)</MultiSelectItem>
|
||||
{providerModels.map((model, index) => (
|
||||
<MultiSelectItem key={index} value={model}>
|
||||
{model}
|
||||
</MultiSelectItem>
|
||||
))}
|
||||
</MultiSelect>
|
||||
) : (
|
||||
<TextInput placeholder={getPlaceholder(selectedProvider.toString())} />
|
||||
)}
|
||||
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, currentValues) => prevValues.model !== currentValues.model}
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const selectedModels = getFieldValue('model') || [];
|
||||
return selectedModels.includes('custom') && (
|
||||
<Form.Item
|
||||
name="custom_model_name"
|
||||
rules={[{ required: true, message: "Please enter a custom model name" }]}
|
||||
className="mt-2"
|
||||
>
|
||||
<TextInput placeholder="Enter custom model name" />
|
||||
</Form.Item>
|
||||
)
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
</Form.Item>
|
||||
<Row>
|
||||
<Col span={10}></Col>
|
||||
<Col span={10}>
|
||||
<Text className="mb-3 mt-1">
|
||||
Actual model name used for making{" "}
|
||||
<Link
|
||||
href="https://docs.litellm.ai/docs/providers"
|
||||
target="_blank"
|
||||
>
|
||||
litellm.completion() call
|
||||
</Link>
|
||||
. We'll{" "}
|
||||
<Link
|
||||
href="https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config"
|
||||
target="_blank"
|
||||
>
|
||||
loadbalance
|
||||
</Link>{" "}
|
||||
models with the same 'public name'
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
label="LiteLLM Params"
|
||||
name="litellm_extra_params"
|
||||
tooltip="Optional litellm params used for making a litellm.completion() call."
|
||||
@@ -2086,15 +2107,20 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
}'
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
</>
|
||||
<div style={{ textAlign: "center", marginTop: "10px" }}>
|
||||
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Tooltip title="Get help on our github">
|
||||
<Typography.Link href="https://github.com/BerriAI/litellm/issues">
|
||||
Need Help?
|
||||
</Typography.Link>
|
||||
</Tooltip>
|
||||
<Button2 htmlType="submit">Add Model</Button2>
|
||||
</div>
|
||||
<Tooltip title="Get help on our github">
|
||||
<Typography.Link href="https://github.com/BerriAI/litellm/issues">
|
||||
Need Help?
|
||||
</Typography.Link>
|
||||
</Tooltip>
|
||||
</Form>
|
||||
</Card>
|
||||
</TabPanel>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import React from "react";
|
||||
import { handleSubmit } from "./model_dashboard";
|
||||
|
||||
export enum Providers {
|
||||
OpenAI = "OpenAI",
|
||||
Azure = "Azure",
|
||||
Azure_AI_Studio = "Azure AI Studio",
|
||||
Anthropic = "Anthropic",
|
||||
Google_AI_Studio = "Google AI Studio",
|
||||
Bedrock = "Amazon Bedrock",
|
||||
Groq = "Groq",
|
||||
MistralAI = "Mistral AI",
|
||||
Deepseek = "Deepseek",
|
||||
OpenAI_Compatible = "OpenAI-Compatible Endpoints (Together AI, etc.)",
|
||||
Vertex_AI = "Vertex AI (Anthropic, Gemini, etc.)",
|
||||
Cohere = "Cohere",
|
||||
Databricks = "Databricks",
|
||||
Ollama = "Ollama",
|
||||
xAI = "xAI",
|
||||
}
|
||||
|
||||
export const provider_map: Record<string, string> = {
|
||||
OpenAI: "openai",
|
||||
Azure: "azure",
|
||||
Azure_AI_Studio: "azure_ai",
|
||||
Anthropic: "anthropic",
|
||||
Google_AI_Studio: "gemini",
|
||||
Bedrock: "bedrock",
|
||||
Groq: "groq",
|
||||
MistralAI: "mistral",
|
||||
Cohere: "cohere_chat",
|
||||
OpenAI_Compatible: "openai",
|
||||
Vertex_AI: "vertex_ai",
|
||||
Databricks: "databricks",
|
||||
xAI: "xai",
|
||||
Deepseek: "deepseek",
|
||||
Ollama: "ollama",
|
||||
};
|
||||
|
||||
export const providerLogoMap: Record<string, string> = {
|
||||
[Providers.OpenAI]: "https://artificialanalysis.ai/img/logos/openai_small.svg",
|
||||
[Providers.Azure]: "https://upload.wikimedia.org/wikipedia/commons/a/a8/Microsoft_Azure_Logo.svg",
|
||||
[Providers.Azure_AI_Studio]: "https://upload.wikimedia.org/wikipedia/commons/a/a8/Microsoft_Azure_Logo.svg",
|
||||
[Providers.Anthropic]: "https://artificialanalysis.ai/img/logos/anthropic_small.svg",
|
||||
[Providers.Google_AI_Studio]: "https://artificialanalysis.ai/img/logos/google_small.svg",
|
||||
[Providers.Bedrock]: "https://artificialanalysis.ai/img/logos/aws_small.png",
|
||||
[Providers.Groq]: "https://artificialanalysis.ai/img/logos/groq_small.png",
|
||||
[Providers.MistralAI]: "https://artificialanalysis.ai/img/logos/mistral_small.png",
|
||||
[Providers.Cohere]: "https://artificialanalysis.ai/img/logos/cohere_small.png",
|
||||
[Providers.OpenAI_Compatible]: "https://upload.wikimedia.org/wikipedia/commons/4/4e/OpenAI_Logo.svg",
|
||||
[Providers.Vertex_AI]: "https://artificialanalysis.ai/img/logos/google_small.svg",
|
||||
[Providers.Databricks]: "https://artificialanalysis.ai/img/logos/databricks_small.png",
|
||||
[Providers.Ollama]: "https://artificialanalysis.ai/img/logos/ollama_small.svg",
|
||||
[Providers.xAI]: "https://artificialanalysis.ai/img/logos/xai_small.svg",
|
||||
[Providers.Deepseek]: "https://artificialanalysis.ai/img/logos/deepseek_small.jpg",
|
||||
};
|
||||
|
||||
export const getProviderLogoAndName = (providerValue: string): { logo: string, displayName: string } => {
|
||||
if (!providerValue) {
|
||||
return { logo: "", displayName: "-" };
|
||||
}
|
||||
|
||||
// Find the enum key by matching provider_map values
|
||||
const enumKey = Object.keys(provider_map).find(
|
||||
key => provider_map[key].toLowerCase() === providerValue.toLowerCase()
|
||||
);
|
||||
|
||||
if (!enumKey) {
|
||||
return { logo: "", displayName: providerValue };
|
||||
}
|
||||
|
||||
// Get the display name from Providers enum and logo from map
|
||||
const displayName = Providers[enumKey as keyof typeof Providers];
|
||||
const logo = providerLogoMap[displayName as keyof typeof providerLogoMap];
|
||||
|
||||
return { logo, displayName };
|
||||
};
|
||||
|
||||
export const getPlaceholder = (selectedProvider: string): string => {
|
||||
if (selectedProvider === Providers.Vertex_AI) {
|
||||
return "gemini-pro";
|
||||
} else if (selectedProvider == Providers.Anthropic) {
|
||||
return "claude-3-opus";
|
||||
} else if (selectedProvider == Providers.Bedrock) {
|
||||
return "claude-3-opus";
|
||||
} else if (selectedProvider == Providers.Google_AI_Studio) {
|
||||
return "gemini-pro";
|
||||
} else if (selectedProvider == Providers.Azure_AI_Studio) {
|
||||
return "azure_ai/command-r-plus";
|
||||
} else if (selectedProvider == Providers.Azure) {
|
||||
return "azure/my-deployment";
|
||||
} else {
|
||||
return "gpt-3.5-turbo";
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user