Просмотр исходного кода

#5654 AI agents improvements (context related chats)

romanlesnikov 1 месяц назад
Родитель
Сommit
5df2ad2e37
3 измененных файлов с 123 добавлено и 8 удалено
  1. 84 1
      inc/classes/BxDolAI.php
  2. 1 4
      inc/classes/BxDolAiAgent.php
  3. 38 3
      template/scripts/BxBaseServices.php

+ 84 - 1
inc/classes/BxDolAI.php

@@ -527,6 +527,89 @@ class BxDolAI extends BxDolFactory implements iBxDolSingleton
         ];
         ];
     }
     }
 
 
+    /**
+     * `{trigger}:{agentId}:{contextPid}:{userSubindex}`.
+     * Context is omitted when 0 so existing site-wide threads still load.
+     * User suffix stays last so adoptGuestChatHistory keeps matching.
+     */
+    public static function chatHistoryThreadId($aAgent, $aParams = [])
+    {
+        $s = ($aAgent['trigger'] ?? '') . ':' . ($aAgent['id'] ?? '');
+        $iContextPid = (int)($aParams['chat_history_context_pid'] ?? 0);
+        if ($iContextPid > 0)
+            $s .= ':' . $iContextPid;
+        if (isset($aParams['chat_history_subindex']) && $aParams['chat_history_subindex'] !== '')
+            $s .= ':' . (string)$aParams['chat_history_subindex'];
+        return $s;
+    }
+
+    /**
+     * Context from chat HTTP (`?context=`). Missing/0 = site-wide. Invalid/unviewable = false (403).
+     * @return int|false
+     */
+    public function resolveChatHistoryContextPid()
+    {
+        $mixed = bx_get('context');
+        if ($mixed === false)
+            return 0;
+
+        $iPid = (int)$mixed;
+        if ($iPid <= 0)
+            return 0;
+
+        $oProfile = BxDolProfile::getInstance($iPid);
+        if (!$oProfile || !bx_srv('system', 'is_module_context', [$oProfile->getModule()]))
+            return false;
+
+        if ($oProfile->checkAllowedProfileView() !== CHECK_ACTION_RESULT_ALLOWED)
+            return false;
+
+        return $iPid;
+    }
+
+    /**
+     * Context of the current page (AI agent block JSON).
+     * Prefer `bx_get_page_info()`; API page JSON falls back to $_GET after getPageAPI merges params.
+     */
+    public function resolveChatHistoryContextPidFromPage()
+    {
+        if (function_exists('bx_get_page_info')) {
+            $aInfo = bx_get_page_info();
+            if ($aInfo && !empty($aInfo['context_profile_id']))
+                return $this->filterViewableContextPid((int)$aInfo['context_profile_id']);
+        }
+
+        if (($iPid = (int)bx_process_input(bx_get('profile_id'), BX_DATA_INT)) > 0) {
+            $oProfile = BxDolProfile::getInstance($iPid);
+            if ($oProfile && bx_srv('system', 'is_module_context', [$oProfile->getModule()]))
+                return $this->filterViewableContextPid((int)$oProfile->id());
+        }
+
+        $oPage = BxDolPage::getObjectInstanceByURI();
+        $sModule = $oPage ? (string)$oPage->getModule() : '';
+        $iContentId = (int)bx_process_input(bx_get('id'), BX_DATA_INT);
+        if ($iContentId > 0 && $sModule !== '' && bx_srv('system', 'is_module_context', [$sModule])) {
+            $oProfile = BxDolProfile::getInstanceByContentAndType($iContentId, $sModule);
+            if ($oProfile)
+                return $this->filterViewableContextPid((int)$oProfile->id());
+        }
+
+        return 0;
+    }
+
+    protected function filterViewableContextPid($iPid)
+    {
+        $iPid = (int)$iPid;
+        if ($iPid <= 0)
+            return 0;
+
+        $oProfile = BxDolProfile::getInstance($iPid);
+        if (!$oProfile || $oProfile->checkAllowedProfileView() !== CHECK_ACTION_RESULT_ALLOWED)
+            return 0;
+
+        return $iPid;
+    }
+
     /**
     /**
      * Transcript for TanStack `useChat` hydrate / `initialMessages`.
      * Transcript for TanStack `useChat` hydrate / `initialMessages`.
      * Loads the same NeuronAI chat history the agent uses when streaming.
      * Loads the same NeuronAI chat history the agent uses when streaming.
@@ -540,7 +623,7 @@ class BxDolAI extends BxDolFactory implements iBxDolSingleton
         if (!$aAgent || empty($aAgent['chat_history_context']))
         if (!$aAgent || empty($aAgent['chat_history_context']))
             return [];
             return [];
 
 
-        $sThreadId = $aAgent['trigger'] . ':' . $aAgent['id'] . ':' . $aParams['chat_history_subindex'];
+        $sThreadId = self::chatHistoryThreadId($aAgent, $aParams);
         $sJson = $this->_oDb->getOne("SELECT `messages` FROM `sys_agents_chat_history` WHERE `thread_id` = :t", [
         $sJson = $this->_oDb->getOne("SELECT `messages` FROM `sys_agents_chat_history` WHERE `thread_id` = :t", [
             't' => $sThreadId,
             't' => $sThreadId,
         ]);
         ]);

+ 1 - 4
inc/classes/BxDolAiAgent.php

@@ -123,10 +123,7 @@ class BxDolAiAgent extends RAG
 
 
     protected function getСhatHistoryThreadId(): string
     protected function getСhatHistoryThreadId(): string
     {
     {
-        $s = $this->aAgent['trigger'] . ':' . $this->aAgent['id'];
-        if (isset($this->aParams['chat_history_subindex']) && $this->aParams['chat_history_subindex'] !== '')
-            $s .= ':' . (string)$this->aParams['chat_history_subindex'];
-        return $s;
+        return BxDolAI::chatHistoryThreadId($this->aAgent, $this->aParams);
     }
     }
 
 
     protected function getAlertTriggerModifyableKeys(): string
     protected function getAlertTriggerModifyableKeys(): string

+ 38 - 3
template/scripts/BxBaseServices.php

@@ -861,7 +861,16 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
         if(!$aAgent || !is_array($aAgent))
         if(!$aAgent || !is_array($aAgent))
             return $bIsApi ? [] : '';
             return $bIsApi ? [] : '';
 
 
-        return $bIsApi ? [bx_api_get_block('ai_agent', ['agent_id' => $iId])] : BxDolTemplate::getInstance()->parseHtmlByName('agent_ai_chat.html', [
+        $iContextPid = 0;
+        $oAi = BxDolAI::getInstance();
+        if ($oAi)
+            $iContextPid = (int)$oAi->resolveChatHistoryContextPidFromPage();
+
+        return $bIsApi ? [bx_api_get_block('ai_agent', [
+            'agent_id' => $iId,
+            'agent_profile' => BxDolProfile::getData($aAgent['profile_id']),
+            'context_profile_id' => $iContextPid,
+        ])] : BxDolTemplate::getInstance()->parseHtmlByName('agent_ai_chat.html', [
             'id' => $iId
             'id' => $iId
         ]);
         ]);
     }
     }
@@ -1827,7 +1836,11 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
         if (!$sPrompt)
         if (!$sPrompt)
             return echoJson(['code' => 400, 'msg' => _t('_sys_agents_json_field_err')]);
             return echoJson(['code' => 400, 'msg' => _t('_sys_agents_json_field_err')]);
 
 
-        $oAi->streamAgentChat($aAgent['id'], $sPrompt, $oAi->resolveChatHistoryParams(), $aData['threadId'] ?? null);
+        $aParams = $this->_aiChatHistoryParams();
+        if (!$aParams)
+            return;
+
+        $oAi->streamAgentChat($aAgent['id'], $sPrompt, $aParams, $aData['threadId'] ?? null);
     }
     }
 
 
     /**
     /**
@@ -1844,13 +1857,35 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
             return;
             return;
 
 
         $oAi = BxDolAI::getInstance();
         $oAi = BxDolAI::getInstance();
+        $aParams = $this->_aiChatHistoryParams();
+        if (!$aParams)
+            return;
+
         return echoJson([
         return echoJson([
-            'messages' => $oAi->getChatHistoryUiMessages($aAgent['id'], $oAi->resolveChatHistoryParams()),
+            'messages' => $oAi->getChatHistoryUiMessages($aAgent['id'], $aParams),
             'activeRun' => null,
             'activeRun' => null,
             'interrupts' => null,
             'interrupts' => null,
         ]);
         ]);
     }
     }
 
 
+    /**
+     * Member/guest subindex + optional `?context=` (group/org profile id).
+     * Invalid context is 403 — never fall back to the site-wide thread.
+     */
+    protected function _aiChatHistoryParams()
+    {
+        $oAi = BxDolAI::getInstance();
+        $mixedContext = $oAi->resolveChatHistoryContextPid();
+        if ($mixedContext === false) {
+            echoJson(['code' => 403, 'msg' => _t('_sys_agents_unauthorized')]);
+            return false;
+        }
+
+        $aParams = $oAi->resolveChatHistoryParams();
+        $aParams['chat_history_context_pid'] = (int)$mixedContext;
+        return $aParams;
+    }
+
     protected function _aiChatLoadAgent($iAgentId)
     protected function _aiChatLoadAgent($iAgentId)
     {
     {
         $iAgentId = (int)$iAgentId;
         $iAgentId = (int)$iAgentId;