From 501235ef121a99203aa3694195a0dd24b1f059eb Mon Sep 17 00:00:00 2001 From: kuaifan Date: Mon, 10 Feb 2025 16:48:08 +0900 Subject: [PATCH] =?UTF-8?q?perf:=20AI=E6=9C=BA=E5=99=A8=E4=BA=BA=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=A4=9A=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/DialogController.php | 121 ++++++++++++++++++ app/Models/UserBot.php | 8 +- app/Models/WebSocketDialogMsg.php | 2 + app/Models/WebSocketDialogSession.php | 34 +++++ .../pages/manage/components/DialogWrapper.vue | 35 +++-- 5 files changed, 186 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 3af7e8a00..6fdcbe7d1 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -25,6 +25,7 @@ use App\Models\WebSocketDialogConfig; use App\Models\WebSocketDialogMsgRead; use App\Models\WebSocketDialogMsgTodo; use App\Models\WebSocketDialogMsgTranslate; +use App\Models\WebSocketDialogSession; use Hhxsv5\LaravelS\Swoole\Task\Task; /** @@ -3090,4 +3091,124 @@ class DialogController extends AbstractController return Base::retSuccess('保存成功'); } + + /** + * @api {get} api/dialog/session/create 62. AI-开启新会话 + * + * @apiDescription 需要token身份,仅限与AI用户会话 + * @apiVersion 1.0.0 + * @apiGroup dialog + * @apiName session_create + * + * @apiParam {Number} dialog_id 对话ID + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function session__create() + { + User::auth(); + // + $dialog_id = intval(Request::input('dialog_id')); + // + $dialog = WebSocketDialog::checkDialog($dialog_id); + // + if ($dialog->type != 'user') { + return Base::retError('当前对话不支持'); + } + // + $hasAiUser = WebSocketDialogUser::join('users as u', 'web_socket_dialog_users.userid', '=', 'u.userid') + ->where('dialog_id', $dialog->id) + ->where('u.email', 'like', 'ai-%@bot.system') + ->exists(); + if (!$hasAiUser) { + return Base::retError('当前对话不支持'); + } + // + $session = WebSocketDialogSession::whereDialogId($dialog->id) + ->whereTitle('') + ->first(); + if ($session) { + $dialog->session_id = $session->id; + $dialog->save(); + return Base::retSuccess('success', $session); + } + // + $session = WebSocketDialogSession::create([ + 'dialog_id' => $dialog->id, + 'status' => 1, + 'title' => '', + ]); + $session->save(); + $dialog->session_id = $session->id; + $dialog->save(); + // + return Base::retSuccess('success', $session); + } + + /** + * @api {get} api/dialog/session/list 63. AI-获取会话列表 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup dialog + * @apiName session_list + * + * @apiParam {Number} dialog_id 对话ID + * + * @apiParam {Number} [page] 当前页,默认:1 + * @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function session__list() + { + User::auth(); + // + $dialog_id = intval(Request::input('dialog_id')); + // + $dialog = WebSocketDialog::checkDialog($dialog_id); + // + $sessions = WebSocketDialogSession::whereDialogId($dialog->id) + ->orderByDesc('id') + ->paginate(Base::getPaginate(100, 10)); + // + return Base::retSuccess('success', $sessions); + } + + /** + * @api {get} api/dialog/session/open 64. AI-打开会话 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup dialog + * @apiName session_open + * + * @apiParam {Number} session_id 会话ID + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function session__open() + { + User::auth(); + // + $session_id = intval(Request::input('session_id')); + // + $session = WebSocketDialogSession::whereId($session_id)->first(); + if (empty($session)) { + return Base::retError('会话不存在或已被删除'); + } + // + $dialog = WebSocketDialog::checkDialog($session->dialog_id); + // + $dialog->session_id = $session->id; + $dialog->save(); + // + return Base::retSuccess('success', $session); + } } diff --git a/app/Models/UserBot.php b/app/Models/UserBot.php index 7ec881550..b62906a7e 100644 --- a/app/Models/UserBot.php +++ b/app/Models/UserBot.php @@ -205,15 +205,15 @@ class UserBot extends AbstractModel ] ], [ - 'key' => '~ai-chat-new', - 'label' => Doo::translate('开启新对话'), + 'key' => '~ai-session-create', + 'label' => Doo::translate('开启新会话'), 'config' => [ 'model' => $aibotSetting[$match[1] . '_model'] ] ], [ - 'key' => '~ai-chat-history', - 'label' => Doo::translate('历史对话'), + 'key' => '~ai-session-history', + 'label' => Doo::translate('历史会话'), ] ]; } diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index 2397b660b..5fe77ce50 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -1196,6 +1196,8 @@ class WebSocketDialogMsg extends AbstractModel $dialogMsg->send = 1; $dialogMsg->generateKeyAndSave($search_key); // + WebSocketDialogSession::updateTitle($dialogMsg->session_id, $dialogMsg); + // if ($dialogMsg->type === 'meeting') { MeetingMsg::createInstance([ 'meetingid' => $dialogMsg->msg['meetingid'], diff --git a/app/Models/WebSocketDialogSession.php b/app/Models/WebSocketDialogSession.php index bd7bba9f7..7a0f6516e 100644 --- a/app/Models/WebSocketDialogSession.php +++ b/app/Models/WebSocketDialogSession.php @@ -2,6 +2,9 @@ namespace App\Models; +use App\Module\Base; +use Cache; + /** * App\Models\WebSocketDialogSession * @@ -48,4 +51,35 @@ class WebSocketDialogSession extends AbstractModel { return $this->belongsTo(WebSocketDialog::class, 'dialog_id'); } + + /** + * @param $sessionId + * @param WebSocketDialogMsg $dialogMsg + * @return void + */ + public static function updateTitle($sessionId, $dialogMsg) + { + if (!$sessionId) { + return; + } + if ($dialogMsg->type != 'text') { + return; + } + $cacheKey = 'dialog_session_title_' . $sessionId; + if (Cache::has($cacheKey)) { + return; + } + $session = self::whereId($sessionId)->first(); + if (!$session) { + return; + } + $title = Base::cutStr($dialogMsg->key ?: $dialogMsg->msg['text'], 100); + if (empty($title)) { + return; + } + $session->title = $title; + $session->save(); + // todo 通过AI生成标题 + Cache::forever($cacheKey, true); + } } diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index f9f382c54..48b4df564 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -1321,12 +1321,7 @@ export default { }, msgType() { - this.getMsgs({ - dialog_id: this.dialogId, - msg_id: this.msgId, - msg_type: this.msgType, - clear_before: true - }).catch(_ => {}) + this.onGetMsgClear() }, searchKey(key) { @@ -1922,15 +1917,26 @@ export default { } break; - // 开启新对话 - case "~ai-chat-new": + // 开启新会话 + case "~ai-session-create": if (!this.isAiBot) { return } + this.$store.dispatch("call", { + url: 'dialog/session/create', + data: { + dialog_id: this.dialogId, + }, + spinner: 300 + }).then(() => { + this.onGetMsgClear() + }).catch(({msg}) => { + $A.modalError(msg) + }); break; - // 历史对话 - case "~ai-chat-history": + // 历史会话 + case "~ai-session-history": if (!this.isAiBot) { return } @@ -2578,6 +2584,15 @@ export default { this.$store.dispatch("openOkr", this.dialogData.link_id); }, + onGetMsgClear() { + this.getMsgs({ + dialog_id: this.dialogId, + msg_id: this.msgId, + msg_type: this.msgType, + clear_before: true + }).catch(_ => {}) + }, + onReGetMsg() { this.scrollToBottomRefresh = false this.getMsgs({