From 24d3c5bc685a0a2b61633547d118c3bc3d985927 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 18 Feb 2026 21:06:54 -0500 Subject: [PATCH] Enable thinking mode in full chat view, keep disabled in widget/panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stream_chat_with_tools now accepts a think parameter. run_generation forwards it to Ollama. The message POST route reads think from the request body. ChatView passes think=true so qwen3 uses chain-of-thought reasoning for full conversations; the dashboard widget and ChatPanel omit it, staying fast. Dashboard button updated to "Think it through in Chat →" to signal the deeper capability. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/stores/chat.ts | 2 ++ frontend/src/views/ChatView.vue | 1 + frontend/src/views/HomeView.vue | 2 +- src/fabledassistant/routes/chat.py | 2 ++ src/fabledassistant/services/generation_task.py | 3 ++- src/fabledassistant/services/llm.py | 7 ++++++- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/stores/chat.ts b/frontend/src/stores/chat.ts index 24d12f1..b9e8395 100644 --- a/frontend/src/stores/chat.ts +++ b/frontend/src/stores/chat.ts @@ -120,6 +120,7 @@ export const useChatStore = defineStore("chat", () => { content: string, contextNoteId?: number | null, excludeNoteIds?: number[], + think = false, ) { if (!currentConversation.value) return; @@ -165,6 +166,7 @@ export const useChatStore = defineStore("chat", () => { content, context_note_id: contextNoteId, exclude_note_ids: excludeNoteIds?.length ? excludeNoteIds : undefined, + think, }, ); assistantMessageId = resp.assistant_message_id; diff --git a/frontend/src/views/ChatView.vue b/frontend/src/views/ChatView.vue index 53b7ecd..0740ea8 100644 --- a/frontend/src/views/ChatView.vue +++ b/frontend/src/views/ChatView.vue @@ -161,6 +161,7 @@ async function sendMessage() { content, contextNoteId, excludedNoteIds.value.size ? [...excludedNoteIds.value] : undefined, + true, // enable thinking in the full chat view ); sending.value = false; diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index c94eb18..30ec2c7 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -378,7 +378,7 @@ function clearDashboardResponse() { class="btn-open-chat" :class="{ prominent: isConversational }" > - {{ isConversational ? 'Continue this conversation →' : 'Continue in Chat →' }} + {{ isConversational ? 'Continue the conversation →' : 'Think it through in Chat →' }} diff --git a/src/fabledassistant/routes/chat.py b/src/fabledassistant/routes/chat.py index 9040c92..6e71fc4 100644 --- a/src/fabledassistant/routes/chat.py +++ b/src/fabledassistant/routes/chat.py @@ -106,6 +106,7 @@ async def send_message_route(conv_id: int): return jsonify({"error": "content is required"}), 400 context_note_id = data.get("context_note_id") exclude_note_ids = data.get("exclude_note_ids") or [] + think = bool(data.get("think", False)) # Reject if generation already running for this conversation existing = get_buffer(conv_id) @@ -134,6 +135,7 @@ async def send_message_route(conv_id: int): uid, conv_id, conv.title, content, context_note_id=context_note_id, exclude_note_ids=exclude_note_ids, + think=think, )) return jsonify({ diff --git a/src/fabledassistant/services/generation_task.py b/src/fabledassistant/services/generation_task.py index 7855f7f..29d31fc 100644 --- a/src/fabledassistant/services/generation_task.py +++ b/src/fabledassistant/services/generation_task.py @@ -166,6 +166,7 @@ async def run_generation( user_content: str, context_note_id: int | None = None, exclude_note_ids: list[int] | None = None, + think: bool = False, ) -> None: """Stream LLM response into buffer with periodic DB flushes.""" MAX_TOOL_ROUNDS = 5 @@ -349,7 +350,7 @@ async def run_generation( buf.append_event("status", {"status": "Generating response..." if _round == 0 else "Composing response..."}) t_stream = time.monotonic() - async for chunk in stream_chat_with_tools(messages, model, tools=tools): + async for chunk in stream_chat_with_tools(messages, model, tools=tools, think=think): if buf.cancel_event.is_set(): cancelled = True break diff --git a/src/fabledassistant/services/llm.py b/src/fabledassistant/services/llm.py index 6f6929e..cc81e68 100644 --- a/src/fabledassistant/services/llm.py +++ b/src/fabledassistant/services/llm.py @@ -114,12 +114,17 @@ async def stream_chat_with_tools( messages: list[dict], model: str, tools: list[dict] | None = None, + think: bool = False, ) -> AsyncGenerator[ChatChunk, None]: """Stream chat completion from Ollama with tool support. Yields ChatChunk objects. If the model returns tool_calls, a ChatChunk(type="tool_calls") is yielded. Always ends with ChatChunk(type="done"). + + Set think=True to enable the model's chain-of-thought reasoning (qwen3+). + Thinking tokens are consumed by Ollama and not forwarded to the caller; + only the final response content is yielded. Expect higher TTFT when enabled. """ options: dict = {"num_ctx": 32768} if tools: @@ -129,7 +134,7 @@ async def stream_chat_with_tools( "messages": messages, "stream": True, "options": options, - "think": False, + "think": think, } if tools: payload["tools"] = tools