optimize msh layouts

This commit is contained in:
team2
2026-04-25 21:34:55 +02:00
parent 06aef93d24
commit c90675d17d
4 changed files with 181 additions and 9 deletions

View File

@@ -463,3 +463,105 @@ span.think {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
/* RetrieX chat meta/status cards */
.retriex-meta-card,
.retriex-alert {
margin: 0.65rem 0;
border: 1px solid rgba(134, 183, 254, 0.26);
border-radius: 14px;
background: rgba(13, 17, 23, 0.72);
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
}
.retriex-meta-card {
padding: 0.85rem 0.95rem;
}
.retriex-meta-card__eyebrow {
color: #86b7fe;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 0.2rem;
}
.retriex-meta-card__title {
color: #f8f9fa;
font-weight: 700;
margin-bottom: 0.55rem;
}
.retriex-meta-card__body {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
margin-bottom: 0.65rem;
}
.retriex-meta-pill {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 0.18rem 0.55rem;
background: rgba(134, 183, 254, 0.14);
color: #cfe2ff;
font-size: 0.78rem;
font-weight: 600;
}
.retriex-meta-query {
display: grid;
gap: 0.25rem;
}
.retriex-meta-query span {
color: rgba(248, 249, 250, 0.68);
font-size: 0.78rem;
}
.retriex-meta-query code {
display: block;
width: 100%;
white-space: pre-wrap;
word-break: break-word;
border-radius: 10px;
padding: 0.45rem 0.55rem;
background: rgba(0, 0, 0, 0.28);
color: #f8f9fa;
}
.retriex-alert {
display: flex;
gap: 0.75rem;
padding: 0.85rem 0.95rem;
}
.retriex-alert--warning {
border-color: rgba(255, 193, 7, 0.32);
background: rgba(60, 44, 5, 0.56);
}
.retriex-alert--error {
border-color: rgba(220, 53, 69, 0.34);
background: rgba(58, 12, 18, 0.56);
}
.retriex-alert__icon {
flex: 0 0 auto;
font-size: 1.15rem;
line-height: 1.2;
}
.retriex-alert__title {
color: #f8f9fa;
font-weight: 700;
margin-bottom: 0.15rem;
}
.retriex-alert__text {
color: rgba(248, 249, 250, 0.82);
font-size: 0.92rem;
line-height: 1.45;
}

View File

@@ -122,6 +122,22 @@ final readonly class AgentRunner
$shopSearchQuery = $optimizedShopQuery !== '' ? $optimizedShopQuery : $prompt; $shopSearchQuery = $optimizedShopQuery !== '' ? $optimizedShopQuery : $prompt;
$shopQueryPreview = $this->shopSearchService->buildSearchQueryPreview(
$shopSearchQuery,
$commerceIntent,
$commerceHistoryContext
);
yield $this->systemMsg(
$this->buildShopSearchMetaMessage(
query: $shopQueryPreview->searchText !== '' ? $shopQueryPreview->searchText : $shopSearchQuery,
commerceIntent: $commerceIntent,
usedOptimizedQuery: $optimizedShopQuery !== '',
originalQuery: $shopSearchQuery
),
'meta'
);
$this->agentLogger->info('Commerce search prepared', [ $this->agentLogger->info('Commerce search prepared', [
'userId' => $userId, 'userId' => $userId,
'commerceIntent' => $commerceIntent, 'commerceIntent' => $commerceIntent,
@@ -790,25 +806,66 @@ final readonly class AgentRunner
} }
} }
private function buildShopSearchMetaMessage(
string $query,
string $commerceIntent,
bool $usedOptimizedQuery,
string $originalQuery
): string {
$query = $this->normalizeOneLine($query);
$originalQuery = $this->normalizeOneLine($originalQuery);
if ($query === '') {
$query = $originalQuery !== '' ? $originalQuery : 'keine Suchquery ermittelt';
}
$badge = $usedOptimizedQuery ? 'optimiert' : 'direkt';
$intentLabel = $commerceIntent !== '' ? $commerceIntent : 'commerce';
return '<div class="retriex-meta-card retriex-shop-meta">'
. '<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__body">'
. '<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>'
. '</div>'
. '<div class="retriex-meta-query"><span>Gesendete Suchquery</span><code>'
. htmlspecialchars($query, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
. '</code></div>'
. '</div>';
}
private function buildShopUnavailableMessage(?string $reason): string private function buildShopUnavailableMessage(?string $reason): string
{ {
$reason = trim((string) $reason); $reason = $this->normalizeOneLine((string) $reason);
if ($reason === '') { if ($reason === '') {
$reason = 'keine Detailmeldung vom Shopware-Server'; $reason = 'Keine Detailmeldung vom Shopware-Server.';
} }
$reason = preg_replace('/\s+/u', ' ', $reason) ?? $reason; if (mb_strlen($reason, 'UTF-8') > 320) {
$reason = rtrim(mb_substr($reason, 0, 317, 'UTF-8')) . '...';
if (mb_strlen($reason, 'UTF-8') > 260) {
$reason = rtrim(mb_substr($reason, 0, 257, 'UTF-8')) . '...';
} }
return '⚠️ Shopsystem aktuell nicht erreichbar oder fehlerhaft. Grund: ' return '<div class="retriex-alert retriex-alert--warning">'
. '<div class="retriex-alert__icon">⚠️</div>'
. '<div class="retriex-alert__content">'
. '<div class="retriex-alert__title">Shopdaten konnten nicht geladen werden</div>'
. '<div class="retriex-alert__text">RetrieX antwortet ohne Live-Shopdaten weiter. Ursache: '
. htmlspecialchars($reason, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . htmlspecialchars($reason, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
. '. Ich antworte ohne Shopdaten weiter.'; . '</div>'
. '</div>'
. '</div>';
} }
private function normalizeOneLine(string $value): string
{
$value = trim($value);
return preg_replace('/\s+/u', ' ', $value) ?? $value;
}
private function buildUserErrorMessage(Throwable $e): string private function buildUserErrorMessage(Throwable $e): string
{ {
$message = trim($e->getMessage()); $message = trim($e->getMessage());
@@ -847,6 +904,7 @@ final readonly class AgentRunner
'err' => sprintf($this->agentRunnerConfig->getErrorHtmlTemplate(), $msg), 'err' => sprintf($this->agentRunnerConfig->getErrorHtmlTemplate(), $msg),
'think' => sprintf($this->agentRunnerConfig->getThinkHtmlTemplate(), $msg), 'think' => sprintf($this->agentRunnerConfig->getThinkHtmlTemplate(), $msg),
'info' => sprintf($this->agentRunnerConfig->getInfoHtmlTemplate(), $msg), 'info' => sprintf($this->agentRunnerConfig->getInfoHtmlTemplate(), $msg),
'meta' => $msg,
'debug' => sprintf( 'debug' => sprintf(
$this->agentRunnerConfig->getDebugHtmlTemplate(), $this->agentRunnerConfig->getDebugHtmlTemplate(),
htmlspecialchars($msg, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') htmlspecialchars($msg, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')

View File

@@ -48,6 +48,18 @@ final class ShopSearchService
return $this->lastSearchFailureReason; return $this->lastSearchFailureReason;
} }
public function buildSearchQueryPreview(
string $originalPrompt,
string $commerceIntent,
string $commerceHistoryContext = ''
): CommerceSearchQuery {
return $this->queryParser->parse(
$originalPrompt,
$commerceIntent,
$commerceHistoryContext
);
}
/** /**
* @return ShopProductResult[] * @return ShopProductResult[]
*/ */

View File

@@ -150,7 +150,7 @@ final class AgentRunnerConfig
public function getErrorHtmlTemplate(): string public function getErrorHtmlTemplate(): string
{ {
return '<span class="text-danger">%s</span>' . "\n<hr>\n"; return '<div class="retriex-alert retriex-alert--error"><div class="retriex-alert__icon">❌</div><div class="retriex-alert__content"><div class="retriex-alert__title">Hinweis</div><div class="retriex-alert__text">%s</div></div></div>' . "\n";
} }
public function getThinkHtmlTemplate(): string public function getThinkHtmlTemplate(): string