p59 + p60
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[]> */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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[]>
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user