move intent an config value into config files
This commit is contained in:
@@ -4,9 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Agent;
|
||||
|
||||
use App\Commerce\CommerceReferenceResolver;
|
||||
use App\Commerce\CommerceReferenceStore;
|
||||
use App\Commerce\Dto\CommerceReferenceContext;
|
||||
use App\Commerce\SearchRepairService;
|
||||
use App\Commerce\ShopSearchService;
|
||||
use App\Config\AgentRunnerConfig;
|
||||
@@ -21,8 +18,6 @@ use Throwable;
|
||||
|
||||
final readonly class AgentRunner
|
||||
{
|
||||
private const COMMERCE_HISTORY_BUDGET_CHARS = 1000;
|
||||
|
||||
private bool $systemMsgOn;
|
||||
|
||||
public function __construct(
|
||||
@@ -33,8 +28,6 @@ final readonly class AgentRunner
|
||||
private RetrieverInterface $retriever,
|
||||
private ShopSearchService $shopSearchService,
|
||||
private SearchRepairService $searchRepairService,
|
||||
private CommerceReferenceStore $commerceReferenceStore,
|
||||
private CommerceReferenceResolver $commerceReferenceResolver,
|
||||
private CommerceIntentLite $commerceIntentLite,
|
||||
private OllamaClient $ollamaClient,
|
||||
private LoggerInterface $agentLogger,
|
||||
@@ -51,14 +44,13 @@ final readonly class AgentRunner
|
||||
$prompt = trim($prompt);
|
||||
|
||||
if ($prompt === '') {
|
||||
yield $this->systemMsg('❌ Empty prompt.', 'err');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getEmptyPromptMessage(), 'err');
|
||||
return;
|
||||
}
|
||||
|
||||
$shopResults = [];
|
||||
$primaryShopResults = [];
|
||||
$factSources = [];
|
||||
$contextSignals = [];
|
||||
$sources = [];
|
||||
$optimizedShopQuery = '';
|
||||
$shopSearchQuery = '';
|
||||
$commerceIntent = CommerceIntentLite::NONE;
|
||||
@@ -66,8 +58,6 @@ final readonly class AgentRunner
|
||||
$attemptedShopRepair = false;
|
||||
$usedShopRepair = false;
|
||||
$shopRepairQueries = [];
|
||||
$activeCommerceReference = null;
|
||||
$shopChecked = false;
|
||||
|
||||
$this->agentLogger->info('Agent run started', [
|
||||
'userId' => $userId,
|
||||
@@ -79,74 +69,39 @@ final readonly class AgentRunner
|
||||
// Additional context strategies can be added here later.
|
||||
}
|
||||
|
||||
yield $this->systemMsg('Ich analysiere deine Anfrage...', 'think');
|
||||
yield $this->systemMsg('Ich prüfe auf Internetquellen...', 'think');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getAnalyzeRequestMessage(), 'think');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getCheckInternetSourcesMessage(), 'think');
|
||||
|
||||
$urlContent = $this->urlAnalyzer->extractContentFromPrompt($prompt);
|
||||
if ($urlContent !== '') {
|
||||
$this->addBadge($factSources, 'Externe URL');
|
||||
$this->addSource($sources, $this->agentRunnerConfig->getExternalUrlSourceLabel());
|
||||
}
|
||||
|
||||
yield $this->systemMsg('Ich hole relevante Daten aus meinem RAG-Wissen...', 'think');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getRetrieveKnowledgeMessage(), 'think');
|
||||
|
||||
$knowledgeChunks = $this->retriever->retrieve($prompt);
|
||||
if ($knowledgeChunks !== []) {
|
||||
$this->addBadge($factSources, 'RAG Wissen');
|
||||
$this->addSource($sources, $this->agentRunnerConfig->getRagKnowledgeSourceLabel());
|
||||
}
|
||||
|
||||
$commerceIntent = $this->detectCommerceIntent($prompt);
|
||||
|
||||
if ($this->isCommerceIntent($commerceIntent)) {
|
||||
yield $this->systemMsg('Ich optimiere die Recherche...', 'think');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getOptimizeSearchMessage(), 'think');
|
||||
|
||||
$commerceHistoryContext = $this->buildCommerceHistoryContext($userId);
|
||||
$activeCommerceReference = $this->loadCommerceReference($userId);
|
||||
|
||||
if ($commerceHistoryContext !== '') {
|
||||
$this->addBadge($contextSignals, 'Gesprächskontext');
|
||||
$this->addSource($sources, $this->agentRunnerConfig->getConversationHistorySourceLabel());
|
||||
}
|
||||
|
||||
if ($activeCommerceReference !== null) {
|
||||
$this->addBadge($contextSignals, 'Commerce-Referenz');
|
||||
}
|
||||
|
||||
$isReferenceOnlyFollowUp = $this->isReferenceOnlyCommerceFollowUp(
|
||||
$optimizedShopQuery = $this->buildOptimizedShopQuery(
|
||||
$prompt,
|
||||
$activeCommerceReference
|
||||
$userId,
|
||||
$commerceHistoryContext
|
||||
);
|
||||
|
||||
if ($isReferenceOnlyFollowUp) {
|
||||
$shopSearchQuery = $this->buildDeterministicReferenceShopQuery($activeCommerceReference);
|
||||
|
||||
if ($shopSearchQuery !== '') {
|
||||
$this->addBadge($contextSignals, 'Deterministische Referenzsuche');
|
||||
}
|
||||
|
||||
$this->agentLogger->info('Using deterministic reference shop query', [
|
||||
'userId' => $userId,
|
||||
'commerceIntent' => $commerceIntent,
|
||||
'prompt' => $prompt,
|
||||
'shopSearchQuery' => $shopSearchQuery,
|
||||
'referenceProductName' => $activeCommerceReference?->productName,
|
||||
'referenceFocusTerms' => $activeCommerceReference?->focusTerms,
|
||||
]);
|
||||
} else {
|
||||
$optimizedShopQuery = $this->buildOptimizedShopQuery(
|
||||
$prompt,
|
||||
$userId,
|
||||
$commerceHistoryContext
|
||||
);
|
||||
|
||||
if ($optimizedShopQuery !== '' && $optimizedShopQuery !== $prompt) {
|
||||
$this->addBadge($contextSignals, 'Query-Optimierung');
|
||||
}
|
||||
|
||||
$shopSearchQuery = $optimizedShopQuery !== '' ? $optimizedShopQuery : $prompt;
|
||||
}
|
||||
|
||||
if ($shopSearchQuery === '') {
|
||||
$shopSearchQuery = $prompt;
|
||||
}
|
||||
$shopSearchQuery = $optimizedShopQuery !== '' ? $optimizedShopQuery : $prompt;
|
||||
|
||||
$this->agentLogger->info('Commerce search prepared', [
|
||||
'userId' => $userId,
|
||||
@@ -154,26 +109,20 @@ final readonly class AgentRunner
|
||||
'usedOptimizedShopQuery' => $optimizedShopQuery !== '',
|
||||
'optimizedShopQuery' => $optimizedShopQuery,
|
||||
'shopSearchQuery' => $shopSearchQuery,
|
||||
'usedDeterministicReferenceQuery' => $isReferenceOnlyFollowUp,
|
||||
'hasCommerceHistoryContext' => $commerceHistoryContext !== '',
|
||||
'commerceHistoryContextLength' => mb_strlen($commerceHistoryContext),
|
||||
'hasActiveCommerceReference' => $activeCommerceReference !== null,
|
||||
'activeCommerceReferenceProduct' => $activeCommerceReference?->productName,
|
||||
]);
|
||||
|
||||
yield $this->systemMsg(
|
||||
'Ich rufe Recherchedaten ab (type: ' . $commerceIntent . ')',
|
||||
sprintf($this->agentRunnerConfig->getFetchSearchDataMessageTemplate(), $commerceIntent),
|
||||
'think'
|
||||
);
|
||||
|
||||
$shopChecked = true;
|
||||
|
||||
$primaryShopResults = $this->searchShop(
|
||||
$shopSearchQuery,
|
||||
$commerceIntent,
|
||||
$userId,
|
||||
$commerceHistoryContext,
|
||||
$activeCommerceReference
|
||||
$commerceHistoryContext
|
||||
);
|
||||
|
||||
$repairPayload = $this->repairShopResults(
|
||||
@@ -192,13 +141,11 @@ final readonly class AgentRunner
|
||||
$shopRepairQueries = $repairPayload['repairQueries'];
|
||||
|
||||
if ($shopResults !== []) {
|
||||
$this->addBadge($factSources, 'Shopsystem');
|
||||
} elseif ($shopChecked) {
|
||||
$this->addBadge($factSources, 'Shopsystem geprüft');
|
||||
$this->addSource($sources, $this->agentRunnerConfig->getShopSystemSourceLabel());
|
||||
}
|
||||
|
||||
if ($attemptedShopRepair) {
|
||||
$this->addBadge($contextSignals, 'Erweiterte Shopsuche');
|
||||
$this->addSource($sources, $this->agentRunnerConfig->getExtendedShopSearchSourceLabel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +153,7 @@ final readonly class AgentRunner
|
||||
$knowledgeChunks = $this->limitKnowledgeChunks($knowledgeChunks, $commerceIntent);
|
||||
}
|
||||
|
||||
yield $this->systemMsg('Ich analysiere alle Informationen...', 'think');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getAnalyzeAllInformationMessage(), 'think');
|
||||
|
||||
$finalPrompt = $this->promptBuilder->build(
|
||||
prompt: $prompt,
|
||||
@@ -226,7 +173,6 @@ final readonly class AgentRunner
|
||||
'shopSearchQuery' => $shopSearchQuery,
|
||||
'primaryShopResultsCount' => count($primaryShopResults),
|
||||
'shopResultsCount' => count($shopResults),
|
||||
'shopChecked' => $shopChecked,
|
||||
'attemptedShopRepair' => $attemptedShopRepair,
|
||||
'usedShopRepair' => $usedShopRepair,
|
||||
'shopRepairQueries' => $shopRepairQueries,
|
||||
@@ -243,21 +189,19 @@ final readonly class AgentRunner
|
||||
]);
|
||||
}
|
||||
|
||||
if ($factSources !== [] || $contextSignals !== []) {
|
||||
yield $this->emitSourceSummary(
|
||||
$factSources,
|
||||
$contextSignals,
|
||||
'Genutzte Datenpfade'
|
||||
if ($sources !== []) {
|
||||
yield $this->emitSources(
|
||||
$sources,
|
||||
$this->agentRunnerConfig->getUsedSourcesPrefix()
|
||||
);
|
||||
}
|
||||
|
||||
$fullOutput = yield from $this->streamFinalAnswer($finalPrompt);
|
||||
|
||||
if ($factSources !== [] || $contextSignals !== []) {
|
||||
yield $this->emitSourceSummary(
|
||||
$factSources,
|
||||
$contextSignals,
|
||||
'Quellen und Signale'
|
||||
if ($sources !== []) {
|
||||
yield $this->emitSources(
|
||||
$sources,
|
||||
$this->agentRunnerConfig->getSourcesPrefix()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -266,11 +210,10 @@ final readonly class AgentRunner
|
||||
}
|
||||
|
||||
if ($fullOutput !== '') {
|
||||
$this->persistConversationState(
|
||||
userId: $userId,
|
||||
prompt: $prompt,
|
||||
fullOutput: $fullOutput,
|
||||
shopResults: $shopResults
|
||||
$this->contextService->appendHistory(
|
||||
$userId,
|
||||
$prompt,
|
||||
$fullOutput
|
||||
);
|
||||
}
|
||||
|
||||
@@ -281,7 +224,6 @@ final readonly class AgentRunner
|
||||
'commerceIntent' => $commerceIntent,
|
||||
'primaryShopResultsCount' => count($primaryShopResults),
|
||||
'shopResultsCount' => count($shopResults),
|
||||
'shopChecked' => $shopChecked,
|
||||
'attemptedShopRepair' => $attemptedShopRepair,
|
||||
'usedShopRepair' => $usedShopRepair,
|
||||
'shopRepairQueries' => $shopRepairQueries,
|
||||
@@ -292,8 +234,6 @@ final readonly class AgentRunner
|
||||
'shopSearchQuery' => $shopSearchQuery,
|
||||
'hasCommerceHistoryContext' => $commerceHistoryContext !== '',
|
||||
'commerceHistoryContextLength' => mb_strlen($commerceHistoryContext),
|
||||
'hasActiveCommerceReference' => $activeCommerceReference !== null,
|
||||
'activeCommerceReferenceProduct' => $activeCommerceReference?->productName,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
$this->agentLogger->error('Agent run failed', [
|
||||
@@ -361,42 +301,6 @@ final readonly class AgentRunner
|
||||
return $this->sanitizeOptimizedShopQuery($optimizedQuery);
|
||||
}
|
||||
|
||||
private function isReferenceOnlyCommerceFollowUp(
|
||||
string $prompt,
|
||||
?CommerceReferenceContext $referenceContext
|
||||
): bool {
|
||||
if ($referenceContext === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalizedPrompt = mb_strtolower(trim($prompt), 'UTF-8');
|
||||
$normalizedPrompt = preg_replace('/[^\p{L}\p{N}\s]+/u', ' ', $normalizedPrompt) ?? $normalizedPrompt;
|
||||
$normalizedPrompt = preg_replace('/\s+/u', ' ', $normalizedPrompt) ?? $normalizedPrompt;
|
||||
$normalizedPrompt = trim($normalizedPrompt);
|
||||
|
||||
if ($normalizedPrompt === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('/\b(testomat|lab|evo|eco|calc|thcl|808|2000)\b/u', $normalizedPrompt) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return preg_match(
|
||||
'/\b(preis|preise|kosten|kostet|dazu|dafuer|dafür|davon|was kostet das|verfuegbarkeit|verfügbarkeit|shop|link)\b/u',
|
||||
$normalizedPrompt
|
||||
) === 1;
|
||||
}
|
||||
|
||||
private function buildDeterministicReferenceShopQuery(?CommerceReferenceContext $referenceContext): string
|
||||
{
|
||||
if ($referenceContext === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trim($referenceContext->buildReferenceSearchText());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* results: array,
|
||||
@@ -445,15 +349,13 @@ final readonly class AgentRunner
|
||||
string $query,
|
||||
string $commerceIntent,
|
||||
string $userId,
|
||||
string $commerceHistoryContext = '',
|
||||
?CommerceReferenceContext $referenceContext = null
|
||||
string $commerceHistoryContext = ''
|
||||
): array {
|
||||
try {
|
||||
return $this->shopSearchService->search(
|
||||
$query,
|
||||
$commerceIntent,
|
||||
$commerceHistoryContext,
|
||||
$referenceContext
|
||||
$commerceHistoryContext
|
||||
);
|
||||
} catch (Throwable $e) {
|
||||
$this->agentLogger->warning('Shop search failed, continuing without shop results', [
|
||||
@@ -462,8 +364,6 @@ final readonly class AgentRunner
|
||||
'query' => $query,
|
||||
'hasCommerceHistoryContext' => $commerceHistoryContext !== '',
|
||||
'commerceHistoryContextLength' => mb_strlen($commerceHistoryContext),
|
||||
'hasReferenceContext' => $referenceContext !== null,
|
||||
'referenceProductName' => $referenceContext?->productName,
|
||||
'exception' => $e,
|
||||
]);
|
||||
|
||||
@@ -475,73 +375,23 @@ final readonly class AgentRunner
|
||||
{
|
||||
return $this->contextService->buildUserContextWithinBudget(
|
||||
$userId,
|
||||
self::COMMERCE_HISTORY_BUDGET_CHARS
|
||||
);
|
||||
}
|
||||
|
||||
private function loadCommerceReference(string $userId): ?CommerceReferenceContext
|
||||
{
|
||||
try {
|
||||
return $this->commerceReferenceStore->load($userId);
|
||||
} catch (Throwable $e) {
|
||||
$this->agentLogger->warning('Failed to load commerce reference context', [
|
||||
'userId' => $userId,
|
||||
'exception' => $e,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $shopResults
|
||||
*/
|
||||
private function storeCommerceReference(string $userId, string $prompt, string $answer, array $shopResults): void
|
||||
{
|
||||
try {
|
||||
$referenceContext = $this->commerceReferenceResolver->resolveFromCommerceTurn(
|
||||
$prompt,
|
||||
$answer,
|
||||
$shopResults
|
||||
);
|
||||
|
||||
if ($referenceContext === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->commerceReferenceStore->save($userId, $referenceContext);
|
||||
} catch (Throwable $e) {
|
||||
$this->agentLogger->warning('Failed to persist commerce reference context', [
|
||||
'userId' => $userId,
|
||||
'exception' => $e,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $shopResults
|
||||
*/
|
||||
private function persistConversationState(
|
||||
string $userId,
|
||||
string $prompt,
|
||||
string $fullOutput,
|
||||
array $shopResults
|
||||
): void {
|
||||
$this->contextService->appendHistory($userId, $prompt, $fullOutput);
|
||||
|
||||
$this->storeCommerceReference(
|
||||
userId: $userId,
|
||||
prompt: $prompt,
|
||||
answer: $fullOutput,
|
||||
shopResults: $shopResults
|
||||
$this->agentRunnerConfig->getCommerceHistoryBudgetChars()
|
||||
);
|
||||
}
|
||||
|
||||
private function limitKnowledgeChunks(array $knowledgeChunks, string $commerceIntent): array
|
||||
{
|
||||
return match ($commerceIntent) {
|
||||
CommerceIntentLite::PRODUCT_SEARCH => array_slice($knowledgeChunks, 0, 2),
|
||||
CommerceIntentLite::ADVISORY_PRODUCT_SEARCH => array_slice($knowledgeChunks, 0, 3),
|
||||
CommerceIntentLite::PRODUCT_SEARCH => array_slice(
|
||||
$knowledgeChunks,
|
||||
0,
|
||||
$this->agentRunnerConfig->getProductSearchKnowledgeChunkLimit()
|
||||
),
|
||||
CommerceIntentLite::ADVISORY_PRODUCT_SEARCH => array_slice(
|
||||
$knowledgeChunks,
|
||||
0,
|
||||
$this->agentRunnerConfig->getAdvisoryProductSearchKnowledgeChunkLimit()
|
||||
),
|
||||
default => $knowledgeChunks,
|
||||
};
|
||||
}
|
||||
@@ -555,8 +405,8 @@ final readonly class AgentRunner
|
||||
}
|
||||
|
||||
$query = preg_split('/\R+/u', $query, 2)[0] ?? $query;
|
||||
$query = preg_replace('/^(?:keywords?|suchquery|search\s*query|query)\s*:\s*/iu', '', $query) ?? $query;
|
||||
$query = trim($query, " \t\n\r\0\x0B\"'`");
|
||||
$query = preg_replace($this->agentRunnerConfig->getOptimizedShopQueryPrefixPattern(), '', $query) ?? $query;
|
||||
$query = trim($query, $this->agentRunnerConfig->getOptimizedShopQueryTrimCharacters());
|
||||
$query = preg_replace('/\s+/u', ' ', $query) ?? $query;
|
||||
|
||||
return trim($query);
|
||||
@@ -582,7 +432,7 @@ final readonly class AgentRunner
|
||||
|
||||
if ($cleanToken === '') {
|
||||
if ($firstThinkLoop) {
|
||||
yield $this->systemMsg('Denke nach...', 'think');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getThinkingWhileStreamingMessage(), 'think');
|
||||
$firstThinkLoop = false;
|
||||
}
|
||||
|
||||
@@ -601,60 +451,46 @@ final readonly class AgentRunner
|
||||
if ($finalChunk !== null) {
|
||||
yield $this->systemMsg($finalChunk, 'answer');
|
||||
} elseif ($fullOutput === '') {
|
||||
yield $this->systemMsg('❌ Es wurden keine Daten vom LLM empfangen.', 'err');
|
||||
yield $this->systemMsg($this->agentRunnerConfig->getNoLlmDataReceivedMessage(), 'err');
|
||||
}
|
||||
|
||||
return $fullOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $factSources
|
||||
* @param string[] $contextSignals
|
||||
* @param string[] $sources
|
||||
*/
|
||||
private function emitSourceSummary(array $factSources, array $contextSignals, string $label): string
|
||||
private function emitSources(array $sources, string $prefix): string
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
if ($factSources !== []) {
|
||||
$parts[] = 'Fakten: ' . implode(' ', $factSources);
|
||||
}
|
||||
|
||||
if ($contextSignals !== []) {
|
||||
$parts[] = 'Kontext: ' . implode(' ', $contextSignals);
|
||||
}
|
||||
|
||||
return $this->systemMsg(
|
||||
$label . ': ' . implode(' ', $parts),
|
||||
'info'
|
||||
);
|
||||
return $this->systemMsg($prefix . implode(' ', $sources), 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $target
|
||||
* @param string[] $sources
|
||||
*/
|
||||
private function addBadge(array &$target, string $label): void
|
||||
private function addSource(array &$sources, string $label): void
|
||||
{
|
||||
$badge = $this->badge($label);
|
||||
|
||||
if (!in_array($badge, $target, true)) {
|
||||
$target[] = $badge;
|
||||
if (!in_array($badge, $sources, true)) {
|
||||
$sources[] = $badge;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildUserErrorMessage(Throwable $e): string
|
||||
{
|
||||
if (!$this->debug) {
|
||||
return '❌ Bei der Verarbeitung der Anfrage ist ein interner Fehler aufgetreten.';
|
||||
return $this->agentRunnerConfig->getGenericInternalErrorMessage();
|
||||
}
|
||||
|
||||
return '❌ Interner Fehler: '
|
||||
return $this->agentRunnerConfig->getDebugInternalErrorPrefix()
|
||||
. htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
private function badge(string $label): string
|
||||
{
|
||||
return sprintf(
|
||||
'<span class="badge bg-info text-black">%s</span>',
|
||||
$this->agentRunnerConfig->getSourceBadgeHtmlTemplate(),
|
||||
htmlspecialchars($label, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
|
||||
);
|
||||
}
|
||||
@@ -667,10 +503,13 @@ final readonly class AgentRunner
|
||||
|
||||
return match ($type) {
|
||||
'answer' => $msg,
|
||||
'err' => '<span class="text-danger">' . $msg . "</span>\n<hr>\n",
|
||||
'think' => '<span class="text-info think">' . $msg . "</span>\n",
|
||||
'info' => "\n\n<span class=\"text-info fw-bolder\">" . $msg . "</span>\n",
|
||||
'debug' => "\n\nDEBUG: <code>" . htmlspecialchars($msg, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . "</code>\n",
|
||||
'err' => sprintf($this->agentRunnerConfig->getErrorHtmlTemplate(), $msg),
|
||||
'think' => sprintf($this->agentRunnerConfig->getThinkHtmlTemplate(), $msg),
|
||||
'info' => sprintf($this->agentRunnerConfig->getInfoHtmlTemplate(), $msg),
|
||||
'debug' => sprintf(
|
||||
$this->agentRunnerConfig->getDebugHtmlTemplate(),
|
||||
htmlspecialchars($msg, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')
|
||||
),
|
||||
default => $msg,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user