Files
MtoRagSystem/src/Config/AgentRunnerConfig.php
2026-05-06 11:02:48 +02:00

1456 lines
43 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Config;
final class AgentRunnerConfig
{
/**
* @param array<string, mixed> $config
*/
public function __construct(
private readonly array $config = [],
private readonly ?DomainVocabularyConfig $vocabulary = null,
) {
}
public function getCommerceHistoryBudgetChars(): int
{
return $this->getRequiredInt('commerce_history_budget_chars');
}
public function getProductSearchKnowledgeChunkLimit(): int
{
return $this->getRequiredInt('product_search_knowledge_chunk_limit');
}
public function getAdvisoryProductSearchKnowledgeChunkLimit(): int
{
return $this->getRequiredInt('advisory_product_search_knowledge_chunk_limit');
}
public function getOptimizedShopQueryPrefixPattern(): string
{
return $this->getRequiredString('optimized_shop_query_prefix_pattern');
}
public function getOptimizedShopQueryTrimCharacters(): string
{
return $this->getRequiredString('optimized_shop_query_trim_characters');
}
/**
* @return string[]
*/
public function getFollowUpStrongReferencePatterns(): array
{
return $this->getRequiredStringList('follow_up_context.strong_reference_patterns');
}
/**
* @return string[]
*/
public function getFollowUpExplicitCommercialSignalTerms(): array
{
return $this->getRequiredStringList('follow_up_context.explicit_commercial_signal_terms');
}
public function isCommercialTableFollowUpEnabled(): bool
{
return $this->getRequiredBool('follow_up_context.commercial_table_follow_up.enabled');
}
/**
* @return string[]
*/
public function getCommercialTableFollowUpPromptPatterns(): array
{
return $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.prompt_patterns');
}
/**
* @return string[]
*/
public function getCommercialTableFollowUpHistoryAnchorPatterns(): array
{
return $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.history_anchor_patterns');
}
/**
* @return string[]
*/
public function getCommercialTableFollowUpTableTerms(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'follow_up_context.commercial_table_follow_up.table_terms',
'follow_up_context.commercial_table_follow_up.vocabulary_views.table_terms'
);
}
/**
* @return string[]
*/
public function getCommercialTableFollowUpCommercialTerms(): array
{
return $this->getRequiredStringList('follow_up_context.commercial_table_follow_up.commercial_terms');
}
/**
* @return string[]
*/
public function getCommercialTableFollowUpIndicatorMarkerPatterns(): array
{
return $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');
}
public function getCommercialTableFollowUpQueryTemplateWithoutModel(): string
{
return $this->getRequiredString('follow_up_context.commercial_table_follow_up.query_template_without_model');
}
public function getFollowUpHistoryQuestionPattern(): string
{
return $this->getRequiredString('follow_up_context.history_question_pattern');
}
public function getFollowUpHistoryTurnSplitPattern(): string
{
return $this->getRequiredString('follow_up_context.history_turn_split_pattern');
}
public function getFollowUpHistoryQuestionStripPattern(): string
{
return $this->getRequiredString('follow_up_context.history_question_strip_pattern');
}
public function getFollowUpReferenceAnchorProductModelPattern(): string
{
return $this->getRequiredString('follow_up_context.reference_anchor.product_model_pattern');
}
public function getFollowUpReferenceAnchorMeasurementValuePattern(): string
{
return $this->getRequiredString('follow_up_context.reference_anchor.measurement_value_pattern');
}
public function getFollowUpContextPreviousUserQuestionTemplate(): string
{
return $this->getRequiredString('follow_up_context.context_labels.previous_user_question_template');
}
public function getFollowUpContextPreviousReferenceAnchorsTemplate(): string
{
return $this->getRequiredString('follow_up_context.context_labels.previous_reference_anchors_template');
}
public function getFollowUpContextCurrentQuestionTemplate(): string
{
return $this->getRequiredString('follow_up_context.context_labels.current_follow_up_question_template');
}
public function isInputNormalizationEnabled(): bool
{
return $this->getRequiredBool('input_normalization.enabled');
}
public function getInputNormalizationMaxInputChars(): int
{
return $this->getRequiredInt('input_normalization.max_input_chars');
}
public function getInputNormalizationMaxOutputChars(): int
{
return $this->getRequiredInt('input_normalization.max_output_chars');
}
public function getInputNormalizationMaxAddedTokens(): int
{
return $this->getRequiredInt('input_normalization.max_added_tokens');
}
public function getInputNormalizationMaxLengthRatioPercent(): int
{
return $this->getRequiredInt('input_normalization.max_length_ratio_percent');
}
public function getInputNormalizationHeartbeatMessage(): string
{
return $this->getRequiredString('input_normalization.heartbeat_message');
}
public function getInputNormalizationOutputPrefixPattern(): string
{
return $this->getRequiredString('input_normalization.output_prefix_pattern');
}
/**
* @return string[]
*/
public function getInputNormalizationPlaceholderOutputs(): array
{
return $this->getRequiredStringList('input_normalization.placeholder_outputs');
}
/**
* @return string[]
*/
public function getInputNormalizationSkipPatterns(): array
{
return $this->getRequiredStringList('input_normalization.skip_patterns');
}
public function getInputNormalizationPrompt(string $prompt): string
{
return $this->implodePromptBlocks([
$this->getInputNormalizationIntro(),
$this->buildRulesBlock($this->getInputNormalizationRules()),
$this->getInputNormalizationOutputFormatBlock(),
$this->getInputNormalizationCurrentUserInputLabel() . ':',
trim($prompt),
]);
}
/**
* @return string[]
*/
public function getInputNormalizationRules(): array
{
return $this->getRequiredStringList('input_normalization.prompt.rules');
}
public function getInputNormalizationIntro(): string
{
return $this->getRequiredString('input_normalization.prompt.intro');
}
public function getInputNormalizationOutputFormatBlock(): string
{
return $this->getRequiredString('input_normalization.prompt.output_format_block');
}
public function getInputNormalizationCurrentUserInputLabel(): string
{
return $this->getRequiredString('input_normalization.prompt.current_user_input_label');
}
public function isInputNormalizationFuzzyRoutingEnabled(): bool
{
return $this->getRequiredBool('input_normalization.fuzzy_routing.enabled');
}
public function getInputNormalizationFuzzyRoutingMinTokenLength(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.min_token_length');
}
public function getInputNormalizationFuzzyRoutingMediumTokenLength(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.medium_token_length');
}
public function getInputNormalizationFuzzyRoutingLongTokenLength(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.long_token_length');
}
public function getInputNormalizationFuzzyRoutingMaxDistanceShort(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.max_distance_short');
}
public function getInputNormalizationFuzzyRoutingMaxDistanceMedium(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.max_distance_medium');
}
public function getInputNormalizationFuzzyRoutingMaxDistanceLong(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.max_distance_long');
}
public function getInputNormalizationFuzzyRoutingMinSimilarityPercent(): int
{
return $this->getRequiredInt('input_normalization.fuzzy_routing.min_similarity_percent');
}
/**
* @return string[]
*/
public function getInputNormalizationFuzzyRoutingTerms(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'input_normalization.fuzzy_routing.terms',
'input_normalization.fuzzy_routing.vocabulary_views.terms'
);
}
private function getRequiredInt(string $key): int
{
$value = $this->requiredValue($key);
if (is_numeric($value)) {
return (int) $value;
}
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be numeric.', $key));
}
private function getRequiredBool(string $key): bool
{
$value = $this->requiredValue($key);
if (is_bool($value)) {
return $value;
}
if (is_scalar($value)) {
$normalized = strtolower(trim((string) $value));
if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
return true;
}
if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
return false;
}
}
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be boolean.', $key));
}
private function getRequiredString(string $key): string
{
$value = $this->requiredValue($key);
if (is_string($value) && $value !== '') {
return $value;
}
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a non-empty string.', $key));
}
private function getOptionalBool(string $key, bool $default): bool
{
$value = $this->optionalValue($key);
if ($value === null) {
return $default;
}
if (is_bool($value)) {
return $value;
}
if (is_scalar($value)) {
$normalized = strtolower(trim((string) $value));
if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
return true;
}
if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
return false;
}
}
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be boolean.', $key));
}
/**
* @return string[]
*/
private function getOptionalStringList(string $key): array
{
$value = $this->optionalValue($key);
if ($value === null) {
return [];
}
if (!is_array($value)) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a list.', $key));
}
$out = [];
foreach ($value as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item !== '') {
$out[] = $item;
}
}
return array_values(array_unique($out));
}
/**
* @return string[]
*/
private function getRequiredStringList(string $key): array
{
$value = $this->requiredValue($key);
if (!is_array($value)) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a list.', $key));
}
$out = [];
foreach ($value as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item !== '') {
$out[] = $item;
}
}
if ($out === []) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must contain at least one non-empty value.', $key));
}
return $out;
}
/**
* @return string[]
*/
private function getConfiguredStringListOrVocabularyView(string $configPath, string $viewPathConfigPath): array
{
if ($this->optionalValue($configPath) !== null) {
return $this->getRequiredStringList($configPath);
}
if ($this->vocabulary === null) {
throw new \InvalidArgumentException(sprintf(
'RetrieX agent config path "%s" is missing and no vocabulary resolver is available.',
$configPath
));
}
$viewPath = $this->getRequiredString($viewPathConfigPath);
$terms = $this->vocabulary->view($viewPath, []);
if ($terms === []) {
throw new \InvalidArgumentException(sprintf(
'RetrieX agent vocabulary view "%s" resolved to an empty list.',
$viewPath
));
}
return $terms;
}
/**
* @return array<string, string[]>
*/
private function getConfiguredStringListMapOrVocabularyMap(string $configPath, string $mapPathConfigPath): array
{
if ($this->optionalValue($configPath) !== null) {
return $this->getRequiredStringListMap($configPath);
}
if ($this->vocabulary === null) {
throw new \InvalidArgumentException(sprintf(
'RetrieX agent config path "%s" is missing and no vocabulary resolver is available.',
$configPath
));
}
$mapPath = $this->getRequiredString($mapPathConfigPath);
$terms = $this->vocabulary->map($mapPath, []);
if ($terms === []) {
throw new \InvalidArgumentException(sprintf(
'RetrieX agent vocabulary map "%s" resolved to an empty map.',
$mapPath
));
}
return $terms;
}
/**
* @return array<int, array{label:string, prompt:string}>
*/
private function getRequiredActionList(string $key): array
{
$value = $this->requiredValue($key);
if (!is_array($value)) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a list of action definitions.', $key));
}
$out = [];
foreach ($value as $item) {
if (!is_array($item)) {
continue;
}
$label = isset($item['label']) && is_scalar($item['label']) ? trim((string) $item['label']) : '';
$prompt = isset($item['prompt']) && is_scalar($item['prompt']) ? trim((string) $item['prompt']) : '';
if ($label === '' || $prompt === '') {
continue;
}
$out[] = [
'label' => $label,
'prompt' => $prompt,
];
}
if ($out === []) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must contain at least one valid action definition.', $key));
}
return $out;
}
/**
* @return array<string, string[]>
*/
private function getRequiredStringListMap(string $key): array
{
$value = $this->requiredValue($key);
if (!is_array($value)) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a map of string lists.', $key));
}
$out = [];
foreach ($value as $mapKey => $items) {
if (!is_scalar($mapKey) || !is_array($items)) {
continue;
}
$mapKey = trim((string) $mapKey);
if ($mapKey === '') {
continue;
}
$terms = [];
foreach ($items as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item !== '' && !in_array($item, $terms, true)) {
$terms[] = $item;
}
}
if ($terms !== []) {
$out[$mapKey] = $terms;
}
}
if ($out === []) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must contain at least one valid entry.', $key));
}
return $out;
}
/**
* @return array<string, string>
*/
private function getOptionalStringMap(string $key): array
{
$value = $this->optionalValue($key);
if ($value === null) {
return [];
}
if (!is_array($value)) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be a string map.', $key));
}
$out = [];
foreach ($value as $mapKey => $mapValue) {
if (!is_scalar($mapKey) || !is_scalar($mapValue)) {
continue;
}
$mapKey = trim((string) $mapKey);
$mapValue = trim((string) $mapValue);
if ($mapKey !== '' && $mapValue !== '') {
$out[$mapKey] = $mapValue;
}
}
return $out;
}
private function requiredValue(string $key): mixed
{
$current = $this->config;
foreach (explode('.', $key) as $segment) {
if (!is_array($current) || !array_key_exists($segment, $current)) {
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" is required.', $key));
}
$current = $current[$segment];
}
return $current;
}
private function optionalValue(string $key): mixed
{
$current = $this->config;
foreach (explode('.', $key) as $segment) {
if (!is_array($current) || !array_key_exists($segment, $current)) {
return null;
}
$current = $current[$segment];
}
return $current;
}
public function getEmptyPromptMessage(): string
{
return $this->getRequiredString('messages.empty_prompt');
}
public function getAnalyzeRequestMessage(): string
{
return $this->getRequiredString('messages.analyze_request');
}
public function getCheckInternetSourcesMessage(): string
{
return $this->getRequiredString('messages.check_internet_sources');
}
public function getRetrieveKnowledgeMessage(): string
{
return $this->getRequiredString('messages.retrieve_knowledge');
}
public function getOptimizeSearchMessage(): string
{
return $this->getRequiredString('messages.optimize_search');
}
public function getNoConcreteShopQueryMessage(): string
{
return $this->getRequiredString('messages.no_concrete_shop_query');
}
public function getFetchSearchDataMessageTemplate(): string
{
return $this->getRequiredString('messages.fetch_search_data_template');
}
public function getAnalyzeAllInformationMessage(): string
{
return $this->getRequiredString('messages.analyze_all_information');
}
public function getThinkingWhileStreamingMessage(): string
{
return $this->getRequiredString('messages.thinking_while_streaming');
}
public function getNoLlmDataReceivedMessage(): string
{
return $this->getRequiredString('messages.no_llm_data_received');
}
public function isFinalAnswerGuardEnabled(): bool
{
return $this->getRequiredBool('final_answer_guard.enabled');
}
public function getFinalAnswerGuardMaxOutputChars(): int
{
return $this->getRequiredInt('final_answer_guard.max_output_chars');
}
public function getFinalAnswerGuardTruncationMessage(): string
{
return $this->getRequiredString('final_answer_guard.truncation_message');
}
public function isFinalAnswerRepeatedLineGuardEnabled(): bool
{
return $this->getRequiredBool('final_answer_guard.repeated_line.enabled');
}
public function getFinalAnswerRepeatedLineMinOutputChars(): int
{
return $this->getRequiredInt('final_answer_guard.repeated_line.min_output_chars');
}
public function getFinalAnswerRepeatedLineMinLineChars(): int
{
return $this->getRequiredInt('final_answer_guard.repeated_line.min_line_chars');
}
public function getFinalAnswerRepeatedLineMaxRepetitions(): int
{
return $this->getRequiredInt('final_answer_guard.repeated_line.max_line_repetitions');
}
public function getFinalAnswerRepeatedLineTrailingWindowLines(): int
{
return $this->getRequiredInt('final_answer_guard.repeated_line.trailing_window_lines');
}
/**
* @return string[]
*/
public function getFinalAnswerRepeatedLineIgnorePatterns(): array
{
return $this->getRequiredStringList('final_answer_guard.repeated_line.ignore_patterns');
}
public function isDirectShopResultAnswerEnabled(): bool
{
return $this->getRequiredBool('direct_shop_result_answer.enabled');
}
public function getDirectShopResultAnswerMaxResults(): int
{
return $this->getRequiredInt('direct_shop_result_answer.max_results');
}
public function getDirectShopResultAnswerIntro(): string
{
return $this->getRequiredString('direct_shop_result_answer.intro');
}
public function getDirectShopResultAnswerNoResultsMessage(): string
{
return $this->getRequiredString('direct_shop_result_answer.no_results');
}
public function getDirectShopResultAnswerSortedByLengthNote(): string
{
return $this->getRequiredString('direct_shop_result_answer.sorted_by_length_note');
}
public function getDirectShopResultAnswerMinLengthFilterNote(): string
{
return $this->getRequiredString('direct_shop_result_answer.min_length_filter_note');
}
public function getDirectShopResultAnswerMaxLengthFilterNote(): string
{
return $this->getRequiredString('direct_shop_result_answer.max_length_filter_note');
}
public function getNoLlmFallbackMaxShopResults(): int
{
return $this->getRequiredInt('no_llm_fallback.max_shop_results');
}
public function getRagEvidenceCleanupProfile(): string
{
return $this->getRequiredString('rag_evidence_guard.cleanup_profile');
}
/**
* @return string[]
*/
public function getRagEvidenceStopTerms(): array
{
return $this->getRequiredStringList('rag_evidence_guard.stop_terms');
}
/**
* @return array<string, string[]>
*/
public function getRagEvidenceSynonyms(): array
{
return $this->getConfiguredStringListMapOrVocabularyMap(
'rag_evidence_guard.synonyms',
'rag_evidence_guard.vocabulary_maps.synonyms'
);
}
/**
* @return string[]
*/
public function getRagEvidenceAggregateQueryPatterns(): array
{
return $this->getRequiredStringList('rag_evidence_guard.aggregate_query_patterns');
}
/**
* @return string[]
*/
public function getRagEvidenceAggregateEvidenceTerms(): array
{
return $this->getRequiredStringList('rag_evidence_guard.aggregate_evidence_terms');
}
/**
* @return string[]
*/
public function getRagEvidenceAggregateAnswerEvidencePatterns(): array
{
return $this->getRequiredStringList('rag_evidence_guard.aggregate_answer_evidence_patterns');
}
public function getNoLlmFallbackShopOnlyMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.shop_only');
}
public function getNoLlmFallbackShopWithKnowledgeMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.shop_with_knowledge');
}
public function getNoLlmFallbackAccessoryOnlyForMainDeviceMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.accessory_only_for_main_device');
}
public function getNoLlmFallbackEscalationMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.escalation');
}
public function getNoLlmFallbackKnowledgeOnlyMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.knowledge_only');
}
public function getNoLlmFallbackNoDataMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.no_data');
}
public function getShopRepairCheckMessage(): string
{
return $this->getRequiredString('messages.shop_repair_check');
}
public function getShopQueryOptimizationHeartbeatMessage(): string
{
return $this->getRequiredString('messages.shop_query_optimization_heartbeat');
}
public function getProductionUiStageLabel(string $key): string
{
return $this->getRequiredString('production_ui.stage_labels.' . $key);
}
public function getProductionUiConfidenceLabel(string $key): string
{
return $this->getRequiredString('production_ui.confidence_labels.' . $key);
}
public function getProductionUiText(string $key): string
{
return $this->getRequiredString('production_ui.text.' . $key);
}
public function getProductionUiTemplate(string $key): string
{
return $this->getRequiredString('production_ui.templates.' . $key);
}
public function getProductionUiShopResultsMaxCards(): int
{
return $this->getRequiredInt('production_ui.shop_results.max_cards');
}
/**
* @return array<int, array{label:string, prompt:string}>
*/
public function getProductionUiFollowUpActions(string $group): array
{
return $this->getRequiredActionList('production_ui.follow_up_actions.' . $group);
}
public function getNoLlmProductField(string $key): string
{
return $this->getRequiredString('no_llm_fallback.product_fields.' . $key);
}
public function getNoLlmFallbackNoShopResultsWithKnowledgeMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.no_shop_results_with_knowledge');
}
public function getNoLlmFallbackNoShopResultsNoKnowledgeMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.no_shop_results_no_knowledge');
}
/**
* @return string[]
*/
public function getNoLlmMainDeviceRequestRoleKeywords(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'no_llm_fallback.product_roles.main_device_request_keywords',
'no_llm_fallback.product_roles.vocabulary_views.main_device_request_keywords'
);
}
/**
* @return string[]
*/
public function getNoLlmAccessoryProductRoleKeywords(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'no_llm_fallback.product_roles.accessory_product_keywords',
'no_llm_fallback.product_roles.vocabulary_views.accessory_product_keywords'
);
}
public function getNoLlmFallbackShopUnavailableWithKnowledgeMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.shop_unavailable_with_knowledge');
}
public function getNoLlmFallbackShopUnavailableNoKnowledgeMessage(): string
{
return $this->getRequiredString('no_llm_fallback.messages.shop_unavailable_no_knowledge');
}
public function getGenericInternalErrorMessage(): string
{
return $this->getRequiredString('messages.generic_internal_error');
}
public function getDebugInternalErrorPrefix(): string
{
return $this->getRequiredString('messages.debug_internal_error_prefix');
}
public function getExternalUrlSourceLabel(): string
{
return $this->getRequiredString('source_labels.external_url');
}
public function getRagKnowledgeSourceLabel(): string
{
return $this->getRequiredString('source_labels.rag_knowledge');
}
public function getConversationHistorySourceLabel(): string
{
return $this->getRequiredString('source_labels.conversation_history');
}
public function getShopSystemSourceLabel(): string
{
return $this->getRequiredString('source_labels.shop_system');
}
public function getExtendedShopSearchSourceLabel(): string
{
return $this->getRequiredString('source_labels.extended_shop_search');
}
public function getUsedSourcesPrefix(): string
{
return $this->getRequiredString('source_labels.used_sources_prefix');
}
public function getSourcesPrefix(): string
{
return $this->getRequiredString('source_labels.sources_prefix');
}
public function getSourceBadgeHtmlTemplate(): string
{
return $this->getRequiredString('html.source_badge_template');
}
public function getErrorHtmlTemplate(): string
{
return $this->getRequiredString('html.error_template');
}
public function getThinkHtmlTemplate(): string
{
return $this->getRequiredString('html.think_template');
}
public function getInfoHtmlTemplate(): string
{
return $this->getRequiredString('html.info_template');
}
public function getDebugHtmlTemplate(): string
{
return $this->getRequiredString('html.debug_template');
}
public function getShopPrompt(string $prompt, string $commerceHistoryContext = ''): string
{
$historyBlock = '';
if (trim($commerceHistoryContext) !== '') {
$historyBlock = $this->buildHistoryBlock($commerceHistoryContext);
}
return $this->implodePromptBlocks([
$this->getShopPromptIntro(),
$this->buildRulesBlock($this->getShopPromptRules()),
$this->getShopPromptOutputFormatBlock(),
$historyBlock,
$this->getCurrentUserInputLabel() . ':',
trim($prompt),
]);
}
private function buildHistoryBlock(string $commerceHistoryContext): string
{
return $this->implodePromptBlocks([
$this->getRecentConversationContextLabel() . ':',
trim($commerceHistoryContext),
$this->buildRulesBlock($this->getConversationContextRules(), 'Additional rules for conversation context:'),
]);
}
/**
* @return string[]
*/
public function getShopPromptRules(): array
{
return $this->getRequiredStringList('shop_prompt.rules');
}
/**
* @return string[]
*/
public function getConversationContextRules(): array
{
return $this->getRequiredStringList('shop_prompt.conversation_context_rules');
}
/**
* @return string[]
*/
public function getShopQueryContextUsageReferentialTerms(): array
{
return $this->getRequiredStringList('shop_prompt.context_usage.referential_terms');
}
public function isShopQueryCurrentInputPreservationEnabled(): bool
{
return $this->getOptionalBool('shop_prompt.current_input_preservation.enabled', true);
}
/**
* @return string[]
*/
public function getShopQueryCurrentInputPreservationTerms(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'shop_prompt.current_input_preservation.terms',
'shop_prompt.current_input_preservation.vocabulary_views.terms'
);
}
public function isShopQueryProductAttributeCleanupEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.product_attribute_query_cleanup.enabled');
}
public function getShopQueryProductAttributeCleanupMinTokens(): int
{
return $this->getRequiredInt('shop_prompt.product_attribute_query_cleanup.min_query_tokens_after_cleanup');
}
/**
* @return string[]
*/
public function getShopQueryProductAttributeCleanupProductTypeTerms(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'shop_prompt.product_attribute_query_cleanup.product_type_terms',
'shop_prompt.product_attribute_query_cleanup.vocabulary_views.product_type_terms'
);
}
/**
* @return string[]
*/
public function getShopQueryProductAttributeCleanupStopTerms(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'shop_prompt.product_attribute_query_cleanup.stop_terms',
'shop_prompt.product_attribute_query_cleanup.vocabulary_views.stop_terms'
);
}
/**
* @return string[]
*/
public function getShopQueryProductAttributeCleanupComparativeConstraintPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.product_attribute_query_cleanup.comparative_constraint_patterns');
}
public function isShopQueryStopwordCleanupEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.query_stopword_cleanup.enabled');
}
public function getShopQueryStopwordCleanupMinTokens(): int
{
return $this->getRequiredInt('shop_prompt.query_stopword_cleanup.min_query_tokens_after_cleanup');
}
/**
* @return string[]
*/
public function getShopQueryStopwordCleanupTerms(): array
{
return $this->getRequiredStringList('shop_prompt.query_stopword_cleanup.terms');
}
public function isDirectShopResultGuardEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.direct_result_guard.enabled');
}
public function isShopResultLengthSortEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.length_sort.enabled');
}
/**
* @return string[]
*/
public function getShopResultLengthSortTriggerPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.length_sort.trigger_patterns');
}
/**
* @return string[]
*/
public function getShopResultLengthSortValuePatterns(): array
{
return $this->getRequiredStringList('shop_prompt.length_sort.value_patterns');
}
public function isShopResultLengthFilterEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.length_filter.enabled');
}
/**
* @return string[]
*/
public function getShopResultMinLengthFilterPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.length_filter.min_patterns');
}
/**
* @return string[]
*/
public function getShopResultMaxLengthFilterPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.length_filter.max_patterns');
}
public function getShopPromptIntro(): string
{
return $this->getRequiredString('shop_prompt.intro');
}
public function getShopPromptOutputFormatBlock(): string
{
return $this->getRequiredString('shop_prompt.output_format_block');
}
public function getRecentConversationContextLabel(): string
{
return $this->getRequiredString('shop_prompt.recent_conversation_context_label');
}
public function getCurrentUserInputLabel(): string
{
return $this->getRequiredString('shop_prompt.current_user_input_label');
}
public function isShopQueryLanguagePreservationEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.language_preservation.enabled');
}
/**
* @return array<string, string[]>
*/
public function getShopQueryLanguageMarkers(): array
{
$value = $this->requiredValue('shop_prompt.language_preservation.language_markers');
if (!is_array($value)) {
throw new \InvalidArgumentException('RetrieX agent config key "shop_prompt.language_preservation.language_markers" must be a map of marker lists.');
}
$out = [];
foreach ($value as $language => $markers) {
if (!is_string($language) || !is_array($markers)) {
continue;
}
$cleanMarkers = [];
foreach ($markers as $marker) {
if (!is_scalar($marker)) {
continue;
}
$marker = strtolower((string) $marker);
if ($marker !== '') {
$cleanMarkers[] = $marker;
}
}
if ($cleanMarkers !== []) {
$out[$language] = array_values(array_unique($cleanMarkers));
}
}
if ($out === []) {
throw new \InvalidArgumentException('RetrieX agent config key "shop_prompt.language_preservation.language_markers" must contain at least one valid marker list.');
}
return $out;
}
public function isShopQueryMetaGuardEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.meta_query_guard.enabled');
}
public function getShopQueryContextFallbackCleanupProfile(): string
{
$value = $this->optionalValue('shop_prompt.meta_query_guard.cleanup_profile');
if (is_string($value) && trim($value) !== '') {
return trim($value);
}
return 'shop_context_fallback';
}
/**
* @return string[]
*/
public function getShopQueryMetaOnlyTerms(): array
{
return $this->getRequiredStringList('shop_prompt.meta_query_guard.meta_only_terms');
}
public function isShopQueryContextFallbackEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.meta_query_guard.context_fallback_enabled');
}
public function getShopQueryContextFallbackQuestionLimit(): int
{
return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_question_limit');
}
public function getShopQueryContextFallbackHistoryBudgetChars(): int
{
return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_history_budget_chars');
}
public function shouldUseFullHistoryForShopQueryContextFallback(): bool
{
return $this->getRequiredBool('shop_prompt.meta_query_guard.context_fallback_use_full_history');
}
public function getShopQueryContextFallbackMaxTerms(): int
{
return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_max_terms');
}
/**
* @return string[]
*/
public function getShopQueryContextFallbackFilterTerms(): array
{
return $this->getRequiredStringList('shop_prompt.meta_query_guard.context_fallback_filter_terms');
}
public function isShopQueryContextAnchorEnrichmentEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.context_anchor_enrichment.enabled');
}
public function getShopQueryContextAnchorEnrichmentMaxQueryTerms(): int
{
return $this->getRequiredInt('shop_prompt.context_anchor_enrichment.max_query_terms');
}
/**
* @return string[]
*/
public function getShopQueryContextAnchorEnrichmentTriggerTerms(): array
{
return $this->getConfiguredStringListOrVocabularyView(
'shop_prompt.context_anchor_enrichment.trigger_terms',
'shop_prompt.context_anchor_enrichment.vocabulary_views.trigger_terms'
);
}
/**
* @return string[]
*/
public function getShopQueryContextAnchorEnrichmentPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.context_anchor_enrichment.anchor_patterns');
}
public function getShopQueryContextAnchorEnrichmentTemplate(): string
{
return $this->getRequiredString('shop_prompt.context_anchor_enrichment.template');
}
public function isShopQueryRagAnchorEnrichmentEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.rag_anchor_enrichment.enabled');
}
public function getShopQueryRagAnchorEnrichmentMinScore(): int
{
return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.min_score');
}
public function getShopQueryRagAnchorEnrichmentMaxQueryTerms(): int
{
return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.max_query_terms');
}
public function getShopQueryRagAnchorEnrichmentEarlyChunkBonusMax(): int
{
return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.early_chunk_bonus_max');
}
public function getShopQueryRagAnchorEnrichmentExactValueUnitScore(): int
{
return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.scores.exact_value_with_unit');
}
public function getShopQueryRagAnchorEnrichmentExactValueScore(): int
{
return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.scores.exact_value_only');
}
public function getShopQueryRagAnchorEnrichmentAnchorBonusScore(): int
{
return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.scores.anchor_bonus');
}
public function getShopQueryRagAnchorEnrichmentTemplate(): string
{
return $this->getRequiredString('shop_prompt.rag_anchor_enrichment.template');
}
/**
* @return string[]
*/
public function getShopQueryRagAnchorEnrichmentNumericFocusPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.numeric_focus_patterns');
}
/**
* @return string[]
*/
public function getShopQueryRagAnchorEnrichmentProductTitlePatterns(): array
{
return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.product_title_patterns');
}
/**
* @return string[]
*/
public function getShopQueryRagAnchorEnrichmentAnchorBonusPatterns(): array
{
return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.anchor_bonus_patterns');
}
/**
* @return string[]
*/
public function getShopQueryRagAnchorEnrichmentSubjectTerms(): array
{
return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.subject_terms');
}
public function getShopQueryTranslationReplacements(string $language): array
{
$value = $this->getOptionalStringMap('shop_prompt.language_preservation.translation_replacements.' . $language);
$out = [];
foreach ($value as $source => $target) {
$source = strtolower(trim($source));
$target = trim($target);
if ($source !== '' && $target !== '') {
$out[$source] = $target;
}
}
uksort($out, static fn(string $a, string $b): int => strlen($b) <=> strlen($a));
return $out;
}
private function buildRulesBlock(array $rules, string $headline = 'Rules:'): string
{
return $headline . "\n" . implode("\n", $rules);
}
/**
* @param string[] $blocks
*/
private function implodePromptBlocks(array $blocks): string
{
$normalized = array_values(array_filter(
array_map(
static fn(string $block): string => trim($block),
$blocks
),
static fn(string $block): bool => $block !== ''
));
return implode("\n\n", $normalized);
}
}