From 9af8ab8f70dd38da54d77b1e3033042837ca0eb9 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 6 Apr 2026 06:15:18 -0400 Subject: [PATCH] fix(briefing): use briefing context for follow-ups; add slot separator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - build_context: when conversation_type is 'briefing', inject a system prompt instruction telling the model to answer from conversation history and article context instead of searching the web - Consolidate briefing conversation type detection to one DB query (was being checked twice — once for the system prompt addition, once for article context injection) - ChatPanel: render a visual 'New Briefing Update' separator line before 2nd+ briefing slot messages (identified by metadata.rss_item_ids) - types/chat.ts: add metadata field to Message interface Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/ChatPanel.vue | 42 +++++++++++++++++++++++---- frontend/src/types/chat.ts | 1 + src/fabledassistant/services/llm.py | 33 ++++++++++++++------- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/ChatPanel.vue b/frontend/src/components/ChatPanel.vue index 3be41ef..48c05e2 100644 --- a/frontend/src/components/ChatPanel.vue +++ b/frontend/src/components/ChatPanel.vue @@ -299,12 +299,22 @@ defineExpose({ focus, prefill, send })
- + > + +
+ New Briefing Update +
+ + @@ -523,6 +533,28 @@ defineExpose({ focus, prefill, send }) justify-content: flex-end; } +/* Briefing slot separator */ +.briefing-slot-separator { + display: flex; + align-items: center; + gap: 0.75rem; + margin: 1.25rem 0 0.5rem; + color: var(--color-text-muted, #888); + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + opacity: 0.7; +} +.briefing-slot-separator::before, +.briefing-slot-separator::after { + content: ''; + flex: 1; + height: 1px; + background: var(--color-border, #333); + opacity: 0.5; +} + /* Context sidebar */ .context-sidebar { width: 200px; diff --git a/frontend/src/types/chat.ts b/frontend/src/types/chat.ts index 9d97812..162dd99 100644 --- a/frontend/src/types/chat.ts +++ b/frontend/src/types/chat.ts @@ -28,6 +28,7 @@ export interface Message { context_note_id: number | null; context_note_title?: string | null; tool_calls?: ToolCallRecord[] | null; + metadata?: Record | null; created_at: string; timing?: GenerationTiming; thinking?: string; diff --git a/src/fabledassistant/services/llm.py b/src/fabledassistant/services/llm.py index 67abd71..178e501 100644 --- a/src/fabledassistant/services/llm.py +++ b/src/fabledassistant/services/llm.py @@ -607,6 +607,25 @@ async def build_context( f"\n\n--- Earlier Conversation ---\n{history_summary}\n--- End Earlier Conversation ---" ) + # Detect briefing conversation — used for both system prompt instruction and article injection + _is_briefing_conv = False + if conv_id is not None: + from fabledassistant.models import async_session as _async_session + from fabledassistant.models.conversation import Conversation as _Conversation + async with _async_session() as _sess: + _conv = await _sess.get(_Conversation, conv_id) + if _conv and getattr(_conv, "conversation_type", None) == "briefing": + _is_briefing_conv = True + + if _is_briefing_conv: + system_content += ( + "\n\nYou are in a briefing conversation. " + "The conversation history contains today's briefing — news stories, weather, and tasks. " + "When the user asks about a topic, person, or event from the briefing, answer directly " + "from the conversation history and the article context that follows. " + "Do NOT search the web for information that is already present in the briefing." + ) + context_meta: dict = { "context_note_id": None, "context_note_title": None, @@ -769,16 +788,10 @@ async def build_context( ) # Briefing article context for follow-up Q&A - if conv_id is not None: - from fabledassistant.models import async_session as _async_session - from fabledassistant.models.conversation import Conversation - - async with _async_session() as _sess: - _conv = await _sess.get(Conversation, conv_id) - if _conv and getattr(_conv, "conversation_type", None) == "briefing": - article_context = await _build_briefing_article_context(conv_id) - if article_context: - user_context_parts.append(article_context.strip()) + if _is_briefing_conv: + article_context = await _build_briefing_article_context(conv_id) # type: ignore[arg-type] + if article_context: + user_context_parts.append(article_context.strip()) # Build final user message — context prefix (if any) followed by the actual message if user_context_parts: