$config */ public function __construct( private readonly array $config = [], ) { } public function getCharsPerToken(): int { return $this->getRequiredInt('budget.chars_per_token'); } public function getHistoryPaddingChars(): int { return $this->getRequiredInt('budget.history_padding_chars'); } public function getOutputReserveRatio(): float { return $this->getRequiredFloat('budget.output_reserve_ratio'); } public function getOutputReserveMinTokens(): int { return $this->getRequiredInt('budget.output_reserve_min_tokens'); } public function getOutputReserveMaxTokens(): int { return $this->getRequiredInt('budget.output_reserve_max_tokens'); } public function getSafetyReserveRatio(): float { return $this->getRequiredFloat('budget.safety_reserve_ratio'); } public function getSafetyReserveMinTokens(): int { return $this->getRequiredInt('budget.safety_reserve_min_tokens'); } public function getSafetyReserveMaxTokens(): int { return $this->getRequiredInt('budget.safety_reserve_max_tokens'); } public function getMinPromptBudgetTokens(): int { return $this->getRequiredInt('budget.min_prompt_budget_tokens'); } public function getMaxShopResultsInPrompt(): int { return $this->getRequiredInt('shop_results.max_results_in_prompt'); } public function getDetailedShopResultsMaxCount(): int { return $this->getRequiredInt('shop_results.detailed_max_count'); } public function getTechnicalProductKeywordMatchThreshold(): int { return $this->getRequiredInt('technical_product_keyword_match_threshold'); } private function getRequiredInt(string $path): int { $value = $this->getRequiredValue($path); if (!is_numeric($value)) { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be numeric.', $path)); } return (int) $value; } private function getRequiredFloat(string $path): float { $value = $this->getRequiredValue($path); if (!is_numeric($value)) { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be numeric.', $path)); } return (float) $value; } private function getRequiredString(string $path): string { $value = $this->getRequiredValue($path); if (!is_scalar($value)) { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be a scalar string.', $path)); } $value = (string) $value; if (trim($value) === '') { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must not be empty.', $path)); } return $value; } /** * @return string[] */ private function getRequiredStringList(string $path): array { $value = $this->getRequiredValue($path); if (!is_array($value)) { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be a string list.', $path)); } $out = []; foreach ($value as $item) { if (!is_scalar($item)) { continue; } $item = trim((string) $item); if ($item === '' || in_array($item, $out, true)) { continue; } $out[] = $item; } if ($out === []) { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must contain at least one string.', $path)); } return $out; } /** * @return string[] */ private function getOptionalStringList(string $path): array { $value = $this->getOptionalValue($path); if ($value === null) { return []; } if (!is_array($value)) { throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be a string list.', $path)); } $out = []; foreach ($value as $item) { if (!is_scalar($item)) { continue; } $item = trim((string) $item); if ($item === '' || in_array($item, $out, true)) { continue; } $out[] = $item; } return $out; } private function getOptionalValue(string $path): mixed { $current = $this->config; foreach (explode('.', $path) as $segment) { if (!is_array($current) || !array_key_exists($segment, $current)) { return null; } $current = $current[$segment]; } return $current; } private function getRequiredValue(string $path): mixed { $current = $this->config; foreach (explode('.', $path) as $segment) { if (!is_array($current) || !array_key_exists($segment, $current)) { throw new \InvalidArgumentException(sprintf('Missing required RetrieX prompt config path "%s".', $path)); } $current = $current[$segment]; } return $current; } public function getSystemSectionLabel(): string { return $this->getRequiredString('sections.system_label'); } public function getUserQuestionSectionLabel(): string { return $this->getRequiredString('sections.user_question_label'); } public function getConversationContextSectionLabel(): string { return $this->getRequiredString('sections.conversation_context_label'); } /** * @return string[] */ public function getConversationContextIntroLines(): array { return $this->getRequiredStringList('conversation_context.intro_lines'); } public function getShopSearchQuerySectionLabel(): string { return $this->getRequiredString('sections.shop_search_query_label'); } public function getShopSearchQuerySourceLine(): string { return $this->getRequiredString('shop_search.source_line'); } /** * @return string[] */ public function getLiveShopResultsHeaderLines(): array { return $this->getRequiredStringList('shop_results.header_lines'); } public function getLiveShopResultsOverflowNoticeTemplate(): string { return $this->getRequiredString('shop_results.overflow_notice_template'); } public function getShopRecordHeaderTemplate(): string { return $this->getRequiredString('shop_results.record_header_template'); } public function getShopExactProductNameLabel(): string { return $this->getRequiredString('shop_results.exact_product_name_label'); } /** * @return string[] */ public function getShopAtomicRecordNoteLines(): array { return $this->getRequiredStringList('shop_results.atomic_record_note_lines'); } public function getOutputPrioritySectionLabel(): string { return $this->getRequiredString('sections.output_priority_label'); } /** * @return string[] */ public function getOutputPriorityRules(): array { return $this->getRequiredStringList('output_priority.rules'); } /** * @return string[] */ public function getOutputPriorityTechnicalRules(): array { return $this->getRequiredStringList('output_priority.technical_rules'); } public function getFallbackEscalationSectionLabel(): string { return $this->getRequiredString('sections.fallback_escalation_label'); } public function getFallbackEscalationStateLineTemplate(): string { return $this->getRequiredString('fallback_escalation.state_line_template'); } /** * @return string[] */ public function getFallbackEscalationBaseRules(): array { return $this->getRequiredStringList('fallback_escalation.base_rules'); } /** * @return string[] */ public function getFallbackEscalationStateRules(string $state): array { return $this->getOptionalStringList('fallback_escalation.states.' . $state); } /** * @return string[] */ public function getFallbackEscalationWithoutShopCheckRules(): array { return $this->getRequiredStringList('fallback_escalation.without_shop_check_rules'); } public function getResponseFormatSectionLabel(): string { return $this->getRequiredString('sections.response_format_label'); } /** * @return string[] */ public function getResponseFormatBaseRules(): array { return $this->getRequiredStringList('response_format.base_rules'); } /** * @return string[] */ public function getResponseFormatWithShopRules(): array { return $this->getRequiredStringList('response_format.with_shop_rules'); } /** * @return string[] */ public function getResponseFormatWithoutShopRules(): array { return $this->getRequiredStringList('response_format.without_shop_rules'); } /** * @return string[] */ public function getResponseFormatTechnicalRules(): array { return $this->getRequiredStringList('response_format.technical_rules'); } /** * @return string[] */ public function getResponseFormatAccessoryRules(): array { return $this->getRequiredStringList('response_format.accessory_rules'); } public function getLanguageRulesSectionLabel(): string { return $this->getRequiredString('sections.language_rules_label'); } /** * @return string[] */ public function getLanguageRules(): array { return $this->getRequiredStringList('language.rules'); } public function getFactGroundingRulesSectionLabel(): string { return $this->getRequiredString('sections.fact_grounding_rules_label'); } /** * @return string[] */ public function getFactGroundingBaseRules(): array { return $this->getRequiredStringList('fact_grounding.base_rules'); } /** * @return string[] */ public function getFactGroundingWithShopRules(): array { return $this->getRequiredStringList('fact_grounding.with_shop_rules'); } /** * @return string[] */ public function getFactGroundingWithoutShopRules(): array { return $this->getRequiredStringList('fact_grounding.without_shop_rules'); } /** * @return string[] */ public function getFactGroundingTechnicalRules(): array { return $this->getRequiredStringList('fact_grounding.technical_rules'); } public function getRetrievedKnowledgeSectionLabel(): string { return $this->getRequiredString('sections.retrieved_knowledge_label'); } public function getRetrievedKnowledgeSourceLine(): string { return $this->getRequiredString('retrieved_knowledge.source_line'); } public function getUrlContentSectionLabel(): string { return $this->getRequiredString('sections.url_content_label'); } public function getUrlContentSourceLine(): string { return $this->getRequiredString('url_content.source_line'); } public function getShopProductNumberLabel(): string { return $this->getRequiredString('shop_results.fields.product_number_label'); } public function getShopManufacturerLabel(): string { return $this->getRequiredString('shop_results.fields.manufacturer_label'); } public function getShopPriceLabel(): string { return $this->getRequiredString('shop_results.fields.price_label'); } public function getShopAvailabilityLabel(): string { return $this->getRequiredString('shop_results.fields.availability_label'); } public function getShopAvailabilityYesLabel(): string { return $this->getRequiredString('shop_results.fields.availability_yes_label'); } public function getShopAvailabilityNoLabel(): string { return $this->getRequiredString('shop_results.fields.availability_no_label'); } public function getShopHighlightPrefix(): string { return $this->getRequiredString('shop_results.fields.highlight_prefix'); } public function getShopUrlLabel(): string { return $this->getRequiredString('shop_results.fields.url_label'); } public function getShopProductImageLabel(): string { return $this->getRequiredString('shop_results.fields.product_image_label'); } public function getShopDescriptionLabel(): string { return $this->getRequiredString('shop_results.fields.description_label'); } public function getShopMetaInformationLabel(): string { return $this->getRequiredString('shop_results.fields.meta_information_label'); } public function getShopRequestedRoleLabel(): string { return $this->getRequiredString('shop_results.fields.requested_role_label'); } public function getShopInferredRoleLabel(): string { return $this->getRequiredString('shop_results.fields.inferred_role_label'); } public function getShopRoleCompatibilityLabel(): string { return $this->getRequiredString('shop_results.fields.role_compatibility_label'); } public function getShopRoleIncompatibleCommercialSuppressionNote(): string { return $this->getRequiredString('shop_results.fields.role_incompatible_commercial_suppression_note'); } /** * @return string[] */ public function getMainDeviceRequestRoleKeywords(): array { return $this->getRequiredStringList('role_guard.main_device_request_keywords'); } /** * @return string[] */ public function getMainDeviceProductRoleKeywords(): array { return $this->getRequiredStringList('role_guard.main_device_product_keywords'); } /** * @return string[] */ public function getAccessoryProductRoleKeywords(): array { return $this->getRequiredStringList('role_guard.accessory_product_keywords'); } /** * @return string[] */ public function getDirectMainDeviceRequestPatterns(): array { return $this->getRequiredStringList('role_guard.direct_main_device_request_patterns'); } /** * @return string[] */ public function getTechnicalProductKeywords(): array { return $this->getRequiredStringList('technical_product_keywords'); } /** * @return string[] */ public function getAccessoryRequestKeywords(): array { return $this->getRequiredStringList('accessory_request_keywords'); } public function getMeasurementEvidenceSectionLabel(): string { return $this->getRequiredString('sections.measurement_evidence_label'); } /** * @return string[] */ public function getMeasurementEvidenceIntroRules(): array { return $this->getRequiredStringList('measurement_evidence_guard.intro_rules'); } /** * @return string[] */ public function getMeasurementEvidenceProductSpecificRules(): array { return $this->getRequiredStringList('measurement_evidence_guard.product_specific_rules'); } /** * @return string[] */ public function getMeasurementEvidenceGenericRequestPatterns(): array { return $this->getRequiredStringList('measurement_evidence_guard.generic_request_patterns'); } /** * @return string[] */ public function getMeasurementEvidenceGenericPositiveContextTerms(): array { return $this->getRequiredStringList('measurement_evidence_guard.generic_positive_context_terms'); } /** * @return string[] */ public function getMeasurementEvidenceGenericNegativeContextTerms(): array { return $this->getRequiredStringList('measurement_evidence_guard.generic_negative_context_terms'); } public function getMeasurementEvidenceGenericSafeNoEvidenceAnswerTemplate(): string { return $this->getRequiredString('measurement_evidence_guard.generic_safe_no_evidence_answer_template_de'); } public function getMeasurementEvidenceGenericSafeNoAccessoryEvidenceAnswerTemplate(): string { return $this->getRequiredString('measurement_evidence_guard.generic_safe_no_accessory_evidence_answer_template_de'); } /** * @return array> */ public function getMeasurementEvidenceParameters(): array { $value = $this->getRequiredValue('measurement_evidence_guard.parameters'); if (!is_array($value)) { throw new \InvalidArgumentException('RetrieX prompt config value "measurement_evidence_guard.parameters" must be a list of parameter definitions.'); } $out = []; foreach ($value as $item) { if (!is_array($item)) { continue; } $id = isset($item['id']) && is_scalar($item['id']) ? trim((string) $item['id']) : ''; $label = isset($item['label']) && is_scalar($item['label']) ? trim((string) $item['label']) : ''; if ($id === '' || $label === '') { continue; } $out[] = [ 'id' => $id, 'label' => $label, 'request_terms' => $this->normalizeMixedStringList($item['request_terms'] ?? []), 'positive_terms' => $this->normalizeMixedStringList($item['positive_terms'] ?? []), 'positive_context_terms' => $this->normalizeMixedStringList($item['positive_context_terms'] ?? []), 'negative_context_terms' => $this->normalizeMixedStringList($item['negative_context_terms'] ?? []), 'non_equivalent_terms' => $this->normalizeMixedStringList($item['non_equivalent_terms'] ?? []), 'safe_no_evidence_answer_de' => isset($item['safe_no_evidence_answer_de']) && is_scalar($item['safe_no_evidence_answer_de']) ? trim((string) $item['safe_no_evidence_answer_de']) : '', 'safe_no_accessory_evidence_answer_de' => isset($item['safe_no_accessory_evidence_answer_de']) && is_scalar($item['safe_no_accessory_evidence_answer_de']) ? trim((string) $item['safe_no_accessory_evidence_answer_de']) : '', ]; } if ($out === []) { throw new \InvalidArgumentException('RetrieX prompt config value "measurement_evidence_guard.parameters" must contain at least one valid parameter definition.'); } return $out; } /** * @return string[] */ private function normalizeMixedStringList(mixed $value): array { if (!is_array($value)) { return []; } $out = []; foreach ($value as $item) { if (!is_scalar($item)) { continue; } $item = trim((string) $item); if ($item !== '' && !in_array($item, $out, true)) { $out[] = $item; } } return $out; } public function getTechnicalProductModelPattern(): string { return $this->getRequiredString('technical_product_model_pattern'); } }