mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-08 16:22:07 +00:00
UI - Model Hub Page - minor fixes + improvements (+ Make Model Hub OSS 🚀) (#12553)
* fix(model_hub_table.tsx): fix null link * fix(model_hub_table.tsx): add tooltip on disabled make public tell user how to enable button * feat(model_hub_table.tsx): make Model Hub OSS allow more teams to share available models via litellm * fix(model_hub_table_columns.tsx): make table view only model hub view, rename 'model' column to 'public model name', make model hub url copyable * fix(page.tsx): fix logo re-rendering * fix(public_model_hub.tsx): fix theme * style(public_model_hub.tsx): move filters into same card as model table filters are for table * fix(page.tsx): fix ui linting error
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { modelHubCall } from "@/components/networking";
|
||||
import ModelHub from "@/components/model_hub";
|
||||
import PublicModelHubPage from "@/components/public_model_hub";
|
||||
|
||||
export default function PublicModelHub() {
|
||||
const searchParams = useSearchParams()!;
|
||||
@@ -20,6 +20,6 @@ export default function PublicModelHub() {
|
||||
*
|
||||
*/
|
||||
return (
|
||||
<ModelHub accessToken={accessToken} publicPage={true} premiumUser={false} />
|
||||
<PublicModelHubPage accessToken={accessToken} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import GeneralSettings from "@/components/general_settings";
|
||||
import PassThroughSettings from "@/components/pass_through_settings";
|
||||
import BudgetPanel from "@/components/budgets/budget_panel";
|
||||
import SpendLogsTable from "@/components/view_logs";
|
||||
import ModelHub from "@/components/model_hub";
|
||||
import ModelHubTable from "@/components/model_hub_table";
|
||||
import NewUsagePage from "@/components/new_usage";
|
||||
import APIRef from "@/components/api_ref";
|
||||
@@ -258,6 +257,7 @@ export default function CreateKeyPage() {
|
||||
setProxySettings={setProxySettings}
|
||||
proxySettings={proxySettings}
|
||||
accessToken={accessToken}
|
||||
isPublicPage={false}
|
||||
/>
|
||||
<div className="flex flex-1 overflow-auto">
|
||||
<div className="mt-8">
|
||||
@@ -370,12 +370,6 @@ export default function CreateKeyPage() {
|
||||
accessToken={accessToken}
|
||||
modelData={modelData}
|
||||
/>
|
||||
) : page == "model-hub" ? (
|
||||
<ModelHub
|
||||
accessToken={accessToken}
|
||||
publicPage={false}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
) : page == "model-hub-table" ? (
|
||||
<ModelHubTable
|
||||
accessToken={accessToken}
|
||||
|
||||
@@ -66,13 +66,9 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
{ key: "14", page: "api_ref", label: "API Reference", icon: <ApiOutlined /> },
|
||||
{
|
||||
key: "16",
|
||||
page: "model-hub",
|
||||
page: "model-hub-table",
|
||||
label: "Model Hub",
|
||||
icon: <AppstoreOutlined />,
|
||||
children: [
|
||||
{ key: "16a", page: "model-hub", label: "Card View", icon: <AppstoreOutlined /> },
|
||||
{ key: "16b", page: "model-hub-table", label: "Table View", icon: <LineChartOutlined /> }
|
||||
]
|
||||
icon: <AppstoreOutlined />
|
||||
},
|
||||
{ key: "15", page: "logs", label: "Logs", icon: <LineChartOutlined />},
|
||||
{ key: "11", page: "guardrails", label: "Guardrails", icon: <SafetyOutlined />, roles: all_admin_roles },
|
||||
|
||||
@@ -1,375 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useRouter, usePathname, useSearchParams } from "next/navigation";
|
||||
|
||||
import { modelHubCall } from "./networking";
|
||||
import { getConfigFieldSetting, updateConfigFieldSetting } from "./networking";
|
||||
import {
|
||||
Card,
|
||||
Text,
|
||||
Title,
|
||||
Grid,
|
||||
Button,
|
||||
Badge,
|
||||
Tab,
|
||||
TabGroup,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
} from "@tremor/react";
|
||||
import { RightOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
|
||||
import { Modal, Tooltip, message } from "antd";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
|
||||
interface ModelHubProps {
|
||||
accessToken: string | null;
|
||||
publicPage: boolean;
|
||||
premiumUser: boolean;
|
||||
}
|
||||
|
||||
interface ModelInfo {
|
||||
model_group: string;
|
||||
mode: string;
|
||||
supports_function_calling: boolean;
|
||||
supports_vision: boolean;
|
||||
max_input_tokens?: number;
|
||||
max_output_tokens?: number;
|
||||
input_cost_per_token?: number;
|
||||
output_cost_per_token?: number;
|
||||
supported_openai_params?: string[];
|
||||
}
|
||||
|
||||
const ModelHub: React.FC<ModelHubProps> = ({
|
||||
accessToken,
|
||||
publicPage,
|
||||
premiumUser,
|
||||
}) => {
|
||||
const [publicPageAllowed, setPublicPageAllowed] = useState<boolean>(false);
|
||||
const [modelHubData, setModelHubData] = useState<ModelInfo[] | null>(null);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [isPublicPageModalVisible, setIsPublicPageModalVisible] =
|
||||
useState(false);
|
||||
const [selectedModel, setSelectedModel] = useState<null | ModelInfo>(null);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const _modelHubData = await modelHubCall(accessToken);
|
||||
|
||||
console.log("ModelHubData:", _modelHubData);
|
||||
|
||||
setModelHubData(_modelHubData.data);
|
||||
|
||||
getConfigFieldSetting(accessToken, "enable_public_model_hub")
|
||||
.then((data) => {
|
||||
console.log(`data: ${JSON.stringify(data)}`);
|
||||
if (data.field_value == true) {
|
||||
setPublicPageAllowed(true);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// do nothing
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("There was an error fetching the model data", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [accessToken, publicPage]);
|
||||
|
||||
const showModal = (model: ModelInfo) => {
|
||||
setSelectedModel(model);
|
||||
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const goToPublicModelPage = () => {
|
||||
router.replace(`/model_hub?key=${accessToken}`);
|
||||
};
|
||||
const handleMakePublicPage = async () => {
|
||||
if (!accessToken) {
|
||||
return;
|
||||
}
|
||||
updateConfigFieldSetting(accessToken, "enable_public_model_hub", true).then(
|
||||
(data) => {
|
||||
setIsPublicPageModalVisible(true);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalVisible(false);
|
||||
setIsPublicPageModalVisible(false);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsModalVisible(false);
|
||||
setIsPublicPageModalVisible(false);
|
||||
setSelectedModel(null);
|
||||
};
|
||||
|
||||
const copyToClipboard = (text: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{(publicPage && publicPageAllowed) || publicPage == false ? (
|
||||
<div className="w-full m-2 mt-2 p-8">
|
||||
<div className="relative w-full"></div>
|
||||
|
||||
<div
|
||||
className={`flex ${publicPage ? "justify-between" : "items-center"}`}
|
||||
>
|
||||
<Title className="ml-8 text-center ">Model Hub</Title>
|
||||
{publicPage == false ? (
|
||||
premiumUser ? (
|
||||
<Button className="ml-4" onClick={() => handleMakePublicPage()}>
|
||||
✨ Make Public
|
||||
</Button>
|
||||
) : (
|
||||
<Button className="ml-4">
|
||||
<a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank">
|
||||
✨ Make Public
|
||||
</a>
|
||||
</Button>
|
||||
)
|
||||
) : (
|
||||
<div className="flex justify-between items-center">
|
||||
<p>Filter by key:</p>
|
||||
<Text className="bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center">{`/ui/model_hub?key=<YOUR_KEY>`}</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 pr-8">
|
||||
{modelHubData &&
|
||||
modelHubData.map((model: ModelInfo) => (
|
||||
<Card key={model.model_group} className="mt-5 mx-8">
|
||||
<pre className="flex justify-between">
|
||||
<Title className="text-wrap">{model.model_group}</Title>
|
||||
<Tooltip title={model.model_group}>
|
||||
<CopyOutlined
|
||||
onClick={() => copyToClipboard(model.model_group)}
|
||||
style={{ cursor: "pointer", marginRight: "10px" }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</pre>
|
||||
<div className="my-5">
|
||||
<Text>
|
||||
Max Input Tokens:{" "}
|
||||
{model?.max_input_tokens
|
||||
? model?.max_input_tokens
|
||||
: "Unknown"}
|
||||
</Text>
|
||||
<Text>
|
||||
Max Output Tokens:{" "}
|
||||
{model?.max_output_tokens
|
||||
? model?.max_output_tokens
|
||||
: "Unknown"}
|
||||
</Text>
|
||||
<Text>
|
||||
Input Cost Per 1M Tokens (USD):{" "}
|
||||
{model?.input_cost_per_token
|
||||
? `$${(model.input_cost_per_token * 1_000_000).toFixed(2)}`
|
||||
: "Unknown"}
|
||||
</Text>
|
||||
<Text>
|
||||
Output Cost Per 1M Tokens (USD):{" "}
|
||||
{model?.output_cost_per_token
|
||||
? `$${(model.output_cost_per_token * 1_000_000).toFixed(2)}`
|
||||
: "Unknown"}
|
||||
</Text>
|
||||
</div>
|
||||
<div style={{ marginTop: "auto", textAlign: "right" }}>
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => showModal(model)}
|
||||
style={{ color: "#1890ff", fontSize: "smaller" }}
|
||||
>
|
||||
View more <RightOutlined />
|
||||
</a>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Card className="mx-auto max-w-xl mt-10">
|
||||
<Text className="text-xl text-center mb-2 text-black">
|
||||
Public Model Hub not enabled.
|
||||
</Text>
|
||||
<p className="text-base text-center text-slate-800">
|
||||
Ask your proxy admin to enable this on their Admin UI.
|
||||
</p>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
title={"Public Model Hub"}
|
||||
width={600}
|
||||
visible={isPublicPageModalVisible}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<div className="pt-5 pb-5">
|
||||
<div className="flex justify-between mb-4">
|
||||
<Text className="text-base mr-2">Shareable Link:</Text>
|
||||
<Text className="max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded">{`<proxy_base_url>/ui/model_hub?key=<YOUR_API_KEY>`}</Text>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={goToPublicModelPage}>See Page</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={
|
||||
selectedModel && selectedModel.model_group
|
||||
? selectedModel.model_group
|
||||
: "Unknown Model"
|
||||
}
|
||||
width={800}
|
||||
visible={isModalVisible}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
{selectedModel && (
|
||||
<div>
|
||||
<p className="mb-4">
|
||||
<strong>Model Information & Usage</strong>
|
||||
</p>
|
||||
|
||||
<TabGroup>
|
||||
<TabList>
|
||||
<Tab>Model Information</Tab>
|
||||
<Tab>OpenAI Python SDK</Tab>
|
||||
<Tab>Supported OpenAI Params</Tab>
|
||||
<Tab>LlamaIndex</Tab>
|
||||
<Tab>Langchain Py</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<Text>
|
||||
<strong>Model Group:</strong>
|
||||
<pre>{JSON.stringify(selectedModel, null, 2)}</pre>
|
||||
</Text>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<SyntaxHighlighter language="python">
|
||||
{`
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="your_api_key",
|
||||
base_url="http://0.0.0.0:4000" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="${selectedModel.model_group}", # model to send to the proxy
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
print(response)
|
||||
`}
|
||||
</SyntaxHighlighter>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<SyntaxHighlighter language="python">
|
||||
{`${selectedModel.supported_openai_params?.map((param) => `${param}\n`).join("")}`}
|
||||
</SyntaxHighlighter>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<SyntaxHighlighter language="python">
|
||||
{`
|
||||
import os, dotenv
|
||||
|
||||
from llama_index.llms import AzureOpenAI
|
||||
from llama_index.embeddings import AzureOpenAIEmbedding
|
||||
from llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext
|
||||
|
||||
llm = AzureOpenAI(
|
||||
engine="${selectedModel.model_group}", # model_name on litellm proxy
|
||||
temperature=0.0,
|
||||
azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint
|
||||
api_key="sk-1234", # litellm proxy API Key
|
||||
api_version="2023-07-01-preview",
|
||||
)
|
||||
|
||||
embed_model = AzureOpenAIEmbedding(
|
||||
deployment_name="azure-embedding-model",
|
||||
azure_endpoint="http://0.0.0.0:4000",
|
||||
api_key="sk-1234",
|
||||
api_version="2023-07-01-preview",
|
||||
)
|
||||
|
||||
|
||||
documents = SimpleDirectoryReader("llama_index_data").load_data()
|
||||
service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)
|
||||
index = VectorStoreIndex.from_documents(documents, service_context=service_context)
|
||||
|
||||
query_engine = index.as_query_engine()
|
||||
response = query_engine.query("What did the author do growing up?")
|
||||
print(response)
|
||||
|
||||
`}
|
||||
</SyntaxHighlighter>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<SyntaxHighlighter language="python">
|
||||
{`
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from langchain.prompts.chat import (
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import HumanMessage, SystemMessage
|
||||
|
||||
chat = ChatOpenAI(
|
||||
openai_api_base="http://0.0.0.0:4000",
|
||||
model = "${selectedModel.model_group}",
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
messages = [
|
||||
SystemMessage(
|
||||
content="You are a helpful assistant that im using to make a test request to."
|
||||
),
|
||||
HumanMessage(
|
||||
content="test from litellm. tell me why it's amazing in 1 sentence"
|
||||
),
|
||||
]
|
||||
response = chat(messages)
|
||||
|
||||
print(response)
|
||||
|
||||
`}
|
||||
</SyntaxHighlighter>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
|
||||
{/* <p><strong>Additional Params:</strong> {JSON.stringify(selectedModel.litellm_params)}</p> */}
|
||||
|
||||
{/* Add other model details here */}
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelHub;
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { modelHubCall, makeModelGroupPublic, modelHubPublicModelsCall, proxyBaseUrl } from "./networking";
|
||||
import { modelHubCall, makeModelGroupPublic, modelHubPublicModelsCall, getProxyBaseUrl } from "./networking";
|
||||
import { getConfigFieldSetting, updateConfigFieldSetting } from "./networking";
|
||||
import { ModelDataTable } from "./model_dashboard/table";
|
||||
import { modelHubColumns } from "./model_hub_table_columns";
|
||||
@@ -13,9 +13,10 @@ import {
|
||||
Badge,
|
||||
Flex,
|
||||
} from "@tremor/react";
|
||||
import { Modal, message } from "antd";
|
||||
import { Modal, message, Tooltip } from "antd";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { Table as TableInstance } from '@tanstack/react-table';
|
||||
import { Copy } from "lucide-react";
|
||||
|
||||
interface ModelHubTableProps {
|
||||
accessToken: string | null;
|
||||
@@ -284,32 +285,38 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({
|
||||
{publicPage == false ? (
|
||||
<div className="w-full m-2 mt-2 p-8">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<Title className="text-center">Model Hub - Table View</Title>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Text>Model Hub URL:</Text>
|
||||
<Text className="bg-gray-200 px-2 py-1 rounded">{`${proxyBaseUrl}/ui/model_hub_table`}</Text>
|
||||
<div className="flex flex-col items-start">
|
||||
<Title className="text-center">Model Hub</Title>
|
||||
<p className="text-sm text-gray-600">
|
||||
A list of all public model names personally available to you.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Text>Model Hub URL:</Text>
|
||||
<div className="flex items-center bg-gray-200 px-2 py-1 rounded">
|
||||
<Text className="mr-2">{`${getProxyBaseUrl()}/ui/model_hub_table`}</Text>
|
||||
<button
|
||||
onClick={() => copyToClipboard(`${getProxyBaseUrl()}/ui/model_hub_table`)}
|
||||
className="p-1 hover:bg-gray-300 rounded transition-colors"
|
||||
title="Copy URL"
|
||||
>
|
||||
<Copy size={16} className="text-gray-600" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{publicPage == false ? (
|
||||
premiumUser ? (
|
||||
<Button
|
||||
className="ml-4"
|
||||
onClick={() => handleMakePublicPage()}
|
||||
disabled={selectedModels.size === 0}
|
||||
>
|
||||
✨ Make Public
|
||||
</Button>
|
||||
) : (
|
||||
<Button className="ml-4">
|
||||
<a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank">
|
||||
✨ Make Public
|
||||
</a>
|
||||
</Button>
|
||||
)
|
||||
) : (
|
||||
<div className="flex items-center space-x-4">
|
||||
<Text>Filter by key:</Text>
|
||||
<Text className="bg-gray-200 px-2 py-1 rounded">{`/ui/model_hub_table?key=<YOUR_KEY>`}</Text>
|
||||
</div>
|
||||
{publicPage == false && (
|
||||
<Tooltip
|
||||
title={selectedModels.size === 0 ? "Select models to make them publicly known" : ""}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
className="ml-4"
|
||||
onClick={() => handleMakePublicPage()}
|
||||
disabled={selectedModels.size === 0}
|
||||
>
|
||||
Make Public
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -432,7 +439,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({
|
||||
<div className="flex justify-between mb-4">
|
||||
<Text className="text-base mr-2">Shareable Link:</Text>
|
||||
<Text className="max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded">
|
||||
{`${proxyBaseUrl}/model_hub_table`}
|
||||
{`${getProxyBaseUrl()}/ui/model_hub_table`}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
|
||||
@@ -87,7 +87,7 @@ export const modelHubColumns = (
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Model",
|
||||
header: "Public Model Name",
|
||||
accessorKey: "model_group",
|
||||
enableSorting: true,
|
||||
sortingFn: "alphanumeric",
|
||||
|
||||
@@ -24,6 +24,7 @@ interface NavbarProps {
|
||||
setProxySettings: React.Dispatch<React.SetStateAction<any>>;
|
||||
proxySettings: any;
|
||||
accessToken: string | null;
|
||||
isPublicPage: boolean;
|
||||
}
|
||||
|
||||
const Navbar: React.FC<NavbarProps> = ({
|
||||
@@ -34,6 +35,7 @@ const Navbar: React.FC<NavbarProps> = ({
|
||||
proxySettings,
|
||||
setProxySettings,
|
||||
accessToken,
|
||||
isPublicPage = false,
|
||||
}) => {
|
||||
const baseUrl = getProxyBaseUrl();
|
||||
const imageUrl = baseUrl + "/get_image";
|
||||
@@ -143,6 +145,7 @@ const Navbar: React.FC<NavbarProps> = ({
|
||||
Docs
|
||||
</a>
|
||||
|
||||
{!isPublicPage && (
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: userItems,
|
||||
@@ -170,6 +173,7 @@ const Navbar: React.FC<NavbarProps> = ({
|
||||
</svg>
|
||||
</button>
|
||||
</Dropdown>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { generateCodeSnippet } from "./chat_ui/CodeSnippets";
|
||||
import { EndpointType, getEndpointType } from "./chat_ui/mode_endpoint_mapping";
|
||||
import { MessageType } from "./chat_ui/types";
|
||||
import { getProviderLogoAndName } from "./provider_info_helpers";
|
||||
import Navbar from "./navbar";
|
||||
// Simple approach without react-markdown dependency
|
||||
|
||||
interface ModelGroupInfo {
|
||||
@@ -54,6 +55,7 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
const [serviceStatus, setServiceStatus] = useState<string>("I'm alive! ✓");
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [selectedModel, setSelectedModel] = useState<null | ModelGroupInfo>(null);
|
||||
const [proxySettings, setProxySettings] = useState<any>({});
|
||||
const tableRef = useRef<TableInstance<any>>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -474,22 +476,27 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Header */}
|
||||
<div className="bg-green-600 text-white px-8 py-6">
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<Title className="text-white text-2xl font-semibold">{pageTitle}</Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-h-screen bg-white">
|
||||
{/* Navigation */}
|
||||
<Navbar
|
||||
userID={null}
|
||||
userEmail={null}
|
||||
userRole={null}
|
||||
premiumUser={false}
|
||||
setProxySettings={setProxySettings}
|
||||
proxySettings={proxySettings}
|
||||
accessToken={accessToken || null}
|
||||
isPublicPage={true}
|
||||
/>
|
||||
|
||||
<div className="w-full px-8 py-12">
|
||||
{/* About Section */}
|
||||
<Card className="mb-10 p-8">
|
||||
<Title className="text-3xl font-semibold mb-6">About</Title>
|
||||
<p className="text-gray-700 mb-6 text-lg leading-relaxed">{customDocsDescription ? customDocsDescription : "Proxy Server to call 100+ LLMs in the OpenAI format."}</p>
|
||||
<div className="flex items-center space-x-3 text-base text-gray-600">
|
||||
<Card className="mb-10 p-8 bg-white border border-gray-200 rounded-lg shadow-sm">
|
||||
<Title className="text-2xl font-semibold mb-6 text-gray-900">About</Title>
|
||||
<p className="text-gray-700 mb-6 text-base leading-relaxed">{customDocsDescription ? customDocsDescription : "Proxy Server to call 100+ LLMs in the OpenAI format."}</p>
|
||||
<div className="flex items-center space-x-3 text-sm text-gray-600">
|
||||
<span className="flex items-center">
|
||||
<span className="w-5 h-5 mr-2">🔧</span>
|
||||
<span className="w-4 h-4 mr-2">🔧</span>
|
||||
Built with litellm: v{litellmVersion}
|
||||
</span>
|
||||
</div>
|
||||
@@ -497,17 +504,17 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
|
||||
{/* Useful Links */}
|
||||
{usefulLinks && Object.keys(usefulLinks).length > 0 && (
|
||||
<Card className="mb-10 p-8">
|
||||
<Title className="text-3xl font-semibold mb-6">Useful Links</Title>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<Card className="mb-10 p-8 bg-white border border-gray-200 rounded-lg shadow-sm">
|
||||
<Title className="text-2xl font-semibold mb-6 text-gray-900">Useful Links</Title>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{Object.entries(usefulLinks || {}).map(([title, url]) => (
|
||||
<button
|
||||
key={title}
|
||||
onClick={() => window.open(url, '_blank')}
|
||||
className="flex items-center space-x-3 text-blue-600 hover:text-blue-800 transition-colors p-4 rounded-lg hover:bg-blue-50"
|
||||
className="flex items-center space-x-3 text-blue-600 hover:text-blue-800 transition-colors p-3 rounded-lg hover:bg-blue-50 border border-gray-200"
|
||||
>
|
||||
<ExternalLinkIcon className="w-5 h-5" />
|
||||
<Text className="text-base font-medium">{title}</Text>
|
||||
<ExternalLinkIcon className="w-4 h-4" />
|
||||
<Text className="text-sm font-medium">{title}</Text>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -515,36 +522,41 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
)}
|
||||
|
||||
{/* Health and Endpoint Status */}
|
||||
<Card className="mb-10 p-8">
|
||||
<Title className="text-3xl font-semibold mb-6">Health and Endpoint Status</Title>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<Text className="text-green-600 font-medium text-base">Service status: {serviceStatus}</Text>
|
||||
<Card className="mb-10 p-8 bg-white border border-gray-200 rounded-lg shadow-sm">
|
||||
<Title className="text-2xl font-semibold mb-6 text-gray-900">Health and Endpoint Status</Title>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<Text className="text-green-600 font-medium text-sm">Service status: {serviceStatus}</Text>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Filters */}
|
||||
<Card className="mb-10 p-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{/* Models Table */}
|
||||
<Card className="p-8 bg-white border border-gray-200 rounded-lg shadow-sm">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<Title className="text-2xl font-semibold text-gray-900">Available Models</Title>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 p-6 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<div>
|
||||
<div className="flex items-center space-x-2 mb-3">
|
||||
<Text className="text-base font-medium">Search Models:</Text>
|
||||
<Text className="text-sm font-medium text-gray-700">Search Models:</Text>
|
||||
<Tooltip title="Smart search with relevance ranking - finds models containing your search terms, ranked by relevance. Try searching 'xai grok-4', 'claude-4', 'gpt-4', or 'sonnet'" placement="top">
|
||||
<Info className="w-4 h-4 text-gray-400 cursor-help" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<SearchIcon className="w-5 h-5 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2" />
|
||||
<SearchIcon className="w-4 h-4 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search model names... (smart search enabled)"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="border rounded-lg pl-10 pr-4 py-3 w-full text-base focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
className="border border-gray-300 rounded-lg pl-10 pr-4 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="text-base font-medium mb-3">Provider:</Text>
|
||||
<Text className="text-sm font-medium mb-3 text-gray-700">Provider:</Text>
|
||||
<Select
|
||||
mode="multiple"
|
||||
value={selectedProviders}
|
||||
@@ -574,25 +586,13 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
>
|
||||
{modelHubData && getUniqueProviders(modelHubData).map(provider => (
|
||||
<Select.Option key={provider} value={provider}>
|
||||
<div className="flex items-center space-x-2">
|
||||
{getProviderLogoAndName(provider).logo && (
|
||||
<img
|
||||
src={getProviderLogoAndName(provider).logo}
|
||||
alt={provider}
|
||||
className="w-5 h-5 flex-shrink-0 object-contain"
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="capitalize">{provider}</span>
|
||||
</div>
|
||||
{provider}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="text-base font-medium mb-3">Mode:</Text>
|
||||
<Text className="text-sm font-medium mb-3 text-gray-700">Mode:</Text>
|
||||
<Select
|
||||
mode="multiple"
|
||||
value={selectedModes}
|
||||
@@ -608,7 +608,7 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="text-base font-medium mb-3">Features:</Text>
|
||||
<Text className="text-sm font-medium mb-3 text-gray-700">Features:</Text>
|
||||
<Select
|
||||
mode="multiple"
|
||||
value={selectedFeatures}
|
||||
@@ -624,13 +624,6 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Models Table */}
|
||||
<Card className="p-8">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<Title className="text-3xl font-semibold">Available Models</Title>
|
||||
</div>
|
||||
|
||||
<ModelDataTable
|
||||
columns={publicModelHubColumns()}
|
||||
@@ -641,7 +634,7 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken }) => {
|
||||
/>
|
||||
|
||||
<div className="mt-8 text-center">
|
||||
<Text className="text-base text-gray-600">
|
||||
<Text className="text-sm text-gray-600">
|
||||
Showing {filteredData.length} of {modelHubData?.length || 0} models
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user