From bc3f7c57461cf2bcea4ad484739696470d5052e0 Mon Sep 17 00:00:00 2001 From: geekr Date: Thu, 23 Mar 2023 18:49:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=87=8D=E6=96=B0=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=93=8D=E5=BA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/ChatController.php | 44 ++++++++-- resources/js/Pages/Chat.vue | 106 ++++++++++++++++-------- resources/js/api/chat.js | 9 +- resources/js/store.js | 55 ++++++++++-- 4 files changed, 162 insertions(+), 52 deletions(-) diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index 19c2887..be57e61 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -36,12 +36,25 @@ public function chat(Request $request): JsonResponse { $request->validate([ 'prompt' => 'required|string', + 'regen' => ['required', 'in:true,false'], 'api_key' => 'sometimes|string', ]); + $regen = $request->boolean('regen'); $messages = $request->session()->get('messages', [$this->preset]); + if ($regen && count($messages) == 1) { + // 处理服务端session已过期的情况 + $regen = false; + } $userMessage = ['role' => 'user', 'content' => $request->input('prompt')]; - $messages[] = $userMessage; - $request->session()->put('messages', $messages); // 基于session存储当前会话信息 + if (!$regen) { + // 基于session存储当前会话信息 + $messages[] = $userMessage; + $request->session()->put('messages', $messages); + } elseif ($messages[count($messages) - 1]['role'] == 'assistant') { + // 重试则删除最后一条回复 + array_pop($messages); + $request->session()->put('messages', $messages); + } $chatId = Str::uuid(); // 生成一个唯一聊天ID作为下次请求的凭证 $request->session()->put('chat_id', $chatId); return response()->json(['chat_id' => $chatId, 'message' => $userMessage]); @@ -54,15 +67,25 @@ public function translate(Request $request): JsonResponse { $request->validate([ 'prompt' => 'required|string', + 'regen' => ['required', 'in:true,false'], 'api_key' => 'sometimes|string', ]); + $regen = $request->boolean('regen'); $messages = $request->session()->get('messages', [$this->preset]); + if ($regen && count($messages) == 1) { + $regen = false; + } // 判断内容是中文还是英文 $isChinese = preg_match('/[\x{4e00}-\x{9fa5}]/u', $request->input('prompt')); $prefix = $isChinese ? '请将以下内容翻译成英文: ' : '请将以下内容翻译成中文: '; $userMessage = ['role' => 'user', 'content' => $prefix . $request->input('prompt')]; - $messages[] = $userMessage; - $request->session()->put('messages', $messages); + if (!$regen) { + $messages[] = $userMessage; + $request->session()->put('messages', $messages); // 基于session存储当前会话信息 + } elseif ($messages[count($messages) - 1]['role'] == 'assistant') { + array_pop($messages); + $request->session()->put('messages', $messages); + } $chatId = Str::uuid(); $request->session()->put('chat_id', $chatId); return response()->json(['chat_id' => $chatId, 'message' => $userMessage]); @@ -154,7 +177,7 @@ public function audio(Request $request): JsonResponse $path = $request->audio->storeAs($dir, $fileName, 'local'); $messages = $request->session()->get('messages', [ - ['role' => 'system', 'content' => 'You are GeekChat - A ChatGPT clone. Answer as concisely as possible. Make Mandarin Chinese the primary language'] + ['role' => 'system', 'content' => 'You are GeekChat - A chatbot that can understand text, voice, draw image and translate. Answer as concisely as possible. Make Mandarin Chinese the primary language'] ]); // $path = 'audios/2023/03/09/test.wav';(测试用) // 调用 speech to text API 将语音转化为文字 @@ -190,12 +213,21 @@ public function image(Request $request): JsonResponse { $request->validate([ 'prompt' => 'required|string', + 'regen' => ['required', 'in:true,false'], 'api_key' => 'sometimes|string', ]); + $regen = $request->boolean('regen'); $messages = $request->session()->get('messages', [$this->preset]); + if ($regen && count($messages) == 1) { + $regen = false; + } $prompt = $request->input('prompt'); $userMsg = ['role' => 'user', 'content' => $prompt]; - $messages[] = $userMsg; + if (!$regen) { + $messages[] = $userMsg; + } elseif ($messages[count($messages) - 1]['role'] == 'assistant') { + array_pop($messages); + } $apiKey = $request->input('api_key'); $size = '256x256'; if (!empty($apiKey)) { diff --git a/resources/js/Pages/Chat.vue b/resources/js/Pages/Chat.vue index 7a2ba8e..fedfa9e 100644 --- a/resources/js/Pages/Chat.vue +++ b/resources/js/Pages/Chat.vue @@ -13,6 +13,8 @@ onMounted(() => { const messages = computed(() => store.state.messages.filter(message => message != undefined)) const isTyping = computed(() => store.state.isTyping) const apiKey = computed(() => store.state.apiKey ? '*'.repeat(4) + store.state.apiKey.substr(length - 4, 4) : '') +const lastMessage = computed(() => store.state.lastMessage) +const lastAction = computed(() => store.state.lastAction) const form = useForm({ prompt: null @@ -22,7 +24,7 @@ const chat = () => { if (isTyping.value) { return; } - store.dispatch('chatMessage', form.prompt) + store.dispatch('chatMessage', { message: form.prompt }) form.reset() } @@ -53,7 +55,7 @@ const image = () => { if (isTyping.value) { return; } - store.dispatch('imageMessage', form.prompt) + store.dispatch('imageMessage', { message: form.prompt }) form.reset() } @@ -65,15 +67,38 @@ const translate = () => { if (isTyping.value) { return; } - store.dispatch('translateMessage', form.prompt) + store.dispatch('translateMessage', { message: form.prompt }) form.reset() } const enterApiKey = () => { - const api_key = prompt("输入你的 OpenAI API Key(使用自己的 Key 可以专享独立的调用通道,不受共享通道频率影响,并且可以绘制更精准的大尺寸图片):"); + const api_key = prompt("输入你的 OpenAI API KEY(使用自己的 KEY 可以不受共享通道频率限制影响,还可以绘制大尺寸图片):"); store.dispatch('validAndSetApiKey', api_key) } +const regenerate = () => { + if (!lastMessage.value || !lastAction.value) { + return + } + switch (lastAction.value) { + case "chat": + store.dispatch('chatMessage', { message: lastMessage.value, regen: true }) + break + case "image": + store.dispatch('imageMessage', { message: lastMessage.value, regen: true }) + break + case "translate": + store.dispatch('translateMessage', { message: lastMessage.value, regen: true }) + break + case "audio": + store.dispatch('chatMessage', { message: lastMessage.value, regen: true }) + break + default: + alert('未知消息类型') + break + } +} +