p59 + p60

This commit is contained in:
team 1
2026-05-07 07:52:52 +02:00
parent 56646a0c3b
commit 87c2134e6c
20 changed files with 808 additions and 1256 deletions

View File

@@ -75,7 +75,8 @@ final class AgentRunnerConfig
*/
public function getCommercialTableFollowUpHistoryAnchorPatterns(): array
{
return $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.history_anchor_patterns');
return $this->genreStringList('context_resolution.commercial_table_follow_up.history_anchor_patterns')
?: $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.history_anchor_patterns');
}
/**
@@ -102,17 +103,20 @@ final class AgentRunnerConfig
*/
public function getCommercialTableFollowUpIndicatorMarkerPatterns(): array
{
return $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.indicator_marker_patterns');
return $this->genreStringList('context_resolution.commercial_table_follow_up.indicator_marker_patterns')
?: $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.indicator_marker_patterns');
}
public function getCommercialTableFollowUpQueryTemplateWithModel(): string
{
return $this->getRequiredString('follow_up_context.commercial_table_follow_up.query_template_with_model');
return $this->genreString('context_resolution.commercial_table_follow_up.query_template_with_model')
?: $this->getRequiredString('follow_up_context.commercial_table_follow_up.query_template_with_model');
}
public function getCommercialTableFollowUpQueryTemplateWithoutModel(): string
{
return $this->getRequiredString('follow_up_context.commercial_table_follow_up.query_template_without_model');
return $this->genreString('context_resolution.commercial_table_follow_up.query_template_without_model')
?: $this->getRequiredString('follow_up_context.commercial_table_follow_up.query_template_without_model');
}
public function getFollowUpHistoryQuestionPattern(): string

View File

@@ -116,7 +116,7 @@ final class CommerceIntentConfig
{
return $this->renderPatternTemplate('patterns.size_extraction_template', [
'size_pattern' => $this->getSizePattern(),
]);
], 'product_attributes.size_and_color_terms.patterns.size_extraction_template');
}
/** @return string[] */
@@ -148,21 +148,21 @@ final class CommerceIntentConfig
{
return $this->renderPatternTemplate('patterns.size_value_template', [
'size_pattern' => $this->getSizePattern(),
]);
], 'product_attributes.size_and_color_terms.patterns.size_value_template');
}
public function getSizeTokenValuePattern(): string
{
return $this->renderPatternTemplate('patterns.size_token_value_template', [
'size_token_pattern' => $this->getSizeTokenPattern(),
]);
], 'product_attributes.size_and_color_terms.patterns.size_token_value_template');
}
public function getColorValuePattern(): string
{
return $this->renderPatternTemplate('patterns.color_value_template', [
'color_pattern' => $this->getColorPattern(),
]);
], 'product_attributes.size_and_color_terms.patterns.color_value_template');
}
public function getSupportOrDiagnosticSignalLabel(): string
@@ -257,7 +257,8 @@ final class CommerceIntentConfig
public function getModelLikeProductPattern(): string
{
return $this->requiredString('patterns.model_like_product');
return $this->genreConfig?->getValueString('intent_and_routing.commerce_intent.model_like_product_pattern')
?: $this->requiredString('patterns.model_like_product');
}
public function getModelLikeProductSignalLabel(): string
@@ -327,9 +328,12 @@ final class CommerceIntentConfig
/**
* @param array<string, string> $replacements
*/
private function renderPatternTemplate(string $key, array $replacements): string
private function renderPatternTemplate(string $key, array $replacements, ?string $genrePath = null): string
{
$template = $this->requiredString($key);
$template = $genrePath !== null ? ($this->genreConfig?->getValueString($genrePath) ?? '') : '';
if ($template === '') {
$template = $this->requiredString($key);
}
$replace = [];
foreach ($replacements as $placeholder => $value) {
$replace['{' . $placeholder . '}'] = $value;

View File

@@ -32,6 +32,21 @@ final class DomainVocabularyConfig
'search_repair.direct_product_type_terms' => 'product_attributes.direct_attribute_cleanup.product_type_terms',
'search_repair.direct_product_attribute_stop_terms' => 'product_attributes.direct_attribute_cleanup.stop_terms',
'search_repair.requested_accessory_code_terms' => 'product_roles.requested_accessory_code_terms.terms',
'search_repair.generic_candidate_tokens' => 'search_repair.candidate_terms.generic_candidate_tokens',
'search_repair.accessory_candidate_terms' => 'search_repair.candidate_terms.accessory_candidate_terms',
'search_repair.accessory_or_bundle_terms' => 'search_repair.candidate_terms.accessory_or_bundle_terms',
'search_repair.specificity_boost_terms' => 'search_repair.candidate_terms.specificity_boost_terms',
'shop.semantic_search_tokens' => 'shop_query_runtime.semantic_shop_search_tokens.terms',
'retrieval.generic_product_tokens' => 'retrieval_and_language.retrieval_vocabulary_views.generic_product_tokens',
'retrieval.important_short_model_tokens' => 'retrieval_and_language.retrieval_vocabulary_views.important_short_model_tokens',
'retrieval.family_descriptor_tokens' => 'retrieval_and_language.retrieval_vocabulary_views.family_descriptor_tokens',
'retrieval.looks_like_reagent_tokens' => 'retrieval_and_language.retrieval_vocabulary_views.looks_like_reagent_tokens',
'retrieval.looks_like_safety_docs' => 'retrieval_and_language.retrieval_vocabulary_views.looks_like_safety_docs',
'retrieval.looks_like_reagent_words' => 'retrieval_and_language.retrieval_vocabulary_views.looks_like_reagent_words',
'retrieval.looks_like_document_words' => 'retrieval_and_language.retrieval_vocabulary_views.looks_like_document_words',
'retrieval.looks_like_safety_words' => 'retrieval_and_language.retrieval_vocabulary_views.looks_like_safety_words',
'retrieval.looks_like_device_words' => 'retrieval_and_language.retrieval_vocabulary_views.looks_like_device_words',
'prompt.technical_product_keywords' => 'result_identity_and_answer_policy.prompt_keyword_views.technical_product_keywords',
'agent.rag_evidence_guard.accessory_lookup_guard_terms' => 'result_identity_and_answer_policy.measurement_evidence_guard_terms.accessory_lookup_guard_terms',
'agent.rag_evidence_guard.accessory_lookup_passthrough_terms' => 'result_identity_and_answer_policy.measurement_evidence_guard_terms.accessory_lookup_passthrough_terms',
'agent.rag_evidence_guard.generic_positive_context_terms' => 'result_identity_and_answer_policy.measurement_evidence_guard_terms.generic_positive_context_terms',
@@ -41,6 +56,13 @@ final class DomainVocabularyConfig
private const MAP_GENRE_VALUE_PATHS = [
'shop.accessory_focus_variants' => 'brands_and_canonical_terms.accessory_focus_variants.map',
'agent.rag_evidence_guard.synonyms' => 'brands_and_canonical_terms.rag_evidence_synonyms.map',
'prompt.measurement_evidence_guard.request_terms' => 'result_identity_and_answer_policy.measurement_evidence_maps.request_terms',
'prompt.measurement_evidence_guard.positive_terms' => 'result_identity_and_answer_policy.measurement_evidence_maps.positive_terms',
'prompt.measurement_evidence_guard.non_equivalent_terms' => 'result_identity_and_answer_policy.measurement_evidence_maps.non_equivalent_terms',
];
private const VIEW_GENRE_INCLUDE_CLASS_PATHS = [
'search_repair.model_candidate_exclude_terms' => 'search_repair.candidate_terms.model_candidate_exclude_terms',
];
public function __construct(
@@ -181,11 +203,26 @@ final class DomainVocabularyConfig
/** @return string[] */
private function genreStringListForView(string $path): array
{
if ($this->genreConfig === null || !isset(self::VIEW_GENRE_VALUE_PATHS[$path])) {
if ($this->genreConfig === null) {
return [];
}
return $this->genreConfig->getValueStringList(self::VIEW_GENRE_VALUE_PATHS[$path]);
if (isset(self::VIEW_GENRE_VALUE_PATHS[$path])) {
return $this->genreConfig->getValueStringList(self::VIEW_GENRE_VALUE_PATHS[$path]);
}
if (!isset(self::VIEW_GENRE_INCLUDE_CLASS_PATHS[$path])) {
return [];
}
$terms = [];
foreach ($this->genreConfig->getValueStringList(self::VIEW_GENRE_INCLUDE_CLASS_PATHS[$path]) as $className) {
foreach ($this->domainClass($className) as $term) {
$terms[] = $term;
}
}
return $this->uniqueStringList($terms);
}
/** @return array<string, string[]> */

View File

@@ -9,8 +9,10 @@ final class GovernanceConfig
/**
* @param array<string, mixed> $config
*/
public function __construct(private readonly array $config = [])
{
public function __construct(
private readonly array $config = [],
private readonly ?GenreConfig $genreConfig = null,
) {
}
/** @return array<string, mixed> */
@@ -22,49 +24,57 @@ final class GovernanceConfig
/** @return string[] */
public function getRegressionProtectedShortModelTokens(): array
{
return $this->requiredStringList('regression_baseline.protected_short_model_tokens');
return $this->genreStringList('governance_and_regression.regression_baseline.protected_short_model_tokens')
?: $this->requiredStringList('regression_baseline.protected_short_model_tokens');
}
/** @return string[] */
public function getRegressionProtectedMeasurementValues(): array
{
return $this->requiredStringList('regression_baseline.protected_measurement_values');
return $this->genreStringList('governance_and_regression.regression_baseline.protected_measurement_values')
?: $this->requiredStringList('regression_baseline.protected_measurement_values');
}
/** @return string[] */
public function getRegressionProtectedTechnicalPromptKeywords(): array
{
return $this->requiredStringList('regression_baseline.protected_technical_prompt_keywords');
return $this->genreStringList('governance_and_regression.regression_baseline.protected_technical_prompt_keywords')
?: $this->requiredStringList('regression_baseline.protected_technical_prompt_keywords');
}
/** @return string[] */
public function getRegressionTechnicalPriorityRequiredMarkers(): array
{
return $this->requiredStringList('regression_baseline.technical_priority_required_markers');
return $this->genreStringList('governance_and_regression.regression_baseline.technical_priority_required_markers')
?: $this->requiredStringList('regression_baseline.technical_priority_required_markers');
}
/** @return string[] */
public function getRegressionProtectedAccessoryPromptKeywords(): array
{
return $this->requiredStringList('regression_baseline.protected_accessory_prompt_keywords');
return $this->genreStringList('governance_and_regression.regression_baseline.protected_accessory_prompt_keywords')
?: $this->requiredStringList('regression_baseline.protected_accessory_prompt_keywords');
}
/** @return string[] */
public function getRegressionProtectedSearchRepairSpecificityTerms(): array
{
return $this->requiredStringList('regression_baseline.protected_search_repair_specificity_terms');
return $this->genreStringList('governance_and_regression.regression_baseline.protected_search_repair_specificity_terms')
?: $this->requiredStringList('regression_baseline.protected_search_repair_specificity_terms');
}
/** @return string[] */
public function getRegressionProtectedRetrievalReagentWords(): array
{
return $this->requiredStringList('regression_baseline.protected_retrieval_reagent_words');
return $this->genreStringList('governance_and_regression.regression_baseline.protected_retrieval_reagent_words')
?: $this->requiredStringList('regression_baseline.protected_retrieval_reagent_words');
}
/** @return array<string, string[]> */
public function getRegressionProtectedRetrievalDeviceWordGroups(): array
{
$value = $this->requiredValue('regression_baseline.protected_retrieval_device_word_groups');
$value = $this->genreArray('governance_and_regression.regression_baseline.protected_retrieval_device_word_groups')
?: $this->requiredValue('regression_baseline.protected_retrieval_device_word_groups');
if (!is_array($value)) {
throw $this->invalid('regression_baseline.protected_retrieval_device_word_groups', 'must be a map of string lists');
}
@@ -99,31 +109,36 @@ final class GovernanceConfig
public function getRegressionShopPromptOriginalQuery(): string
{
return $this->requiredString('regression_baseline.shop_prompt_regression_original_query');
return $this->genreString('governance_and_regression.regression_baseline.shop_prompt_regression_original_query')
?: $this->requiredString('regression_baseline.shop_prompt_regression_original_query');
}
/** @return string[] */
public function getRegressionShopPromptRequiredOutputInstructionMarkers(): array
{
return $this->requiredStringList('regression_baseline.shop_prompt_required_output_instruction_markers');
return $this->genreStringList('governance_and_regression.regression_baseline.shop_prompt_required_output_instruction_markers')
?: $this->requiredStringList('regression_baseline.shop_prompt_required_output_instruction_markers');
}
/** @return string[] */
public function getRegressionShopQueryMetaGuardTerms(): array
{
return $this->requiredStringList('regression_baseline.shop_query_meta_guard_terms');
return $this->genreStringList('governance_and_regression.regression_baseline.shop_query_meta_guard_terms')
?: $this->requiredStringList('regression_baseline.shop_query_meta_guard_terms');
}
/** @return string[] */
public function getRegressionShopQueryContextFallbackFilterTerms(): array
{
return $this->requiredStringList('regression_baseline.shop_query_context_fallback_filter_terms');
return $this->genreStringList('governance_and_regression.regression_baseline.shop_query_context_fallback_filter_terms')
?: $this->requiredStringList('regression_baseline.shop_query_context_fallback_filter_terms');
}
/** @return string[] */
public function getRegressionShopQueryCurrentInputPreservationTerms(): array
{
return $this->requiredStringList('regression_baseline.shop_query_current_input_preservation_terms');
return $this->genreStringList('governance_and_regression.regression_baseline.shop_query_current_input_preservation_terms')
?: $this->requiredStringList('regression_baseline.shop_query_current_input_preservation_terms');
}
/** @return string[] */
@@ -138,7 +153,8 @@ final class GovernanceConfig
/** @return string[] */
public function getLanguageProtectedStopwordTerms(): array
{
return $this->requiredStringList('language.protected_stopword_terms');
return $this->genreStringList('retrieval_and_language.protected_terms.terms')
?: $this->requiredStringList('language.protected_stopword_terms');
}
/** @return string[] */
@@ -241,7 +257,8 @@ final class GovernanceConfig
/** @return string[] */
public function getCorePatternAuditDomainMarkerTerms(): array
{
return $this->requiredStringList('core_pattern_audit.domain_marker_terms');
return $this->genreStringList('governance_and_regression.core_pattern_audit.domain_marker_terms')
?: $this->requiredStringList('core_pattern_audit.domain_marker_terms');
}
/** @return array<int, array{path:string, pattern:string, reason:string}> */
@@ -288,6 +305,23 @@ final class GovernanceConfig
return $this->requiredInt('core_pattern_audit.max_snippet_length', 20);
}
/** @return string[] */
private function genreStringList(string $path): array
{
return $this->genreConfig?->getValueStringList($path) ?? [];
}
private function genreString(string $path): string
{
return $this->genreConfig?->getValueString($path) ?? '';
}
/** @return array<int|string, mixed> */
private function genreArray(string $path): array
{
return $this->genreConfig?->getValueArray($path) ?? [];
}
private function requiredInt(string $path, int $min = PHP_INT_MIN): int
{
$value = $this->requiredValue($path);

View File

@@ -18,8 +18,10 @@ final class LanguageCleanupConfig
/**
* @param array<string, mixed> $config
*/
public function __construct(private readonly array $config)
{
public function __construct(
private readonly array $config,
private readonly ?GenreConfig $genreConfig = null,
) {
}
/** @return string[] */
@@ -31,7 +33,8 @@ final class LanguageCleanupConfig
/** @return string[] */
public function getProtectedTerms(): array
{
return $this->requiredTopLevelStringList('protected_terms');
return $this->genreConfig?->getValueStringList('retrieval_and_language.protected_terms.terms')
?: $this->requiredTopLevelStringList('protected_terms');
}
public function isProtectedTerm(string $term): bool

View File

@@ -14,6 +14,7 @@ final class NdjsonHybridRetrieverConfig
public function __construct(
private array $config = [],
private ?DomainVocabularyConfig $vocabulary = null,
private ?GenreConfig $genreConfig = null,
) {
}
@@ -146,7 +147,8 @@ final class NdjsonHybridRetrieverConfig
/** @return array<string, string[]> */
public function exactSelectionTokenVariantPrefixes(): array
{
return $this->requiredStringListMap('exact_selection_token_variant_prefixes');
return $this->genreStringListMap('retrieval_and_language.exact_selection.token_variant_prefixes')
?: $this->requiredStringListMap('exact_selection_token_variant_prefixes');
}
/** @return string[] */
@@ -158,43 +160,50 @@ final class NdjsonHybridRetrieverConfig
/** @return string[] */
public function exactSelectionIndicatorQuestionTokens(): array
{
return $this->requiredStringList('exact_selection_indicator_question_tokens');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_question_tokens')
?: $this->requiredStringList('exact_selection_indicator_question_tokens');
}
/** @return string[] */
public function exactSelectionIndicatorQuestionPhrases(): array
{
return $this->requiredStringList('exact_selection_indicator_question_phrases');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_question_phrases')
?: $this->requiredStringList('exact_selection_indicator_question_phrases');
}
/** @return string[] */
public function exactSelectionIndicatorTableHeadingPatterns(): array
{
return $this->requiredStringList('exact_selection_indicator_table_heading_patterns');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_table_heading_patterns')
?: $this->requiredStringList('exact_selection_indicator_table_heading_patterns');
}
/** @return string[] */
public function exactSelectionIndicatorTableHeaderPatterns(): array
{
return $this->requiredStringList('exact_selection_indicator_table_header_patterns');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_table_header_patterns')
?: $this->requiredStringList('exact_selection_indicator_table_header_patterns');
}
/** @return string[] */
public function exactSelectionIndicatorTableRowPatterns(): array
{
return $this->requiredStringList('exact_selection_indicator_table_row_patterns');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_table_row_patterns')
?: $this->requiredStringList('exact_selection_indicator_table_row_patterns');
}
/** @return string[] */
public function exactSelectionIndicatorTableRequiredPrimaryTerms(): array
{
return $this->requiredStringList('exact_selection_indicator_table_required_primary_terms');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_table_required_primary_terms')
?: $this->requiredStringList('exact_selection_indicator_table_required_primary_terms');
}
/** @return string[] */
public function exactSelectionIndicatorTableRequiredContextTerms(): array
{
return $this->requiredStringList('exact_selection_indicator_table_required_context_terms');
return $this->genreStringList('retrieval_and_language.exact_selection.indicator_table_required_context_terms')
?: $this->requiredStringList('exact_selection_indicator_table_required_context_terms');
}
/** @return string[] */
@@ -370,6 +379,23 @@ final class NdjsonHybridRetrieverConfig
];
}
/** @return string[] */
private function genreStringList(string $path): array
{
return $this->genreConfig?->getValueStringList($path) ?? [];
}
/** @return array<string, string[]> */
private function genreStringListMap(string $path): array
{
$value = $this->genreConfig?->getValueArray($path) ?? [];
if ($value === []) {
return [];
}
return $this->normalizeStringListMap($value);
}
private function requiredInt(string $key, int $min = PHP_INT_MIN, ?int $max = null): int
{
$value = $this->requiredValue($key);
@@ -458,6 +484,35 @@ final class NdjsonHybridRetrieverConfig
return $out;
}
/** @return array<string, string[]> */
private function normalizeStringListMap(array $value): array
{
$out = [];
foreach ($value as $mapKey => $items) {
if (!is_string($mapKey) || trim($mapKey) === '' || !is_array($items)) {
continue;
}
$cleanItems = [];
foreach ($items as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item !== '' && !in_array($item, $cleanItems, true)) {
$cleanItems[] = $item;
}
}
if ($cleanItems !== []) {
$out[trim($mapKey)] = $cleanItems;
}
}
return $out;
}
/**
* @return array<string, string[]>
*/

View File

@@ -12,6 +12,7 @@ final class PromptBuilderConfig
public function __construct(
private readonly array $config = [],
private readonly ?DomainVocabularyConfig $vocabulary = null,
private readonly ?GenreConfig $genreConfig = null,
) {
}
@@ -160,6 +161,12 @@ final class PromptBuilderConfig
return $out;
}
/** @return string[] */
private function getGenreStringList(string $path): array
{
return $this->genreConfig?->getValueStringList($path) ?? [];
}
/**
* @return string[]
*/
@@ -392,7 +399,8 @@ final class PromptBuilderConfig
*/
public function getOutputPriorityTechnicalRules(): array
{
return $this->getRequiredStringList('output_priority.technical_rules');
return $this->getGenreStringList('result_identity_and_answer_policy.prompt_rules.output_priority_technical')
?: $this->getRequiredStringList('output_priority.technical_rules');
}
public function getFallbackEscalationSectionLabel(): string
@@ -468,7 +476,8 @@ final class PromptBuilderConfig
*/
public function getResponseFormatTechnicalRules(): array
{
return $this->getRequiredStringList('response_format.technical_rules');
return $this->getGenreStringList('result_identity_and_answer_policy.prompt_rules.response_format_technical')
?: $this->getRequiredStringList('response_format.technical_rules');
}
/**
@@ -476,7 +485,8 @@ final class PromptBuilderConfig
*/
public function getResponseFormatAccessoryRules(): array
{
return $this->getRequiredStringList('response_format.accessory_rules');
return $this->getGenreStringList('result_identity_and_answer_policy.prompt_rules.response_format_accessory')
?: $this->getRequiredStringList('response_format.accessory_rules');
}
public function getLanguageRulesSectionLabel(): string
@@ -510,7 +520,8 @@ final class PromptBuilderConfig
*/
public function getFactGroundingWithShopRules(): array
{
return $this->getRequiredStringList('fact_grounding.with_shop_rules');
return $this->getGenreStringList('result_identity_and_answer_policy.prompt_rules.fact_grounding_with_shop')
?: $this->getRequiredStringList('fact_grounding.with_shop_rules');
}
/**
@@ -526,7 +537,8 @@ final class PromptBuilderConfig
*/
public function getFactGroundingTechnicalRules(): array
{
return $this->getRequiredStringList('fact_grounding.technical_rules');
return $this->getGenreStringList('result_identity_and_answer_policy.prompt_rules.fact_grounding_technical')
?: $this->getRequiredStringList('fact_grounding.technical_rules');
}
public function getRetrievedKnowledgeSectionLabel(): string

View File

@@ -15,8 +15,10 @@ final class SalesIntentConfig
/**
* @param array<string, mixed> $config
*/
public function __construct(private readonly array $config)
{
public function __construct(
private readonly array $config,
private readonly ?GenreConfig $genreConfig = null,
) {
}
public function getDominanceDelta(): int
@@ -32,31 +34,42 @@ final class SalesIntentConfig
/** @return string[] */
public function getSalesSignals(): array
{
return $this->requiredStringList('sales_signals');
return $this->genreStringList('intent_and_routing.sales_intent.sales_signals')
?: $this->requiredStringList('sales_signals');
}
/** @return string[] */
public function getComparisonSignals(): array
{
return $this->requiredStringList('comparison_signals');
return $this->genreStringList('intent_and_routing.sales_intent.comparison_signals')
?: $this->requiredStringList('comparison_signals');
}
/** @return string[] */
public function getObjectionSignals(): array
{
return $this->requiredStringList('objection_signals');
return $this->genreStringList('intent_and_routing.sales_intent.objection_signals')
?: $this->requiredStringList('objection_signals');
}
/** @return string[] */
public function getImplementationSignals(): array
{
return $this->requiredStringList('implementation_signals');
return $this->genreStringList('intent_and_routing.sales_intent.implementation_signals')
?: $this->requiredStringList('implementation_signals');
}
/** @return string[] */
public function getRoiSignals(): array
{
return $this->requiredStringList('roi_signals');
return $this->genreStringList('intent_and_routing.sales_intent.roi_signals')
?: $this->requiredStringList('roi_signals');
}
/** @return string[] */
private function genreStringList(string $path): array
{
return $this->genreConfig?->getValueStringList($path) ?? [];
}
private function requiredNonNegativeInt(string $key): int

View File

@@ -22,6 +22,7 @@ final class SearchRepairConfig
private readonly int $minPrimaryResultsWithoutRepair,
private readonly array $config,
private readonly DomainVocabularyConfig $vocabulary,
private readonly ?GenreConfig $genreConfig = null,
) {
}
@@ -52,36 +53,44 @@ final class SearchRepairConfig
public function isDirectProductAttributeLookupRepairEnabled(): bool
{
return $this->requiredBool('direct_product_attribute_lookup.enabled');
return $this->genreBool('search_repair.direct_product_attribute_lookup.enabled')
?? $this->requiredBool('direct_product_attribute_lookup.enabled');
}
public function getDirectProductAttributeLookupMinTokens(): int
{
return $this->requiredPositiveInt('direct_product_attribute_lookup.min_query_tokens_after_cleanup');
$genreValue = $this->genreInt('search_repair.direct_product_attribute_lookup.min_query_tokens_after_cleanup');
return $genreValue !== null && $genreValue > 0
? $genreValue
: $this->requiredPositiveInt('direct_product_attribute_lookup.min_query_tokens_after_cleanup');
}
/** @return string[] */
public function getDirectProductAttributeLookupProductTypeTerms(): array
{
return $this->configOrVocabularyStringList(
'direct_product_attribute_lookup.product_type_terms',
'search_repair.direct_product_type_terms'
);
return $this->genreStringList('product_attributes.direct_attribute_cleanup.product_type_terms')
?: $this->configOrVocabularyStringList(
'direct_product_attribute_lookup.product_type_terms',
'search_repair.direct_product_type_terms'
);
}
/** @return string[] */
public function getDirectProductAttributeLookupStopTerms(): array
{
return $this->configOrVocabularyStringList(
'direct_product_attribute_lookup.stop_terms',
'search_repair.direct_product_attribute_stop_terms'
);
return $this->genreStringList('product_attributes.direct_attribute_cleanup.stop_terms')
?: $this->configOrVocabularyStringList(
'direct_product_attribute_lookup.stop_terms',
'search_repair.direct_product_attribute_stop_terms'
);
}
/** @return string[] */
public function getDirectProductAttributeLookupComparativeConstraintPatterns(): array
{
return $this->requiredStringList('direct_product_attribute_lookup.comparative_constraint_patterns');
return $this->genreStringList('product_attributes.direct_attribute_cleanup.comparative_constraint_patterns')
?: $this->requiredStringList('direct_product_attribute_lookup.comparative_constraint_patterns');
}
/** @return string[] */
@@ -116,7 +125,8 @@ final class SearchRepairConfig
/** @return string[] */
public function getSpecificModelCandidatePatterns(): array
{
return $this->requiredStringList('specific_model_candidate_patterns');
return $this->genreStringList('search_repair.candidate_patterns.specific_model_candidate_patterns')
?: $this->requiredStringList('specific_model_candidate_patterns');
}
/** @return string[] */
@@ -135,40 +145,46 @@ final class SearchRepairConfig
public function getModelCandidatePattern(): string
{
return $this->requiredString('patterns.model_candidate');
return $this->genreString('search_repair.candidate_patterns.patterns.model_candidate')
?: $this->requiredString('patterns.model_candidate');
}
public function getAccessoryCandidatePattern(): string
{
return $this->renderPatternTemplate(
'patterns.accessory_candidate_template',
['terms' => $this->patternAlternation($this->getAccessoryCandidateTerms())]
['terms' => $this->patternAlternation($this->getAccessoryCandidateTerms())],
'search_repair.candidate_patterns.patterns.accessory_candidate_template'
);
}
public function getRequestedAccessoryCodePattern(): string
{
return $this->requiredString('patterns.requested_accessory_code');
return $this->genreString('search_repair.candidate_patterns.patterns.requested_accessory_code')
?: $this->requiredString('patterns.requested_accessory_code');
}
public function getAccessoryOrBundlePattern(): string
{
return $this->renderPatternTemplate(
'patterns.accessory_or_bundle_template',
['terms' => $this->patternAlternation($this->getAccessoryOrBundleTerms())]
['terms' => $this->patternAlternation($this->getAccessoryOrBundleTerms())],
'search_repair.candidate_patterns.patterns.accessory_or_bundle_template'
);
}
public function getModelLikePattern(): string
{
return $this->requiredString('patterns.model_like');
return $this->genreString('search_repair.candidate_patterns.patterns.model_like')
?: $this->requiredString('patterns.model_like');
}
public function getSpecificityBoostPattern(): string
{
return $this->renderPatternTemplate(
'patterns.specificity_boost_template',
['terms' => $this->patternAlternation($this->getSpecificityBoostTerms())]
['terms' => $this->patternAlternation($this->getSpecificityBoostTerms())],
'search_repair.candidate_patterns.patterns.specificity_boost_template'
);
}
@@ -286,6 +302,27 @@ final class SearchRepairConfig
);
}
/** @return string[] */
private function genreStringList(string $path): array
{
return $this->genreConfig?->getValueStringList($path) ?? [];
}
private function genreString(string $path): string
{
return $this->genreConfig?->getValueString($path) ?? '';
}
private function genreBool(string $path): ?bool
{
return $this->genreConfig?->getValueBool($path);
}
private function genreInt(string $path): ?int
{
return $this->genreConfig?->getValueInt($path);
}
/** @return string[] */
private function configOrVocabularyStringList(string $configKey, string $vocabularyPath): array
{
@@ -305,9 +342,12 @@ final class SearchRepairConfig
}
/** @param array<string, string> $variables */
private function renderPatternTemplate(string $path, array $variables): string
private function renderPatternTemplate(string $path, array $variables, ?string $genrePath = null): string
{
$template = $this->requiredString($path);
$template = $genrePath !== null ? $this->genreString($genrePath) : '';
if ($template === '') {
$template = $this->requiredString($path);
}
foreach ($variables as $key => $value) {
$template = str_replace('{' . $key . '}', $value, $template);