From 0852e4a7edbe115b8f9d3b2f2d53948e7fa0f085 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Mon, 28 Sep 2026 15:37:21 +0200 Subject: [PATCH] ui : route chat through the active provider Chat goes through protocol adapters, so an OpenAI-compatible endpoint speaks its own wire format: per-backend paths and headers, the model on the request, tools kept on the local server, and token counts synthesized for endpoints that do not stream their own timings. The server store keeps the local server's props while another provider is active, a conversation resolves the provider its model belongs to before sending, and the chat screen never blocks on the local probe when the install has none. Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash --- .../ContextGaugePopup.svelte | 2 +- .../ChatMessageAssistant.svelte | 1 - .../app/chat/ChatScreen/ChatScreen.svelte | 7 +- .../chat/ChatScreen/ChatScreenGreeting.svelte | 4 +- .../app/server/ServerErrorSplash.svelte | 4 +- .../components/app/server/ServerStatus.svelte | 2 +- .../hooks/use-backend-availability.svelte.ts | 38 +++ .../src/lib/hooks/use-context-gauge.svelte.ts | 10 +- tools/ui/src/lib/services/chat.service.ts | 287 +++++++++++------- tools/ui/src/lib/services/models.service.ts | 122 ++++++-- tools/ui/src/lib/services/props.service.ts | 8 +- tools/ui/src/lib/services/protocols/index.ts | 24 ++ tools/ui/src/lib/services/protocols/openai.ts | 183 +++++++++++ tools/ui/src/lib/services/protocols/types.ts | 42 +++ tools/ui/src/lib/services/tools.service.ts | 14 +- tools/ui/src/lib/stores/chat/index.svelte.ts | 21 +- tools/ui/src/lib/stores/index.ts | 5 + tools/ui/src/lib/stores/init.ts | 38 ++- .../ui/src/lib/stores/models/index.svelte.ts | 254 ++++++++++++++-- .../ui/src/lib/stores/models/props.svelte.ts | 50 ++- .../ui/src/lib/stores/models/status.svelte.ts | 136 ++++++++- tools/ui/src/lib/stores/server.svelte.ts | 153 +++++++++- tools/ui/src/lib/stores/tools.svelte.ts | 11 + tools/ui/src/lib/stores/ui.svelte.ts | 1 - tools/ui/src/routes/(chat)/+page.ts | 7 +- tools/ui/src/routes/(chat)/chat/[id]/+page.ts | 7 +- 26 files changed, 1224 insertions(+), 207 deletions(-) create mode 100644 tools/ui/src/lib/hooks/use-backend-availability.svelte.ts create mode 100644 tools/ui/src/lib/services/protocols/index.ts create mode 100644 tools/ui/src/lib/services/protocols/openai.ts create mode 100644 tools/ui/src/lib/services/protocols/types.ts diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte index de3d2f4ea8..e1a006a404 100644 --- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormContextGauge/ContextGaugePopup.svelte @@ -63,7 +63,7 @@ {/snippet} {#snippet body()} - {#if gauge.activeModelId !== null && !gauge.isActiveModelLoaded} + {#if gauge.canLoadActiveModel && gauge.activeModelId !== null && !gauge.isActiveModelLoaded} diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index 6cea95d0d0..95780596ee 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -13,6 +13,7 @@ } from '$lib/components/app'; import { LANDING_SETTLE_MAX_MS, LANDING_STABLE_FRAMES, ROUTES } from '$lib/constants'; import { createAutoScrollController } from '$lib/hooks/use-auto-scroll.svelte'; + import { useBackendAvailability } from '$lib/hooks/use-backend-availability.svelte'; import { useChatScreenActiveModel } from '$lib/hooks/use-chat-screen-active-model.svelte'; import { useChatScreenDragAndDrop } from '$lib/hooks/use-chat-screen-drag-and-drop.svelte'; import { useChatScreenFileUpload } from '$lib/hooks/use-chat-screen-file-upload.svelte'; @@ -44,8 +45,10 @@ showCenteredEmpty && conversationsStore.activeMessages.length === 0 && !chatStore.isLoading ); let activeErrorDialog = $derived(chatStore.errorDialogState); - let isServerLoading = $derived(serverStore.loading); - let hasPropsError = $derived(!!serverStore.error); + // no local server in this deployment: never block on its probe + let isServerLoading = $derived(serverStore.loading && !serverStore.localServerMissing); + const availability = useBackendAvailability(); + let hasPropsError = $derived(availability.isOffline); let isCurrentConversationLoading = $derived(chatStore.isLoading || chatStore.isStreaming()); let chatFormBottomPosition = $derived.by(() => { if (!deviceStore.isMobile) return '1rem'; diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenGreeting.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenGreeting.svelte index 5af00ebb47..b4d951392b 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenGreeting.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenGreeting.svelte @@ -17,7 +17,7 @@

Hello there

- {serverStore.props?.modalities?.audio ? 'Record audio, type a message ' : 'Type a message'} or upload - files to get started + {serverStore.localProps?.modalities?.audio ? 'Record audio, type a message ' : 'Type a message'} or + upload files to get started

diff --git a/tools/ui/src/lib/components/app/server/ServerErrorSplash.svelte b/tools/ui/src/lib/components/app/server/ServerErrorSplash.svelte index e8154e5ce2..41af511298 100644 --- a/tools/ui/src/lib/components/app/server/ServerErrorSplash.svelte +++ b/tools/ui/src/lib/components/app/server/ServerErrorSplash.svelte @@ -1,13 +1,13 @@