harden information getter services and optimize user msg

This commit is contained in:
team2
2026-04-28 07:22:29 +02:00
parent 79adf8f1df
commit 643d847ce2
6 changed files with 758 additions and 17 deletions

View File

@@ -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);

View File

@@ -565,3 +565,164 @@ span.think {
font-size: 0.92rem;
line-height: 1.45;
}
.retriex-run-meta[data-retriex-meta-state="completed"] {
border-color: rgba(25, 135, 84, 0.36);
}
.retriex-meta-pill--confidence {
background: rgba(255, 193, 7, 0.14);
color: #ffe8a1;
}
.retriex-meta-pill--status {
background: rgba(25, 135, 84, 0.15);
color: #b7f5cf;
}
.retriex-source-overview {
display: grid;
gap: 0.35rem;
margin-top: 0.15rem;
}
.retriex-source-overview > span,
.retriex-product-results__summary {
color: rgba(248, 249, 250, 0.72);
font-size: 0.82rem;
font-weight: 600;
}
.retriex-source-overview__badges,
.retriex-action-chip-row {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.retriex-source-chip {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 0.16rem 0.55rem;
background: rgba(13, 202, 240, 0.14);
color: #b6effb;
font-size: 0.78rem;
font-weight: 700;
}
.retriex-source-overview__empty {
color: rgba(248, 249, 250, 0.58);
font-size: 0.86rem;
}
.retriex-product-results__summary {
margin-bottom: 0.65rem;
}
.retriex-meta-query--compact {
margin-bottom: 0.8rem;
}
.retriex-product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 0.65rem;
}
.retriex-product-card {
display: grid;
gap: 0.55rem;
padding: 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
background: rgba(255, 255, 255, 0.045);
}
.retriex-product-card__title {
color: #f8f9fa;
font-weight: 800;
line-height: 1.3;
}
.retriex-product-card__title a {
color: inherit;
text-decoration: none;
}
.retriex-product-card__title a:hover {
text-decoration: underline;
}
.retriex-product-card__facts {
display: grid;
gap: 0.35rem;
margin: 0;
}
.retriex-product-card__facts div {
display: grid;
grid-template-columns: minmax(88px, 0.8fr) 1.2fr;
gap: 0.45rem;
}
.retriex-product-card__facts dt {
color: rgba(248, 249, 250, 0.58);
font-size: 0.76rem;
font-weight: 700;
}
.retriex-product-card__facts dd {
color: rgba(248, 249, 250, 0.9);
font-size: 0.8rem;
margin: 0;
word-break: break-word;
}
.retriex-product-card__relevance {
border-top: 1px solid rgba(255, 255, 255, 0.08);
padding-top: 0.5rem;
color: rgba(248, 249, 250, 0.78);
font-size: 0.8rem;
line-height: 1.35;
}
.retriex-product-card__relevance span {
display: block;
color: #86b7fe;
font-size: 0.72rem;
font-weight: 800;
letter-spacing: 0.05em;
text-transform: uppercase;
margin-bottom: 0.18rem;
}
.retriex-action-chip {
border: 1px solid rgba(134, 183, 254, 0.32);
border-radius: 999px;
padding: 0.34rem 0.72rem;
background: rgba(134, 183, 254, 0.11);
color: #d8e8ff;
font-size: 0.83rem;
font-weight: 700;
cursor: pointer;
}
.retriex-action-chip:hover,
.retriex-action-chip:focus-visible {
border-color: rgba(134, 183, 254, 0.68);
background: rgba(134, 183, 254, 0.2);
color: #ffffff;
}
@media (max-width: 576px) {
.retriex-product-grid {
grid-template-columns: 1fr;
}
.retriex-product-card__facts div {
grid-template-columns: 1fr;
gap: 0.1rem;
}
}