diff --git a/src/assets/model_icons/embedding.svg b/src/assets/model_icons/embedding.svg
new file mode 100644
index 00000000..6776c323
--- /dev/null
+++ b/src/assets/model_icons/embedding.svg
@@ -0,0 +1,24 @@
+
diff --git a/src/assets/model_icons/image.svg b/src/assets/model_icons/image.svg
new file mode 100644
index 00000000..34448241
--- /dev/null
+++ b/src/assets/model_icons/image.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/assets/model_icons/llm.svg b/src/assets/model_icons/llm.svg
new file mode 100644
index 00000000..cbf12a16
--- /dev/null
+++ b/src/assets/model_icons/llm.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/assets/model_icons/reranker.svg b/src/assets/model_icons/reranker.svg
new file mode 100644
index 00000000..36377aa6
--- /dev/null
+++ b/src/assets/model_icons/reranker.svg
@@ -0,0 +1,19 @@
+
diff --git a/src/assets/model_icons/stt.svg b/src/assets/model_icons/stt.svg
new file mode 100644
index 00000000..d0a111c0
--- /dev/null
+++ b/src/assets/model_icons/stt.svg
@@ -0,0 +1,18 @@
+
diff --git a/src/assets/model_icons/tts.svg b/src/assets/model_icons/tts.svg
new file mode 100644
index 00000000..2640d56a
--- /dev/null
+++ b/src/assets/model_icons/tts.svg
@@ -0,0 +1,23 @@
+
diff --git a/src/pages/llmodels/components/model-item.tsx b/src/pages/llmodels/components/model-item.tsx
index bf2202e5..09b8c33f 100644
--- a/src/pages/llmodels/components/model-item.tsx
+++ b/src/pages/llmodels/components/model-item.tsx
@@ -20,7 +20,11 @@ import {
MyModelsStatusValueMap
} from '../config';
import { categoryToPathMap } from '../config/button-actions';
-import { getModelLogo } from '../utils/model-logo';
+import {
+ defaultModelLogo,
+ getCategoryLogo,
+ getModelLogo
+} from '../utils/model-logo';
const CardWrapper = styled.div`
&:hover {
@@ -165,6 +169,11 @@ const ModelItem: React.FC<{
navigate(`/playground/chat?model=${modelName}`);
};
+ // Logo priority: brand logo matched from the name → tinted category
+ // icon (from model_icons) → generic default image.
+ const brandLogo = getModelLogo(model.name);
+ const categoryLogo = brandLogo ? null : getCategoryLogo(model.categories);
+
// context length
const maxToken = useMemo(() => {
const meta = model.meta || {};
@@ -194,7 +203,13 @@ const ModelItem: React.FC<{
header={
-
+ {brandLogo ? (
+
+ ) : categoryLogo ? (
+
+ ) : (
+
+ )}
{model.name}
= {
+ llm: 'llm',
+ embedding: 'embedding',
+ reranker: 'reranker',
+ image: 'image',
+ text_to_speech: 'tts',
+ speech_to_text: 'stt'
+};
+
/**
- * Resolve a model logo from its name.
+ * Resolve a category icon (from model_icons) for a model's categories.
+ * Returns the first matching category icon url, or null when none match.
+ */
+export const getCategoryLogo = (categories?: string[]): string | null => {
+ const iconName = categories
+ ?.map((category) => CATEGORY_ICON_MAP[category])
+ .find((name) => name && iconMap[name]);
+
+ return iconName ? iconMap[iconName] : null;
+};
+
+/**
+ * Resolve a brand logo from a model name.
* 1. Match against the keyword rules in order (first substring hit wins).
* 2. Fall back to a direct match where the name contains an icon filename.
- * 3. Fall back to the default llm icon.
+ * 3. Return null when nothing matches — the caller then falls back to a
+ * category-based icon (see `categoryConfig`) rather than a default image.
*/
-export const getModelLogo = (modelName?: string): string => {
+export const getModelLogo = (modelName?: string): string | null => {
const name = (modelName || '').toLowerCase();
const rule = MODEL_ICON_RULES.find(([keyword]) => name.includes(keyword));
@@ -90,5 +118,5 @@ export const getModelLogo = (modelName?: string): string => {
.sort((a, b) => b.length - a.length)
.find((iconName) => name.includes(iconName));
- return iconMap[directHit || DEFAULT_ICON] || iconMap[DEFAULT_ICON];
+ return directHit ? iconMap[directHit] : null;
};