BxDolAi.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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 BxDolAi extends BxDolFactory implements iBxDolSingleton
  10. {
  11. protected $_oDb;
  12. protected $_iProfileId;
  13. protected $_aAgentProfiles = [];
  14. protected function __construct()
  15. {
  16. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  17. trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
  18. parent::__construct();
  19. $this->_oDb = new BxDolAiQuery();
  20. $this->_iProfileId = (int)getParam('sys_profile_bot');
  21. }
  22. /**
  23. * Prevent cloning the instance
  24. */
  25. public function __clone()
  26. {
  27. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  28. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  29. }
  30. /**
  31. * Get singleton instance of the class
  32. */
  33. public static function getInstance()
  34. {
  35. if (!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
  36. $GLOBALS['bxDolClasses'][__CLASS__] = BxDolDb::getInstance()->isTableExists('sys_agents_agents') ? new BxDolAi() : null;
  37. }
  38. return $GLOBALS['bxDolClasses'][__CLASS__];
  39. }
  40. public static function getAgentInstance(int $iId, array $aParams = []): NeuronAI\Agent\Agent
  41. {
  42. if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_Agent_' . $iId]))
  43. return $GLOBALS['bxDolClasses'][__CLASS__ . '_Agent_' . $iId];
  44. $a = BxDolAiQuery::getAgentObject($iId);
  45. if (!$a) {
  46. $s = "Agent with id {$iId} not found";
  47. bx_log('sys_agents', $s, BX_LOG_ERR);
  48. throw new Exception($s);
  49. }
  50. if (!$a['active']) {
  51. $s = "Agent with id {$iId} isn't active";
  52. bx_log('sys_agents', $s, BX_LOG_ERR);
  53. throw new Exception($s);
  54. }
  55. if (!$a['prompt_system'] || !$a['model_id']) {
  56. $s = "Agent with id {$iId} can't be used because it haven't prompt or AI model";
  57. bx_log('sys_agents', $s, BX_LOG_ERR);
  58. throw new Exception($s);
  59. }
  60. $aVectorStore = false;
  61. if ($a['vector_store_id']) {
  62. $aVectorStore = BxDolAiQuery::getVectorStoreObject($a['vector_store_id']);
  63. if (is_array($aVectorStore) && (string)($aVectorStore['type'] ?? '') === 'una-proxy')
  64. $a['vector_store_type'] = 'una-proxy';
  65. }
  66. $o = BxDolAiAgent::make($a, $aParams);
  67. if ((int)$a['tools_max_run'] > 0)
  68. $o->toolMaxRuns($a['tools_max_run']);
  69. if (is_array($aVectorStore) && ($a['vector_store_type'] ?? '') !== 'una-proxy' && !empty($aVectorStore['embedding_provider_id'])) {
  70. $oEmbedder = self::getAiEmbeddingsProviderInstance($aVectorStore['embedding_provider_id']);
  71. $o->setEmbeddingsProvider($oEmbedder);
  72. }
  73. $GLOBALS['bxDolClasses'][__CLASS__ . '_Agent_' . $iId] = $o;
  74. return $o;
  75. }
  76. public static function getAiProviderInstance(int $iId):NeuronAI\Providers\AIProviderInterface
  77. {
  78. return BxDolAIModelFactory::getModelInstance($iId);
  79. }
  80. public static function getAiEmbeddingsProviderInstance(int $iId):NeuronAI\RAG\Embeddings\EmbeddingsProviderInterface
  81. {
  82. return BxDolAIModelFactory::getModelInstance($iId);
  83. }
  84. public function getProfileId()
  85. {
  86. return $this->_iProfileId;
  87. }
  88. public function getModels($aParams = [])
  89. {
  90. $aParamsDb = ['sample' => 'all_pairs'];
  91. if(isset($aParams['active']))
  92. $aParamsDb['active'] = $aParams['active'] === true ? 1 : 0;
  93. if(isset($aParams['capabilities']))
  94. $aParamsDb['capabilities'] = $aParams['capabilities'];
  95. return $this->_oDb->getModelsBy($aParamsDb);
  96. }
  97. public function callAgent($sType, $aAgent, $mixedParams = [])
  98. {
  99. return BxDolAiTrigger::getInstance($sType)->call($aAgent, $mixedParams);
  100. }
  101. /**
  102. * Whether the current (or given) member may interact with the agent
  103. * via chat, messenger, or form-input. Empty acl_levels means Nobody.
  104. * Site admins / Studio operators always may, so they can debug.
  105. */
  106. public function canInteract($aAgent, $iProfileId = false, $bAllowOperators = true)
  107. {
  108. if (!is_array($aAgent) || empty($aAgent['id']))
  109. return false;
  110. if ($bAllowOperators && isAdmin())
  111. return true;
  112. $iLevels = (int)($aAgent['acl_levels'] ?? 0);
  113. if (!$iLevels)
  114. return false;
  115. return (bool)BxDolAcl::getInstance()->isMemberLevelInSet($iLevels, $iProfileId);
  116. }
  117. /**
  118. * Direct HTTP chat (`sys-ai-chat`). Operators may talk to any trigger;
  119. * everyone else only `manual` and `message` agents they can interact with.
  120. */
  121. public function canChatDirectly($aAgent, $iProfileId = false)
  122. {
  123. if (!$this->canInteract($aAgent, $iProfileId))
  124. return false;
  125. if (isAdmin())
  126. return true;
  127. return in_array($aAgent['trigger'] ?? '', ['manual', 'message'], true);
  128. }
  129. public function extractChatPromptFromRequest($aData)
  130. {
  131. return BxDolAiTrigger::getInstance('chat')->extractPromptFromRequest($aData);
  132. }
  133. public function streamAgentChat($iAgentId, $sPrompt, $aParams = [], $sThreadId = null)
  134. {
  135. return BxDolAiTrigger::getInstance('chat')->stream($iAgentId, $sPrompt, $aParams, $sThreadId);
  136. }
  137. public function sendMessengerMessage($iSender, $iRecipient, $sMsg)
  138. {
  139. return BxDolAiTrigger::getInstance('message')->sendMessengerMessage($iSender, $iRecipient, $sMsg);
  140. }
  141. public function getAgentsByAlertUnitAndAction($sUnit, $sAction)
  142. {
  143. return BxDolAiTrigger::getInstance('alert')->getAgentsByUnitAndAction($sUnit, $sAction);
  144. }
  145. public function getAgentsBy($aParams)
  146. {
  147. return $this->_oDb->getAgentsBy($aParams);
  148. }
  149. public function getAgentsByProfileId($iProfileId)
  150. {
  151. return BxDolAiTrigger::getInstance('message')->getAgentsByProfileId($iProfileId);
  152. }
  153. public function getAgentsByFormObject($sFormObject)
  154. {
  155. return $this->_oDb->getAgentsByFormObject($sFormObject);
  156. }
  157. public function getAgentsByTriggerType($sTrigger)
  158. {
  159. return $this->_oDb->getAgentsByTriggerType($sTrigger);
  160. }
  161. public function getAgentByTriggerWebhookKey($sKey)
  162. {
  163. return BxDolAiTrigger::getInstance('webhook')->getAgentByKey($sKey);
  164. }
  165. /**
  166. * Profile is used by an active agent (any trigger). Cached per request.
  167. */
  168. public function isAgentProfile($iProfileId)
  169. {
  170. $iProfileId = (int)$iProfileId;
  171. if ($iProfileId <= 0)
  172. return false;
  173. if (!array_key_exists($iProfileId, $this->_aAgentProfiles))
  174. $this->_aAgentProfiles[$iProfileId] = $this->_oDb->isAgentProfile($iProfileId);
  175. return $this->_aAgentProfiles[$iProfileId];
  176. }
  177. }
  178. /** @} */