diff --git a/RETRIEX_PROMPT_CONFIG_FIX_README.md b/RETRIEX_PROMPT_CONFIG_FIX_README.md new file mode 100644 index 0000000..8f4c989 --- /dev/null +++ b/RETRIEX_PROMPT_CONFIG_FIX_README.md @@ -0,0 +1,35 @@ +# RetrieX Prompt Config Centralization Fix + +This patch centralizes PromptBuilder wording and label configuration in `config/retriex/prompt.yaml` while keeping the previous PHP defaults as fallbacks. + +Changed files: + +- `config/retriex/prompt.yaml` +- `src/Config/PromptBuilderConfig.php` + +Scope: + +- Section labels +- Conversation context intro lines +- Live shop result prompt rules +- Output priority rules +- Response format rules +- Language rules +- Fact grounding rules +- Retrieved knowledge / URL source labels +- Shop result field labels +- Technical product model pattern + +Intentionally unchanged: + +- Main active system prompt from the database +- Retrieval logic +- Shop search logic +- Vocabulary / intent / agent config logic +- PromptBuilderConfig keyword detection, which remains backed by `vocabulary.yaml` + +After applying: + +1. Clear Symfony cache. +2. Run the known 1.4.2 regression prompts. +3. Verify that generated prompt sections remain identical to the previous behavior. diff --git a/config/retriex/prompt.yaml b/config/retriex/prompt.yaml index 12f9f6f..c7e60f4 100644 --- a/config/retriex/prompt.yaml +++ b/config/retriex/prompt.yaml @@ -1,5 +1,5 @@ -# Prompt budget and prompt rendering limits. -# Existing prompt wording/rules remain in PromptBuilderConfig for this minimal-invasive round. +# Prompt budget, prompt rendering limits and prompt wording rules. +# Prompt text values are mirrored from PromptBuilderConfig defaults; PHP fallbacks remain active. parameters: retriex.prompt.config: budget: @@ -15,4 +15,165 @@ parameters: shop_results: max_results_in_prompt: 24 detailed_max_count: 5 + header_lines: + - 'LIVE SHOP RESULTS (authoritative for current commercial details):' + - Use these results as the primary source for current price, availability, URL, and current shop-visible product naming. + - If retrieved documents conflict with shop data on price, availability, URL, or current naming, prefer the shop data. + - Output real URL values exactly as provided in the shop results. Do not replace them with placeholders, link labels, or product names. + - Do not infer undocumented technical specifications from shop data. + - Commercial fields from shop data may only be assigned to a product if the shop item clearly matches the same product identity. + - Do not merge a device identified in retrieved knowledge with price, URL, product number, or availability from a different shop item such as a reagent, accessory, + kit, consumable, or service item. + - If a shop result has no price field, do not state a price for it. + - Never interpret a missing price or a zero price as free, kostenlos, gratis, or available for 0.00 EUR. + overflow_notice_template: Only the top %d ranked shop results are shown here out of %d total results. + fields: + product_number_label: Product number + manufacturer_label: Manufacturer + price_label: Price + availability_label: Available + availability_yes_label: 'yes' + availability_no_label: 'no' + highlight_prefix: '- ' + url_label: URL + product_image_label: Product image + description_label: Description + meta_information_label: Meta information technical_product_keyword_match_threshold: 2 + sections: + system_label: SYSTEM + user_question_label: USER QUESTION + conversation_context_label: CONVERSATION CONTEXT (contextual only) + shop_search_query_label: SHOP SEARCH QUERY + output_priority_label: OUTPUT PRIORITY + response_format_label: RESPONSE FORMAT RULES + language_rules_label: LANGUAGE RULES + fact_grounding_rules_label: FACT GROUNDING RULES + retrieved_knowledge_label: RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation) + url_content_label: CONTENT FROM URL (authoritative if user-provided) + conversation_context: + intro_lines: + - The following messages are previous turns of this conversation. + - Use them only to resolve references, follow-up questions, and user intent. + - Previous assistant answers are not a factual source for technical values, product compatibility, indicators, ranges, prices, or availability. + - All factual claims must come from retrieved factual knowledge, user-provided URL content, or live shop data. + - Conversation context must not override retrieved factual knowledge or live shop data. + shop_search: + source_line: 'Source: Shop Search' + output_priority: + rules: + - '- Use retrieved knowledge first to determine the technically matching product or answer.' + - '- If shop results are present, use them afterwards to add current price, availability, and the actual URL.' + - '- Do not let bundles, accessories, or service items override a better technical match unless the user explicitly asks for them.' + response_format: + base_rules: + - '- Keep normal spacing between all words. Never fuse words together.' + - '- Use short, clean paragraphs or short labeled sections.' + - '- Do not use persuasive or promotional wording.' + - '- Do not repeat the same fact in slightly different wording.' + - '- Never mention brands, manufacturers, model names, or product families that do not appear in the provided shop results, retrieved knowledge, URL content, + or conversation context.' + - '- If no suitable product is explicitly grounded in the provided sources, say that plainly instead of inventing alternatives.' + - '- Do not generate external alternative lists, vendor suggestions, or purchase recommendations unless they are explicitly present in the provided sources.' + - '- Do not combine technical identity from one source with commercial fields from a different product.' + - '- Product number, price, availability, and URL must belong to the same explicitly grounded product.' + with_shop_rules: + - '- If a product is identified, prefer this structure per product: product name, product number, price, availability, URL, then only the most relevant technical + facts.' + - '- Keep price, availability, and URL on separate lines when they are present.' + - '- Only use shop price, URL, product number, or availability for the main product when the shop result clearly matches that same main product.' + - '- If the matching shop item appears to be an accessory, reagent, consumable, set, or kit, keep it separate and do not present its commercial fields as the + main device.' + - '- If the commercial match is uncertain, say that commercial details for the main product are not clearly available in the provided shop results.' + - '- If no price is shown for a shop item, omit the price instead of writing 0,00 €, free, kostenlos, or a guessed price.' + without_shop_rules: + - '- If no shop results are present, do not compensate by inventing external products or external manufacturers.' + technical_rules: + - '- Write like technical documentation: precise, neutral, and source-close.' + - '- Prefer exact values, ranges, thresholds, compatibility notes, and application areas over general explanation.' + - '- If the sources only support a negative finding, output only that negative finding and do not add speculative alternatives.' + accessory_rules: + - '- If the user asks for a matching accessory, separate the answer into: main device and matching accessory.' + - '- The main device must come first. The accessory must not replace the main device.' + - '- Only name an accessory as matching if compatibility is explicitly grounded in the provided sources.' + - '- Do not call accessories, indicators, reagents, kits, sets, or consumables a device, measuring device, or main product unless the source explicitly says + so.' + language: + rules: + - '- Answer only in the same language as the user question.' + - '- All headings, labels, notes, and structural elements must be in the same language as the user question.' + - '- Do not switch languages unless the user does.' + - '- If headings are used, write them in the user''s language.' + fact_grounding: + base_rules: + - '- State only facts that are explicitly present in the provided sources.' + - '- Extract concrete values exactly when they are present, including units, ranges, model names, indicator names, IP classes, temperatures, pressures, dimensions, + counts, relay outputs, current outputs, and error codes.' + - '- Do not invent missing values.' + - '- Do not replace missing values with estimates, defaults, or typical industry assumptions.' + - '- Do not claim that information is missing if it appears in the provided sources.' + - '- Do not compare with other products unless those products are also present in the provided sources.' + - '- Prefer source-faithful wording over persuasive wording.' + - '- Avoid marketing language such as ''ideal'', ''perfect'', ''unverzichtbar'', ''entscheidend'', ''optimal'', ''kosteneffizient'', ''prozesssicher'', or ''state-of-the-art''.' + - '- Clearly separate explicit facts from inferences.' + - '- If a conclusion goes beyond the source wording, label it exactly as ''Inference:''.' + - '- If a sentence cannot be traced to the provided sources, do not write it.' + - '- For follow-up questions, use the conversation only to resolve what the user refers to; do not copy technical facts from previous assistant answers unless + the same fact is present in the current retrieved sources.' + - '- Never mention external manufacturers, external brands, or external products unless they are explicitly present in the provided sources.' + - '- If the sources do not identify a suitable product, do not invent one.' + with_shop_rules: + - '- Use shop data as highest priority only for current commercial fields: price, availability, URL, and current shop-visible naming.' + - '- Use retrieved knowledge as highest priority for technical matching, thresholds, measurement principles, and technical explanation.' + - '- When shop results are present and relevant, include current price and the actual URL if available.' + - '- If the shop data does not provide a positive price for a result, do not output any price for that result.' + - '- Do not let accessories, bundles, or service items override a technically better product match unless the user explicitly asks for them.' + - '- Do not call accessories, indicators, reagents, kits, sets, or consumables a device, measuring device, or main product unless the source explicitly says + so.' + - '- Do not claim that an accessory is required, necessary, used for calibration, or sets the measurement range unless this is explicitly stated in the provided + sources.' + - '- Do not assign the product number, price, URL, or availability of a reagent, accessory, kit, set, consumable, or service item to a device identified in + retrieved knowledge.' + - '- Only use commercial fields for the main product when the shop item and the technically identified product clearly refer to the same product identity.' + - '- If the shop match is ambiguous, keep the technical identification and commercial details separate.' + without_shop_rules: + - '- Use retrieved knowledge as authoritative for factual answers.' + - '- If no shop results are present, do not compensate with external recommendations or external product suggestions.' + technical_rules: + - '- For technical product questions, answer primarily with explicitly stated facts.' + - '- Behave like a technical documentation assistant, not like a sales advisor.' + - '- Keep interpretations minimal and do not generalize application areas beyond the provided sources.' + - '- Do not describe benefits, consequences, risks, or operational outcomes unless they are explicitly stated in the sources.' + - '- Do not translate technical facts into business value unless the source explicitly does so.' + - '- Do not recommend process changes unless explicitly present in the source.' + - '- Do not use persuasive summaries or advisory conclusions.' + - '- If the retrieved knowledge describes one specific named product, stay within that product and do not merge related product families or variants.' + - '- Use neutral engineering language.' + - '- Do not name specific chemicals, indicator substances, standards, or mechanisms unless explicitly stated in the source.' + - '- If the source states signal logic such as green/red, output that signal logic only and do not expand it into operational recommendations or alarm semantics + unless explicitly stated.' + - '- If the source lists application areas, repeat only those areas and do not broaden them.' + - '- If the source names an indicator and threshold, reproduce that exactly without extrapolation.' + - '- For lowest, highest, smallest, largest, minimum, maximum, Grenzwert, Messbereich or Aufloesung questions, first identify the exact numeric extreme from + the retrieved knowledge and answer that value directly.' + - '- For lowest/highest/minimum/maximum questions, answer only the requested extreme unless the user explicitly asks for a comparison or alternatives.' + - '- For direct numeric lookup questions such as which device measures a given threshold, answer with the exact matching device/value pair first and avoid advisory + caveats.' + - '- Do not add the runner-up product, second-lowest value, or adjacent range unless the user asks for it.' + - '- Do not add calibration, accuracy, pretreatment, temperature, or application notes unless those exact notes are requested and explicitly present in the + retrieved source.' + - '- For follow-up questions such as "which indicator measures that value", first resolve the referenced value/device, then use the retrieved source entry that + explicitly connects value, device and indicator.' + - '- For numeric extreme questions, do not combine a value, device name, indicator name, range or product variant from different chunks unless the same retrieved + entry explicitly connects them.' + - '- If several devices or indicators are present, keep each device-indicator-range assignment separate and do not transfer an indicator from one product to + another.' + - '- If the source states only a threshold function, do not expand it into broader control logic.' + - '- If a detail is not explicitly stated in the provided sources, say so plainly.' + - '- Prefer short, source-close sentences over explanatory expansion.' + - '- If the sources only support that a product family is not suitable, output only that unsuitability and stop there.' + retrieved_knowledge: + source_line: 'Source: Documents' + url_content: + source_line: 'Source: URL' + technical_product_model_pattern: /\b[\p{L}]{2,}\s?\d{2,5}\b/u diff --git a/src/Config/PromptBuilderConfig.php b/src/Config/PromptBuilderConfig.php index 58dbc93..5a051b9 100644 --- a/src/Config/PromptBuilderConfig.php +++ b/src/Config/PromptBuilderConfig.php @@ -151,6 +151,19 @@ final class PromptBuilderConfig return is_numeric($value) ? (float) $value : $default; } + private function getString(string $path, string $default): string + { + $value = $this->getValue($path, $default); + + if (!is_scalar($value)) { + return $default; + } + + $value = (string) $value; + + return $value !== '' ? $value : $default; + } + /** * @return string[] */ @@ -204,17 +217,17 @@ final class PromptBuilderConfig public function getSystemSectionLabel(): string { - return 'SYSTEM'; + return $this->getString('sections.system_label', 'SYSTEM'); } public function getUserQuestionSectionLabel(): string { - return 'USER QUESTION'; + return $this->getString('sections.user_question_label', 'USER QUESTION'); } public function getConversationContextSectionLabel(): string { - return 'CONVERSATION CONTEXT (contextual only)'; + return $this->getString('sections.conversation_context_label', 'CONVERSATION CONTEXT (contextual only)'); } /** @@ -222,23 +235,23 @@ final class PromptBuilderConfig */ public function getConversationContextIntroLines(): array { - return [ + return $this->getStringList('conversation_context.intro_lines', [ 'The following messages are previous turns of this conversation.', 'Use them only to resolve references, follow-up questions, and user intent.', 'Previous assistant answers are not a factual source for technical values, product compatibility, indicators, ranges, prices, or availability.', 'All factual claims must come from retrieved factual knowledge, user-provided URL content, or live shop data.', 'Conversation context must not override retrieved factual knowledge or live shop data.', - ]; + ]); } public function getShopSearchQuerySectionLabel(): string { - return 'SHOP SEARCH QUERY'; + return $this->getString('sections.shop_search_query_label', 'SHOP SEARCH QUERY'); } public function getShopSearchQuerySourceLine(): string { - return 'Source: Shop Search'; + return $this->getString('shop_search.source_line', 'Source: Shop Search'); } /** @@ -246,7 +259,7 @@ final class PromptBuilderConfig */ public function getLiveShopResultsHeaderLines(): array { - return [ + return $this->getStringList('shop_results.header_lines', [ 'LIVE SHOP RESULTS (authoritative for current commercial details):', 'Use these results as the primary source for current price, availability, URL, and current shop-visible product naming.', 'If retrieved documents conflict with shop data on price, availability, URL, or current naming, prefer the shop data.', @@ -256,17 +269,17 @@ final class PromptBuilderConfig 'Do not merge a device identified in retrieved knowledge with price, URL, product number, or availability from a different shop item such as a reagent, accessory, kit, consumable, or service item.', 'If a shop result has no price field, do not state a price for it.', 'Never interpret a missing price or a zero price as free, kostenlos, gratis, or available for 0.00 EUR.', - ]; + ]); } public function getLiveShopResultsOverflowNoticeTemplate(): string { - return 'Only the top %d ranked shop results are shown here out of %d total results.'; + return $this->getString('shop_results.overflow_notice_template', 'Only the top %d ranked shop results are shown here out of %d total results.'); } public function getOutputPrioritySectionLabel(): string { - return 'OUTPUT PRIORITY'; + return $this->getString('sections.output_priority_label', 'OUTPUT PRIORITY'); } /** @@ -274,16 +287,16 @@ final class PromptBuilderConfig */ public function getOutputPriorityRules(): array { - return [ + return $this->getStringList('output_priority.rules', [ '- Use retrieved knowledge first to determine the technically matching product or answer.', '- If shop results are present, use them afterwards to add current price, availability, and the actual URL.', '- Do not let bundles, accessories, or service items override a better technical match unless the user explicitly asks for them.', - ]; + ]); } public function getResponseFormatSectionLabel(): string { - return 'RESPONSE FORMAT RULES'; + return $this->getString('sections.response_format_label', 'RESPONSE FORMAT RULES'); } /** @@ -291,7 +304,7 @@ final class PromptBuilderConfig */ public function getResponseFormatBaseRules(): array { - return [ + return $this->getStringList('response_format.base_rules', [ '- Keep normal spacing between all words. Never fuse words together.', '- Use short, clean paragraphs or short labeled sections.', '- Do not use persuasive or promotional wording.', @@ -301,7 +314,7 @@ final class PromptBuilderConfig '- Do not generate external alternative lists, vendor suggestions, or purchase recommendations unless they are explicitly present in the provided sources.', '- Do not combine technical identity from one source with commercial fields from a different product.', '- Product number, price, availability, and URL must belong to the same explicitly grounded product.', - ]; + ]); } /** @@ -309,14 +322,14 @@ final class PromptBuilderConfig */ public function getResponseFormatWithShopRules(): array { - return [ + return $this->getStringList('response_format.with_shop_rules', [ '- If a product is identified, prefer this structure per product: product name, product number, price, availability, URL, then only the most relevant technical facts.', '- Keep price, availability, and URL on separate lines when they are present.', '- Only use shop price, URL, product number, or availability for the main product when the shop result clearly matches that same main product.', '- If the matching shop item appears to be an accessory, reagent, consumable, set, or kit, keep it separate and do not present its commercial fields as the main device.', '- If the commercial match is uncertain, say that commercial details for the main product are not clearly available in the provided shop results.', '- If no price is shown for a shop item, omit the price instead of writing 0,00 €, free, kostenlos, or a guessed price.', - ]; + ]); } /** @@ -324,9 +337,9 @@ final class PromptBuilderConfig */ public function getResponseFormatWithoutShopRules(): array { - return [ + return $this->getStringList('response_format.without_shop_rules', [ '- If no shop results are present, do not compensate by inventing external products or external manufacturers.', - ]; + ]); } /** @@ -334,11 +347,11 @@ final class PromptBuilderConfig */ public function getResponseFormatTechnicalRules(): array { - return [ + return $this->getStringList('response_format.technical_rules', [ '- Write like technical documentation: precise, neutral, and source-close.', '- Prefer exact values, ranges, thresholds, compatibility notes, and application areas over general explanation.', '- If the sources only support a negative finding, output only that negative finding and do not add speculative alternatives.', - ]; + ]); } /** @@ -346,17 +359,17 @@ final class PromptBuilderConfig */ public function getResponseFormatAccessoryRules(): array { - return [ + return $this->getStringList('response_format.accessory_rules', [ '- If the user asks for a matching accessory, separate the answer into: main device and matching accessory.', '- The main device must come first. The accessory must not replace the main device.', '- Only name an accessory as matching if compatibility is explicitly grounded in the provided sources.', '- Do not call accessories, indicators, reagents, kits, sets, or consumables a device, measuring device, or main product unless the source explicitly says so.', - ]; + ]); } public function getLanguageRulesSectionLabel(): string { - return 'LANGUAGE RULES'; + return $this->getString('sections.language_rules_label', 'LANGUAGE RULES'); } /** @@ -364,17 +377,17 @@ final class PromptBuilderConfig */ public function getLanguageRules(): array { - return [ + return $this->getStringList('language.rules', [ '- Answer only in the same language as the user question.', '- All headings, labels, notes, and structural elements must be in the same language as the user question.', '- Do not switch languages unless the user does.', '- If headings are used, write them in the user\'s language.', - ]; + ]); } public function getFactGroundingRulesSectionLabel(): string { - return 'FACT GROUNDING RULES'; + return $this->getString('sections.fact_grounding_rules_label', 'FACT GROUNDING RULES'); } /** @@ -382,7 +395,7 @@ final class PromptBuilderConfig */ public function getFactGroundingBaseRules(): array { - return [ + return $this->getStringList('fact_grounding.base_rules', [ '- State only facts that are explicitly present in the provided sources.', '- Extract concrete values exactly when they are present, including units, ranges, model names, indicator names, IP classes, temperatures, pressures, dimensions, counts, relay outputs, current outputs, and error codes.', '- Do not invent missing values.', @@ -390,14 +403,14 @@ final class PromptBuilderConfig '- Do not claim that information is missing if it appears in the provided sources.', '- Do not compare with other products unless those products are also present in the provided sources.', '- Prefer source-faithful wording over persuasive wording.', - '- Avoid marketing language such as \'ideal\', \'perfect\', \'unverzichtbar\', \'entscheidend\', \'optimal\', \'kosteneffizient\', \'prozesssicher\', or \'state-of-the-art\'.', + "- Avoid marketing language such as 'ideal', 'perfect', 'unverzichtbar', 'entscheidend', 'optimal', 'kosteneffizient', 'prozesssicher', or 'state-of-the-art'.", '- Clearly separate explicit facts from inferences.', - '- If a conclusion goes beyond the source wording, label it exactly as \'Inference:\'.', + "- If a conclusion goes beyond the source wording, label it exactly as 'Inference:'.", '- If a sentence cannot be traced to the provided sources, do not write it.', '- For follow-up questions, use the conversation only to resolve what the user refers to; do not copy technical facts from previous assistant answers unless the same fact is present in the current retrieved sources.', '- Never mention external manufacturers, external brands, or external products unless they are explicitly present in the provided sources.', '- If the sources do not identify a suitable product, do not invent one.', - ]; + ]); } /** @@ -405,7 +418,7 @@ final class PromptBuilderConfig */ public function getFactGroundingWithShopRules(): array { - return [ + return $this->getStringList('fact_grounding.with_shop_rules', [ '- Use shop data as highest priority only for current commercial fields: price, availability, URL, and current shop-visible naming.', '- Use retrieved knowledge as highest priority for technical matching, thresholds, measurement principles, and technical explanation.', '- When shop results are present and relevant, include current price and the actual URL if available.', @@ -416,7 +429,7 @@ final class PromptBuilderConfig '- Do not assign the product number, price, URL, or availability of a reagent, accessory, kit, set, consumable, or service item to a device identified in retrieved knowledge.', '- Only use commercial fields for the main product when the shop item and the technically identified product clearly refer to the same product identity.', '- If the shop match is ambiguous, keep the technical identification and commercial details separate.', - ]; + ]); } /** @@ -424,10 +437,10 @@ final class PromptBuilderConfig */ public function getFactGroundingWithoutShopRules(): array { - return [ + return $this->getStringList('fact_grounding.without_shop_rules', [ '- Use retrieved knowledge as authoritative for factual answers.', '- If no shop results are present, do not compensate with external recommendations or external product suggestions.', - ]; + ]); } /** @@ -435,7 +448,7 @@ final class PromptBuilderConfig */ public function getFactGroundingTechnicalRules(): array { - return [ + return $this->getStringList('fact_grounding.technical_rules', [ '- For technical product questions, answer primarily with explicitly stated facts.', '- Behave like a technical documentation assistant, not like a sales advisor.', '- Keep interpretations minimal and do not generalize application areas beyond the provided sources.', @@ -461,82 +474,82 @@ final class PromptBuilderConfig '- If a detail is not explicitly stated in the provided sources, say so plainly.', '- Prefer short, source-close sentences over explanatory expansion.', '- If the sources only support that a product family is not suitable, output only that unsuitability and stop there.', - ]; + ]); } public function getRetrievedKnowledgeSectionLabel(): string { - return 'RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation)'; + return $this->getString('sections.retrieved_knowledge_label', 'RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation)'); } public function getRetrievedKnowledgeSourceLine(): string { - return 'Source: Documents'; + return $this->getString('retrieved_knowledge.source_line', 'Source: Documents'); } public function getUrlContentSectionLabel(): string { - return 'CONTENT FROM URL (authoritative if user-provided)'; + return $this->getString('sections.url_content_label', 'CONTENT FROM URL (authoritative if user-provided)'); } public function getUrlContentSourceLine(): string { - return 'Source: URL'; + return $this->getString('url_content.source_line', 'Source: URL'); } public function getShopProductNumberLabel(): string { - return 'Product number'; + return $this->getString('shop_results.fields.product_number_label', 'Product number'); } public function getShopManufacturerLabel(): string { - return 'Manufacturer'; + return $this->getString('shop_results.fields.manufacturer_label', 'Manufacturer'); } public function getShopPriceLabel(): string { - return 'Price'; + return $this->getString('shop_results.fields.price_label', 'Price'); } public function getShopAvailabilityLabel(): string { - return 'Available'; + return $this->getString('shop_results.fields.availability_label', 'Available'); } public function getShopAvailabilityYesLabel(): string { - return 'yes'; + return $this->getString('shop_results.fields.availability_yes_label', 'yes'); } public function getShopAvailabilityNoLabel(): string { - return 'no'; + return $this->getString('shop_results.fields.availability_no_label', 'no'); } public function getShopHighlightPrefix(): string { - return '- '; + return $this->getString('shop_results.fields.highlight_prefix', '- '); } public function getShopUrlLabel(): string { - return 'URL'; + return $this->getString('shop_results.fields.url_label', 'URL'); } public function getShopProductImageLabel(): string { - return 'Product image'; + return $this->getString('shop_results.fields.product_image_label', 'Product image'); } public function getShopDescriptionLabel(): string { - return 'Description'; + return $this->getString('shop_results.fields.description_label', 'Description'); } public function getShopMetaInformationLabel(): string { - return 'Meta information'; + return $this->getString('shop_results.fields.meta_information_label', 'Meta information'); } /** @@ -563,6 +576,6 @@ final class PromptBuilderConfig public function getTechnicalProductModelPattern(): string { - return '/\b[\p{L}]{2,}\s?\d{2,5}\b/u'; + return $this->getString('technical_product_model_pattern', '/\b[\p{L}]{2,}\s?\d{2,5}\b/u'); } } \ No newline at end of file