harden information getter services and optimize user msg
This commit is contained in:
@@ -45,6 +45,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function extractAssistantContextText(bubble) {
|
||||
if (!bubble) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const clone = bubble.cloneNode(true);
|
||||
clone.querySelectorAll('.think, .retriex-meta-card, .retriex-alert').forEach((element) => {
|
||||
element.remove();
|
||||
});
|
||||
|
||||
return normalizeContextHintText(clone.innerText || clone.textContent || '');
|
||||
}
|
||||
|
||||
function isClientMetaOnlyShopPrompt(value) {
|
||||
const tokens = tokenizeClientMetaGuardText(value);
|
||||
|
||||
@@ -62,9 +75,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
function isNoConcreteShopResponse(value) {
|
||||
return normalizeContextHintText(value)
|
||||
.toLowerCase()
|
||||
.includes('keine konkrete shop-suchanfrage erkannt');
|
||||
const normalized = normalizeContextHintText(value).toLowerCase();
|
||||
|
||||
return normalized.includes('keine konkrete shop-suchanfrage erkannt')
|
||||
|| normalized.includes('shop-suche noch nicht belastbar auflösen')
|
||||
|| normalized.includes('shop-suche noch nicht belastbar aufloesen');
|
||||
}
|
||||
|
||||
function rememberCompletedTurn(userPrompt, assistantText) {
|
||||
@@ -130,7 +145,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
messages.forEach((message) => {
|
||||
const bubble = message.querySelector('.bubble');
|
||||
const text = normalizeContextHintText(bubble?.innerText || bubble?.textContent || '');
|
||||
const text = message.classList.contains('assistant')
|
||||
? extractAssistantContextText(bubble)
|
||||
: normalizeContextHintText(bubble?.innerText || bubble?.textContent || '');
|
||||
|
||||
if (!text) {
|
||||
return;
|
||||
@@ -220,12 +237,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
function addLoader() {
|
||||
return addMessage('assistant', 'AI is thinking…', 'loader');
|
||||
return addMessage('assistant', 'Antwort wird vorbereitet…', 'loader');
|
||||
}
|
||||
|
||||
function hasMeaningfulChildContent(element) {
|
||||
return element.querySelector(
|
||||
'img, table, pre, code, ul, ol, h1, h2, h3, h4, h5, h6, a, hr, .badge'
|
||||
'img, table, pre, code, ul, ol, h1, h2, h3, h4, h5, h6, a, hr, .badge, .retriex-meta-card, .retriex-alert, .retriex-action-chip'
|
||||
) !== null;
|
||||
}
|
||||
|
||||
@@ -414,24 +431,50 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
removeThinkSpansOnly(container);
|
||||
}
|
||||
|
||||
function copyElementAttributes(target, source) {
|
||||
Array.from(target.attributes).forEach((attribute) => {
|
||||
target.removeAttribute(attribute.name);
|
||||
});
|
||||
|
||||
Array.from(source.attributes).forEach((attribute) => {
|
||||
target.setAttribute(attribute.name, attribute.value);
|
||||
});
|
||||
}
|
||||
|
||||
function deduplicateRetriexMetaCards(container) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cards = Array.from(container.querySelectorAll('.retriex-meta-card[data-retriex-meta-id]'));
|
||||
const latestCardById = new Map();
|
||||
|
||||
cards.forEach((card) => {
|
||||
latestCardById.set(card.getAttribute('data-retriex-meta-id'), card);
|
||||
});
|
||||
const cardsById = new Map();
|
||||
|
||||
cards.forEach((card) => {
|
||||
const cardId = card.getAttribute('data-retriex-meta-id');
|
||||
|
||||
if (latestCardById.get(cardId) !== card) {
|
||||
card.remove();
|
||||
if (!cardId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cardsById.has(cardId)) {
|
||||
cardsById.set(cardId, []);
|
||||
}
|
||||
|
||||
cardsById.get(cardId).push(card);
|
||||
});
|
||||
|
||||
cardsById.forEach((group) => {
|
||||
if (group.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstCard = group[0];
|
||||
const latestCard = group[group.length - 1];
|
||||
|
||||
firstCard.innerHTML = latestCard.innerHTML;
|
||||
copyElementAttributes(firstCard, latestCard);
|
||||
|
||||
group.slice(1).forEach((card) => card.remove());
|
||||
});
|
||||
|
||||
cleanupEmptyBlocks(container);
|
||||
@@ -515,11 +558,45 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
}
|
||||
|
||||
function finalizeStream(bubble, raw) {
|
||||
function finalizeRetriexRunMetaCards(container, options = {}) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isError = options.state === 'error';
|
||||
const titleText = isError ? 'Antwort wurde unterbrochen' : 'Abgeschlossen';
|
||||
const statusText = isError ? 'Status: unterbrochen' : 'Status: abgeschlossen';
|
||||
const emptySourceText = isError ? 'nicht vollständig geprüft' : 'keine belastbare Datenbasis';
|
||||
|
||||
container.querySelectorAll('.retriex-run-meta[data-retriex-meta-id="run-status"]').forEach((card) => {
|
||||
card.setAttribute('data-retriex-meta-state', isError ? 'error' : 'completed');
|
||||
|
||||
const title = card.querySelector('.retriex-meta-card__title');
|
||||
|
||||
if (title) {
|
||||
title.textContent = titleText;
|
||||
}
|
||||
|
||||
const statusPill = card.querySelector('.retriex-meta-pill--status');
|
||||
|
||||
if (statusPill) {
|
||||
statusPill.textContent = statusText;
|
||||
}
|
||||
|
||||
const emptySource = card.querySelector('.retriex-source-overview__empty');
|
||||
|
||||
if (emptySource && emptySource.textContent.trim() === 'wird geprüft') {
|
||||
emptySource.textContent = emptySourceText;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function finalizeStream(bubble, raw, options = {}) {
|
||||
clearScheduledRender();
|
||||
bubble.innerHTML = renderMarkdown(raw);
|
||||
removeThinkSpansOnly(bubble);
|
||||
deduplicateRetriexMetaCards(bubble);
|
||||
finalizeRetriexRunMetaCards(bubble, options);
|
||||
bubble.classList.remove('loader');
|
||||
enhanceChatLinks(bubble);
|
||||
scrollChatToBottom();
|
||||
@@ -575,6 +652,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
chatEl?.addEventListener('click', (event) => {
|
||||
const actionButton = event.target?.closest?.('.retriex-action-chip[data-retriex-action-prompt]');
|
||||
|
||||
if (!actionButton || state.isStreaming) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionPrompt = normalizeContextHintText(actionButton.getAttribute('data-retriex-action-prompt') || '');
|
||||
|
||||
if (!actionPrompt) {
|
||||
return;
|
||||
}
|
||||
|
||||
promptEl.value = actionPrompt;
|
||||
promptEl.focus();
|
||||
sendBtn.click();
|
||||
});
|
||||
|
||||
loadHistory();
|
||||
|
||||
promptEl.addEventListener('keydown', (event) => {
|
||||
@@ -631,7 +726,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const formattedMessage = `<em>${safeMessage}</em>`;
|
||||
raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
|
||||
finalizeStream(bubble, raw);
|
||||
finalizeStream(bubble, raw, {state: 'error'});
|
||||
};
|
||||
|
||||
const finishEventStream = () => {
|
||||
@@ -781,7 +876,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (raw.trim() !== '') {
|
||||
const formattedMessage = `<em>${userMessage}</em>`;
|
||||
raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
|
||||
renderBubbleContent(bubble, raw);
|
||||
finalizeStream(bubble, raw, {state: 'error'});
|
||||
} else {
|
||||
bubble.innerHTML = `<em>${userMessage}</em>`;
|
||||
enhanceChatLinks(bubble);
|
||||
|
||||
Reference in New Issue
Block a user