optimize cleanup search query shop api extends part 2

This commit is contained in:
team2
2026-04-25 22:47:50 +02:00
parent 6cf8aac872
commit 2797834a5f
5 changed files with 119 additions and 12 deletions

View File

@@ -179,6 +179,59 @@ document.addEventListener('DOMContentLoaded', () => {
cleanupEmptyBlocks(container); cleanupEmptyBlocks(container);
} }
function hasVisibleContentAfterNode(container, markerNode) {
const walker = document.createTreeWalker(
container,
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
null
);
let afterMarker = false;
while (walker.nextNode()) {
const node = walker.currentNode;
if (node === markerNode) {
afterMarker = true;
continue;
}
if (!afterMarker) {
continue;
}
if (node.nodeType === Node.TEXT_NODE) {
if (node.parentElement?.closest('.think')) {
continue;
}
if ((node.textContent || '').trim() !== '') {
return true;
}
continue;
}
if (node.nodeType !== Node.ELEMENT_NODE) {
continue;
}
if (node.classList?.contains('think') || node.closest?.('.think')) {
continue;
}
if (node.tagName === 'BR') {
continue;
}
if ((node.textContent || '').trim() !== '' || hasMeaningfulChildContent(node)) {
return true;
}
}
return false;
}
function cleanupThinkSpans(container) { function cleanupThinkSpans(container) {
if (!container) { if (!container) {
return; return;
@@ -191,12 +244,14 @@ document.addEventListener('DOMContentLoaded', () => {
return; return;
} }
if (hasNonThinkContent(container)) { const lastThink = thinkSpans[thinkSpans.length - 1];
removeThinkSpansOnly(container);
if (!hasVisibleContentAfterNode(container, lastThink)) {
keepOnlyLastThink(container);
return; return;
} }
keepOnlyLastThink(container); removeThinkSpansOnly(container);
} }
function renderBubbleContent(bubble, raw) { function renderBubbleContent(bubble, raw) {

View File

@@ -765,11 +765,14 @@ final readonly class AgentRunner
private function streamFinalAnswer(string $finalPrompt): Generator private function streamFinalAnswer(string $finalPrompt): Generator
{ {
$fullOutput = ''; $fullOutput = '';
$firstThinkLoop = true; $thinkingNoticeShown = false;
$chunker = new StreamChunker(); $chunker = new StreamChunker();
$this->thinkSuppressor->reset(); $this->thinkSuppressor->reset();
yield $this->systemMsg($this->agentRunnerConfig->getThinkingWhileStreamingMessage(), 'think');
$thinkingNoticeShown = true;
foreach ($this->ollamaClient->stream($finalPrompt) as $token) { foreach ($this->ollamaClient->stream($finalPrompt) as $token) {
if (!is_string($token)) { if (!is_string($token)) {
continue; continue;
@@ -778,9 +781,9 @@ final readonly class AgentRunner
$cleanToken = $this->thinkSuppressor->filter($token); $cleanToken = $this->thinkSuppressor->filter($token);
if ($cleanToken === '') { if ($cleanToken === '') {
if ($firstThinkLoop) { if (!$thinkingNoticeShown) {
yield $this->systemMsg($this->agentRunnerConfig->getThinkingWhileStreamingMessage(), 'think'); yield $this->systemMsg($this->agentRunnerConfig->getThinkingWhileStreamingMessage(), 'think');
$firstThinkLoop = false; $thinkingNoticeShown = true;
} }
continue; continue;
@@ -902,7 +905,7 @@ final readonly class AgentRunner
return '<div class="retriex-meta-card retriex-shop-meta">' return '<div class="retriex-meta-card retriex-shop-meta">'
. '<div class="retriex-meta-card__eyebrow">Live-Shopdaten</div>' . '<div class="retriex-meta-card__eyebrow">Live-Shopdaten</div>'
. '<div class="retriex-meta-card__title">Shopware-Suche wird ausgeführt</div>' . '<div class="retriex-meta-card__title">Shop-Suche wird ausgeführt</div>'
. '<div class="retriex-meta-card__body">' . '<div class="retriex-meta-card__body">'
. '<span class="retriex-meta-pill">' . htmlspecialchars($badge, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</span>' . '<span class="retriex-meta-pill">' . htmlspecialchars($badge, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</span>'
. '<span class="retriex-meta-pill">Intent: ' . htmlspecialchars($intentLabel, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</span>' . '<span class="retriex-meta-pill">Intent: ' . htmlspecialchars($intentLabel, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</span>'

View File

@@ -79,7 +79,6 @@ final readonly class CommerceQueryParser
private function normalize(string $prompt): string private function normalize(string $prompt): string
{ {
$value = $this->textNormalizer->normalize($prompt); $value = $this->textNormalizer->normalize($prompt);
$value = $this->queryCleaner->clean($value);
$value = mb_strtolower(trim($value)); $value = mb_strtolower(trim($value));
$value = str_replace( $value = str_replace(
$this->config->getNormalizationSearch(), $this->config->getNormalizationSearch(),
@@ -274,7 +273,16 @@ final readonly class CommerceQueryParser
for ($offset = 1; $offset <= $this->config->getModelContextTokenWindow(); $offset++) { for ($offset = 1; $offset <= $this->config->getModelContextTokenWindow(); $offset++) {
$previousIndex = $index - $offset; $previousIndex = $index - $offset;
if (!isset($tokens[$previousIndex]) || !$this->isLikelyModelContextToken($tokens[$previousIndex])) { if (!isset($tokens[$previousIndex])) {
break;
}
if ($this->isSemanticShopToken($tokens[$previousIndex])) {
$keep[$previousIndex] = true;
continue;
}
if (!$this->isLikelyModelContextToken($tokens[$previousIndex])) {
break; break;
} }
@@ -287,7 +295,7 @@ final readonly class CommerceQueryParser
} }
} }
if ($this->isSemanticShopToken($token) || $this->isKnownBrandToken($token)) { if ($this->isSemanticShopToken($token) || $this->isKnownBrandToken($token) || $this->isMeasurementValueToken($token)) {
$keep[$index] = true; $keep[$index] = true;
} }
} }
@@ -314,6 +322,10 @@ final readonly class CommerceQueryParser
return true; return true;
} }
if ($this->isMeasurementValueToken($token)) {
return false;
}
if (preg_match($this->config->getContainsDigitPattern(), $token) === 1) { if (preg_match($this->config->getContainsDigitPattern(), $token) === 1) {
return false; return false;
} }
@@ -334,6 +346,11 @@ final readonly class CommerceQueryParser
return preg_match($this->config->getModelNumberTokenPattern(), $token) === 1; return preg_match($this->config->getModelNumberTokenPattern(), $token) === 1;
} }
private function isMeasurementValueToken(string $token): bool
{
return preg_match($this->config->getMeasurementValueTokenPattern(), $token) === 1;
}
private function isLikelyModelContextToken(string $token): bool private function isLikelyModelContextToken(string $token): bool
{ {
if ($this->isQueryNoiseToken($token)) { if ($this->isQueryNoiseToken($token)) {

View File

@@ -37,6 +37,13 @@ final class CommerceIntentConfig
'kalibrierlösung', 'kalibrierlösung',
'kalibrierloesung', 'kalibrierloesung',
'kalibrierung', 'kalibrierung',
'chemie',
'reagenz',
'reagenzien',
'verbrauchsmaterial',
'zubehör',
'zubehoer',
'ersatzteil',
]; ];
} }
@@ -50,6 +57,10 @@ final class CommerceIntentConfig
'eignet', 'eignet',
'besser', 'besser',
'besten', 'besten',
'gut für',
'gut fuer',
'passend für',
'passend fuer',
'geeignet', 'geeignet',
'geeigent', 'geeigent',
'empfiehl', 'empfiehl',
@@ -195,6 +206,12 @@ final class CommerceIntentConfig
'/\bartikel\b/u', '/\bartikel\b/u',
'/\bsku\b/u', '/\bsku\b/u',
'/\bonline\b/u', '/\bonline\b/u',
'/\bchemie\b/u',
'/\breagenz(?:ien)?\b/u',
'/\bverbrauchsmaterial(?:ien)?\b/u',
'/\bzubehör\b/u',
'/\bzubehoer\b/u',
'/\bersatzteil(?:e)?\b/u',
]; ];
} }

View File

@@ -133,6 +133,14 @@ final class CommerceQueryParserConfig
'kostet', 'kostet',
'kosten', 'kosten',
'ua', 'ua',
'also',
'gut',
'gute',
'guten',
'guter',
'gutes',
'passen',
'passend',
]; ];
} }
@@ -297,7 +305,7 @@ final class CommerceQueryParserConfig
public function getModelContextTokenWindow(): int public function getModelContextTokenWindow(): int
{ {
return 2; return 4;
} }
public function getMinMeaningfulAlphaTokenLength(): int public function getMinMeaningfulAlphaTokenLength(): int
@@ -312,7 +320,11 @@ final class CommerceQueryParserConfig
public function getInstructionOrPresentationTokenPattern(): string public function getInstructionOrPresentationTokenPattern(): string
{ {
return '/^(?:zeig(?:e)?|such(?:e)?|find(?:e)?|gib|gebe|nenn(?:e)?|liefer(?:e)?|erstelle?|mach(?:e)?|brauch(?:e)?|will|möchte|moechte|hätte|haette|kannst|bitte|mal|alle|alles|komplett|vollständig|vollstaendig|gesamt|ganze|ganzen|liste|listung|auflistung|tabelle|tabellarisch|übersicht|uebersicht|anzeigen?|ausgeben?|darstellen?|antwort(?:e)?|erklär(?:e)?|erklaer(?:e)?|info|infos|informationen|dazu|hierzu|damit|davon|an|als|mit|ohne|inkl|inklusive)$/u'; return '/^(?:zeig(?:e)?|such(?:e)?|find(?:e)?|gib|gebe|nenn(?:e)?|liefer(?:e)?|erstelle?|mach(?:e)?|brauch(?:e)?|will|möchte|moechte|hätte|haette|kannst|bitte|mal|alle|alles|komplett|vollständig|vollstaendig|gesamt|ganze|ganzen|liste|listung|auflistung|tabelle|tabellarisch|übersicht|uebersicht|anzeigen?|ausgeben?|darstellen?|antwort(?:e)?|erklär(?:e)?|erklaer(?:e)?|info|infos|informationen|dazu|hierzu|damit|davon|an|als|mit|ohne|inkl|inklusive|also|gut|gute|guten|guter|gutes|passend|passen)$/u';
}
public function getMeasurementValueTokenPattern(): string
{
return '/^\d+[.,]\d+$/u';
} }
/** /**
@@ -332,6 +344,9 @@ final class CommerceQueryParserConfig
'zubehor', 'zubehor',
'ersatzteil', 'ersatzteil',
'verbrauchsmaterial', 'verbrauchsmaterial',
'chemie',
'indikatorchemie',
'reagenzchemie',
'kit', 'kit',
'set', 'set',
'filter', 'filter',