chatgpt开发包
这里我用的是orhanerday/open-ai
composer 包安装
composer require orhanerday/open-ai
简单调用实例
<?phprequire __DIR__ . '/vendor/autoload.php'; // remove this line if you use a PHP Framework.use Orhanerday\OpenAi\OpenAi;$open_ai_key = getenv('OPENAI_API_KEY');
$open_ai = new OpenAi($open_ai_key);$complete = $open_ai->chat(['model' => 'gpt-3.5-turbo','messages' => [["role" => "system","content" => "You are a helpful assistant."],["role" => "user","content" => "Who won the world series in 2020?"],["role" => "assistant","content" => "The Los Angeles Dodgers won the World Series in 2020."],["role" => "user","content" => "Where was it played?"],],'temperature' => 1.0,'max_tokens' => 4000,'frequency_penalty' => 0,'presence_penalty' => 0,
]);var_dump($complete);
集成到项目
思路一 websoket方式
- 后台开启websoket
采用wokerman GatewayWorker 启动websoket服务
protected function configure(){// 指令配置$this->setName('chat:server')->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman server.', null)->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of workerman server.', null)->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the workerman server in daemon mode.')->setDescription('chatWebsoket server');}protected function execute(Input $input, Output $output){$action = $input->getArgument('action');if (DIRECTORY_SEPARATOR !== '\\') {if (!in_array($action, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) {$output->writeln("Invalid argument action:{$action}, Expected start|stop|restart|reload|status|connections .");exit(1);}global $argv;array_shift($argv);array_shift($argv);array_unshift($argv, 'think', $action);} else {$output->writeln("GatewayWorker Not Support On Windows.");exit(1);}if ('start' == $action) {$output->writeln('Starting GatewayWorker server...');}$this->startRegister();$this->startBusinessWorker();$this->startGateway();Worker::runAll();}protected function startRegister(){//初始化registernew Register('text://0.0.0.0:1236');}protected function startBusinessWorker(){//bussinessWorker $worker = new BusinessWorker();// worker名称$worker->name = 'BusinessWorker';// bussinessWorker进程数量$worker->count = 4;// 服务注册地址$worker->registerAddress = '127.0.0.1:1236';$worker->eventHandler = '\plugin\echoChatgpt\controller\Events';}protected function startGateway(){// 初始化 gateway 进程$gateway = new Gateway("websocket://0.0.0.0:8282");$gateway->name = 'Gateway';$gateway->count = 4;$gateway->lanIp = '127.0.0.1';$gateway->startPort = 2900;$gateway->registerAddress = '127.0.0.1:1236';// 心跳间隔$gateway->pingInterval = 55;$gateway->pingNotResponseLimit = 1;}
2.后台异步任务
接收到问题后 采用Queue 进入队列 异步消费
Queue::push('plugin\echoChatgpt\job\Task', ['chatid' => $chat->id]);
消费任务
class Task
{public function fire(Job $job, $data){$rt = $this->doJob($data);if ($rt) {$job->delete();return true;}// 重试三次失败 todo...if ($job->attempts() >= 3) {$this->dofail($data);$job->delete();return true;}}public function doJob($data){//这里发起处理chatgpt问题}
}
3.前端连接websoket
uniapp store 中 链接 websoket
import Vue from 'vue'
const state = {isOpen:false,SocketTask:null,lockReconnect:false
}
const getters = {}
const mutations = {updateisOpen(state, status) {state.isOpen = status;},updateTask(state,task){state.SocketTask = task;},updateLockReconnect(state, status) {state.lockReconnect = status;}
}
const actions = {connectSocket({commit,state,dispatch}) {return new Promise((resolve, reject) => {try{const socketTask = uni.connectSocket({url: 'wss://chatgpt.wxhpco.com/ws',success :(res)=>{}});commit('updateTask', socketTask);uni.onSocketOpen(function (res) {console.info('websoket链接成功')commit('updateisOpen', true);console.log(state.isOpen)//心跳heartCheck.reset().start();//初始化内容// dispatch('WebSocketinit');resolve();});uni.onSocketClose(function () {if(state.isOpen){dispatch('reconnect');}console.log("ws连接关闭!"+new Date().toLocaleString());});uni.onSocketError(function () {commit('updateisOpen', false);console.log(state.isOpen)dispatch('reconnect');console.log("ws连接错误!"+new Date().toLocaleString());});uni.onSocketMessage(function (res) {heartCheck.reset().start();});//心跳检测var heartCheck = {timeout: 2000, //2分钟发一次心跳timeoutObj: null,serverTimeoutObj: null,reset: function(){clearTimeout(this.timeoutObj);clearTimeout(this.serverTimeoutObj);return this;},start: function(){var self = this;this.timeoutObj = setTimeout(function(){//这里发送一个心跳,后端收到后,返回一个心跳消息,//onmessage拿到返回的心跳就说明连接正常socketTask.send({data:'{"type":"ping","content":""}'});self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了socketTask.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次}, self.timeout)}, this.timeout)}}}catch(e){dispatch('reconnect');}})},reconnect({commit,state,dispatch}){if(state.lockReconnect) {return false;};commit('updateLockReconnect', true);setTimeout(function () {dispatch('connectSocket');commit('updateLockReconnect', false);}, 2000);},WebSocketinit({commit,state,dispatch},params){var data = {'type':'init','data': params}state.SocketTask.send({data:JSON.stringify(data)});}
}export default {namespaced: true,state,getters,mutations,actions
}
4.前端提问和接收回答
this.websoket.onMessage(function(res){console.log(res)var data = JSON.parse(res.data);switch(data.type){case 'ask':that.$refs.chat.updateText(data.data)break;case 'reply':// that.replyMsg = data.datathat.$refs.chat.updateText(data.data)break;case 'modal':uni.showModal({title: '提示',content: data.data.message,success: function (res) {if (res.confirm) {console.log(data)} }});break;case 'msg':uni.$u.toast(data.data.message);break;default:return ;}})
效果
适配的PC端效果图