From 4fb7236ea90792a91d9b945fbbd5c788efd81169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E4=BD=B3=E8=B1=AA?= Date: Wed, 21 Feb 2024 22:48:09 +0800 Subject: [PATCH] feat: supports GROQ API --- src/assets/models-list.ts | 17 +- src/chat.completion/ChatCompletionHandler.ts | 55 ++++--- src/chat.completion/ConversationController.ts | 3 + src/components/ConversationBody.vue | 56 ++++++- src/components/ConversationItem.vue | 3 +- src/components/EditSessionSettingsDialog.vue | 4 +- src/components/FunctionCallingDetailView.vue | 4 +- src/components/GroqSetting.vue | 150 ++++++++++++++++++ src/components/MessageRecordItem.vue | 32 ++-- src/components/OpenAISetting.vue | 10 +- src/components/PromptEditor.vue | 2 +- src/components/SettingSlideBar.vue | 6 +- src/database/db.ts | 3 +- src/database/table-type.ts | 2 + src/groq/logic/service.ts | 110 +++++++++++++ src/groq/parser/ChatCompletionParser.ts | 60 +++++++ src/locales/en.ts | 12 ++ src/locales/zh-CN.ts | 12 ++ src/openai/logic/services.ts | 72 +-------- src/store/localstorage.ts | 6 + 20 files changed, 494 insertions(+), 125 deletions(-) create mode 100644 src/components/GroqSetting.vue create mode 100644 src/groq/logic/service.ts create mode 100644 src/groq/parser/ChatCompletionParser.ts diff --git a/src/assets/models-list.ts b/src/assets/models-list.ts index 7e0c30b..d1cfffa 100644 --- a/src/assets/models-list.ts +++ b/src/assets/models-list.ts @@ -41,4 +41,19 @@ const gpt_models: { }, ] -export default gpt_models +const groq_models: { + value: string + name: string +}[] = [{ + value: 'llama2-70b-4096', + name: 'LLaMA2-70b-chat', +}, +{ + value: 'mixtral-8x7b-32768', + name: 'Mixtral-8x7b-Instruct-v0.1', +}] + +export { + groq_models, + gpt_models, +} diff --git a/src/chat.completion/ChatCompletionHandler.ts b/src/chat.completion/ChatCompletionHandler.ts index f901a92..295bb27 100644 --- a/src/chat.completion/ChatCompletionHandler.ts +++ b/src/chat.completion/ChatCompletionHandler.ts @@ -3,13 +3,15 @@ import messageController from './MessageController' import conversationController from './ConversationController' import type { NewMessageInfo, TBConverstationInfo, TBMessageInfo, TBPromptInfo } from '@/database/table-type' import type { ChatCompletionMessage } from '@/openai/type/chat.completion.message' -import openAIServices from '@/openai/logic/services' import { handleChatCompletionPrecondition } from '@/openai/handler/ChatHandler' +import openAIServices from '@/openai/logic/services' import ChatCompletionParser from '@/openai/parser/ChatCompletionParser' +import ChatCompletionParserByGroq from '@/groq/parser/ChatCompletionParser' import useEditorStore from '@/store/editor-store' import handleChatVisionPrecondition from '@/openai/handler/VisionHandler' import type { ToolCallsInfo } from '@/openai/type/chat.completion.tool.calls' import chatFunctionCallingController from '@/chat.function.calling/ChatFunctionCallingController' +import groqServices from '@/groq/logic/service' /** * 当前的对话处理器 @@ -77,30 +79,17 @@ class ChatCompletionHandler { } } - /** - * 获取某个对话的结果 - * @param messageId - * @param resultCallback - * @returns - */ - async getMessageAnswerAsync(messageId: number, resultCallback: (value: string) => void, functionCallingResultCallback: ( - tool_call_id: string, - functionName: string, - args: string, - isDone: boolean - ) => void): Promise { + async getMessageAnswerByGroqAsync(messageId: number, resultCallback: (value: string) => void) { this.editorStore.thinking = true this.lastMessageId = messageId const messageInfo = await messageController.getMessageInfoByIdAsync(messageId) - const useVisionAPI = messageInfo.vision_file !== undefined - const messageList = await this.generateMessageListByMessageInfoAsync(messageInfo) // 得到请求包 - const chatCompletionResponse = await openAIServices.createChatCompletionsRequest(messageList, useVisionAPI) + const chatCompletionResponse = await groqServices.createChatCompletionsRequest(messageList) if (chatCompletionResponse.code !== 1) { this.editorStore.thinking = false @@ -112,17 +101,14 @@ class ChatCompletionHandler { let markResult = false - let needFunctionResult = false + const needFunctionResult = false if (chatCompletionResponse.data !== null) { let gptContent = '' - parserResult = await ChatCompletionParser(chatCompletionResponse.data!, (text) => { + parserResult = await ChatCompletionParserByGroq(chatCompletionResponse.data!, (text) => { gptContent += text resultCallback(text) - }, (tool_call_id, fname, args, isDone) => { - needFunctionResult = true - functionCallingResultCallback(tool_call_id, fname, args, isDone) }) if (parserResult) @@ -134,16 +120,31 @@ class ChatCompletionHandler { return (parserResult && markResult) } - async getDataWorkAnswerAsync(messageId: number, messageList: ChatCompletionMessage[], resultCallback: (value: string) => void) { + /** + * 获取某个对话的结果 + * @param messageId + * @param resultCallback + * @returns + */ + async getMessageAnswerAsync(messageId: number, resultCallback: (value: string) => void, functionCallingResultCallback: ( + tool_call_id: string, + functionName: string, + args: string, + isDone: boolean + ) => void): Promise { this.editorStore.thinking = true this.lastMessageId = messageId - // 得到请求包 - const chatCompletionResponse = await openAIServices.createDataWorkRequest(messageList) - const messageInfo = await messageController.getMessageInfoByIdAsync(messageId) + const useVisionAPI = messageInfo.vision_file !== undefined + + const messageList = await this.generateMessageListByMessageInfoAsync(messageInfo) + + // 得到请求包 + const chatCompletionResponse = await openAIServices.createChatCompletionsRequest(messageList, useVisionAPI) + if (chatCompletionResponse.code !== 1) { this.editorStore.thinking = false await this.markMessageErrorAsync(messageInfo, chatCompletionResponse.message) @@ -162,8 +163,9 @@ class ChatCompletionHandler { parserResult = await ChatCompletionParser(chatCompletionResponse.data!, (text) => { gptContent += text resultCallback(text) - }, () => { + }, (tool_call_id, fname, args, isDone) => { needFunctionResult = true + functionCallingResultCallback(tool_call_id, fname, args, isDone) }) if (parserResult) @@ -225,6 +227,7 @@ class ChatCompletionHandler { conversation_token: conversationInfo.conversation_token, fixed_top: conversationInfo.fixed_top, type: conversationInfo.type, + use_groq: conversationInfo.use_groq, } as TBConverstationInfo conversationController.updateConversationInfoAsync(newInfo) diff --git a/src/chat.completion/ConversationController.ts b/src/chat.completion/ConversationController.ts index f27835c..ddd1be0 100644 --- a/src/chat.completion/ConversationController.ts +++ b/src/chat.completion/ConversationController.ts @@ -94,6 +94,7 @@ class ConversationController { description: '', conversation_token: uid(32), type: 'chat', + use_groq: false, } as NewConverstationInfo return await this.addConversationAsync(newInfo) @@ -110,6 +111,7 @@ class ConversationController { description: '', conversation_token: uid(32), type: 'dataworker', + use_groq: false, } as NewConverstationInfo return await this.addConversationAsync(newInfo) @@ -126,6 +128,7 @@ class ConversationController { description: '', conversation_token: uid(32), type: 'draw_img_mode', + use_groq: false, } as NewConverstationInfo return await this.addConversationAsync(newInfo) diff --git a/src/components/ConversationBody.vue b/src/components/ConversationBody.vue index 12e4416..980b662 100644 --- a/src/components/ConversationBody.vue +++ b/src/components/ConversationBody.vue @@ -7,6 +7,8 @@ import useConversationStore from '@/store/conversation-store' import conversationController from '@/chat.completion/ConversationController' import type { TBConverstationInfo, TBMessageInfo } from '@/database/table-type' import useChatCompletionStore from '@/store/chat-completion-store' +import { groqApiKey, groqBaseURL, groqModel } from '@/store/localstorage' +import { push } from '@/main' const { t } = useI18n() @@ -50,7 +52,8 @@ function onCreateDrawImageConversation() { color: 'bg-yellow-2', fixed_top: info.fixed_top ?? false, type: 'draw_img_mode', - } + useGroqAPI: info.use_groq, + } as TBConverstationInfo updateConversationInfo(newInfo as TBConverstationInfo) } @@ -58,14 +61,45 @@ function onCreateDrawImageConversation() { function updateConversationInfo(newInfo: TBConverstationInfo) { conversationController.updateConversationInfoAsync(newInfo) } + +function onUseGroqAPI() { + const info = chatCompletionStore.chatCompletionHandler?.getConversationInfo() + + if (info?.use_groq === false) { + if (groqApiKey.value.length === 0 || groqBaseURL.value.length === 0 || groqModel.value.length === 0) { + push.warning(t('use_groq_api.cannot_empty')) + return + } + } + updateConversationUseAPI(!info.use_groq) +} + +function updateConversationUseAPI(useGroqAPI: boolean) { + const conversationStore = useConversationStore() + + const info = conversationStore.conversationInfo! + + const newInfo = { + id: info.id, + title: 'Chat Completion (Grop API)', + create_time: info.create_time, + description: info.description, + conversation_token: info.conversation_token, + color: 'bg-blue', + fixed_top: info.fixed_top ?? false, + type: 'chat', + use_groq: useGroqAPI, + } as TBConverstationInfo + + updateConversationInfo(newInfo as TBConverstationInfo) +} diff --git a/src/components/ConversationItem.vue b/src/components/ConversationItem.vue index e7de94d..60adb1a 100644 --- a/src/components/ConversationItem.vue +++ b/src/components/ConversationItem.vue @@ -57,7 +57,8 @@ function fixedTop() { color: info.color, fixed_top: !(info.fixed_top ?? false), type: info.type, - } + use_groq: info.use_groq, + } as TBConverstationInfo conversationController.updateConversationInfoAsync(newConversationInfo).then((res) => { if (res) diff --git a/src/components/EditSessionSettingsDialog.vue b/src/components/EditSessionSettingsDialog.vue index a43108d..ab648ad 100644 --- a/src/components/EditSessionSettingsDialog.vue +++ b/src/components/EditSessionSettingsDialog.vue @@ -6,6 +6,7 @@ import useConversationStore from '@/store/conversation-store' import SelectColorDialog from '@/ui/SelectColorDialog.vue' import { push } from '@/main' import conversationController from '@/chat.completion/ConversationController' +import type { TBConverstationInfo } from '@/database/table-type' const { t } = useI18n() @@ -34,7 +35,8 @@ async function onSaveSessionSettings() { color: color.value, fixed_top: info.fixed_top ?? false, type: info.type, - } + use_groq: info.use_groq, + } as TBConverstationInfo const result = await conversationController.updateConversationInfoAsync(newConversationInfo) if (result) { diff --git a/src/components/FunctionCallingDetailView.vue b/src/components/FunctionCallingDetailView.vue index 33444be..9254978 100644 --- a/src/components/FunctionCallingDetailView.vue +++ b/src/components/FunctionCallingDetailView.vue @@ -1,11 +1,11 @@ + + + + diff --git a/src/components/MessageRecordItem.vue b/src/components/MessageRecordItem.vue index df93ace..e29896e 100644 --- a/src/components/MessageRecordItem.vue +++ b/src/components/MessageRecordItem.vue @@ -87,19 +87,31 @@ async function getAnswer(messageId: number) { gptContent.value = '' checkingFunctionCalling.value = true let needGetFunctionResult = false - await chatCompletionStore.chatCompletionHandler?.getMessageAnswerAsync(messageId, (value) => { + if (chatCompletionStore.chatCompletionHandler?.getConversationInfo().use_groq ?? false) { + getAnswerByGroq(messageId) + } + else { + await chatCompletionStore.chatCompletionHandler?.getMessageAnswerAsync(messageId, (value) => { + gptContent.value += value + scrollBody() + }, (tool_call_id, functionName, args, _) => { + needGetFunctionResult = true + handleFunction(tool_call_id, functionName, args) + }) + if (!needGetFunctionResult) { + checkingFunctionCalling.value = false + reloadMessageInfoFromDB() + if (props.messageIndex === 0) + chatCompletionStore.chatCompletionHandler?.getChatCompletionTitleFromMessageAsync(messageInfo.value.id) + } + } +} + +async function getAnswerByGroq(messageId: number) { + await chatCompletionStore.chatCompletionHandler?.getMessageAnswerByGroqAsync(messageId, (value) => { gptContent.value += value scrollBody() - }, (tool_call_id, functionName, args, _) => { - needGetFunctionResult = true - handleFunction(tool_call_id, functionName, args) }) - if (!needGetFunctionResult) { - checkingFunctionCalling.value = false - reloadMessageInfoFromDB() - if (props.messageIndex === 0) - chatCompletionStore.chatCompletionHandler?.getChatCompletionTitleFromMessageAsync(messageInfo.value.id) - } } /** diff --git a/src/components/OpenAISetting.vue b/src/components/OpenAISetting.vue index a1d9543..d8da7a7 100644 --- a/src/components/OpenAISetting.vue +++ b/src/components/OpenAISetting.vue @@ -2,7 +2,7 @@ import { ref } from 'vue' import { useI18n } from 'vue-i18n' import Dialog from '@/ui/Dialog.vue' -import gpt_models from '@/assets/models-list' +import { gpt_models } from '@/assets/models-list' import { push } from '@/main' import { apiKey, baseURL, gptModel } from '@/store/localstorage' @@ -73,7 +73,7 @@ function copyOpenAiKey() {
-
+
Open AI
@@ -84,7 +84,7 @@ function copyOpenAiKey() { Base URL
-
+
{{ baseURL ? baseURL : 'undefined' }}
@@ -107,9 +107,9 @@ function copyOpenAiKey() {
{{ t('chat_model') }}
-
+
{{ - gptModel ? gptModel : 'undefined' + gptModel ? gpt_models.filter(a => a.value === gptModel)[0].name : 'undefined' }}
diff --git a/src/components/PromptEditor.vue b/src/components/PromptEditor.vue index 91cd66c..711d3d9 100644 --- a/src/components/PromptEditor.vue +++ b/src/components/PromptEditor.vue @@ -157,7 +157,7 @@ defineExpose({