From 5cfcb2267d5de82cedec6304b647e2af89763ec6 Mon Sep 17 00:00:00 2001 From: liwenrui Date: Tue, 25 Jul 2023 15:38:39 +0800 Subject: [PATCH] * make errors verboser. --- module/ai/control.php | 3 ++- module/ai/lang/de.php | 6 +++++ module/ai/lang/en.php | 6 +++++ module/ai/lang/fr.php | 6 +++++ module/ai/lang/zh-cn.php | 14 ++++++----- module/ai/model.php | 53 ++++++++++++++++++++++++++++++++-------- 6 files changed, 71 insertions(+), 17 deletions(-) diff --git a/module/ai/control.php b/module/ai/control.php index fd1c640b3a..652ba1fd00 100644 --- a/module/ai/control.php +++ b/module/ai/control.php @@ -460,7 +460,8 @@ class ai extends control if(empty($object)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noObjectData']))); $response = $this->ai->executePrompt($prompt, $object); - if(empty($response)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noResponse']))); + 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)))); + if(empty($response)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->ai->execute->failFormat, $this->lang->ai->execute->failReasons['noResponse']))); list($objectData, $rawObject) = $object; diff --git a/module/ai/lang/de.php b/module/ai/lang/de.php index 2c23474151..031d95f315 100644 --- a/module/ai/lang/de.php +++ b/module/ai/lang/de.php @@ -719,6 +719,12 @@ $lang->ai->execute->failReasons['noPrompt'] = 'unable to get prompt'; $lang->ai->execute->failReasons['noObjectData'] = 'unable to get object data'; $lang->ai->execute->failReasons['noResponse'] = 'no response from external service'; $lang->ai->execute->failReasons['noTargetForm'] = 'unable to get target form'; +$lang->ai->execute->executeErrors = array(); +$lang->ai->execute->executeErrors[-1] = 'unable to get prompt'; +$lang->ai->execute->executeErrors[-2] = 'unable to get object data'; +$lang->ai->execute->executeErrors[-3] = 'failed to serialize object data'; +$lang->ai->execute->executeErrors[-4] = 'unable to get target form schema'; +$lang->ai->execute->executeErrors[-5] = 'request failed or API returned error'; $lang->ai->audit = new stdclass(); $lang->ai->audit->designPrompt = 'Prompt Design'; diff --git a/module/ai/lang/en.php b/module/ai/lang/en.php index 906b9b770b..c9a0be653d 100644 --- a/module/ai/lang/en.php +++ b/module/ai/lang/en.php @@ -719,6 +719,12 @@ $lang->ai->execute->failReasons['noPrompt'] = 'unable to get prompt'; $lang->ai->execute->failReasons['noObjectData'] = 'unable to get object data'; $lang->ai->execute->failReasons['noResponse'] = 'no response from external service'; $lang->ai->execute->failReasons['noTargetForm'] = 'unable to get target form'; +$lang->ai->execute->executeErrors = array(); +$lang->ai->execute->executeErrors[-1] = 'unable to get prompt'; +$lang->ai->execute->executeErrors[-2] = 'unable to get object data'; +$lang->ai->execute->executeErrors[-3] = 'failed to serialize object data'; +$lang->ai->execute->executeErrors[-4] = 'unable to get target form schema'; +$lang->ai->execute->executeErrors[-5] = 'request failed or API returned error'; $lang->ai->audit = new stdclass(); $lang->ai->audit->designPrompt = 'Prompt Design'; diff --git a/module/ai/lang/fr.php b/module/ai/lang/fr.php index 09e4904920..fd2af141ca 100644 --- a/module/ai/lang/fr.php +++ b/module/ai/lang/fr.php @@ -719,6 +719,12 @@ $lang->ai->execute->failReasons['noPrompt'] = 'unable to get prompt'; $lang->ai->execute->failReasons['noObjectData'] = 'unable to get object data'; $lang->ai->execute->failReasons['noResponse'] = 'no response from external service'; $lang->ai->execute->failReasons['noTargetForm'] = 'unable to get target form'; +$lang->ai->execute->executeErrors = array(); +$lang->ai->execute->executeErrors[-1] = 'unable to get prompt'; +$lang->ai->execute->executeErrors[-2] = 'unable to get object data'; +$lang->ai->execute->executeErrors[-3] = 'failed to serialize object data'; +$lang->ai->execute->executeErrors[-4] = 'unable to get target form schema'; +$lang->ai->execute->executeErrors[-5] = 'request failed or API returned error'; $lang->ai->audit = new stdclass(); $lang->ai->audit->designPrompt = 'Prompt Design'; diff --git a/module/ai/lang/zh-cn.php b/module/ai/lang/zh-cn.php index 93beece7cc..fc4bfdafee 100644 --- a/module/ai/lang/zh-cn.php +++ b/module/ai/lang/zh-cn.php @@ -657,12 +657,8 @@ $lang->ai->formSchema['tree']['browse'] = new stdclass(); // TODO: This might no $lang->ai->formSchema['tree']['browse']->title = '模块'; $lang->ai->formSchema['tree']['browse']->type = 'array'; $lang->ai->formSchema['tree']['browse']->items = new stdclass(); -$lang->ai->formSchema['tree']['browse']->items->type = 'object'; -$lang->ai->formSchema['tree']['browse']->items->properties = new stdclass(); -$lang->ai->formSchema['tree']['browse']->items->properties->{'modules[]'} = new stdclass(); -$lang->ai->formSchema['tree']['browse']->items->properties->{'modules[]'}->type = 'string'; -$lang->ai->formSchema['tree']['browse']->items->properties->{'modules[]'}->description = '模块的名称'; -$lang->ai->formSchema['tree']['browse']->items->required = array('modules[]'); +$lang->ai->formSchema['tree']['browse']->items->type = 'string'; +$lang->ai->formSchema['tree']['browse']->items->description = '模块的名称'; $lang->ai->formSchema['programplan']['create'] = new stdclass(); $lang->ai->formSchema['programplan']['create']->title = '阶段'; @@ -704,6 +700,12 @@ $lang->ai->execute->failReasons['noPrompt'] = '提词不存在'; $lang->ai->execute->failReasons['noObjectData'] = '对象数据获取失败'; $lang->ai->execute->failReasons['noResponse'] = '请求返回值为空'; $lang->ai->execute->failReasons['noTargetForm'] = '目标表单地址获取失败'; +$lang->ai->execute->executeErrors = array(); +$lang->ai->execute->executeErrors[-1] = '提词不存在'; +$lang->ai->execute->executeErrors[-2] = '对象数据获取失败'; +$lang->ai->execute->executeErrors[-3] = '序列化对象数据失败'; +$lang->ai->execute->executeErrors[-4] = '表单结构获取失败'; +$lang->ai->execute->executeErrors[-5] = 'API 返回值为空或返回了错误'; $lang->ai->audit = new stdclass(); $lang->ai->audit->designPrompt = '提词设计'; diff --git a/module/ai/model.php b/module/ai/model.php index 837cec8e34..7083912bbd 100644 --- a/module/ai/model.php +++ b/module/ai/model.php @@ -18,6 +18,14 @@ class aiModel extends model */ public $modelConfig; + /** + * Errors from last request. + * + * @var array + * @access public + */ + public $errors = array(); + /** * Constructor. Get model config from system.ai settings. * @@ -162,6 +170,29 @@ class aiModel extends model return $postData; } + /** + * Decode response from OpenAI API, add error to $this->errors if any. + * + * @param string $response json string + * @access private + * @return mixed false if error, json object if success + */ + private function decodeResponse($response) + { + $response = json_decode($response); + if(json_last_error()) + { + $this->errors[] = 'JSON decode error: ' . json_last_error_msg(); + return false; + } + if(isset($response->error)) + { + $this->errors[] = isset($response->error->message) ? $response->error->message : 'Unknown error'; + return false; + } + return $response; + } + /** * Parse text responses from simple APIs. For example, completion. * @@ -171,8 +202,8 @@ class aiModel extends model */ private function parseTextResponse($response) { - $response = json_decode($response); - if(isset($response->error)) return false; + $response = $this->decodeResponse($response); + if(empty($response)) return false; /* Extract text response choices. */ if(isset($response->choices) && count($response->choices) > 0) @@ -193,8 +224,8 @@ class aiModel extends model */ private function parseChatResponse($response) { - $response = json_decode($response); - if(isset($response->error)) return false; + $response = $this->decodeResponse($response); + if(empty($response)) return false; /* Extract chat message choices. */ if(isset($response->choices) && count($response->choices) > 0) @@ -215,8 +246,8 @@ class aiModel extends model */ private function parseFunctionCallResponse($response) { - $response = json_decode($response); - if(isset($response->error)) return false; + $response = $this->decodeResponse($response); + if(empty($response)) return false; /* Extract function call choices. */ if(isset($response->choices) && count($response->choices) > 0) @@ -827,24 +858,26 @@ class aiModel extends model * @param int|object $prompt prompt (or id) to execute. * @param int|object $object object (or id) to execute prompt on. * @access public - * @return string|false returns either JSON string or false. + * @return string|int returns either JSON string or negative integer on error. */ public function executePrompt($prompt, $object) { if(is_numeric($prompt)) $prompt = $this->getPromptById($prompt); - if(empty($prompt)) return false; + if(empty($prompt)) return -1; if(is_numeric($object)) $object = $this->getObjectForPromptById($prompt, $object); - if(empty($object)) return false; + if(empty($object)) return -2; list($objectData) = $object; $dataPrompt = $this->serializeDataToPrompt($prompt->module, $prompt->source, $objectData); + if(empty($dataPrompt)) return -3; $wholePrompt = $this->assemblePrompt($prompt, $dataPrompt); $schema = $this->getFunctionCallSchema($prompt->targetForm); + if(empty($schema)) return -4; $response = $this->converseForJSON(array((object)array('role' => 'user', 'content' => $wholePrompt)), $schema); - if(empty($response)) return false; + if(empty($response)) return -5; return current($response); }