optimize msh layouts
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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[]
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user