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:
Krish Dholakia
2025-07-12 14:10:03 -07:00
committed by GitHub
parent 5bd63c5a72
commit 1f5ab2ee74
8 changed files with 90 additions and 471 deletions
@@ -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} />
);
}
+1 -7
View File
@@ -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>