p69
This commit is contained in:
@@ -237,14 +237,20 @@ parameters:
|
|||||||
enabled: true
|
enabled: true
|
||||||
commerce:
|
commerce:
|
||||||
- label: Im Shop suchen
|
- label: Im Shop suchen
|
||||||
prompt: Suche im Shop nach den aktuell genannten Produkten oder Messaufgaben.
|
prompt: Suche im Shop nach {shop_query}.
|
||||||
|
action_type: shop_search
|
||||||
shop_results:
|
shop_results:
|
||||||
- label: Preis anzeigen
|
- label: Preis anzeigen
|
||||||
prompt: Zeige mir die Preise der aktuell relevanten Produkte.
|
prompt: Zeige mir die Preise zu {shop_query}.
|
||||||
|
action_type: price_details
|
||||||
- label: Nur Zubehör anzeigen
|
- label: Nur Zubehör anzeigen
|
||||||
prompt: Zeige aus der aktuellen Produktauswahl nur Zubehör.
|
prompt: Suche im Shop nach {shop_query} und zeige daraus nur Zubehör.
|
||||||
|
action_type: role_filter
|
||||||
|
target_role: accessory_or_consumable
|
||||||
- label: Nur Geräte anzeigen
|
- label: Nur Geräte anzeigen
|
||||||
prompt: Zeige aus der aktuellen Produktauswahl nur Geräte.
|
prompt: Suche im Shop nach {shop_query} und zeige daraus nur Geräte.
|
||||||
|
action_type: role_filter
|
||||||
|
target_role: main_device
|
||||||
knowledge:
|
knowledge:
|
||||||
- label: Technische Details anzeigen
|
- label: Technische Details anzeigen
|
||||||
prompt: Zeige technische Details zur aktuellen Antwort.
|
prompt: Zeige technische Details zur aktuellen Antwort.
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# RetrieX Patch p69 - Context-aware Follow-up Actions
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
|
||||||
|
Dieser Patch härtet die in p67/p68 eingeführten Folgeaktionen: Actions werden nicht mehr nur anhand von `hasShopResults` statisch angezeigt, sondern nach aktuellem Antwort-/Shopkontext gefiltert.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Bei einer Anfrage wie `testomat 808 indikatoren` wurden nach einer Zubehör-/Indikatorliste trotzdem generische Rollenfilter wie `Nur Geräte anzeigen` angeboten. Beim Klick ging der Produktkontext verloren und die Folgeanfrage konnte zu einer generischen Shopquery wie `geräte` degenerieren.
|
||||||
|
|
||||||
|
## Lösung
|
||||||
|
|
||||||
|
- Follow-up-Actions erhalten optionale Metadaten in `config/retriex/chat-messages.yaml`:
|
||||||
|
- `action_type: shop_search`
|
||||||
|
- `action_type: price_details`
|
||||||
|
- `action_type: role_filter`
|
||||||
|
- `target_role: accessory_or_consumable|main_device`
|
||||||
|
- `AgentRunner` baut vor dem Rendern einen Action-Kontext auf:
|
||||||
|
- aktuelle Shopquery
|
||||||
|
- ob die finale Antwort bereits Preise enthält
|
||||||
|
- Rollenverteilung der aktuellen Shop-Treffer
|
||||||
|
- Rollenfilter werden nur angezeigt, wenn sie wirklich verengen:
|
||||||
|
- Zielrolle muss in den Ergebnissen vorkommen
|
||||||
|
- Zielrolle darf nicht bereits die komplette Ergebnismenge sein
|
||||||
|
- Kontextquery muss vorhanden sein
|
||||||
|
- Preis-Actions werden ausgeblendet, wenn die Antwort bereits Preise enthält.
|
||||||
|
- Action-Prompts werden mit `{shop_query}` verankert, damit Klicks nicht mehr zu isolierten Queries wie `geräte` führen.
|
||||||
|
- Wenn nach der Kontextfilterung keine sinnvolle Action übrig bleibt, wird keine Follow-up-Action-Karte gerendert.
|
||||||
|
|
||||||
|
## Bewusst nicht geändert
|
||||||
|
|
||||||
|
- Kein Eingriff in Retrieval, Scoring, Ranking, PromptBuilder oder Shop-Matching.
|
||||||
|
- Keine neuen fachlichen Keywordlisten im PHP-Core.
|
||||||
|
- Produktrollen-Erkennung nutzt bestehende YAML-gepflegte No-LLM-Rollenterms.
|
||||||
|
- `buildShopProductCardsMessage()` bleibt weiterhin nicht eingehängt.
|
||||||
|
|
||||||
|
## Geänderte Dateien
|
||||||
|
|
||||||
|
- `src/Agent/AgentRunner.php`
|
||||||
|
- `src/Config/AgentRunnerConfig.php`
|
||||||
|
- `src/Config/ChatMessagesConfig.php`
|
||||||
|
- `config/retriex/chat-messages.yaml`
|
||||||
|
|
||||||
|
## Lokale Checks
|
||||||
|
|
||||||
|
- `php -l src/Agent/AgentRunner.php`
|
||||||
|
- `php -l src/Config/AgentRunnerConfig.php`
|
||||||
|
- `php -l src/Config/ChatMessagesConfig.php`
|
||||||
|
- YAML-Parsing von `config/retriex/chat-messages.yaml`
|
||||||
|
- `ChatMessagesConfig::validate()`
|
||||||
|
- `AgentRunnerConfig::getProductionUiFollowUpActions('shop_results')`
|
||||||
|
|
||||||
|
## Empfohlene Regressionstests
|
||||||
|
|
||||||
|
1. `testomat 808 indikatoren`
|
||||||
|
- Erwartung: keine Action `Nur Geräte anzeigen`.
|
||||||
|
- Erwartung: `Preis anzeigen` nur, wenn die Antwort noch keine Preise enthält.
|
||||||
|
- Erwartung: keine Action-Karte, falls nur redundante oder nicht sinnvolle Actions übrig wären.
|
||||||
|
|
||||||
|
2. Nach einer gemischten Shop-Trefferliste mit Geräten und Zubehör:
|
||||||
|
- Erwartung: Rollenfilter erscheinen nur, wenn sie die aktuelle Liste sinnvoll verengen.
|
||||||
|
- Erwartung: Klick-Prompt enthält den aktuellen Shopkontext, z. B. `Suche im Shop nach <shop_query> und zeige daraus nur Geräte.`
|
||||||
|
|
||||||
|
3. Smalltalk / No-Evidence:
|
||||||
|
- Erwartung: keine Follow-up-Actions.
|
||||||
|
|
||||||
|
4. Technische RAG-Antwort mit belastbarer Evidenz:
|
||||||
|
- Erwartung: `Technische Details anzeigen` kann weiterhin erscheinen.
|
||||||
@@ -519,7 +519,7 @@ span.think {
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin: 1rem 0 0.25rem;
|
margin: 1rem 0 0.25rem;
|
||||||
color: rgba(248, 249, 250, 0.76);
|
color: rgba(248, 249, 250, 0.76);
|
||||||
font-size: 0.84rem;
|
font-size: 0.7rem;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
border-top: 1px solid #324154;
|
border-top: 1px solid #324154;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace App\Agent;
|
namespace App\Agent;
|
||||||
|
|
||||||
use App\Commerce\Dto\ShopProductResult;
|
use App\Commerce\Dto\ShopProductResult;
|
||||||
|
use App\Commerce\ProductRoleResolver;
|
||||||
use App\Commerce\SearchRepairService;
|
use App\Commerce\SearchRepairService;
|
||||||
use App\Commerce\ShopSearchService;
|
use App\Commerce\ShopSearchService;
|
||||||
use App\Config\AgentRunnerConfig;
|
use App\Config\AgentRunnerConfig;
|
||||||
@@ -704,7 +705,10 @@ final readonly class AgentRunner
|
|||||||
isCommerceIntent: $this->isCommerceIntent($commerceIntent),
|
isCommerceIntent: $this->isCommerceIntent($commerceIntent),
|
||||||
hasShopResults: $shopResults !== [],
|
hasShopResults: $shopResults !== [],
|
||||||
hasKnowledge: $this->isDirectKnowledgeEvidence($knowledgeEvidenceState),
|
hasKnowledge: $this->isDirectKnowledgeEvidence($knowledgeEvidenceState),
|
||||||
shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure
|
shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure,
|
||||||
|
shopResults: $shopResults,
|
||||||
|
shopSearchQuery: $shopSearchQuery,
|
||||||
|
answerText: $fullOutput
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($followUpActionsMessage !== '') {
|
if ($followUpActionsMessage !== '') {
|
||||||
@@ -4861,27 +4865,54 @@ final readonly class AgentRunner
|
|||||||
return $this->agentRunnerConfig->getProductionUiTemplate('relevance_default');
|
return $this->agentRunnerConfig->getProductionUiTemplate('relevance_default');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ShopProductResult[] $shopResults
|
||||||
|
*/
|
||||||
private function buildFollowUpActionsMessage(
|
private function buildFollowUpActionsMessage(
|
||||||
bool $isCommerceIntent,
|
bool $isCommerceIntent,
|
||||||
bool $hasShopResults,
|
bool $hasShopResults,
|
||||||
bool $hasKnowledge,
|
bool $hasKnowledge,
|
||||||
bool $shopSearchHadSystemFailure
|
bool $shopSearchHadSystemFailure,
|
||||||
|
array $shopResults,
|
||||||
|
string $shopSearchQuery,
|
||||||
|
string $answerText
|
||||||
): string {
|
): string {
|
||||||
if (!$this->agentRunnerConfig->isProductionUiFollowUpActionsEnabled()) {
|
if (!$this->agentRunnerConfig->isProductionUiFollowUpActionsEnabled()) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$context = $this->buildFollowUpActionContext(
|
||||||
|
shopResults: $shopResults,
|
||||||
|
shopSearchQuery: $shopSearchQuery,
|
||||||
|
answerText: $answerText
|
||||||
|
);
|
||||||
|
|
||||||
$actions = [];
|
$actions = [];
|
||||||
$seenActionKeys = [];
|
$seenActionKeys = [];
|
||||||
|
|
||||||
if ($hasShopResults) {
|
if ($hasShopResults) {
|
||||||
$this->appendFollowUpActions($actions, $seenActionKeys, $this->agentRunnerConfig->getProductionUiFollowUpActions('shop_results'));
|
$this->appendFollowUpActions(
|
||||||
} elseif ($isCommerceIntent && !$shopSearchHadSystemFailure) {
|
actions: $actions,
|
||||||
$this->appendFollowUpActions($actions, $seenActionKeys, $this->agentRunnerConfig->getProductionUiFollowUpActions('commerce'));
|
seenActionKeys: $seenActionKeys,
|
||||||
|
items: $this->agentRunnerConfig->getProductionUiFollowUpActions('shop_results'),
|
||||||
|
context: $context
|
||||||
|
);
|
||||||
|
} elseif ($isCommerceIntent && !$shopSearchHadSystemFailure && $context['shop_query'] !== '') {
|
||||||
|
$this->appendFollowUpActions(
|
||||||
|
actions: $actions,
|
||||||
|
seenActionKeys: $seenActionKeys,
|
||||||
|
items: $this->agentRunnerConfig->getProductionUiFollowUpActions('commerce'),
|
||||||
|
context: $context
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($hasKnowledge) {
|
if ($hasKnowledge) {
|
||||||
$this->appendFollowUpActions($actions, $seenActionKeys, $this->agentRunnerConfig->getProductionUiFollowUpActions('knowledge'));
|
$this->appendFollowUpActions(
|
||||||
|
actions: $actions,
|
||||||
|
seenActionKeys: $seenActionKeys,
|
||||||
|
items: $this->agentRunnerConfig->getProductionUiFollowUpActions('knowledge'),
|
||||||
|
context: $context
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($actions === []) {
|
if ($actions === []) {
|
||||||
@@ -4907,11 +4938,97 @@ final readonly class AgentRunner
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, array{label:string, prompt:string}> $actions
|
* @param ShopProductResult[] $shopResults
|
||||||
* @param array<string, bool> $seenActionKeys
|
* @return array{shop_query:string, answer_has_price:bool, role_counts:array<string,int>}
|
||||||
* @param array<int, array{label:string, prompt:string}> $items
|
|
||||||
*/
|
*/
|
||||||
private function appendFollowUpActions(array &$actions, array &$seenActionKeys, array $items): void
|
private function buildFollowUpActionContext(array $shopResults, string $shopSearchQuery, string $answerText): array
|
||||||
|
{
|
||||||
|
$roleCounts = [
|
||||||
|
ProductRoleResolver::ROLE_MAIN_DEVICE => 0,
|
||||||
|
ProductRoleResolver::ROLE_ACCESSORY_OR_CONSUMABLE => 0,
|
||||||
|
ProductRoleResolver::ROLE_AMBIGUOUS_MIXED => 0,
|
||||||
|
ProductRoleResolver::ROLE_UNKNOWN => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($shopResults as $product) {
|
||||||
|
if (!$product instanceof ShopProductResult) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$role = $this->resolveFollowUpActionShopProductRole($product);
|
||||||
|
if (!array_key_exists($role, $roleCounts)) {
|
||||||
|
$role = ProductRoleResolver::ROLE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
++$roleCounts[$role];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'shop_query' => $this->normalizeOneLine($shopSearchQuery),
|
||||||
|
'answer_has_price' => $this->followUpActionAnswerAlreadyContainsPrice($answerText),
|
||||||
|
'role_counts' => $roleCounts,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveFollowUpActionShopProductRole(ShopProductResult $product): string
|
||||||
|
{
|
||||||
|
$primaryText = mb_strtolower($this->normalizeOneLine(implode(' ', [
|
||||||
|
$product->name,
|
||||||
|
(string) $product->productNumber,
|
||||||
|
])), 'UTF-8');
|
||||||
|
|
||||||
|
$hasPrimaryAccessory = $this->containsAnyConfiguredTerm($primaryText, $this->agentRunnerConfig->getNoLlmAccessoryProductRoleKeywords());
|
||||||
|
$hasPrimaryDevice = $this->containsAnyConfiguredTerm($primaryText, $this->agentRunnerConfig->getNoLlmMainDeviceRequestRoleKeywords());
|
||||||
|
|
||||||
|
if ($hasPrimaryAccessory && !$hasPrimaryDevice) {
|
||||||
|
return ProductRoleResolver::ROLE_ACCESSORY_OR_CONSUMABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasPrimaryDevice && !$hasPrimaryAccessory) {
|
||||||
|
return ProductRoleResolver::ROLE_MAIN_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasPrimaryAccessory && $hasPrimaryDevice) {
|
||||||
|
return ProductRoleResolver::ROLE_ACCESSORY_OR_CONSUMABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$corpus = mb_strtolower($this->normalizeOneLine(implode(' ', [
|
||||||
|
$product->name,
|
||||||
|
(string) $product->description,
|
||||||
|
(string) $product->customFields,
|
||||||
|
implode(' ', $product->highlights),
|
||||||
|
])), 'UTF-8');
|
||||||
|
|
||||||
|
$hasAccessory = $this->containsAnyConfiguredTerm($corpus, $this->agentRunnerConfig->getNoLlmAccessoryProductRoleKeywords());
|
||||||
|
$hasDevice = $this->containsAnyConfiguredTerm($corpus, $this->agentRunnerConfig->getNoLlmMainDeviceRequestRoleKeywords());
|
||||||
|
|
||||||
|
if ($hasAccessory && !$hasDevice) {
|
||||||
|
return ProductRoleResolver::ROLE_ACCESSORY_OR_CONSUMABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasDevice && !$hasAccessory) {
|
||||||
|
return ProductRoleResolver::ROLE_MAIN_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasAccessory && $hasDevice) {
|
||||||
|
return ProductRoleResolver::ROLE_AMBIGUOUS_MIXED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProductRoleResolver::ROLE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function followUpActionAnswerAlreadyContainsPrice(string $answerText): bool
|
||||||
|
{
|
||||||
|
return preg_match('/(?:\bpreis\b.{0,24}\d+[,.]\d{2}|\d+[,.]\d{2}\s*(?:€|eur)\b|(?:€|eur)\s*\d+[,.]\d{2})/iu', $answerText) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, array<string, mixed>> $actions
|
||||||
|
* @param array<string, bool> $seenActionKeys
|
||||||
|
* @param array<int, array<string, mixed>> $items
|
||||||
|
* @param array{shop_query:string, answer_has_price:bool, role_counts:array<string,int>} $context
|
||||||
|
*/
|
||||||
|
private function appendFollowUpActions(array &$actions, array &$seenActionKeys, array $items, array $context): void
|
||||||
{
|
{
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
$label = trim((string) ($item['label'] ?? ''));
|
$label = trim((string) ($item['label'] ?? ''));
|
||||||
@@ -4921,6 +5038,15 @@ final readonly class AgentRunner
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->shouldShowFollowUpAction($item, $context)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$actionPrompt = $this->renderFollowUpActionPrompt($actionPrompt, $context);
|
||||||
|
if ($actionPrompt === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$key = mb_strtolower($label . "\n" . $actionPrompt, 'UTF-8');
|
$key = mb_strtolower($label . "\n" . $actionPrompt, 'UTF-8');
|
||||||
if (isset($seenActionKeys[$key])) {
|
if (isset($seenActionKeys[$key])) {
|
||||||
continue;
|
continue;
|
||||||
@@ -4934,6 +5060,58 @@ final readonly class AgentRunner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $item
|
||||||
|
* @param array{shop_query:string, answer_has_price:bool, role_counts:array<string,int>} $context
|
||||||
|
*/
|
||||||
|
private function shouldShowFollowUpAction(array $item, array $context): bool
|
||||||
|
{
|
||||||
|
$actionType = isset($item['action_type']) && is_scalar($item['action_type']) ? trim((string) $item['action_type']) : '';
|
||||||
|
$targetRole = isset($item['target_role']) && is_scalar($item['target_role']) ? trim((string) $item['target_role']) : '';
|
||||||
|
|
||||||
|
if ($actionType === 'shop_search') {
|
||||||
|
return $context['shop_query'] !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actionType === 'price_details') {
|
||||||
|
return $context['shop_query'] !== '' && !$context['answer_has_price'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actionType === 'role_filter') {
|
||||||
|
return $context['shop_query'] !== '' && $this->isFollowUpRoleFilterMeaningful($targetRole, $context['role_counts']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, int> $roleCounts
|
||||||
|
*/
|
||||||
|
private function isFollowUpRoleFilterMeaningful(string $targetRole, array $roleCounts): bool
|
||||||
|
{
|
||||||
|
if (!isset($roleCounts[$targetRole]) || $roleCounts[$targetRole] <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalProducts = array_sum($roleCounts);
|
||||||
|
if ($totalProducts <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $roleCounts[$targetRole] < $totalProducts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{shop_query:string, answer_has_price:bool, role_counts:array<string,int>} $context
|
||||||
|
*/
|
||||||
|
private function renderFollowUpActionPrompt(string $prompt, array $context): string
|
||||||
|
{
|
||||||
|
$shopQuery = $context['shop_query'];
|
||||||
|
$rendered = str_replace('{shop_query}', $shopQuery, $prompt);
|
||||||
|
|
||||||
|
return $this->normalizeOneLine($rendered);
|
||||||
|
}
|
||||||
|
|
||||||
private function buildShopSearchMetaMessage(
|
private function buildShopSearchMetaMessage(
|
||||||
string $query,
|
string $query,
|
||||||
string $commerceIntent,
|
string $commerceIntent,
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ final class AgentRunnerConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, array{label:string, prompt:string}>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
private function getChatActionList(string $chatKey, string $legacyKey): array
|
private function getChatActionList(string $chatKey, string $legacyKey): array
|
||||||
{
|
{
|
||||||
@@ -572,7 +572,7 @@ final class AgentRunnerConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, array{label:string, prompt:string}>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
private function getRequiredActionList(string $key): array
|
private function getRequiredActionList(string $key): array
|
||||||
{
|
{
|
||||||
@@ -596,10 +596,21 @@ final class AgentRunnerConfig
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$out[] = [
|
$action = [
|
||||||
'label' => $label,
|
'label' => $label,
|
||||||
'prompt' => $prompt,
|
'prompt' => $prompt,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
foreach (['action_type', 'target_role'] as $optionalKey) {
|
||||||
|
if (isset($item[$optionalKey]) && is_scalar($item[$optionalKey])) {
|
||||||
|
$optionalValue = trim((string) $item[$optionalKey]);
|
||||||
|
if ($optionalValue !== '') {
|
||||||
|
$action[$optionalKey] = $optionalValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$out[] = $action;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($out === []) {
|
if ($out === []) {
|
||||||
@@ -989,7 +1000,7 @@ final class AgentRunnerConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, array{label:string, prompt:string}>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
public function getProductionUiFollowUpActions(string $group): array
|
public function getProductionUiFollowUpActions(string $group): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ final class ChatMessagesConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, array{label:string, prompt:string}>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
public function getActionList(string $path): array
|
public function getActionList(string $path): array
|
||||||
{
|
{
|
||||||
@@ -514,7 +514,7 @@ final class ChatMessagesConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<int, array{label:string, prompt:string}>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
private function actionList(string $path): array
|
private function actionList(string $path): array
|
||||||
{
|
{
|
||||||
@@ -538,10 +538,21 @@ final class ChatMessagesConfig
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$out[] = [
|
$action = [
|
||||||
'label' => $label,
|
'label' => $label,
|
||||||
'prompt' => $prompt,
|
'prompt' => $prompt,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
foreach (['action_type', 'target_role'] as $optionalKey) {
|
||||||
|
if (isset($item[$optionalKey]) && is_scalar($item[$optionalKey])) {
|
||||||
|
$optionalValue = trim((string) $item[$optionalKey]);
|
||||||
|
if ($optionalValue !== '') {
|
||||||
|
$action[$optionalKey] = $optionalValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$out[] = $action;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($out === []) {
|
if ($out === []) {
|
||||||
|
|||||||
Reference in New Issue
Block a user