This commit is contained in:
2024-04-03 18:01:45 +08:00
parent 7a317ff749
commit 73dd6a21a7
3 changed files with 110 additions and 54 deletions

View File

@@ -1,28 +1,31 @@
<script lang="ts" setup> <script lang="ts" setup>
import ChatItem from "~/components/aigc/chat/ChatItem.vue"; import ChatItem from '~/components/aigc/chat/ChatItem.vue'
import Message from "~/components/aigc/chat/Message.vue"; import Message from '~/components/aigc/chat/Message.vue'
import { import {
type Assistant,
type ChatMessage, type ChatMessage,
type ChatMessageId, type ChatMessageId,
type ChatSession, type ChatSession,
type ChatSessionId, type ChatSessionId,
llmModels, llmModels,
type ModelTag type ModelTag,
} from "~/typings/llm"; } from '~/typings/llm'
import {useHistory} from "~/composables/useHistory"; import {useHistory} from '~/composables/useHistory'
import {uuidv4} from "@uniiem/uuid"; import {uuidv4} from '@uniiem/uuid'
import {useLLM} from "~/composables/useLLM"; import {useLLM} from '~/composables/useLLM'
import {trimObject} from "@uniiem/object-trim"; import {trimObject} from '@uniiem/object-trim'
import ModalAuthentication from "~/components/ModalAuthentication.vue"; import ModalAuthentication from '~/components/ModalAuthentication.vue'
import {useAsyncData} from '#app'
useHead({ useHead({
title: '聊天 | XSH AI' title: '聊天 | XSH AI',
}) })
const dayjs = useDayjs() const dayjs = useDayjs()
const toast = useToast() const toast = useToast()
const modal = useModal() const modal = useModal()
const loginState = useLoginState() const loginState = useLoginState()
const {token, user, is_logged_in} = storeToRefs(loginState)
const historyStore = useHistory() const historyStore = useHistory()
const {chatSessions} = storeToRefs(historyStore) const {chatSessions} = storeToRefs(historyStore)
const {setChatSessions} = historyStore const {setChatSessions} = historyStore
@@ -35,14 +38,36 @@ const user_input = ref('')
const responding = ref(false) const responding = ref(false)
const currentModel = ref<ModelTag>('spark3_5') const currentModel = ref<ModelTag>('spark3_5')
const modals = reactive({ const modals = reactive({
modelSelect: false modelSelect: false,
}) })
loginState.$subscribe((mutation, state) => {
console.log(mutation, state)
})
const {
data: assistantTemplates
} = await useAsyncData(
'App.Assistant_Template.GetList',
() => useFetchWrapped<
req.AssistantTemplateList & AuthedRequest, PagedData<Assistant>
>('App.Assistant_Template.GetList', {
user_id: loginState.user.id,
token: loginState.token as string,
page: 1,
perpage: 20,
}),
{
immediate: true,
watch: [is_logged_in],
},
)
/** /**
* 获取指定 ID 的会话数据 * 获取指定 ID 的会话数据
* @param chatSessionId * @param chatSessionId
*/ */
const getSessionCopyById = (chatSessionId: ChatSessionId): ChatSession | undefined => chatSessions.value.find(s => s.id === chatSessionId); const getSessionCopyById = (chatSessionId: ChatSessionId): ChatSession | undefined => chatSessions.value.find(s => s.id === chatSessionId)
/** /**
* 切换当前会话 * 切换当前会话
* @param chatSessionId 指定会话 ID不传则切换到列表中第一个会话 * @param chatSessionId 指定会话 ID不传则切换到列表中第一个会话
@@ -95,7 +120,8 @@ const handleClickCreateSession = () => {
]) ])
// 切换到新的会话 // 切换到新的会话
selectCurrentSessionId(sessionId) selectCurrentSessionId(sessionId)
modals.modelSelect = true // TODO: Model or Assistant Selection
// modals.modelSelect = true
nextTick(() => { nextTick(() => {
insetMessage({ insetMessage({
id: uuidv4(), id: uuidv4(),
@@ -112,12 +138,12 @@ const handleClickCreateSession = () => {
const handleClickSend = (event: any) => { const handleClickSend = (event: any) => {
if (!loginState.is_logged_in) { if (!loginState.is_logged_in) {
modal.open(ModalAuthentication) modal.open(ModalAuthentication)
return; return
} }
if (event.ctrlKey) { if (event.ctrlKey) {
return; return
} }
if (responding.value) return; if (responding.value) return
if (!user_input.value) return if (!user_input.value) return
if (!currentSessionId.value) { if (!currentSessionId.value) {
toast.add({ toast.add({
@@ -126,7 +152,7 @@ const handleClickSend = (event: any) => {
color: 'red', color: 'red',
icon: 'i-tabler-circle-x', icon: 'i-tabler-circle-x',
}) })
return; return
} }
// 插入用户消息 // 插入用户消息
insetMessage({ insetMessage({
@@ -146,10 +172,10 @@ const handleClickSend = (event: any) => {
}) })
// 请求模型回复 // 请求模型回复
const trimmedMessages = trimObject(getMessages(), 2000, { const trimmedMessages = trimObject(getMessages(), 2000, {
keys: ['content'] keys: ['content'],
}) })
useLLM(trimmedMessages, { useLLM(trimmedMessages, {
modelTag: currentModel.value modelTag: currentModel.value,
}).then(reply => { }).then(reply => {
modifyMessageContent(assistantReplyId, reply) modifyMessageContent(assistantReplyId, reply)
}).catch(err => { }).catch(err => {
@@ -173,8 +199,8 @@ const insetMessage = (message: ChatMessage): ChatMessageId => {
...s, ...s,
messages: [ messages: [
...s.messages, ...s.messages,
message message,
] ],
} : s)) } : s))
scrollToMessageListBottom() scrollToMessageListBottom()
return message.id return message.id
@@ -186,7 +212,7 @@ const modifyMessageContent = (
messageId: ChatMessageId, messageId: ChatMessageId,
content: string, content: string,
interrupted: boolean = false, interrupted: boolean = false,
updateTime: boolean = true updateTime: boolean = true,
) => { ) => {
setChatSessions(chatSessions.value.map(s => s.id === currentSessionId.value ? { setChatSessions(chatSessions.value.map(s => s.id === currentSessionId.value ? {
...s, ...s,
@@ -195,7 +221,7 @@ const modifyMessageContent = (
content, content,
interrupted, interrupted,
create_at: updateTime ? dayjs().unix() : m.create_at, create_at: updateTime ? dayjs().unix() : m.create_at,
} : m) } : m),
} : s)) } : s))
scrollToMessageListBottom() scrollToMessageListBottom()
} }
@@ -248,7 +274,8 @@ onMounted(() => {
</div> </div>
</div> </div>
<div class="h-[calc(100vh-4rem)] bg-white dark:bg-neutral-900 flex-1 flex flex-col"> <div class="h-[calc(100vh-4rem)] bg-white dark:bg-neutral-900 flex-1 flex flex-col">
<div class="w-full p-4 bg-neutral-50 dark:bg-neutral-800/50 border-b dark:border-neutral-700/50 flex items-center gap-2"> <div
class="w-full p-4 bg-neutral-50 dark:bg-neutral-800/50 border-b dark:border-neutral-700/50 flex items-center gap-2">
<UButton <UButton
class="md:hidden" class="md:hidden"
color="black" color="black"
@@ -269,40 +296,49 @@ onMounted(() => {
/> />
</TransitionGroup> </TransitionGroup>
</div> </div>
<pre>{{assistantTemplates}}</pre>
</div> </div>
<div <ClientOnly>
class="w-full p-4 pt-2 flex flex-col gap-2 bg-neutral-50 dark:bg-neutral-800/50 border-t dark:border-neutral-700/50"> <div
<div class="flex items-center gap-2 overflow-auto overflow-y-hidden"> class="w-full p-4 pt-2 flex flex-col gap-2 bg-neutral-50 dark:bg-neutral-800/50 border-t dark:border-neutral-700/50">
<button class="chat-option-btn" @click="modals.modelSelect = true"> <div class="flex items-center gap-2 overflow-auto overflow-y-hidden">
<Icon name="tabler:robot-face"/> <button class="chat-option-btn" @click="modals.modelSelect = true">
<span class="text-xs"> <Icon name="tabler:box"/>
<span class="text-xs">
{{ llmModels.find(m => m.tag === currentModel)?.name.toUpperCase() || '模型' }} {{ llmModels.find(m => m.tag === currentModel)?.name.toUpperCase() || '模型' }}
</span> </span>
</button> </button>
<button class="chat-option-btn" @click="modals.modelSelect = true">
<Icon name="tabler:robot-face"/>
<span class="text-xs">
助手
</span>
</button>
</div>
<div class="relative">
<UTextarea
v-model="user_input"
size="lg"
autoresize
:rows="5"
:maxrows="12"
class="font-sans"
placeholder="Enter 发送, Ctrl + Enter 换行"
@keydown.ctrl.enter="user_input += '\n'"
@keydown.enter.prevent="handleClickSend"
/>
<UButton
color="black"
variant="solid"
icon="i-tabler-send-2"
class="absolute bottom-2.5 right-3"
@click.stop="handleClickSend"
>
发送
</UButton>
</div>
</div> </div>
<div class="relative"> </ClientOnly>
<UTextarea
v-model="user_input"
size="lg"
autoresize
:rows="5"
:maxrows="12"
class="font-sans"
placeholder="Enter 发送, Ctrl + Enter 换行"
@keydown.ctrl.enter="user_input += '\n'"
@keydown.enter.prevent="handleClickSend"
/>
<UButton
color="black"
variant="solid"
icon="i-tabler-send-2"
class="absolute bottom-2.5 right-3"
@click.stop="handleClickSend"
>
发送
</UButton>
</div>
</div>
</div> </div>
<!-- Modals --> <!-- Modals -->

View File

@@ -14,10 +14,25 @@ export interface LLMModal {
endpoint: string endpoint: string
} }
export interface Assistant {
id: number;
user_id: number;
create_time: number;
tpl_name: string;
types: string;
des: string;
input_tpl: string;
role: string;
target: string;
style: string;
demand: string;
}
export namespace LLMSpark { export namespace LLMSpark {
export interface request { export interface request {
prompt: string prompt: string
} }
export interface response { export interface response {
request_msg?: string request_msg?: string
request_fail?: { request_fail?: {

View File

@@ -55,6 +55,11 @@ namespace req {
file_size: number file_size: number
} }
} }
interface AssistantTemplateList {
page: number
perpage: number
}
} }
namespace resp { namespace resp {