From 9fe6bed59b9ca5fb3747aa2dc471be397160880c Mon Sep 17 00:00:00 2001 From: Meleki Date: Wed, 1 Apr 2026 09:28:56 +0200 Subject: [PATCH] feat: Auto-Context im Frontend anzeigen und in DB persistieren MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - recalled_context-Header verstärkt (klare Nutzungs-Anweisung statt passivem Hinweis) - SSE-Event 'auto_context' ans Frontend senden für Live-Anzeige - Auto-Context als Message (type=auto_context) in DB speichern - Aufklappbare Auto-Context-Box im Chat-Stream und History-Rendering Co-Authored-By: Claude Opus 4.6 --- Services/AnthropicService.Streaming.cs | 10 ++++++++++ Services/AnthropicService.cs | 2 +- wwwroot/css/chat.css | 12 ++++++++++++ wwwroot/js/chat.js | 4 ++++ wwwroot/js/history.js | 18 ++++++++++++++++++ wwwroot/js/stream-ui.js | 22 ++++++++++++++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Services/AnthropicService.Streaming.cs b/Services/AnthropicService.Streaming.cs index 61a0eda..3857175 100644 --- a/Services/AnthropicService.Streaming.cs +++ b/Services/AnthropicService.Streaming.cs @@ -64,7 +64,17 @@ public partial class AnthropicService { var autoCtx = await BuildAutoContextAsync(messages, chainId, agentId); if (autoCtx != null) + { systemPrompt.Add(new { type = "text", text = autoCtx }); + await SseHelper.TryWriteSse(response, "auto_context", new { context = autoCtx }, sseCt); + await _db.SaveMessage(new MessageRecord + { + ChainId = chainId, + Role = "system", + Type = "auto_context", + Content = autoCtx + }); + } } catch (Exception ex) { diff --git a/Services/AnthropicService.cs b/Services/AnthropicService.cs index 2c03d33..45578ce 100644 --- a/Services/AnthropicService.cs +++ b/Services/AnthropicService.cs @@ -190,7 +190,7 @@ public partial class AnthropicService var ctxSb = new StringBuilder(); ctxSb.AppendLine("\n"); - ctxSb.AppendLine("Die folgende Information wurde automatisch abgerufen und könnte relevant sein:"); + ctxSb.AppendLine("WICHTIG: Der folgende Kontext wurde automatisch per Vektor-Suche aus früheren Gesprächen und Memory abgerufen. Prüfe diesen Kontext ZUERST, bevor du antwortest. Wenn er zur aktuellen Frage passt, beziehe dich aktiv darauf und nutze die Informationen in deiner Antwort. Ignoriere ihn nur, wenn er eindeutig irrelevant ist."); foreach (var r in contextResults) { var date = DateTime.TryParse(r.CreatedAt, out var dt) ? dt.ToString("yyyy-MM-dd HH:mm") : r.CreatedAt; diff --git a/wwwroot/css/chat.css b/wwwroot/css/chat.css index 6165dbb..e16177b 100644 --- a/wwwroot/css/chat.css +++ b/wwwroot/css/chat.css @@ -1136,6 +1136,18 @@ padding: var(--box-padding); } +/* Auto-Context Bubble */ + +.auto-context-box .tool-box-header .tool-name { + color: var(--info); +} + +.auto-context-box .auto-context-content { + padding: var(--box-padding); + font-size: 0.85em; + opacity: 0.85; +} + /* Review Bubbles */ .review-bubble { diff --git a/wwwroot/js/chat.js b/wwwroot/js/chat.js index bc21433..32893d2 100644 --- a/wwwroot/js/chat.js +++ b/wwwroot/js/chat.js @@ -127,6 +127,10 @@ function handleStreamEvent(eventType, data) { finalizeReviewBubble('done', data, 'finalizer'); break; + case 'auto_context': + addAutoContextBubble(data.context); + break; + case 'info': addWarningBubble(data.message, true); break; diff --git a/wwwroot/js/history.js b/wwwroot/js/history.js index b97bc54..b0244b6 100644 --- a/wwwroot/js/history.js +++ b/wwwroot/js/history.js @@ -259,6 +259,24 @@ function _renderMessagesIntoFragment(fragment, msgs, imageMessageIds, opts) { } break; } + case 'auto_context': { + if (msg.content) { + var acDiv = document.createElement('div'); + acDiv.className = 'tool-box auto-context-box' + (archived ? ' archived' : ''); + var acHeader = document.createElement('div'); + acHeader.className = 'tool-box-header clickable'; + acHeader.innerHTML = 'Auto-Context'; + var acContent = document.createElement('div'); + acContent.className = 'auto-context-content'; + acContent.textContent = msg.content.replace(/<\/?recalled_context>/g, '').trim(); + acContent.hidden = true; + acHeader.addEventListener('click', function() { acContent.hidden = !acContent.hidden; }); + acDiv.appendChild(acHeader); + acDiv.appendChild(acContent); + fragment.appendChild(acDiv); + } + break; + } case 'thinking': { if (msg.role === 'assistant' && msg.content) { var thinkDiv = document.createElement('div'); diff --git a/wwwroot/js/stream-ui.js b/wwwroot/js/stream-ui.js index 406064e..474dbc1 100644 --- a/wwwroot/js/stream-ui.js +++ b/wwwroot/js/stream-ui.js @@ -103,6 +103,28 @@ function endThinkingBubble() { _thinkingText = ''; } +// ── Auto-Context Bubble ── + +function addAutoContextBubble(contextText) { + if (!contextText) return; + var box = document.createElement('div'); + box.className = 'tool-box auto-context-box streamed'; + var header = document.createElement('div'); + header.className = 'tool-box-header clickable'; + header.innerHTML = 'Auto-Context'; + var content = document.createElement('div'); + content.className = 'auto-context-content markdown'; + content.hidden = true; + // recalled_context aus XML-Tags extrahieren + var inner = contextText.replace(/<\/?recalled_context>/g, '').trim(); + content.innerHTML = renderMarkdown(inner); + header.addEventListener('click', function() { content.hidden = !content.hidden; }); + box.appendChild(header); + box.appendChild(content); + chatEl().appendChild(box); + scrollDown(); +} + // ── Review Bubbles (Streaming) ── let _reviewBubble = null;