(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:
Ishaan Jaff
2025-01-27 13:15:42 -08:00
committed by GitHub
parent 395e903346
commit c0cc2ea4bb
3 changed files with 276 additions and 172 deletions
-17
View File
@@ -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&apos;ll{" "}
<Link
href="https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config"
target="_blank"
>
loadbalance
</Link>{" "}
models with the same &apos;public name&apos;
</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&apos;ll{" "}
<Link
href="https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config"
target="_blank"
>
loadbalance
</Link>{" "}
models with the same &apos;public name&apos;
</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";
}
};