control.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /**
  3. * The control file of zai module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2025 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author tenghuaian <tenghuaian@chandao.com>
  8. * @link https://www.zentao.net
  9. */
  10. class zai extends control
  11. {
  12. /**
  13. * 配置ZAI。
  14. * Configure ZAI.
  15. *
  16. * @param string $mode
  17. * @access public
  18. * @return void
  19. */
  20. public function setting($mode = 'view')
  21. {
  22. if(!empty($_POST))
  23. {
  24. $setting = new stdClass();
  25. $setting->appID = trim($_POST['appID']);
  26. $setting->host = trim($_POST['host']);
  27. $setting->port = trim($_POST['port']);
  28. $setting->token = trim($_POST['token']);
  29. $setting->adminToken = trim($_POST['adminToken']);
  30. if(empty($setting->host)) $setting = null;
  31. $this->zai->setSetting($setting);
  32. if(dao::isError()) return $this->sendError(dao::getError());
  33. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink('zai', 'setting')));
  34. }
  35. $setting = $this->zai->getSetting(true);
  36. if($mode == 'view')
  37. {
  38. if(!empty($setting->token)) $setting->token = str_repeat('*', strlen($setting->token));
  39. if(!empty($setting->adminToken)) $setting->adminToken = str_repeat('*', strlen($setting->adminToken));
  40. }
  41. $this->view->title = $this->lang->zai->setting;
  42. $this->view->setting = $setting;
  43. $this->view->mode = $mode;
  44. $this->display();
  45. }
  46. /**
  47. * Ajax: 获取当前用户的ZAI Authorization token。
  48. * Ajax: Get ZAI Authorization Token of current user.
  49. *
  50. * @access public
  51. * @return void
  52. */
  53. public function ajaxGetToken()
  54. {
  55. return $this->send($this->zai->getToken());
  56. }
  57. /**
  58. * 禅道数据向量化。
  59. * Vectorized data of ZenTao.
  60. *
  61. * @access public
  62. * @return void
  63. */
  64. public function vectorized()
  65. {
  66. $info = $this->zai->getVectorizedInfo();
  67. $this->view->info = $info;
  68. $this->view->title = $this->lang->zai->vectorized;
  69. $this->view->zaiSetting = $this->zai->getSetting();
  70. $this->view->syncTypes = zaiModel::getSyncTypes();
  71. $this->display();
  72. }
  73. /**
  74. * Ajax: 启用数据向量化。
  75. * Ajax: Enable data vectorization.
  76. *
  77. * @access public
  78. * @return void
  79. */
  80. public function ajaxEnableVectorization()
  81. {
  82. if($_SERVER['REQUEST_METHOD'] !== 'POST')
  83. {
  84. return $this->send(array('result' => 'fail', 'message' => $this->lang->zai->onlyPostRequest));
  85. }
  86. $force = isset($_POST['force']) && $_POST['force'] === 'true';
  87. $result = $this->zai->enableVectorization($force);
  88. return $this->send($result);
  89. }
  90. /**
  91. * Ajax: 同步禅道向量化数据到 ZAI 知识库。
  92. * Ajax: Sync vectorized data of ZenTao to ZAI knowledge base.
  93. *
  94. * @access public
  95. * @return void
  96. */
  97. public function ajaxSyncVectorization()
  98. {
  99. $info = $this->zai->getVectorizedInfo();
  100. if($_SERVER['REQUEST_METHOD'] !== 'POST')
  101. {
  102. return $this->send(array('result' => 'success', 'data' => $info));
  103. }
  104. if(empty($info->key)) return $this->send(array('result' => 'fail', 'message' => $this->lang->zai->vectorizedUnavailableHint, 'data' => $info));
  105. $force = isset($_POST['force']) && $_POST['force'] === 'true';
  106. if($info->status === 'synced' && $force)
  107. {
  108. $info->status = 'wait';
  109. $info->syncedTime = 0;
  110. $info->syncedCount = 0;
  111. $info->syncFailedCount = 0;
  112. $info->syncTime = 0;
  113. $info->syncingType = zaiModel::getNextSyncType();
  114. $info->syncingID = 0;
  115. $info->syncDetails = new stdClass();
  116. }
  117. if($info->status !== 'wait' && $info->status !== 'syncing')
  118. {
  119. return $this->send(array('result' => 'success', 'data' => $info));
  120. }
  121. if($info->status !== 'syncing')
  122. {
  123. $info->status = 'syncing';
  124. $info->synced = 0;
  125. }
  126. $startSyncTime = microtime(true) * 1000;
  127. $info->syncTime = time();
  128. $result = $this->zai->syncNextTarget($info->key, $info->syncingType, $info->syncingID);
  129. $syncingType = $info->syncingType;
  130. if(!isset($info->syncDetails->$syncingType))
  131. {
  132. $syncDetail = new stdClass();
  133. $syncDetail->failed = 0;
  134. $syncDetail->synced = 0;
  135. $info->syncDetails->$syncingType = $syncDetail;
  136. }
  137. if($result)
  138. {
  139. if(isset($result['fatal']) && $result['fatal'])
  140. {
  141. $info->status = 'synced';
  142. $info->syncFailedCount++;
  143. $info->syncDetails->$syncingType->failed++;
  144. $this->zai->setVectorizedInfo($info);
  145. return $this->send(array('result' => 'fail', 'message' => $result['message'], 'data' => $info, 'request' => $this->app->config->debug > 5 ? $result : null));
  146. }
  147. if($result['result'] == 'success')
  148. {
  149. $info->syncedCount++;
  150. $info->syncDetails->$syncingType->synced++;
  151. }
  152. else
  153. {
  154. $info->syncFailedCount++;
  155. $info->syncDetails->$syncingType->failed++;
  156. }
  157. $info->syncingID = (isset($result['id']) ? $result['id'] : $info->syncingID) + 1;
  158. $info->lastSync = ['time' => (microtime(true) * 1000) - $startSyncTime, 'contentLength' => isset($result['syncedData']) ? strlen($result['syncedData']['content']) : 0, 'type' => $info->syncingType, 'id' => $info->syncingID];
  159. }
  160. else
  161. {
  162. $nextSyncType = zaiModel::getNextSyncType($syncingType);
  163. if(empty($nextSyncType))
  164. {
  165. $info->status = 'synced';
  166. $info->syncedTime = $info->syncTime;
  167. $info->syncingID = 0;
  168. $info->syncingType = zaiModel::getNextSyncType();
  169. }
  170. else
  171. {
  172. $info->syncingType = $nextSyncType;
  173. $info->syncingID = 0;
  174. }
  175. }
  176. $this->zai->setVectorizedInfo($info);
  177. unset($info->key);
  178. return $this->send(array('result' => 'success', 'data' => $info));
  179. }
  180. /**
  181. * Ajax: 搜索知识库。
  182. * Ajax: Search knowledge base.
  183. *
  184. * @param string $type 'chunk'(块) | 'content'(内容)
  185. * @param int $limit
  186. * @access public
  187. * @return void
  188. */
  189. public function ajaxSearchKnowledges($type = 'chunk', $limit = 5)
  190. {
  191. if($_SERVER['REQUEST_METHOD'] !== 'POST')
  192. {
  193. return $this->send(array('result' => 'fail', 'message' => $this->lang->zai->onlyPostRequest));
  194. }
  195. $userPrompt = zget($_POST, 'userPrompt', '');
  196. $filters = json_decode(zget($_POST, 'filters', '{}'), true);
  197. if(empty($userPrompt) || empty($filters)) return $this->send(array('result' => 'fail', 'message' => $this->lang->fail));
  198. $knowledges = $this->zai->searchKnowledgesInCollections($userPrompt, $filters, $type, $limit, 0.5);
  199. $results = [];
  200. foreach($knowledges as $knowledge)
  201. {
  202. if($type === 'chunk')
  203. {
  204. $results[] = ['key' => $knowledge['content_key'], 'similarity' => $knowledge['similarity'], 'id' => $knowledge['chunk_id'], 'content' => $knowledge['chunk_content'], 'attrs' => $knowledge['content_attrs']];
  205. }
  206. else
  207. {
  208. $results[] = ['key' => $knowledge['key'], 'similarity' => $knowledge['similarity'], 'id' => $knowledge['id'], 'content' => $knowledge['content'], 'attrs' => $knowledge['attrs']];
  209. }
  210. if(count($results) >= $limit) break;
  211. }
  212. return $this->send(array('result' => 'success', 'data' => $results));
  213. }
  214. }