BxDolAiTrigger.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. interface iBxDolAiTrigger
  10. {
  11. public function getType();
  12. /**
  13. * Host entry point (alert, cron, webhook, HTTP).
  14. * @param mixed $mixed host-specific context
  15. */
  16. public function handle($mixed = null);
  17. /**
  18. * Run one agent. Used by handle() and background jobs.
  19. */
  20. public function call($aAgent, $mixedParams = []);
  21. }
  22. class BxDolAiTrigger extends BxDol implements iBxDolAiTrigger
  23. {
  24. protected $_sType = '';
  25. public static function getInstance($sType)
  26. {
  27. if ('chat' === $sType)
  28. $sType = 'manual';
  29. $aMap = [
  30. 'alert' => 'BxDolAiTriggerAlert',
  31. 'message' => 'BxDolAiTriggerMessage',
  32. 'webhook' => 'BxDolAiTriggerWebhook',
  33. 'scheduler' => 'BxDolAiTriggerScheduler',
  34. 'form-input' => 'BxDolAiTriggerFormInput',
  35. 'manual' => 'BxDolAiTriggerChat',
  36. ];
  37. $sClass = $aMap[$sType] ?? 'BxDolAiTrigger';
  38. $sKey = __CLASS__ . '_' . $sType;
  39. if (!isset($GLOBALS['bxDolClasses'][$sKey])) {
  40. $o = new $sClass();
  41. if ('BxDolAiTrigger' === $sClass)
  42. $o->_sType = $sType;
  43. $GLOBALS['bxDolClasses'][$sKey] = $o;
  44. }
  45. return $GLOBALS['bxDolClasses'][$sKey];
  46. }
  47. public function getType()
  48. {
  49. return $this->_sType;
  50. }
  51. public function handle($mixed = null)
  52. {
  53. return false;
  54. }
  55. public function call($aAgent, $mixedParams = [])
  56. {
  57. $oAi = $this->getAi();
  58. if (!$oAi)
  59. return false;
  60. if ($mixedParams)
  61. $sParams = is_string($mixedParams) ? $mixedParams : json_encode($mixedParams);
  62. else
  63. $sParams = 'START';
  64. $sSampleField = $this->getSampleField();
  65. if ($sSampleField && empty($aAgent[$sSampleField])) {
  66. $oDb = new BxDolAiQuery();
  67. $oDb->updateAgentField($aAgent['id'], $sSampleField, $sParams);
  68. }
  69. $aParams = $this->getCallChatHistoryParams($aAgent, $mixedParams);
  70. $sParams = $this->limitCallInput($aAgent, $mixedParams, $sParams);
  71. $oLimits = BxDolAiChatLimits::getInstance();
  72. $oChat = BxDolAiChat::getInstance();
  73. if ($this->appliesChatLimits()) {
  74. if ($oLimits->isChatTurnLimitReached($aAgent, $oLimits->getChatUserTurnCount($aAgent['id'], $aParams))) {
  75. $oChat->emitConversationClosed('limit', '', $aAgent, $aParams);
  76. return $this->getChatLimitReply($aAgent);
  77. }
  78. if ($oLimits->isChatSessionRateLimited($aAgent, $aParams))
  79. throw new Exception($oLimits->getChatSessionRateLimitError());
  80. }
  81. $oChat->setChatContext($aAgent, $aParams);
  82. $mixed = '';
  83. try {
  84. $o = BxDolAi::getAgentInstance($aAgent['id'], $aParams);
  85. if (!$o)
  86. return false;
  87. $oMessage = $o->chat(new NeuronAI\Chat\Messages\UserMessage($sParams))->getMessage();
  88. $mixed = $oMessage->getContent();
  89. } catch (Exception $exception) {
  90. bx_log('sys_agents', "Exception in '{$aAgent['name']}' agent: " . $exception->getMessage() . " INPUT:" . $sParams, BX_LOG_ERR);
  91. $mixed = isAdmin() ? self::visibleError($exception) : _t('_sys_agents_exception');
  92. }
  93. return $mixed;
  94. }
  95. /**
  96. * Provider failures keep their full text in the log. Operators see the
  97. * provider's own message, such as an authentication error.
  98. */
  99. public static function visibleError(Throwable $oException): string
  100. {
  101. $s = trim($oException->getMessage());
  102. if ($s === '')
  103. return _t('_sys_agents_exception');
  104. $iJson = bx_mb_strpos($s, '{');
  105. if ($iJson !== false) {
  106. $sJson = get_mb_substr($s, $iJson, get_mb_len($s) - $iJson);
  107. $a = json_decode($sJson, true);
  108. if (is_array($a)) {
  109. $sMessage = trim((string)($a['error']['message'] ?? ''));
  110. if ($sMessage !== '')
  111. return $sMessage;
  112. }
  113. }
  114. return $s;
  115. }
  116. protected function getAi()
  117. {
  118. return BxDolAi::getInstance();
  119. }
  120. protected function getSampleField()
  121. {
  122. return '';
  123. }
  124. protected function usesSenderChatHistory()
  125. {
  126. return false;
  127. }
  128. protected function appliesChatLimits()
  129. {
  130. return false;
  131. }
  132. /**
  133. * What call() returns when the thread is at max_turns; null = reply with nothing.
  134. */
  135. protected function getChatLimitReply($aAgent)
  136. {
  137. return BxDolAiChatLimits::getInstance()->getChatLimitMessage($aAgent);
  138. }
  139. protected function getCallChatHistoryParams($aAgent, $mixedParams)
  140. {
  141. if (!$this->usesSenderChatHistory() || !is_array($mixedParams))
  142. return [];
  143. return ['chat_history_subindex' => (int)($mixedParams['sender_profile_id'] ?? 0)];
  144. }
  145. protected function limitCallInput($aAgent, $mixedParams, $sParams)
  146. {
  147. if (!$this->appliesChatLimits())
  148. return $sParams;
  149. $oLimits = BxDolAiChatLimits::getInstance();
  150. if (is_array($mixedParams) && isset($mixedParams['message_text'])) {
  151. $mixedParams['message_text'] = $oLimits->applyChatInputLimit((string)$mixedParams['message_text'], $aAgent);
  152. return json_encode($mixedParams);
  153. }
  154. if (is_string($mixedParams))
  155. return $oLimits->applyChatInputLimit($sParams, $aAgent);
  156. return $sParams;
  157. }
  158. protected function enqueue($aAgent, $mixedParams)
  159. {
  160. BxDolBackgroundJobs::getInstance()->add(bin2hex(random_bytes(16)), [
  161. 'system', 'call_agent',
  162. [$this->getType(), $aAgent, $mixedParams],
  163. 'TemplServices'
  164. ]);
  165. }
  166. protected function runOrEnqueue($aAgent, $mixedParams)
  167. {
  168. if (!empty($aAgent['async'])) {
  169. $this->enqueue($aAgent, $mixedParams);
  170. return null;
  171. }
  172. return $this->call($aAgent, $mixedParams);
  173. }
  174. }
  175. /** @} */