patch 7.2
This commit is contained in:
@@ -87,6 +87,29 @@ final class AgentRunnerConfig
|
|||||||
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be numeric.', $key));
|
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be numeric.', $key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRequiredBool(string $key): bool
|
||||||
|
{
|
||||||
|
$value = $this->requiredValue($key);
|
||||||
|
|
||||||
|
if (is_bool($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_scalar($value)) {
|
||||||
|
$normalized = strtolower(trim((string) $value));
|
||||||
|
|
||||||
|
if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be boolean.', $key));
|
||||||
|
}
|
||||||
|
|
||||||
private function getRequiredString(string $key): string
|
private function getRequiredString(string $key): string
|
||||||
{
|
{
|
||||||
$value = $this->requiredValue($key);
|
$value = $this->requiredValue($key);
|
||||||
@@ -177,6 +200,39 @@ final class AgentRunnerConfig
|
|||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
private function getOptionalStringMap(string $key): array
|
||||||
|
{
|
||||||
|
$value = $this->optionalValue($key);
|
||||||
|
|
||||||
|
if ($value === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($value)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a string map.', $key));
|
||||||
|
}
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($value as $mapKey => $mapValue) {
|
||||||
|
if (!is_scalar($mapKey) || !is_scalar($mapValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mapKey = trim((string) $mapKey);
|
||||||
|
$mapValue = trim((string) $mapValue);
|
||||||
|
|
||||||
|
if ($mapKey !== '' && $mapValue !== '') {
|
||||||
|
$out[$mapKey] = $mapValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $default
|
* @param string[] $default
|
||||||
* @return string[]
|
* @return string[]
|
||||||
@@ -236,6 +292,21 @@ final class AgentRunnerConfig
|
|||||||
return $current;
|
return $current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function optionalValue(string $key): mixed
|
||||||
|
{
|
||||||
|
$current = $this->config;
|
||||||
|
|
||||||
|
foreach (explode('.', $key) as $segment) {
|
||||||
|
if (!is_array($current) || !array_key_exists($segment, $current)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = $current[$segment];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $current;
|
||||||
|
}
|
||||||
|
|
||||||
public function getEmptyPromptMessage(): string
|
public function getEmptyPromptMessage(): string
|
||||||
{
|
{
|
||||||
return $this->getRequiredString('messages.empty_prompt');
|
return $this->getRequiredString('messages.empty_prompt');
|
||||||
@@ -474,25 +545,7 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopPromptRules(): array
|
public function getShopPromptRules(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_prompt.rules', [
|
return $this->getRequiredStringList('shop_prompt.rules');
|
||||||
'- Output only the final search query.',
|
|
||||||
'- Always convert relevant search terms to their singular form.',
|
|
||||||
'- No introduction, no explanation, no quotation marks.',
|
|
||||||
'- Use only shop-relevant search terms from the user input for a shop search.',
|
|
||||||
'- Maximum 6 search terms, preferably fewer.',
|
|
||||||
'- Remove filler words, polite phrases, and irrelevant words.',
|
|
||||||
'- Preserve product names, brands, model numbers, and compound terms exactly if they are relevant.',
|
|
||||||
'- Preserve the language of the CURRENT USER INPUT for generic product/search terms; do not translate German search terms into English.',
|
|
||||||
'- For German user input, output German shop terms, for example "freies Chlor Messung" instead of "free chlorine measurement".',
|
|
||||||
'- Preserve domain terms from the current user input or resolved context in their original language.',
|
|
||||||
'- Numbers that belong to a product name or model must be preserved (e.g. Indikator 300, Testomat 808, Testomat 2000).',
|
|
||||||
'- Separate terms using spaces only.',
|
|
||||||
'- If a relevant product name is present, it must be placed at the beginning of the final search query.',
|
|
||||||
'- Try to always identify all products mentioned in the user input text, even in long prompts.',
|
|
||||||
'- Look for terms such as Testomat, Horiba, Tritromat, or words like indicator/Indikator.',
|
|
||||||
'- If the current user input is vague or referential, use the recent conversation context only as support.',
|
|
||||||
'- Do not output words that only describe conversation flow, such as "same", "again", "also", or "like above".',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -500,39 +553,32 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getConversationContextRules(): array
|
public function getConversationContextRules(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_prompt.conversation_context_rules', [
|
return $this->getRequiredStringList('shop_prompt.conversation_context_rules');
|
||||||
'- The current user input has highest priority.',
|
|
||||||
'- Use the recent conversation context only to resolve omitted references.',
|
|
||||||
'- Use it only for product carry-over, brand carry-over, model carry-over, or variant follow-ups.',
|
|
||||||
'- Do not revive older products unless the current user input clearly refers to them.',
|
|
||||||
'- If the current input starts a new topic, ignore older product context.',
|
|
||||||
'- Prefer the most recent product reference over older ones.',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopPromptIntro(): string
|
public function getShopPromptIntro(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_prompt.intro', 'Generate a short search query for Shopware 6 from the following user input text.');
|
return $this->getRequiredString('shop_prompt.intro');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopPromptOutputFormatBlock(): string
|
public function getShopPromptOutputFormatBlock(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_prompt.output_format_block', "Output format:\nKeyword1 Keyword2 Keyword3");
|
return $this->getRequiredString('shop_prompt.output_format_block');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRecentConversationContextLabel(): string
|
public function getRecentConversationContextLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_prompt.recent_conversation_context_label', 'RECENT CONVERSATION CONTEXT');
|
return $this->getRequiredString('shop_prompt.recent_conversation_context_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCurrentUserInputLabel(): string
|
public function getCurrentUserInputLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_prompt.current_user_input_label', 'CURRENT USER INPUT');
|
return $this->getRequiredString('shop_prompt.current_user_input_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isShopQueryLanguagePreservationEnabled(): bool
|
public function isShopQueryLanguagePreservationEnabled(): bool
|
||||||
{
|
{
|
||||||
return $this->getBool('shop_prompt.language_preservation.enabled', true);
|
return $this->getRequiredBool('shop_prompt.language_preservation.enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -540,20 +586,10 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopQueryLanguageMarkers(): array
|
public function getShopQueryLanguageMarkers(): array
|
||||||
{
|
{
|
||||||
$default = [
|
$value = $this->requiredValue('shop_prompt.language_preservation.language_markers');
|
||||||
'de' => [
|
|
||||||
' ä ', ' ö ', ' ü ', ' ß ',
|
|
||||||
' der ', ' die ', ' das ', ' ein ', ' eine ', ' einer ', ' einen ',
|
|
||||||
' welchem ', ' welchen ', ' welche ', ' welcher ',
|
|
||||||
' kann ', ' nutzen ', ' zur ', ' für ', ' fuer ',
|
|
||||||
' messung ', ' indikator ', ' reagenz ', ' chlor ',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$value = $this->value('shop_prompt.language_preservation.language_markers', $default);
|
|
||||||
|
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
return $default;
|
throw new \InvalidArgumentException('RetrieX agent config key "shop_prompt.language_preservation.language_markers" must be a map of marker lists.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$out = [];
|
$out = [];
|
||||||
@@ -582,15 +618,16 @@ final class AgentRunnerConfig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $out !== [] ? $out : $default;
|
if ($out === []) {
|
||||||
|
throw new \InvalidArgumentException('RetrieX agent config key "shop_prompt.language_preservation.language_markers" must contain at least one valid marker list.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
public function isShopQueryMetaGuardEnabled(): bool
|
public function isShopQueryMetaGuardEnabled(): bool
|
||||||
{
|
{
|
||||||
return $this->getBool('shop_prompt.meta_query_guard.enabled', true);
|
return $this->getRequiredBool('shop_prompt.meta_query_guard.enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -598,59 +635,32 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopQueryMetaOnlyTerms(): array
|
public function getShopQueryMetaOnlyTerms(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_prompt.meta_query_guard.meta_only_terms', [
|
return $this->getRequiredStringList('shop_prompt.meta_query_guard.meta_only_terms');
|
||||||
'shop',
|
|
||||||
'shopsuche',
|
|
||||||
'shop-suche',
|
|
||||||
'suche',
|
|
||||||
'suchen',
|
|
||||||
'such',
|
|
||||||
'finde',
|
|
||||||
'find',
|
|
||||||
'zeige',
|
|
||||||
'zeig',
|
|
||||||
'bitte',
|
|
||||||
'mal',
|
|
||||||
'im',
|
|
||||||
'in',
|
|
||||||
'nach',
|
|
||||||
'danach',
|
|
||||||
'dazu',
|
|
||||||
'damit',
|
|
||||||
'dafür',
|
|
||||||
'dafuer',
|
|
||||||
'hierzu',
|
|
||||||
'den',
|
|
||||||
'die',
|
|
||||||
'das',
|
|
||||||
'der',
|
|
||||||
'dem',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isShopQueryContextFallbackEnabled(): bool
|
public function isShopQueryContextFallbackEnabled(): bool
|
||||||
{
|
{
|
||||||
return $this->getBool('shop_prompt.meta_query_guard.context_fallback_enabled', true);
|
return $this->getRequiredBool('shop_prompt.meta_query_guard.context_fallback_enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopQueryContextFallbackQuestionLimit(): int
|
public function getShopQueryContextFallbackQuestionLimit(): int
|
||||||
{
|
{
|
||||||
return $this->getInt('shop_prompt.meta_query_guard.context_fallback_question_limit', 12);
|
return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_question_limit');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopQueryContextFallbackHistoryBudgetChars(): int
|
public function getShopQueryContextFallbackHistoryBudgetChars(): int
|
||||||
{
|
{
|
||||||
return $this->getInt('shop_prompt.meta_query_guard.context_fallback_history_budget_chars', 20000);
|
return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_history_budget_chars');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldUseFullHistoryForShopQueryContextFallback(): bool
|
public function shouldUseFullHistoryForShopQueryContextFallback(): bool
|
||||||
{
|
{
|
||||||
return $this->getBool('shop_prompt.meta_query_guard.context_fallback_use_full_history', true);
|
return $this->getRequiredBool('shop_prompt.meta_query_guard.context_fallback_use_full_history');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopQueryContextFallbackMaxTerms(): int
|
public function getShopQueryContextFallbackMaxTerms(): int
|
||||||
{
|
{
|
||||||
return $this->getInt('shop_prompt.meta_query_guard.context_fallback_max_terms', 6);
|
return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_max_terms');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -658,50 +668,17 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopQueryContextFallbackFilterTerms(): array
|
public function getShopQueryContextFallbackFilterTerms(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_prompt.meta_query_guard.context_fallback_filter_terms', [
|
return $this->getRequiredStringList('shop_prompt.meta_query_guard.context_fallback_filter_terms');
|
||||||
'mit',
|
|
||||||
'welche',
|
|
||||||
'welcher',
|
|
||||||
'welches',
|
|
||||||
'welchem',
|
|
||||||
'welchen',
|
|
||||||
'ist',
|
|
||||||
'sind',
|
|
||||||
'gut',
|
|
||||||
'geeignet',
|
|
||||||
'was',
|
|
||||||
'wie',
|
|
||||||
'wo',
|
|
||||||
'kann',
|
|
||||||
'koennen',
|
|
||||||
'können',
|
|
||||||
'konnte',
|
|
||||||
'könnte',
|
|
||||||
'ich',
|
|
||||||
'wir',
|
|
||||||
'man',
|
|
||||||
'nutzen',
|
|
||||||
'benutzen',
|
|
||||||
'verwenden',
|
|
||||||
'verwende',
|
|
||||||
'nehmen',
|
|
||||||
'zur',
|
|
||||||
'zum',
|
|
||||||
'für',
|
|
||||||
'fuer',
|
|
||||||
'messen',
|
|
||||||
'gemessen',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isShopQueryContextAnchorEnrichmentEnabled(): bool
|
public function isShopQueryContextAnchorEnrichmentEnabled(): bool
|
||||||
{
|
{
|
||||||
return $this->getBool('shop_prompt.context_anchor_enrichment.enabled', true);
|
return $this->getRequiredBool('shop_prompt.context_anchor_enrichment.enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopQueryContextAnchorEnrichmentMaxQueryTerms(): int
|
public function getShopQueryContextAnchorEnrichmentMaxQueryTerms(): int
|
||||||
{
|
{
|
||||||
return $this->getInt('shop_prompt.context_anchor_enrichment.max_query_terms', 2);
|
return $this->getRequiredInt('shop_prompt.context_anchor_enrichment.max_query_terms');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -709,17 +686,7 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopQueryContextAnchorEnrichmentTriggerTerms(): array
|
public function getShopQueryContextAnchorEnrichmentTriggerTerms(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_prompt.context_anchor_enrichment.trigger_terms', [
|
return $this->getRequiredStringList('shop_prompt.context_anchor_enrichment.trigger_terms');
|
||||||
'indikator',
|
|
||||||
'indikatortyp',
|
|
||||||
'indicator',
|
|
||||||
'reagenz',
|
|
||||||
'reagenzsatz',
|
|
||||||
'reagent',
|
|
||||||
'zubehör',
|
|
||||||
'zubehor',
|
|
||||||
'accessory',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -727,54 +694,21 @@ final class AgentRunnerConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopQueryContextAnchorEnrichmentPatterns(): array
|
public function getShopQueryContextAnchorEnrichmentPatterns(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_prompt.context_anchor_enrichment.anchor_patterns', [
|
return $this->getRequiredStringList('shop_prompt.context_anchor_enrichment.anchor_patterns');
|
||||||
'/\b(?:indikator(?:typ)?|indicator(?:\s+type)?|reagenz(?:satz|typ)?|reagent(?:\s+set|\s+type)?|typ|type)\s+[A-Za-zÄÖÜäöüß]{0,8}\s*\d{1,5}(?:\s*[A-ZÄÖÜ]{1,4})?(?:\s*%)?\b/iu',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopQueryContextAnchorEnrichmentTemplate(): string
|
public function getShopQueryContextAnchorEnrichmentTemplate(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_prompt.context_anchor_enrichment.template', '{anchor} {query}');
|
return $this->getRequiredString('shop_prompt.context_anchor_enrichment.template');
|
||||||
}
|
}
|
||||||
public function getShopQueryTranslationReplacements(string $language): array
|
public function getShopQueryTranslationReplacements(string $language): array
|
||||||
{
|
{
|
||||||
$default = [
|
$value = $this->getOptionalStringMap('shop_prompt.language_preservation.translation_replacements.' . $language);
|
||||||
'de' => [
|
|
||||||
'free chlorine' => 'freies chlor',
|
|
||||||
'free chlor' => 'freies chlor',
|
|
||||||
'total chlorine' => 'gesamtchlor',
|
|
||||||
'chlorine measurement' => 'chlor messung',
|
|
||||||
'water hardness' => 'wasserhärte',
|
|
||||||
'measurement' => 'messung',
|
|
||||||
'measuring' => 'messung',
|
|
||||||
'chlorine' => 'chlor',
|
|
||||||
'indicator' => 'indikator',
|
|
||||||
'indicators' => 'indikatoren',
|
|
||||||
'reagent' => 'reagenz',
|
|
||||||
'reagents' => 'reagenzien',
|
|
||||||
'accessory' => 'zubehör',
|
|
||||||
'accessories' => 'zubehör',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$value = $this->value(
|
|
||||||
'shop_prompt.language_preservation.translation_replacements.' . $language,
|
|
||||||
$default[$language] ?? []
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!is_array($value)) {
|
|
||||||
return $default[$language] ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$out = [];
|
$out = [];
|
||||||
|
|
||||||
foreach ($value as $source => $target) {
|
foreach ($value as $source => $target) {
|
||||||
if (!is_scalar($source) || !is_scalar($target)) {
|
$source = strtolower(trim($source));
|
||||||
continue;
|
$target = trim($target);
|
||||||
}
|
|
||||||
|
|
||||||
$source = strtolower(trim((string) $source));
|
|
||||||
$target = trim((string) $target);
|
|
||||||
|
|
||||||
if ($source !== '' && $target !== '') {
|
if ($source !== '' && $target !== '') {
|
||||||
$out[$source] = $target;
|
$out[$source] = $target;
|
||||||
@@ -783,7 +717,7 @@ final class AgentRunnerConfig
|
|||||||
|
|
||||||
uksort($out, static fn(string $a, string $b): int => strlen($b) <=> strlen($a));
|
uksort($out, static fn(string $a, string $b): int => strlen($b) <=> strlen($a));
|
||||||
|
|
||||||
return $out !== [] ? $out : ($default[$language] ?? []);
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildRulesBlock(array $rules, string $headline = 'Rules:'): string
|
private function buildRulesBlock(array $rules, string $headline = 'Rules:'): string
|
||||||
|
|||||||
Reference in New Issue
Block a user