From 0e6f09a0a9f676f051c5bdf4050806664c79bdcf Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 23 May 2025 19:13:18 +0400 Subject: [PATCH] enh: ollama loaded model display --- backend/open_webui/routers/ollama.py | 21 +++-- .../chat/ModelSelector/Selector.svelte | 85 +++++++++++++------ 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 86b4b3f7d..da2e8b388 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -9,6 +9,8 @@ import os import random import re import time +from datetime import datetime + from typing import Optional, Union from urllib.parse import urlparse import aiohttp @@ -389,6 +391,19 @@ async def get_all_models(request: Request, user: UserModel = None): ) } + loaded_models = await get_ollama_loaded_models(request, user=user) + expires_map = { + m["name"]: m["expires_at"] + for m in loaded_models["models"] + if "expires_at" in m + } + + for m in models["models"]: + if m["name"] in expires_map: + # Parse ISO8601 datetime with offset, get unix timestamp as int + dt = datetime.fromisoformat(expires_map[m["name"]]) + m["expires_at"] = int(dt.timestamp()) + else: models = {"models": []} @@ -470,7 +485,7 @@ async def get_ollama_tags( @router.get("/api/ps") -async def get_ollama_loaded_models(request: Request, user=Depends(get_verified_user)): +async def get_ollama_loaded_models(request: Request, user=Depends(get_admin_user)): """ List models that are currently loaded into Ollama memory, and which node they are loaded on. """ @@ -525,10 +540,6 @@ async def get_ollama_loaded_models(request: Request, user=Depends(get_verified_u ) ) } - - if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL: - models["models"] = await get_filtered_models(models, user) - else: models = {"models": []} diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte index 5d0bf6b65..f60620996 100644 --- a/src/lib/components/chat/ModelSelector/Selector.svelte +++ b/src/lib/components/chat/ModelSelector/Selector.svelte @@ -29,6 +29,9 @@ import Switch from '$lib/components/common/Switch.svelte'; import ChatBubbleOval from '$lib/components/icons/ChatBubbleOval.svelte'; import { goto } from '$app/navigation'; + import dayjs from '$lib/dayjs'; + import relativeTime from 'dayjs/plugin/relativeTime'; + dayjs.extend(relativeTime); const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); @@ -326,8 +329,17 @@ aria-label={placeholder} id="model-selector-{id}-button" > -
{ + models.set( + await getModels( + localStorage.token, + $config?.features?.enable_direct_connections && ($settings?.directConnections ?? null) + ) + ); + }} + type="button" > {#if selectedModel} {selectedModel.label} @@ -335,7 +347,7 @@ {placeholder} {/if} -
+ {item.label} - - {#if item.model.owned_by === 'ollama' && (item.model.ollama?.details?.parameter_size ?? '') !== ''} -
- - {item.model.ollama?.details?.parameter_size ?? ''} - -
- {/if} + {#if item.model.owned_by === 'ollama'} + {#if (item.model.ollama?.details?.parameter_size ?? '') !== ''} +
+ + {item.model.ollama?.details?.parameter_size ?? ''} + +
+ {/if} + {#if item.model.ollama?.expires_at && new Date(item.model.ollama?.expires_at * 1000) > new Date()} +
+ +
+ + + + +
+
+
+ {/if} + {/if} + {#if item.model?.direct} - +
{:else if item.model.connection_type === 'external'} - +
{#if showTemporaryChatControl} -
+