From 57271632ec1a4c5a1a93eda8e8d113a0afb9b2ce Mon Sep 17 00:00:00 2001 From: liwenrui Date: Sun, 8 Oct 2023 09:54:02 +0800 Subject: [PATCH 1/9] * add specialized function for getting JSON from LLMs without forced function calling feature. --- module/ai/lang/de.php | 3 +++ module/ai/lang/en.php | 3 +++ module/ai/lang/fr.php | 3 +++ module/ai/lang/zh-cn.php | 3 +++ module/ai/model.php | 52 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/module/ai/lang/de.php b/module/ai/lang/de.php index 8989e14c45..1805a0298b 100644 --- a/module/ai/lang/de.php +++ b/module/ai/lang/de.php @@ -793,3 +793,6 @@ $lang->ai->audit->exit = 'Exit Audit'; $lang->ai->audit->backLocationList = array(); $lang->ai->audit->backLocationList[0] = 'back to audit page.'; $lang->ai->audit->backLocationList[1] = 'back to audit page and regenerate.'; + +$lang->ai->engineeredPrompts = new stdclass(); +$lang->ai->engineeredPrompts->askForFunctionCalling = array((object)array('role' => 'user', 'content' => 'Please convert my next message into a function call.'), (object)array('role' => 'assistant', 'content' => 'Sure, I\'ll convert your next message into a function call.')); diff --git a/module/ai/lang/en.php b/module/ai/lang/en.php index 8989e14c45..1805a0298b 100644 --- a/module/ai/lang/en.php +++ b/module/ai/lang/en.php @@ -793,3 +793,6 @@ $lang->ai->audit->exit = 'Exit Audit'; $lang->ai->audit->backLocationList = array(); $lang->ai->audit->backLocationList[0] = 'back to audit page.'; $lang->ai->audit->backLocationList[1] = 'back to audit page and regenerate.'; + +$lang->ai->engineeredPrompts = new stdclass(); +$lang->ai->engineeredPrompts->askForFunctionCalling = array((object)array('role' => 'user', 'content' => 'Please convert my next message into a function call.'), (object)array('role' => 'assistant', 'content' => 'Sure, I\'ll convert your next message into a function call.')); diff --git a/module/ai/lang/fr.php b/module/ai/lang/fr.php index 8989e14c45..1805a0298b 100644 --- a/module/ai/lang/fr.php +++ b/module/ai/lang/fr.php @@ -793,3 +793,6 @@ $lang->ai->audit->exit = 'Exit Audit'; $lang->ai->audit->backLocationList = array(); $lang->ai->audit->backLocationList[0] = 'back to audit page.'; $lang->ai->audit->backLocationList[1] = 'back to audit page and regenerate.'; + +$lang->ai->engineeredPrompts = new stdclass(); +$lang->ai->engineeredPrompts->askForFunctionCalling = array((object)array('role' => 'user', 'content' => 'Please convert my next message into a function call.'), (object)array('role' => 'assistant', 'content' => 'Sure, I\'ll convert your next message into a function call.')); diff --git a/module/ai/lang/zh-cn.php b/module/ai/lang/zh-cn.php index c08791a854..14a0ea74b6 100644 --- a/module/ai/lang/zh-cn.php +++ b/module/ai/lang/zh-cn.php @@ -793,3 +793,6 @@ $lang->ai->audit->exit = '退出调试'; $lang->ai->audit->backLocationList = array(); $lang->ai->audit->backLocationList[0] = '返回调试页面'; $lang->ai->audit->backLocationList[1] = '返回调试页面并重新生成'; + +$lang->ai->engineeredPrompts = new stdclass(); +$lang->ai->engineeredPrompts->askForFunctionCalling = array((object)array('role' => 'user', 'content' => '请把我所发的下一条消息内容转换为 function 调用。'), (object)array('role' => 'assistant', 'content' => '好的,我会把下一条消息转换为 function 调用。')); diff --git a/module/ai/model.php b/module/ai/model.php index 70b55cc10a..a57c8ee7ac 100644 --- a/module/ai/model.php +++ b/module/ai/model.php @@ -459,9 +459,55 @@ class aiModel extends model } /** - * Get list of prompts. + * Generate conversations with LLM for JSON output, but seperate the conversation into two parts. * - * TODO: fully implement this. + * Usage is the same as function `converseForJSON`, but this function will generate two conversations: + * one for manipulating the natural language data, and the other for generating the JSON output. + * + * @param array $messages array of chat messages + * @param object $schema schema of the output + * @param array $options optional params, see https://platform.openai.com/docs/api-reference/chat/create + * @access public + * @return mixed false if error, array of JSON object if success + */ + public function converseTwiceForJSON($messages, $schema, $options = array()) + { + /* First conversation. */ + $data = compact('messages'); + if(!empty($options)) + { + foreach($options as $key => $value) $data[$key] = $value; + } + + $postData = $this->assembleRequestData('chat', $data); + if(!$postData) return false; + + $chatResponse = $this->makeRequest('chat', $postData); + $chatMessages = $this->parseChatResponse($chatResponse); + + $chatMessage = end($chatMessages); + if(empty($chatMessage)) return false; + + /* Second conversation for JSON output. */ + $messages = array_merge($this->lang->ai->engineeredPrompts->askForFunctionCalling, array((object)array('role' => 'user', 'content' => $chatMessage))); + $functions = array((object)array('name' => 'function', 'parameters' => $schema)); + $functionCall = (object)array('name' => 'function'); + + $data = compact('messages', 'functions', 'functionCall'); + if(!empty($options)) + { + foreach($options as $key => $value) $data[$key] = $value; + } + + $postData = $this->assembleRequestData('function', $data); + if(!$postData) return false; + + $response = $this->makeRequest('function', $postData); + return $this->parseFunctionCallResponse($response); + } + + /** + * Get list of prompts. * * @param string $module * @param string $status @@ -484,8 +530,6 @@ class aiModel extends model /** * Get prompt by id. * - * TODO: fully implement this. - * * @param int $id * @access public * @return object From c93c9b09692506c39fcbfc4c7840c187b1c08356 Mon Sep 17 00:00:00 2001 From: liwenrui Date: Sun, 8 Oct 2023 16:38:49 +0800 Subject: [PATCH 2/9] + baidu ernie configurations. --- module/ai/config.php | 43 ++++++++++++++++++++++++------- module/ai/control.php | 10 +++---- module/ai/lang/de.php | 8 +++--- module/ai/lang/en.php | 8 +++--- module/ai/lang/fr.php | 8 +++--- module/ai/lang/zh-cn.php | 9 ++++--- module/ai/view/editmodel.html.php | 38 ++++++++++++++++++--------- module/ai/view/models.html.php | 10 +++++-- 8 files changed, 89 insertions(+), 45 deletions(-) diff --git a/module/ai/config.php b/module/ai/config.php index 130cff0b13..017107d03e 100644 --- a/module/ai/config.php +++ b/module/ai/config.php @@ -2,10 +2,12 @@ $config->ai->vendorList = array(); $config->ai->vendorList['openai']['requiredFields'] = array('key'); $config->ai->vendorList['azure']['requiredFields'] = array('key', 'resource', 'deployment'); +$config->ai->vendorList['baidu']['requiredFields'] = array('key', 'secret'); +/* OpenAI GPT configurations. */ $config->ai->openai = new stdclass(); $config->ai->openai->api = new stdclass(); -$config->ai->openai->api->vendor = array('openai', 'azure'); +$config->ai->openai->api->vendor = array('openai', 'azure'); $config->ai->openai->api->openai = new stdclass(); $config->ai->openai->api->openai->version = 'v1'; // OpenAI API version, required. $config->ai->openai->api->openai->format = 'https://api.openai.com/%s/%s'; // OpenAI API format, args: API version, API name. @@ -16,35 +18,58 @@ $config->ai->openai->api->azure->deployment = 'the-first-gpt'; / $config->ai->openai->api->azure->apiVersion = '2023-07-01-preview'; // Azure OpenAI API version, required. $config->ai->openai->api->azure->format = 'https://%s.openai.azure.com/openai/deployments/%s/%s?api-version=%s'; // Azure API format, args: resource name, deployment name, API name, API version. $config->ai->openai->api->azure->authFormat = 'api-key: %s'; // Azure API auth header format. -$config->ai->openai->api->methods = array('function' => 'chat/completions', 'chat' => 'chat/completions', 'completion' => 'completions', 'edit' => 'edits'); +$config->ai->openai->api->methods = array('function' => 'chat/completions', 'chat' => 'chat/completions', 'completion' => 'completions'); $config->ai->openai->params = new stdclass(); $config->ai->openai->params->chat = new stdclass(); $config->ai->openai->params->function = new stdclass(); $config->ai->openai->params->completion = new stdclass(); -$config->ai->openai->params->edit = new stdclass(); $config->ai->openai->params->chat->required = array('messages'); $config->ai->openai->params->chat->optional = array('max_tokens', 'temperature', 'top_p', 'n', 'stream', 'stop', 'presence_penalty', 'frequency_penalty', 'logit_bias', 'user'); $config->ai->openai->params->function->required = array('messages', 'functions', 'function_call'); $config->ai->openai->params->function->optional = array('max_tokens', 'temperature', 'top_p', 'n', 'stream', 'stop', 'presence_penalty', 'frequency_penalty', 'logit_bias', 'user'); $config->ai->openai->params->completion->required = array('prompt', 'max_tokens'); $config->ai->openai->params->completion->optional = array('suffix', 'temperature', 'top_p', 'n', 'stream', 'logprobs', 'echo', 'stop', 'presence_penalty', 'frequency_penalty', 'best_of', 'logit_bias', 'user'); -$config->ai->openai->params->edit->required = array('input', 'instruction'); -$config->ai->openai->params->edit->optional = array('temperature', 'top_p', 'n'); $config->ai->openai->model = new stdclass(); $config->ai->openai->model->chat = 'gpt-3.5-turbo'; -$config->ai->openai->model->function = 'gpt-3.5-turbo-0613'; -$config->ai->openai->model->completion = 'text-davinci-003'; -$config->ai->openai->model->edit = 'text-davinci-edit-001'; +$config->ai->openai->model->function = 'gpt-3.5-turbo'; +$config->ai->openai->model->completion = 'gpt-3.5-turbo-instruct'; -$config->ai->openai->contentTypeMapping = array('Content-Type: application/json' => array('', 'function', 'chat', 'completion', 'edit'), 'Content-Type: multipart/form-data' => array()); +$config->ai->openai->contentTypeMapping = array('Content-Type: application/json' => array('', 'function', 'chat', 'completion'), 'Content-Type: multipart/form-data' => array()); $config->ai->openai->contentType = array(); foreach($config->ai->openai->contentTypeMapping as $contentType => $apis) { foreach($apis as $api) $config->ai->openai->contentType[$api] = $contentType; } +/* Baidu ERNIE configurations. */ +$config->ai->ernie = new stdclass(); +$config->ai->ernie->api = new stdclass(); +$config->ai->ernie->api->vendor = array('baidu'); +$config->ai->ernie->api->baidu = new stdclass(); +$config->ai->ernie->api->baidu->format = 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=%s'; // ERNIE API format, arg: access_token (obtained from bce oauth). +$config->ai->ernie->api->baidu->auth = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s'; // BCE auth URL format, args: client_id, client_secret. + +$config->ai->ernie->params = new stdclass(); +$config->ai->ernie->params->chat = new stdclass(); +$config->ai->ernie->params->function = new stdclass(); +$config->ai->ernie->params->chat->required = array('messages'); +$config->ai->ernie->params->chat->optional = array('temperature', 'top_p', 'penalty_score', 'stream', 'user_id'); +$config->ai->ernie->params->function->required = array('messages', 'functions'); +$config->ai->ernie->params->function->optional = array('temperature', 'top_p', 'penalty_score', 'stream', 'user_id'); + +$config->ai->ernie->model = new stdclass(); +$config->ai->ernie->model->chat = 'ernie-bot-turbo'; +$config->ai->ernie->model->function = 'ernie-bot-turbo'; + +$config->ai->ernie->contentTypeMapping = array('Content-Type: application/json' => array('', 'function', 'chat'), 'Content-Type: multipart/form-data' => array()); +$config->ai->ernie->contentType = array(); +foreach($config->ai->ernie->contentTypeMapping as $contentType => $apis) +{ + foreach($apis as $api) $config->ai->ernie->contentType[$api] = $contentType; +} + /* Required fields of forms. */ $config->ai->createprompt = new stdclass(); $config->ai->testPrompt = new stdclass(); diff --git a/module/ai/control.php b/module/ai/control.php index 38de4dada4..7e2ec3fe07 100644 --- a/module/ai/control.php +++ b/module/ai/control.php @@ -81,7 +81,7 @@ class ai extends control { $modelConfig = fixer::input('post')->get(); - $currentVendor = empty($modelConfig->vendor) ? key($lang->ai->models->openaiVendorList) : $modelConfig->vendor; + $currentVendor = empty($modelConfig->vendor) ? key($this->lang->ai->models->vendorList->{empty($modelConfig->type) ? key($this->lang->ai->models->typeList) : $modelConfig->type}) : $modelConfig->vendor; $vendorRequiredFields = $this->config->ai->vendorList[$currentVendor]['requiredFields']; $errors = array(); @@ -128,7 +128,7 @@ class ai extends control { $modelConfig = fixer::input('post')->get(); - $currentVendor = empty($modelConfig->vendor) ? key($lang->ai->models->openaiVendorList) : $modelConfig->vendor; + $currentVendor = empty($modelConfig->vendor) ? key($this->lang->ai->models->vendorList->{empty($modelConfig->type) ? key($this->lang->ai->models->typeList) : $modelConfig->type}) : $modelConfig->vendor; $vendorRequiredFields = $this->config->ai->vendorList[$currentVendor]['requiredFields']; $errors = array(); @@ -147,10 +147,8 @@ class ai extends control if($currentVendor == 'azure') { - $messages = array( - (object)array('role' => 'user', 'content' => 'Hello?') - ); - $result = $this->ai->converse($messages); + $messages = array((object)array('role' => 'user', 'content' => 'test')); + $result = $this->ai->converse($messages, array('maxTokens' => 1)); } else { diff --git a/module/ai/lang/de.php b/module/ai/lang/de.php index 1805a0298b..5e7cc8282b 100644 --- a/module/ai/lang/de.php +++ b/module/ai/lang/de.php @@ -423,11 +423,11 @@ $lang->ai->models->statusList['off'] = 'Disable'; $lang->ai->models->typeList = array(); $lang->ai->models->typeList['openai-gpt35'] = 'OpenAI / GPT-3.5'; -// $lang->ai->models->typeList['azure-gpt35'] = 'Azure / GPT-3.5'; +$lang->ai->models->typeList['baidu-ernie'] = 'Baidu / ERNIE'; -$lang->ai->models->openaiVendorList = array(); -$lang->ai->models->openaiVendorList['openai'] = 'OpenAI'; -$lang->ai->models->openaiVendorList['azure'] = 'Azure'; +$lang->ai->models->vendorList = new stdclass(); +$lang->ai->models->vendorList->{'openai-gpt35'} = array('openai' => 'OpenAI', 'azure' => 'Azure'); +$lang->ai->models->vendorList->{'baidu-ernie'} = array('baidu' => 'Baidu Qianfan LLM Platform'); $lang->ai->models->proxyTypes = array(); $lang->ai->models->proxyTypes[''] = 'No Proxy'; diff --git a/module/ai/lang/en.php b/module/ai/lang/en.php index 1805a0298b..5e7cc8282b 100644 --- a/module/ai/lang/en.php +++ b/module/ai/lang/en.php @@ -423,11 +423,11 @@ $lang->ai->models->statusList['off'] = 'Disable'; $lang->ai->models->typeList = array(); $lang->ai->models->typeList['openai-gpt35'] = 'OpenAI / GPT-3.5'; -// $lang->ai->models->typeList['azure-gpt35'] = 'Azure / GPT-3.5'; +$lang->ai->models->typeList['baidu-ernie'] = 'Baidu / ERNIE'; -$lang->ai->models->openaiVendorList = array(); -$lang->ai->models->openaiVendorList['openai'] = 'OpenAI'; -$lang->ai->models->openaiVendorList['azure'] = 'Azure'; +$lang->ai->models->vendorList = new stdclass(); +$lang->ai->models->vendorList->{'openai-gpt35'} = array('openai' => 'OpenAI', 'azure' => 'Azure'); +$lang->ai->models->vendorList->{'baidu-ernie'} = array('baidu' => 'Baidu Qianfan LLM Platform'); $lang->ai->models->proxyTypes = array(); $lang->ai->models->proxyTypes[''] = 'No Proxy'; diff --git a/module/ai/lang/fr.php b/module/ai/lang/fr.php index 1805a0298b..5e7cc8282b 100644 --- a/module/ai/lang/fr.php +++ b/module/ai/lang/fr.php @@ -423,11 +423,11 @@ $lang->ai->models->statusList['off'] = 'Disable'; $lang->ai->models->typeList = array(); $lang->ai->models->typeList['openai-gpt35'] = 'OpenAI / GPT-3.5'; -// $lang->ai->models->typeList['azure-gpt35'] = 'Azure / GPT-3.5'; +$lang->ai->models->typeList['baidu-ernie'] = 'Baidu / ERNIE'; -$lang->ai->models->openaiVendorList = array(); -$lang->ai->models->openaiVendorList['openai'] = 'OpenAI'; -$lang->ai->models->openaiVendorList['azure'] = 'Azure'; +$lang->ai->models->vendorList = new stdclass(); +$lang->ai->models->vendorList->{'openai-gpt35'} = array('openai' => 'OpenAI', 'azure' => 'Azure'); +$lang->ai->models->vendorList->{'baidu-ernie'} = array('baidu' => 'Baidu Qianfan LLM Platform'); $lang->ai->models->proxyTypes = array(); $lang->ai->models->proxyTypes[''] = 'No Proxy'; diff --git a/module/ai/lang/zh-cn.php b/module/ai/lang/zh-cn.php index 14a0ea74b6..11953d6b0b 100644 --- a/module/ai/lang/zh-cn.php +++ b/module/ai/lang/zh-cn.php @@ -400,6 +400,7 @@ $lang->ai->models->common = '语言模型'; $lang->ai->models->type = '语言模型'; $lang->ai->models->vendor = '供应商'; $lang->ai->models->key = 'API Key'; +$lang->ai->models->secret = 'Secret Key'; $lang->ai->models->resource = 'Resource'; $lang->ai->models->deployment = 'Deployment'; $lang->ai->models->proxyType = '代理类型'; @@ -423,11 +424,11 @@ $lang->ai->models->statusList['off'] = '停用'; $lang->ai->models->typeList = array(); $lang->ai->models->typeList['openai-gpt35'] = 'OpenAI / GPT-3.5'; -// $lang->ai->models->typeList['azure-gpt35'] = 'Azure / GPT-3.5'; +$lang->ai->models->typeList['baidu-ernie'] = '百度 / 文心一言'; -$lang->ai->models->openaiVendorList = array(); -$lang->ai->models->openaiVendorList['openai'] = 'OpenAI'; -$lang->ai->models->openaiVendorList['azure'] = 'Azure'; +$lang->ai->models->vendorList = new stdclass(); +$lang->ai->models->vendorList->{'openai-gpt35'} = array('openai' => 'OpenAI', 'azure' => 'Azure'); +$lang->ai->models->vendorList->{'baidu-ernie'} = array('baidu' => '百度千帆大模型平台'); $lang->ai->models->proxyTypes = array(); $lang->ai->models->proxyTypes[''] = '不使用代理'; diff --git a/module/ai/view/editmodel.html.php b/module/ai/view/editmodel.html.php index e23106e655..34bde3d1a8 100644 --- a/module/ai/view/editmodel.html.php +++ b/module/ai/view/editmodel.html.php @@ -11,9 +11,10 @@ ?> vendor) ? key($lang->ai->models->openaiVendorList) : $modelConfig->vendor; +$currentVendor = empty($modelConfig->vendor) ? key($lang->ai->models->vendorList->{empty($modelConfig->type) ? key($lang->ai->models->typeList) : $modelConfig->type}) : $modelConfig->vendor; $requiredFields = $config->ai->vendorList[$currentVendor]['requiredFields']; js::set('vendorList', $config->ai->vendorList); +js::set('vendorListLang', $lang->ai->models->vendorList); ?>