wip
This commit is contained in:
@@ -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 -->
|
||||||
|
|||||||
@@ -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?: {
|
||||||
|
|||||||
5
typings/schema.d.ts → typings/types.d.ts
vendored
5
typings/schema.d.ts → typings/types.d.ts
vendored
@@ -55,6 +55,11 @@ namespace req {
|
|||||||
file_size: number
|
file_size: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AssistantTemplateList {
|
||||||
|
page: number
|
||||||
|
perpage: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace resp {
|
namespace resp {
|
||||||
Reference in New Issue
Block a user