control.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <?php
  2. /**
  3. * The control file of ai module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Wenrui LI <liwenrui@easycorp.ltd>
  8. * @package ai
  9. * @link https://www.zentao.net
  10. */
  11. class ai extends control
  12. {
  13. /**
  14. * AI model.
  15. *
  16. * @var aiModel
  17. * @access public
  18. */
  19. public $ai;
  20. /**
  21. * User model.
  22. *
  23. * @var userModel
  24. * @access public
  25. */
  26. public $user;
  27. public function __construct($module = '', $method = '')
  28. {
  29. parent::__construct($module, $method);
  30. $this->loadModel('user');
  31. }
  32. /**
  33. * Check for user's privs and model configs, redirect to most relevant page.
  34. *
  35. * @access public
  36. * @return void
  37. */
  38. public function adminIndex()
  39. {
  40. /* Redirect to model edit ifuser has priv and model is unconfigured. */
  41. if(commonModel::hasPriv('ai', 'models') && !$this->ai->hasModelsAvailable()) return $this->locate($this->createLink('ai', 'models'));
  42. /* Redirect to prompts ifuser has priv. */
  43. if(commonModel::hasPriv('ai', 'prompts')) return $this->locate($this->createLink('ai', 'prompts'));
  44. /* Redirect to miniPrograms ifuser has priv. */
  45. if(commonModel::hasPriv('ai', 'miniPrograms')) return $this->locate($this->createLink('ai', 'miniPrograms'));
  46. /* Redirect to models ifuser has priv. */
  47. if(commonModel::hasPriv('ai', 'models')) return $this->locate($this->createLink('ai', 'models'));
  48. /* Redirect to assistants ifuser has priv. */
  49. if(commonModel::hasPriv('ai', 'assistants')) return $this->locate($this->createLink('ai', 'assistants'));
  50. /* User has no priv, deny access. */
  51. return $this->send(array('result' => 'fail', 'message' => $this->lang->error->accessDenied, 'locate' => 'back'));
  52. }
  53. /**
  54. * List mini programs.
  55. *
  56. * @access public
  57. * @return void
  58. */
  59. public function miniPrograms($category = '', $status = '', $orderBy = 'createdDate_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
  60. {
  61. $this->lang->aiapp->menu->generalAgent['subModule'] = 'ai';
  62. $this->app->loadClass('pager', true);
  63. $pager = new pager($recTotal, $recPerPage, $pageID);
  64. $order = common::appendOrder($orderBy);
  65. $categoryList = array_merge($this->lang->ai->miniPrograms->categoryList, $this->ai->getCustomCategories());
  66. $programs = $this->ai->getMiniPrograms($category, $status, $order, $pager);
  67. foreach($programs as $program)
  68. {
  69. $program->canPublish = empty($program->published) && $this->ai->canPublishMiniProgram($program);
  70. $program->createdByLabel = $program->createdBy === 'system' ? $this->lang->admin->system : $this->loadModel('user')->getById($program->createdBy, 'account')->realname;
  71. $program->categoryLabel = $categoryList[$program->category];
  72. $program->publishedLabel = $program->published == '1'
  73. ? $this->lang->ai->miniPrograms->statuses['active']
  74. : $this->lang->ai->miniPrograms->statuses['draft'];
  75. }
  76. $this->app->loadLang('aiapp');
  77. $this->view->title = $this->lang->aiapp->manageGeneralAgent;
  78. $this->view->miniPrograms = $programs;
  79. $this->view->category = $category;
  80. $this->view->categoryList = $categoryList;
  81. $this->view->status = $status;
  82. $this->view->orderBy = $orderBy;
  83. $this->view->pager = $pager;
  84. $this->display();
  85. }
  86. /**
  87. * Edit mini program category.
  88. *
  89. * @access public
  90. * @return void
  91. */
  92. public function editMiniProgramCategory()
  93. {
  94. $this->lang->aiapp->menu->generalAgent['subModule'] = 'ai';
  95. if(!empty($_POST))
  96. {
  97. $_POST = array_filter($_POST, function($key)
  98. {
  99. return strpos($key, 'custom') === 0;
  100. }, ARRAY_FILTER_USE_KEY);
  101. if($this->ai->checkDuplicatedCategory()) return $this->sendError($this->lang->ai->maintenanceGroupDuplicated);
  102. $this->ai->updateCustomCategories();
  103. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
  104. }
  105. $this->view->usedCustomCategories = $this->ai->getUsedCustomCategories();
  106. $this->view->categoryList = $this->ai->getCustomCategories();
  107. $this->view->title = $this->lang->ai->miniPrograms->common;
  108. $this->display();
  109. }
  110. /**
  111. * Publish a mini program.
  112. *
  113. * @param string $appID
  114. * @access public
  115. * @return void
  116. */
  117. public function publishMiniProgram($appID)
  118. {
  119. $result = $this->ai->publishMiniProgram($appID, '1');
  120. if($result) return $this->send(array('result' => 'success', 'load' => true, 'message' => $this->lang->ai->publishSuccess));
  121. $this->sendError(dao::getError());
  122. }
  123. /**
  124. * Unpublish a mini program.
  125. *
  126. * @param string $appID
  127. * @access public
  128. * @return void
  129. */
  130. public function unpublishMiniProgram($appID)
  131. {
  132. $result = $this->ai->publishMiniProgram($appID, '0');
  133. if($result) return $this->send(array('result' => 'success', 'load' => true, 'message' => $this->lang->ai->unpublishSuccess));
  134. $this->sendError(dao::getError());
  135. }
  136. /**
  137. * Import mini program from zip file.
  138. *
  139. * @access public
  140. * @return void
  141. */
  142. public function importMiniProgram()
  143. {
  144. if(empty($_FILES)) return $this->sendError(array('file' => sprintf($this->lang->error->notempty, $this->lang->ai->installPackage)));
  145. $failResponse = array('result' => 'fail', 'message' => $this->lang->ai->saveFail, 'locate' => $this->createLink('ai', 'miniprograms'));
  146. if(empty($_POST)) return $this->send($failResponse);
  147. $errors = $this->ai->verifyRequiredFields(array('category' => $this->lang->ai->miniPrograms->category, 'published' => $this->lang->ai->toPublish));
  148. if($errors !== false) return $this->sendError($errors);
  149. $file = $_FILES['file'];
  150. $filePath = $file['tmp_name'];
  151. $result = $this->ai->extractZtAppZip($filePath);
  152. if(!is_array($result)) return $this->send($failResponse);
  153. $info = $result[0];
  154. $fileName = $info['filename'];
  155. if(!is_file($fileName)) return $this->send($failResponse);
  156. $content = file_get_contents($fileName);
  157. unlink($fileName);
  158. if(empty($content)) return $this->send($failResponse);
  159. $ztApp = json_decode($content);
  160. if(!is_object($ztApp)) return $this->send($failResponse);
  161. $ztApp->name = $this->ai->getUniqueAppName($ztApp->name);
  162. $ztApp->published = $_POST['published'];
  163. $ztApp->category = $_POST['category'];
  164. $this->ai->createMiniProgram($ztApp);
  165. return $this->sendSuccess(array('message' => $this->lang->saveSuccess, 'load' => true, 'closeModal' => true));
  166. }
  167. /**
  168. * List prompts.
  169. *
  170. * @param string $module
  171. * @param string $status
  172. * @access public
  173. * @return void
  174. */
  175. public function prompts($module = '', $status = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
  176. {
  177. $this->loadModel('user');
  178. $users = $this->user->getPairs('noletter');
  179. $userList = $this->user->getList('nodeleted');
  180. /* Set pager and order. */
  181. $this->app->loadClass('pager', $static = true);
  182. $pager = new pager($recTotal, $recPerPage, $pageID);
  183. $order = common::appendOrder($orderBy);
  184. $this->view->prompts = $this->ai->getPrompts($module, $status, $order, $pager);
  185. $this->view->module = $module;
  186. $this->view->status = $status;
  187. $this->view->orderBy = $orderBy;
  188. $this->view->pager = $pager;
  189. $this->view->title = $this->lang->aiapp->zentaoAgent;
  190. $this->view->users = $users;
  191. $this->view->userList = $userList;
  192. if($this->config->edition == 'open')
  193. {
  194. $this->view->promptModules = array_map(function ($prompt)
  195. {
  196. return $prompt->module;
  197. }, $this->ai->getPrompts());
  198. }
  199. $this->display();
  200. }
  201. /**
  202. * View prompt details.
  203. *
  204. * @param int $id
  205. * @access public
  206. * @return void
  207. */
  208. public function promptView($id)
  209. {
  210. $prompt = $this->ai->getPromptById($id);
  211. $this->view->prompt = $prompt;
  212. $this->view->preAndNext = $this->loadModel('common')->getPreAndNextObject('prompt', $id);
  213. $this->view->actions = $this->loadModel('action')->getList('prompt', $id);
  214. $this->view->dataPreview = $this->ai->generateDemoDataPrompt($prompt->module, $prompt->source);
  215. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  216. $this->view->title = "{$this->lang->aiapp->zentaoAgent}#{$prompt->id} $prompt->name";
  217. $this->view->fieldConfig = $this->ai->getPromptFields($id);
  218. $this->display();
  219. }
  220. /**
  221. * Create a prompt.
  222. *
  223. * @access public
  224. * @return void
  225. */
  226. public function createPrompt()
  227. {
  228. if($_POST)
  229. {
  230. $prompt = form::data($this->config->ai->form->createPrompt)->get();
  231. $promptID = $this->ai->createPrompt($prompt);
  232. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  233. $url = commonModel::hasPriv('ai', 'designPrompt') ? $this->createLink('ai', 'promptassignrole', "prompt=$promptID") : $this->createLink('ai', 'promptview', "id=$promptID");
  234. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $url));
  235. }
  236. $this->view->title = $this->lang->ai->prompts->create;
  237. $this->display();
  238. }
  239. /**
  240. * Edit a prompt.
  241. *
  242. * @param int $id
  243. * @access public
  244. * @return void
  245. */
  246. public function promptEdit($id)
  247. {
  248. $prompt = $this->ai->getPromptByID($id);
  249. if($_POST)
  250. {
  251. $data = form::data($this->config->ai->form->createPrompt)->get();
  252. $prompt->name = $data->name;
  253. $prompt->desc = $data->desc;
  254. $this->ai->updatePrompt($prompt);
  255. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  256. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->inlink('promptView', "id={$prompt->id}")));
  257. }
  258. $this->view->prompt = $prompt;
  259. $this->view->title = $this->lang->ai->prompts->edit;
  260. $this->display();
  261. }
  262. /**
  263. * Delete a prompt.
  264. *
  265. * @param int $prompt
  266. * @access public
  267. * @return void
  268. */
  269. public function promptDelete($prompt)
  270. {
  271. $result = $this->ai->deletePrompt($prompt);
  272. if(dao::isError() || $result === false) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  273. return $this->send(array('result' => 'success', 'message' => $this->lang->ai->prompts->action->deleteSuccess, 'load' => $this->inlink('prompts')));
  274. }
  275. /**
  276. * Edit role of prompt, prompt editing step 2.
  277. *
  278. * @param int $promptID
  279. * @access public
  280. * @return void
  281. */
  282. public function promptAssignRole($promptID)
  283. {
  284. if(!common::hasPriv('ai', 'designPrompt')) $this->loadModel('common')->deny('ai', 'designPrompt', false);
  285. $prompt = $this->ai->getPromptByID($promptID);
  286. if($_POST)
  287. {
  288. $data = fixer::input('post')->get();
  289. $originalPrompt = clone $prompt;
  290. $prompt->model = $data->model;
  291. $prompt->role = $data->role;
  292. $prompt->characterization = $data->characterization;
  293. if(!empty($data->saveTemplate) && $data->saveTemplate == 'save')
  294. {
  295. $this->ai->createRoleTemplate($prompt->role, $prompt->characterization);
  296. }
  297. $this->ai->updatePrompt($prompt, $originalPrompt);
  298. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  299. if(!empty($data->jumpToNext)) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptSelectDataSource', "promptID=$promptID")));
  300. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptAssignRole', "promptID=$promptID")));
  301. }
  302. $this->view->prompt = $prompt;
  303. $this->view->promptID = $promptID;
  304. $this->view->lastActiveStep = $this->ai->getLastActiveStep($prompt);
  305. $this->view->title = "{$this->lang->ai->prompts->common}#{$prompt->id} $prompt->name {$this->lang->hyphen} " . $this->lang->ai->prompts->assignRole . " {$this->lang->hyphen} " . $this->lang->ai->prompts->common;
  306. $this->view->roleTemplates = $this->ai->getRoleTemplates();
  307. $this->display();
  308. }
  309. /**
  310. * Set data source of prompt, prompt editing step 3.
  311. *
  312. * @param int $promptID
  313. * @access public
  314. * @return void
  315. */
  316. public function promptSelectDataSource($promptID)
  317. {
  318. if(!common::hasPriv('ai', 'designPrompt')) $this->loadModel('common')->deny('ai', 'designPrompt', false);
  319. $prompt = $this->ai->getPromptByID($promptID);
  320. if($_POST)
  321. {
  322. $data = fixer::input('post')->get();
  323. $originalPrompt = clone $prompt;
  324. $prompt->module = $data->datagroup;
  325. $prompt->source = ",$data->datasource,";
  326. $this->ai->updatePrompt($prompt, $originalPrompt);
  327. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  328. if(!empty($data->jumpToNext)) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptSetPurpose', "promptID=$promptID")));
  329. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptSelectDataSource', "promptID=$promptID")));
  330. }
  331. $this->view->activeDataSource = empty($prompt->module) ? current(array_keys($this->config->ai->dataSource)) : $prompt->module;
  332. $this->view->dataSource = $this->config->ai->dataSource;
  333. $this->view->prompt = $prompt;
  334. $this->view->promptID = $promptID;
  335. $this->view->lastActiveStep = $this->ai->getLastActiveStep($prompt);
  336. $this->view->title = "{$this->lang->ai->prompts->common}#{$prompt->id} $prompt->name {$this->lang->hyphen} " . $this->lang->ai->prompts->selectDataSource . " {$this->lang->hyphen} " . $this->lang->ai->prompts->common;
  337. $this->display();
  338. }
  339. /**
  340. * Edit purpose and purpose elaboration of prompt, prompt editing step 4.
  341. *
  342. * @param int $promptID
  343. * @access public
  344. * @return void
  345. */
  346. public function promptSetPurpose($promptID)
  347. {
  348. if(!common::hasPriv('ai', 'designPrompt')) $this->loadModel('common')->deny('ai', 'designPrompt', false);
  349. $prompt = $this->ai->getPromptByID($promptID);
  350. if($_SERVER['REQUEST_METHOD'] === 'POST')
  351. {
  352. if(!empty($_POST))
  353. {
  354. $data = fixer::input('post')->get();
  355. }
  356. else
  357. {
  358. $input = file_get_contents('php://input');
  359. $data = json_decode($input);
  360. if(json_last_error() !== JSON_ERROR_NONE)
  361. {
  362. return $this->send(array('result' => 'fail', 'message' => 'JSON解析失败:' . json_last_error_msg()));
  363. }
  364. }
  365. if(!is_object($data)) $data = new stdClass();
  366. $originalPrompt = clone $prompt;
  367. $prompt->purpose = isset($data->purpose) ? $data->purpose : '';
  368. $prompt->elaboration = '';
  369. $prompt->knowledgeLib = $data->knowledgeLib ?? '';
  370. if(isset($data->fields))
  371. {
  372. $fields = is_array($data->fields) ? $data->fields : array();
  373. $this->ai->savePromptFields($promptID, $fields);
  374. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  375. }
  376. $this->ai->updatePrompt($prompt, $originalPrompt);
  377. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  378. if(!empty($data->jumpToNext)) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptSetTargetForm', "promptID=$promptID")));
  379. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptSetPurpose', "promptID=$promptID")));
  380. }
  381. $knowledgeLibIds = [];
  382. if(!empty($prompt->knowledgeLib)) $knowledgeLibIds = explode(',', $prompt->knowledgeLib);
  383. $knowledgeLibs = (empty($knowledgeLibIds)) ? [] : $this->ai->getKnowledgeLibsByIDs($knowledgeLibIds);
  384. $currentPrompt = $prompt->purpose;
  385. if(!empty($prompt->elaboration)) $currentPrompt .= "\n\n" . $prompt->elaboration;
  386. $this->view->dataPreview = $this->ai->generateDemoDataPrompt($prompt->module, $prompt->source);
  387. $this->view->prompt = $prompt;
  388. $this->view->promptID = $promptID;
  389. $this->view->currentFields = $this->ai->getPromptFields($promptID);
  390. $this->view->currentPrompt = $currentPrompt;
  391. $this->view->knowledgeLibs = $knowledgeLibs;
  392. $this->view->lastActiveStep = $this->ai->getLastActiveStep($prompt);
  393. $this->view->title = "{$this->lang->ai->prompts->common}#{$prompt->id} $prompt->name {$this->lang->hyphen} " . $this->lang->ai->prompts->setPurpose . " {$this->lang->hyphen} " . $this->lang->ai->prompts->common;
  394. $this->display();
  395. }
  396. /**
  397. * Set target form of prompt, prompt editing step 5.
  398. *
  399. * @param int $promptID
  400. * @access public
  401. * @return void
  402. */
  403. public function promptSetTargetForm($promptID)
  404. {
  405. if(!common::hasPriv('ai', 'designPrompt')) $this->loadModel('common')->deny('ai', 'designPrompt', false);
  406. $prompt = $this->ai->getPromptByID($promptID);
  407. if($_POST)
  408. {
  409. $data = fixer::input('post')->get();
  410. $originalPrompt = clone $prompt;
  411. $prompt->targetForm = $data->targetForm;
  412. $this->ai->updatePrompt($prompt, $originalPrompt);
  413. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  414. if(!empty($data->goTesting)) // Go to testing object view.
  415. {
  416. $location = $this->ai->getTestingLocation($prompt);
  417. return $this->send(empty($location) ? array('result' => 'fail', 'target' => '#go-test-btn', 'message' => $this->lang->ai->prompts->goingTestingFail) : array('result' => 'success', 'target' => '#go-test-btn', 'msg' => $this->lang->ai->prompts->goingTesting, 'locate' => $location));
  418. }
  419. if(!empty($data->jumpToNext)) $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptFinalize', "promptID=$promptID")));
  420. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptSetTargetForm', "promptID=$promptID")));
  421. }
  422. $this->view->dataPreview = $this->ai->generateDemoDataPrompt($prompt->module, $prompt->source);
  423. $this->view->prompt = $prompt;
  424. $this->view->promptID = $promptID;
  425. $this->view->lastActiveStep = $this->ai->getLastActiveStep($prompt);
  426. $this->view->title = "{$this->lang->ai->prompts->common}#{$prompt->id} $prompt->name {$this->lang->hyphen} " . $this->lang->ai->prompts->setTargetForm . " {$this->lang->hyphen} " . $this->lang->ai->prompts->common;
  427. $this->display();
  428. }
  429. /**
  430. * Edit additional information of prompt, final prompt editing step.
  431. *
  432. * @param int $promptID
  433. * @access public
  434. * @return void
  435. */
  436. public function promptFinalize($promptID)
  437. {
  438. if(!common::hasPriv('ai', 'designPrompt')) $this->loadModel('common')->deny('ai', 'designPrompt', false);
  439. $prompt = $this->ai->getPromptByID($promptID);
  440. if($_POST)
  441. {
  442. $data = fixer::input('post')->get();
  443. $originalPrompt = clone $prompt;
  444. $prompt->name = $data->name;
  445. $prompt->desc = $data->desc;
  446. $this->ai->updatePrompt($prompt, $originalPrompt);
  447. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  448. if(!empty($data->jumpToNext)) $this->ai->togglePromptStatus($prompt, 'active');
  449. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  450. if(!empty($data->jumpToNext)) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('prompts')));
  451. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->inlink('promptFinalize', "promptID=$promptID")));
  452. }
  453. $this->view->prompt = $prompt;
  454. $this->view->promptID = $promptID;
  455. $this->view->lastActiveStep = $this->ai->getLastActiveStep($prompt);
  456. $this->view->title = "{$this->lang->ai->prompts->common}#{$prompt->id} $prompt->name {$this->lang->hyphen} " . $this->lang->ai->prompts->finalize . " {$this->lang->hyphen} " . $this->lang->ai->prompts->common;
  457. $this->display();
  458. }
  459. /**
  460. * Execute prompt on obejct, and redirect to target form page.
  461. *
  462. * @param int $promptId
  463. * @param int $objectId
  464. * @param string $mode Execution mode, 'testing' or 'normal'.
  465. * @access public
  466. * @return void
  467. */
  468. public function promptExecute($promptId, $objectId, $mode = 'testing')
  469. {
  470. $prompt = $this->ai->getPromptByID($promptId);
  471. if(empty($prompt)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noPrompt'])));
  472. $object = $this->ai->getObjectForPromptById($prompt, $objectId);
  473. if(empty($object)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noObjectData'])));
  474. list($objectData, $rawObject) = $object;
  475. list($location, $stop) = $this->ai->getTargetFormLocation($prompt, $rawObject);
  476. if(!empty($stop)) return header("location: $location", true, 302);
  477. /* Execute prompt and catch exceptions. */
  478. try
  479. {
  480. $response = $this->ai->executePrompt($prompt, $object);
  481. }
  482. catch (AIResponseException $e)
  483. {
  484. $output = array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $e->getMessage()));
  485. /* Audition shall quit on such exception. */
  486. if(isset($_SESSION['auditPrompt']) && time() - $_SESSION['auditPrompt']['time'] < 10 * 60)
  487. {
  488. $output['locate'] = $this->inlink('promptAudit', "promptID=$promptId&objectId=$objectId&exit=true");
  489. }
  490. return $this->send($output);
  491. }
  492. if(is_int($response)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->executeErrors["$response"]) . (empty($this->ai->errors) ? '' : implode(', ', $this->ai->errors))));
  493. if(empty($response)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noResponse'])));
  494. if(!empty($prompt->targetForm))
  495. {
  496. $targetFormPaths = explode('.', $prompt->targetForm);
  497. $response['targetFormName'] = $this->lang->ai->targetForm[$targetFormPaths[0]][$targetFormPaths[1]];
  498. $response['dataPropNames'] = $this->lang->ai->dataSource[$prompt->module];
  499. }
  500. $fields = array_values($this->ai->getPromptFields($promptId));
  501. if($fields)
  502. {
  503. $response['fields'] = $fields;
  504. $response['formConfig'] = [];
  505. $response['formConfig']['title'] = $this->lang->ai->prompts->formDefaultTitle;
  506. $response['formConfig']['submitBtnText'] = $this->lang->ai->prompts->formSubmitBtnText;
  507. }
  508. $response['objectID'] = $objectId;
  509. $response['objectType'] = $prompt->module;
  510. $response['knowledgeLib'] = $prompt->knowledgeLib;
  511. $response['object'] = $objectData;
  512. $response['formLocation'] = $location;
  513. $response['model'] = $prompt->model;
  514. return $this->send(array('result' => 'success', 'callback' => array('name' => 'parent.executeZentaoPrompt', 'params' => array($response, $mode === 'testing'))));
  515. }
  516. /**
  517. * Reset prompt execution.
  518. *
  519. * @param bool $failed Whether the execution failed, triggers error message.
  520. * @access public
  521. * @return void
  522. */
  523. public function promptExecutionReset($failed = false)
  524. {
  525. /* Reset session. */
  526. unset($_SESSION['aiPrompt']);
  527. unset($_SESSION['auditPrompt']);
  528. if($failed) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noTargetForm']), 'locate' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''));
  529. return $this->send(array('result' => 'success'));
  530. }
  531. /**
  532. * Audit prompt modal.
  533. *
  534. * @param int $promptId
  535. * @param int $objectId
  536. * @param bool $exit
  537. * @access public
  538. * @return void|int
  539. */
  540. public function promptAudit($promptId, $objectId, $exit = false)
  541. {
  542. if(!common::hasPriv('ai', 'designPrompt')) $this->loadModel('common')->deny('ai', 'designPrompt', false);
  543. if(!empty($exit))
  544. {
  545. unset($_SESSION['auditPrompt']);
  546. return $this->send(array('result' => 'success', 'load' => $this->inlink('promptview', "promptID=$promptId")));
  547. }
  548. $prompt = $this->ai->getPromptByID($promptId);
  549. if($_POST)
  550. {
  551. $data = fixer::input('post')->get();
  552. $originalPrompt = clone $prompt;
  553. $prompt->role = $data->role;
  554. $prompt->characterization = $data->characterization;
  555. $prompt->purpose = $data->purpose;
  556. $prompt->elaboration = '';
  557. $this->ai->updatePrompt($prompt, $originalPrompt);
  558. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  559. if(empty($data->backLocation))
  560. {
  561. $response = array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true);
  562. }
  563. else
  564. {
  565. $response = array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => array('name' => 'getTestingLocation', 'params' => array($promptId)));
  566. }
  567. $this->sendSuccess($response);
  568. }
  569. $objectForPrompt = $this->ai->getObjectForPromptById($prompt, $objectId);
  570. if(empty($objectForPrompt)) return $this->send(array('result' => 'fail', 'message' => $this->lang->ai->execute->fail));
  571. list($objectData, $object) = $objectForPrompt;
  572. $currentPrompt = $prompt->purpose;
  573. if(!empty($prompt->elaboration)) $currentPrompt .= "\n\n" . $prompt->elaboration;
  574. $this->view->prompt = $prompt;
  575. $this->view->currentPrompt = $currentPrompt;
  576. $this->view->object = $object;
  577. $this->view->dataPrompt = $this->ai->serializeDataToPrompt($prompt->module, $prompt->source, $objectData);
  578. $this->display();
  579. }
  580. /**
  581. * Publish prompt, set status to active.
  582. *
  583. * @param int $id
  584. * @param bool $backToTestingLocation
  585. * @access public
  586. * @return void
  587. */
  588. public function promptPublish($id, $backToTestingLocation = false)
  589. {
  590. unset($_SESSION['auditPrompt']);
  591. $this->ai->togglePromptStatus($id, 'active');
  592. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  593. if($backToTestingLocation)
  594. {
  595. return $this->send(array('result' => 'success', 'message' => $this->lang->ai->prompts->action->publishSuccess, 'load' => $this->inlink('promptview', "id=$id")));
  596. }
  597. return $this->send(array('result' => 'success', 'load' => true, 'message' => $this->lang->ai->prompts->action->publishSuccess));
  598. }
  599. /**
  600. * Unpublish prompt, set status to draft.
  601. *
  602. * @param int $id
  603. * @access public
  604. * @return void
  605. */
  606. public function promptUnpublish($id)
  607. {
  608. $this->ai->togglePromptStatus($id, 'draft');
  609. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  610. return $this->send(array('result' => 'success', 'message' => $this->lang->ai->prompts->action->unpublishSuccess, 'load' => true));
  611. }
  612. /**
  613. * Get testing location.
  614. *
  615. * @param int $promptID
  616. * @param string $module
  617. * @param string $targetForm
  618. * @access public
  619. * @return void
  620. */
  621. public function ajaxTestPrompt($promptID)
  622. {
  623. $prompt = $this->ai->getPromptByID($promptID);
  624. if(empty($prompt)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noPrompt'])));
  625. $object = $this->ai->getTestPromptData($prompt);
  626. list($objectData, $showText) = $object;
  627. /* Execute prompt and catch exceptions. */
  628. try
  629. {
  630. $response = $this->ai->executePrompt($prompt, $object);
  631. }
  632. catch (AIResponseException $e)
  633. {
  634. $output = array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $e->getMessage()));
  635. /* Audition shall quit on such exception. */
  636. if(isset($_SESSION['auditPrompt']) && time() - $_SESSION['auditPrompt']['time'] < 10 * 60)
  637. {
  638. $output['locate'] = $this->inlink('promptAudit', "promptID=$promptID&objectId=0&exit=true");
  639. }
  640. return $this->send($output);
  641. }
  642. if(is_int($response)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->executeErrors["$response"]) . (empty($this->ai->errors) ? '' : implode(', ', $this->ai->errors))));
  643. if(empty($response)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noResponse'])));
  644. if(!empty($prompt->targetForm) && $prompt->targetForm != 'empty')
  645. {
  646. $targetFormPaths = explode('.', $prompt->targetForm);
  647. $response['targetFormName'] = $this->lang->ai->targetForm[$targetFormPaths[0]][$targetFormPaths[1]];
  648. $response['dataPropNames'] = $this->lang->ai->dataSource[$prompt->module];
  649. }
  650. $response['objectType'] = $prompt->module;
  651. $response['object'] = $objectData;
  652. $response['formLocation'] = '';
  653. $response['model'] = $prompt->model;
  654. $response['promptAudit'] = $this->ai->isClickable($prompt, 'promptaudit');
  655. $response['content'] = $showText;
  656. return $this->send(array('result' => 'success', 'data' => $response));
  657. }
  658. /**
  659. * Return html element for the role template list.
  660. * This is used in prompt designing step 2.
  661. * Return html will deserialize in the front end js and replace the original role template list.
  662. *
  663. * @access public
  664. * @return void
  665. */
  666. public function roleTemplates()
  667. {
  668. if($_POST)
  669. {
  670. $data = fixer::input('post')->get();
  671. $method = $data->method;
  672. $result = false;
  673. $message = '';
  674. switch ($method)
  675. {
  676. case 'create':
  677. $result = $this->ai->createRoleTemplate($data->role, $data->characterization);
  678. $message = $result ? $this->lang->saveSuccess : $this->lang->ai->saveFail;
  679. break;
  680. case 'delete':
  681. $result = $this->ai->deleteRoleTemplate($data->id);
  682. $message = $result ? $this->lang->ai->prompts->roleDelSuccess : $this->lang->ai->saveFail;
  683. break;
  684. case 'edit':
  685. $result = $this->ai->updateRoleTemplate($data->id, $data->role, $data->characterization);
  686. $message = $result ? $this->lang->saveSuccess : $this->lang->ai->saveFail;
  687. break;
  688. }
  689. $roleTemplates = $this->ai->getRoleTemplates();
  690. $roleTemplatesArray = array_values((array)$roleTemplates);
  691. return $this->send(array(
  692. 'result' => $result ? 'success' : 'fail',
  693. 'message' => $message,
  694. 'data' => array(
  695. 'roleTemplates' => $roleTemplatesArray,
  696. 'method' => $method
  697. )
  698. ));
  699. }
  700. }
  701. }