]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
webui: load the model selected via ?model= when ?load=true (#26707)
authorEmanuil Rusev <redacted>
Fri, 7 Aug 2026 13:31:40 +0000 (16:31 +0300)
committerGitHub <redacted>
Fri, 7 Aug 2026 13:31:40 +0000 (15:31 +0200)
* webui: load the model selected via ?model=

Opening the WebUI with ?model= selects the model but doesn't load it. The load only starts when you send your first message, so you wait for it then.

This loads it as soon as the page opens, while you're still typing your prompt. It's what the model dropdown already does, and it isn't awaited, so the UI still works while the model loads.

This is the path the Llama macOS app uses to open the WebUI, so it's a common way in.

* webui: gate the load behind ?load=true

Loading on landing is opt-in, so a plain ?model= link behaves as before and doesn't allocate memory on its own.

* webui: name the chat URL params

Collects the query params the chat routes read into a URL_PARAMS constant, instead of repeating the literals across three files. NEW_CHAT_PARAM folds into it.

tools/ui/src/lib/components/app/dialogs/DialogModelNotAvailable.svelte
tools/ui/src/lib/constants/routes.ts
tools/ui/src/routes/(chat)/+page.svelte
tools/ui/src/routes/(chat)/chat/[id]/+page.svelte

index 89d23cd4b29279d2c698555b08f4109f7b0876f0..583c5fcb0394338bf5644cae7dec9338141d81b2 100644 (file)
@@ -1,5 +1,6 @@
 <script lang="ts">
        import { ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
+       import { URL_PARAMS } from '$lib/constants';
        import * as AlertDialog from '$lib/components/ui/alert-dialog';
        import { AlertTriangle, ArrowRight } from '@lucide/svelte';
        import { goto } from '$app/navigation';
@@ -22,7 +23,7 @@
        function handleSelectModel(model: string) {
                // Build URL with selected model, preserving other params
                const url = new URL(page.url);
-               url.searchParams.set('model', model);
+               url.searchParams.set(URL_PARAMS.MODEL, model);
 
                handleOpenChange(false);
                goto(url.toString());
index 0d6b5942fcd64d126b10c9d47d184c1ed977c90f..26c7d9a293a648ba5b189f953d9f5a40ec0255dc 100644 (file)
@@ -1,4 +1,14 @@
-export const NEW_CHAT_PARAM = 'new_chat';
+/** Query params the chat routes read from the URL. */
+export const URL_PARAMS = {
+       /** Prompt to send on arrival. */
+       QUERY: 'q',
+       /** Model to select. */
+       MODEL: 'model',
+       /** Load the selected model instead of waiting for the first message. */
+       LOAD: 'load',
+       /** Start a new chat. */
+       NEW_CHAT: 'new_chat'
+} as const;
 
 /** Settings section slugs — used for routes and navigation. */
 export const SETTINGS_SECTION_SLUGS = {
@@ -16,7 +26,7 @@ export const ROUTES = {
        /** Root — start of the app. */
        START: '#/',
        /** New chat — root with new chat query param. */
-       NEW_CHAT: `?${NEW_CHAT_PARAM}=true#/`,
+       NEW_CHAT: `?${URL_PARAMS.NEW_CHAT}=true#/`,
        /** Chat base — for dynamic chat URLs use RouterService. */
        CHAT: '#/chat',
        /** MCP servers. */
index 9db1d445fe8429cef1e44e0b3a7d6189d1f2df8b..fef9b9f53f5e96c6be93e3c5e241a4f7a5692d6f 100644 (file)
@@ -3,14 +3,16 @@
        import { chatStore } from '$lib/stores/chat.svelte';
        import { conversationsStore, isConversationsInitialized } from '$lib/stores/conversations.svelte';
        import { modelsStore, modelOptions } from '$lib/stores/models.svelte';
+       import { isRouterMode } from '$lib/stores/server.svelte';
        import { onMount } from 'svelte';
        import { page } from '$app/state';
        import { replaceState } from '$app/navigation';
-       import { APP_NAME, NEW_CHAT_PARAM } from '$lib/constants';
+       import { APP_NAME, URL_PARAMS } from '$lib/constants';
 
-       let qParam = $derived(page.url.searchParams.get('q'));
-       let modelParam = $derived(page.url.searchParams.get('model'));
-       let newChatParam = $derived(page.url.searchParams.get(NEW_CHAT_PARAM));
+       let qParam = $derived(page.url.searchParams.get(URL_PARAMS.QUERY));
+       let modelParam = $derived(page.url.searchParams.get(URL_PARAMS.MODEL));
+       let newChatParam = $derived(page.url.searchParams.get(URL_PARAMS.NEW_CHAT));
+       let loadParam = $derived(page.url.searchParams.get(URL_PARAMS.LOAD));
 
        // Dialog state for model not available error
        let showModelNotAvailable = $state(false);
        function clearUrlParams() {
                const url = new URL(page.url);
 
-               url.searchParams.delete('q');
-               url.searchParams.delete('model');
-               url.searchParams.delete(NEW_CHAT_PARAM);
+               url.searchParams.delete(URL_PARAMS.QUERY);
+               url.searchParams.delete(URL_PARAMS.MODEL);
+               url.searchParams.delete(URL_PARAMS.LOAD);
+               url.searchParams.delete(URL_PARAMS.NEW_CHAT);
 
                replaceState(url.toString(), {});
        }
                        if (model) {
                                try {
                                        await modelsStore.selectModelById(model.id);
+
+                                       // with ?load=true, start loading right away so the model is ready sooner;
+                                       // not awaited, so the UI stays usable during the load
+                                       if (loadParam === 'true' && isRouterMode() && !modelsStore.isModelLoaded(model.id)) {
+                                               modelsStore
+                                                       .loadModel(model.id)
+                                                       .catch((error) => console.error('Failed to load model:', error));
+                                       }
                                } catch (error) {
                                        console.error('Failed to select model:', error);
                                        requestedModelName = modelParam;
index f14553c90a9077f9842c65de093dbb373d061314..f5fe42eaa010350842f17cc0e430a74e994b6e8c 100644 (file)
@@ -3,7 +3,7 @@
        import { page } from '$app/state';
        import { afterNavigate } from '$app/navigation';
        import { DialogModelNotAvailable } from '$lib/components/app';
-       import { APP_NAME, ROUTES } from '$lib/constants';
+       import { APP_NAME, ROUTES, URL_PARAMS } from '$lib/constants';
        import { chatStore } from '$lib/stores/chat.svelte';
        import { conversationsStore, activeConversation } from '$lib/stores/conversations.svelte';
        import { modelsStore, modelOptions } from '$lib/stores/models.svelte';
@@ -12,8 +12,8 @@
        let currentChatId: string | undefined = undefined;
 
        // URL parameters for prompt and model selection
-       let qParam = $derived(page.url.searchParams.get('q'));
-       let modelParam = $derived(page.url.searchParams.get('model'));
+       let qParam = $derived(page.url.searchParams.get(URL_PARAMS.QUERY));
+       let modelParam = $derived(page.url.searchParams.get(URL_PARAMS.MODEL));
 
        // Dialog state for model not available error
        let showModelNotAvailable = $state(false);
@@ -28,8 +28,8 @@
         */
        function clearUrlParams() {
                const url = new URL(page.url);
-               url.searchParams.delete('q');
-               url.searchParams.delete('model');
+               url.searchParams.delete(URL_PARAMS.QUERY);
+               url.searchParams.delete(URL_PARAMS.MODEL);
                replaceState(url.toString(), {});
        }