diff --git a/tools/ui/src/lib/stores/chat/index.svelte.ts b/tools/ui/src/lib/stores/chat/index.svelte.ts index 7c97a104bb..ac95fde4d7 100644 --- a/tools/ui/src/lib/stores/chat/index.svelte.ts +++ b/tools/ui/src/lib/stores/chat/index.svelte.ts @@ -809,6 +809,15 @@ class ChatStore implements ChatStreamHost, ChatFlowsHost { modelOverride?: string | null, firstUserMessageContent?: string ): Promise { + // a conversation keeps the model that generated it, which can belong to + // another backend; make that backend active so the request is not sent to + // a server that does not serve the model + const requestedModel = modelOverride ?? getConversationModel(allMessages); + + if (requestedModel) { + await modelsStore.ensureModelBackend(requestedModel); + } + // the ::model suffix in the stream identity is only for router mode, where it routes to the // owning child. in single-model mode the identity stays the bare conv id so that attach, stop // and reattach all agree, regardless of fresh send vs regenerate passing a resolved model diff --git a/tools/ui/src/lib/stores/models/index.svelte.ts b/tools/ui/src/lib/stores/models/index.svelte.ts index 0008e04047..24c564a0d4 100644 --- a/tools/ui/src/lib/stores/models/index.svelte.ts +++ b/tools/ui/src/lib/stores/models/index.svelte.ts @@ -222,6 +222,19 @@ class ModelsStore implements ModelPropsHost, ModelStatusHost { await this.selectModelById(availableModels[0].id); } + /** + * Make the backend serving `modelName` active when it is not already. + * A conversation keeps the model that generated it, which can belong to a + * backend other than the active one. + */ + async ensureModelBackend(modelName: string): Promise { + const option = this.models.find((model) => model.model === modelName); + + if (!option?.backendId || option.backendId === backendsStore.active.id) return; + + await this.selectModelById(option.id); + } + /** * Fetch list of models from server and detect server role. * Also fetches modalities for MODEL mode (single model).