add new configs
This commit is contained in:
@@ -5,27 +5,22 @@ declare(strict_types=1);
|
||||
namespace App\Commerce;
|
||||
|
||||
use App\Commerce\Dto\CommerceSearchQuery;
|
||||
use App\Config\CommerceIntentConfig;
|
||||
use App\Config\CommerceQueryParserConfig;
|
||||
use App\Knowledge\Retrieval\QueryCleaner;
|
||||
use App\Knowledge\Text\TextNormalizer;
|
||||
|
||||
final class CommerceQueryParser
|
||||
final readonly class CommerceQueryParser
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TextNormalizer $textNormalizer,
|
||||
private readonly QueryCleaner $queryCleaner,
|
||||
private TextNormalizer $textNormalizer,
|
||||
private QueryCleaner $queryCleaner,
|
||||
private CommerceQueryParserConfig $config,
|
||||
private CommerceIntentConfig $intentConfig,
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $knownBrands = [
|
||||
'heyl',
|
||||
'horiba'
|
||||
];
|
||||
|
||||
public function parse(string $originalPrompt, string $intent): CommerceSearchQuery
|
||||
{
|
||||
$normalized = $this->normalize($originalPrompt);
|
||||
@@ -103,7 +98,8 @@ final class CommerceQueryParser
|
||||
{
|
||||
$sizes = [];
|
||||
|
||||
if (preg_match_all('/\b(?:größe|groesse|grösse)\s*([a-z0-9.-]+)\b/u', $prompt, $matches) === false) {
|
||||
$sizePattern = $this->intentConfig->getSizePattern();
|
||||
if (preg_match_all('/\b(?:' . $sizePattern . ')\s*([a-z0-9.-]+)\b/u', $prompt, $matches) === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -111,7 +107,8 @@ final class CommerceQueryParser
|
||||
$sizes[] = trim($size);
|
||||
}
|
||||
|
||||
if (preg_match_all('/\b(xs|s|m|l|xl|xxl|xxxl)\b/u', $prompt, $tokenMatches) !== false) {
|
||||
$sizeTokenPattern = $this->intentConfig->getSizeTokenPattern();
|
||||
if (preg_match_all('/\b(' . $sizeTokenPattern . ')\b/u', $prompt, $tokenMatches) !== false) {
|
||||
foreach ($tokenMatches[1] as $sizeToken) {
|
||||
$sizes[] = trim($sizeToken);
|
||||
}
|
||||
@@ -122,16 +119,12 @@ final class CommerceQueryParser
|
||||
|
||||
private function extractBrand(string $prompt): ?string
|
||||
{
|
||||
foreach ($this->knownBrands as $brand) {
|
||||
foreach ($this->config->getKnownBrands() as $brand) {
|
||||
if (str_contains($prompt, $brand)) {
|
||||
return $brand;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/\bheyl\s+([a-z0-9][a-z0-9\s\-]+)/u', $prompt, $m) === 1) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -145,20 +138,7 @@ final class CommerceQueryParser
|
||||
{
|
||||
$text = ' ' . $prompt . ' ';
|
||||
|
||||
$phrasesToRemove = [
|
||||
'ich suche',
|
||||
'suche',
|
||||
'habt ihr',
|
||||
'gibt es',
|
||||
'zeige mir',
|
||||
'welches gerät',
|
||||
'welche gerät',
|
||||
'welches modell',
|
||||
'welches ist besser',
|
||||
'welches ist am besten',
|
||||
'alternative',
|
||||
'alternativen',
|
||||
];
|
||||
$phrasesToRemove = $this->config->getPhrasesToRemove();
|
||||
|
||||
foreach ($phrasesToRemove as $phrase) {
|
||||
$text = str_replace($phrase, ' ', $text);
|
||||
@@ -173,11 +153,9 @@ final class CommerceQueryParser
|
||||
}
|
||||
|
||||
if ($priceMin !== null || $priceMax !== null) {
|
||||
if ($priceMin !== null || $priceMax !== null) {
|
||||
$text = preg_replace('/\bzwischen\s+\d+(?:[.,]\d+)?\s+und\s+\d+(?:[.,]\d+)?\s*euro\b/u', ' ', $text) ?? $text;
|
||||
$text = preg_replace('/\b(?:unter|bis|max(?:imal)?|ab|mindestens|min)\s+\d+(?:[.,]\d+)?\s*euro\b/u', ' ', $text) ?? $text;
|
||||
$text = preg_replace('/\beuro\b/u', ' ', $text) ?? $text;
|
||||
}
|
||||
$text = preg_replace('/\bzwischen\s+\d+(?:[.,]\d+)?\s+und\s+\d+(?:[.,]\d+)?\s*euro\b/u', ' ', $text) ?? $text;
|
||||
$text = preg_replace('/\b(?:unter|bis|max(?:imal)?|ab|mindestens|min)\s+\d+(?:[.,]\d+)?\s*euro\b/u', ' ', $text) ?? $text;
|
||||
$text = preg_replace('/\b'.$this->intentConfig->getPricePattern().'\b/u', ' ', $text) ?? $text;
|
||||
}
|
||||
|
||||
$text = preg_replace('/\s+/u', ' ', $text) ?? $text;
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Commerce\Dto\ShopProductResult;
|
||||
use App\Shopware\ShopwareCriteriaBuilder;
|
||||
use App\Shopware\StoreApiClient;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
@@ -16,14 +15,13 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
final readonly class ShopSearchService
|
||||
{
|
||||
public function __construct(
|
||||
private CommerceQueryParser $queryParser,
|
||||
private CommerceQueryParser $queryParser,
|
||||
private ShopwareCriteriaBuilder $criteriaBuilder,
|
||||
private StoreApiClient $storeApiClient,
|
||||
private bool $enabled = true,
|
||||
private int $maxResults = 25,
|
||||
private string $baseUrl
|
||||
)
|
||||
{
|
||||
private StoreApiClient $storeApiClient,
|
||||
private bool $enabled = true,
|
||||
private int $maxResults = 25,
|
||||
private string $baseUrl
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,18 +34,17 @@ final readonly class ShopSearchService
|
||||
}
|
||||
|
||||
$response = [];
|
||||
|
||||
$query = $this->queryParser->parse($originalPrompt, $commerceIntent);
|
||||
$criteria = $this->criteriaBuilder->build($query, $this->maxResults);
|
||||
|
||||
try {
|
||||
$response = $this->storeApiClient->searchProducts($criteria);
|
||||
} catch (ClientExceptionInterface|DecodingExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $e) {
|
||||
} catch (ClientExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface|TransportExceptionInterface $e) {
|
||||
|
||||
}
|
||||
|
||||
$result = $this->mapProducts($response);;
|
||||
|
||||
return $result;
|
||||
return $this->mapProducts($response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,42 +65,44 @@ final readonly class ShopSearchService
|
||||
}
|
||||
|
||||
$results[] = new ShopProductResult(
|
||||
id: (string)($row['id'] ?? ''),
|
||||
name: trim((string)($row['translated']['name'] ?? '')),
|
||||
productNumber: isset($row['productNumber']) ? (string)$row['productNumber'] : null,
|
||||
id: (string) ($row['id'] ?? ''),
|
||||
name: trim((string) ($row['translated']['name'] ?? '')),
|
||||
productNumber: isset($row['productNumber']) ? (string) $row['productNumber'] : null,
|
||||
manufacturer: $this->extractManufacturer($row),
|
||||
price: $this->extractPrice($row),
|
||||
available: isset($row['available']) ? (bool)$row['available'] : null,
|
||||
available: isset($row['available']) ? (bool) $row['available'] : null,
|
||||
url: $this->baseUrl . $this->extractUrl($row),
|
||||
highlights: $this->extractHighlights($row),
|
||||
description: $this->cleanUpDescription($row),
|
||||
productImage: $row['cover']['media']['thumbnails'][0]['url'] ?? 'no-image',
|
||||
customFields: $this->getRelevantCustomFields($row['customFields'])
|
||||
customFields: $this->getRelevantCustomFields($row['customFields'] ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
return array_values(array_filter(
|
||||
$results,
|
||||
static fn(ShopProductResult $product): bool => $product->name !== ''
|
||||
static fn (ShopProductResult $product): bool => $product->name !== ''
|
||||
));
|
||||
}
|
||||
|
||||
private function getRelevantCustomFields($customField): string
|
||||
private function getRelevantCustomFields(array $customField): string
|
||||
{
|
||||
$result = ($customField['migration_Backup_product_attr1'] ?? '') . ': ' . ($customField['migration_Backup_product_attr2'] ?? '');
|
||||
$result .= ' | Einsatzgebiete: ' . ($customField['migration_Backup_product_attr4'] ?? '');
|
||||
$result .= ' | Sprachen: ' . ($customField['migration_Backup_product_attr5'] ?? '');
|
||||
|
||||
return $result;
|
||||
return trim($result);
|
||||
}
|
||||
|
||||
private function cleanUpDescription($description): string
|
||||
private function cleanUpDescription(array $description): string
|
||||
{
|
||||
if (isset($description['translated']['description'])) {
|
||||
$newDesc = strip_tags((string)$description['translated']['description']);
|
||||
$newDesc = preg_replace('/^[ \t]*\R/m', '', $newDesc); // leere Zeilen weg
|
||||
$newDesc = preg_replace('/[ \t]{2,}/', ' ', $newDesc); // mehrere Spaces zu einem
|
||||
$result = trim($newDesc);
|
||||
return substr($result, 0, 500);
|
||||
$newDesc = strip_tags((string) $description['translated']['description']);
|
||||
$newDesc = preg_replace('/^[ \t]*\R/m', '', $newDesc);
|
||||
$newDesc = preg_replace('/[ \t]{2,}/', ' ', $newDesc);
|
||||
$result = trim((string) $newDesc);
|
||||
|
||||
return mb_substr($result, 0, 500);
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -114,7 +113,9 @@ final readonly class ShopSearchService
|
||||
$manufacturer = $row['manufacturer'] ?? null;
|
||||
|
||||
if (is_array($manufacturer) && isset($manufacturer['name']) && is_string($manufacturer['name'])) {
|
||||
return trim($manufacturer['name']) !== '' ? trim($manufacturer['name']) : null;
|
||||
$name = trim($manufacturer['name']);
|
||||
|
||||
return $name !== '' ? $name : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -128,12 +129,18 @@ final readonly class ShopSearchService
|
||||
return null;
|
||||
}
|
||||
|
||||
$unitPrice = $calculatedPrice['unitPrice'] ?? $calculatedPrice['totalPrice'] ?? $calculatedPrice['referencePrice'] ?? $calculatedPrice['listPrice'] ?? $calculatedPrice['regulationPrice'] ?? 0;
|
||||
$unitPrice = $calculatedPrice['unitPrice']
|
||||
?? $calculatedPrice['totalPrice']
|
||||
?? $calculatedPrice['referencePrice']
|
||||
?? $calculatedPrice['listPrice']
|
||||
?? $calculatedPrice['regulationPrice']
|
||||
?? null;
|
||||
|
||||
if (!is_numeric($unitPrice)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return number_format((float)$unitPrice, 2, ',', '.') . ' €';
|
||||
return number_format((float) $unitPrice, 2, ',', '.') . ' €';
|
||||
}
|
||||
|
||||
private function extractUrl(array $row): ?string
|
||||
@@ -166,7 +173,7 @@ final readonly class ShopSearchService
|
||||
$highlights = [];
|
||||
|
||||
if (isset($row['available'])) {
|
||||
$highlights[] = ((bool)$row['available']) ? 'Verfügbar' : 'Nicht verfügbar';
|
||||
$highlights[] = (bool) $row['available'] ? 'Verfügbar' : 'Nicht verfügbar';
|
||||
}
|
||||
|
||||
if (isset($row['productNumber']) && is_string($row['productNumber']) && trim($row['productNumber']) !== '') {
|
||||
|
||||
Reference in New Issue
Block a user