BxDolAiChatLimits.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. class BxDolAiChatLimits
  10. {
  11. protected $_oDb;
  12. public static function getInstance()
  13. {
  14. if (!isset($GLOBALS['bxDolClasses'][__CLASS__]))
  15. $GLOBALS['bxDolClasses'][__CLASS__] = new self();
  16. return $GLOBALS['bxDolClasses'][__CLASS__];
  17. }
  18. public function __construct()
  19. {
  20. $this->_oDb = new BxDolAiQuery();
  21. }
  22. /**
  23. * Runner limits from the agent row. 0 = no cap. Never put these in the prompt.
  24. */
  25. public function applyChatInputLimit($sPrompt, $aAgent)
  26. {
  27. $sPrompt = (string)$sPrompt;
  28. $iMax = (int)($aAgent['max_input_chars'] ?? 0);
  29. if ($iMax <= 0)
  30. return $sPrompt;
  31. if (function_exists('mb_strlen') && function_exists('mb_substr')) {
  32. if (mb_strlen($sPrompt, 'UTF-8') > $iMax)
  33. return mb_substr($sPrompt, 0, $iMax, 'UTF-8');
  34. return $sPrompt;
  35. }
  36. return strlen($sPrompt) > $iMax ? substr($sPrompt, 0, $iMax) : $sPrompt;
  37. }
  38. public function getChatLimitMessage($aAgent)
  39. {
  40. $s = trim((string)($aAgent['limit_message'] ?? ''));
  41. return $s !== '' ? $s : 'This chat has reached its limit.';
  42. }
  43. public function getChatSessionRateLimitError()
  44. {
  45. $s = _t('_sys_agents_err_session_rate_limit');
  46. return ($s !== '' && $s !== '_sys_agents_err_session_rate_limit')
  47. ? $s
  48. : 'Too many chat sessions from this IP. Try again later.';
  49. }
  50. public function countChatUserTurns($aUiMessages)
  51. {
  52. $i = 0;
  53. if (!is_array($aUiMessages))
  54. return 0;
  55. foreach ($aUiMessages as $aMessage) {
  56. if (is_array($aMessage) && ($aMessage['role'] ?? '') === 'user')
  57. $i++;
  58. }
  59. return $i;
  60. }
  61. public function isChatTurnLimitReached($aAgent, $iTurns, $iRequestUserTurns = 0)
  62. {
  63. $iMax = (int)($aAgent['max_turns'] ?? 0);
  64. if ($iMax <= 0)
  65. return false;
  66. if ((int)$iTurns >= $iMax)
  67. return true;
  68. // Client transcript includes the message being submitted.
  69. return (int)$iRequestUserTurns > $iMax;
  70. }
  71. public function getChatUserTurnCount($iAgentId, $aParams = [])
  72. {
  73. if (!isset($aParams['chat_history_subindex']))
  74. $aParams = array_merge(BxDolAiChat::getInstance()->resolveChatHistoryParams($iAgentId), $aParams);
  75. try {
  76. $o = BxDolAi::getAgentInstance((int)$iAgentId, $aParams);
  77. } catch (Throwable $oException) {
  78. return 0;
  79. }
  80. $i = 0;
  81. foreach ($o->getChatHistory()->getMessages() as $oMessage) {
  82. if ($oMessage instanceof NeuronAI\Chat\Messages\ToolCallMessage
  83. || $oMessage instanceof NeuronAI\Chat\Messages\ToolResultMessage
  84. ) {
  85. continue;
  86. }
  87. if ($oMessage instanceof NeuronAI\Chat\Messages\UserMessage)
  88. $i++;
  89. }
  90. return $i;
  91. }
  92. /**
  93. * New chat threads per IP for this agent. 0 on the agent = no cap.
  94. * Continuation of an existing thread is not a new session.
  95. */
  96. public function isChatSessionRateLimited($aAgent, $aParams = [])
  97. {
  98. $iHour = (int)($aAgent['max_sessions_per_hour'] ?? 0);
  99. $iDay = (int)($aAgent['max_sessions_per_day'] ?? 0);
  100. if ($iHour <= 0 && $iDay <= 0)
  101. return false;
  102. if (!isset($aParams['chat_history_subindex']))
  103. $aParams = array_merge(BxDolAiChat::getInstance()->resolveChatHistoryParams((int)($aAgent['id'] ?? 0)), $aParams);
  104. $sThreadId = BxDolAiChat::threadId($aAgent, $aParams);
  105. if (!$this->_oDb->isNewChatSession($sThreadId))
  106. return false;
  107. $sIp = $this->getVisitorIpHash();
  108. if ($sIp === '0' || $sIp === '')
  109. return false;
  110. $sPrefix = ($aAgent['trigger'] ?? '') . ':' . ($aAgent['id'] ?? '');
  111. if ($sPrefix === ':')
  112. return false;
  113. if ($iHour > 0 && $this->_oDb->countChatSessionsByIp($sIp, $sPrefix, 3600) >= $iHour)
  114. return true;
  115. if ($iDay > 0 && $this->_oDb->countChatSessionsByIp($sIp, $sPrefix, 86400) >= $iDay)
  116. return true;
  117. return false;
  118. }
  119. protected function getVisitorIpHash()
  120. {
  121. if (!function_exists('getVisitorIP') || !function_exists('bx_get_ip_hash'))
  122. return '0';
  123. return (string)bx_get_ip_hash(getVisitorIP());
  124. }
  125. }
  126. /** @} */