提交 01bf39bc 作者: fxjhello

文件接口

上级 a256f25e
...@@ -3,7 +3,7 @@ import { api } from './api' ...@@ -3,7 +3,7 @@ import { api } from './api'
export const chat = (params: any) => { export const chat = (params: any) => {
return api({ return api({
url: '/chat-docs/chatno', url: '/chat',
method: 'post', method: 'post',
data: JSON.stringify(params), data: JSON.stringify(params),
}) })
...@@ -17,20 +17,18 @@ export const chatfile = (params: any) => { ...@@ -17,20 +17,18 @@ export const chatfile = (params: any) => {
}) })
} }
export const getfilelist = () => { export const getfilelist = (knowledge_base_id: any) => {
return api({ return api({
url: '/chat-docs/list', url: '/local_doc_qa/list_files',
method: 'get', method: 'get',
params: { params: { knowledge_base_id },
knowledge_base_id: '123',
},
}) })
} }
export const deletefile = (params: any) => { export const deletefile = (params: any) => {
return api({ return api({
url: '/chat-docs/delete', url: '/local_doc_qa/delete_file',
method: 'post', method: 'post',
data: JSON.stringify(params), data: JSON.stringify(params),
}) })
......
...@@ -13,19 +13,18 @@ import HeaderComponent from './components/Header/index.vue' ...@@ -13,19 +13,18 @@ import HeaderComponent from './components/Header/index.vue'
import { HoverButton, SvgIcon } from '@/components/common' import { HoverButton, SvgIcon } from '@/components/common'
import { useBasicLayout } from '@/hooks/useBasicLayout' import { useBasicLayout } from '@/hooks/useBasicLayout'
import { useChatStore, usePromptStore } from '@/store' import { useChatStore, usePromptStore } from '@/store'
import { fetchChatAPIProcess } from '@/api'
import { t } from '@/locales' import { t } from '@/locales'
import { chat, chatfile } from '@/api/chat' import { chat, chatfile } from '@/api/chat'
let controller = new AbortController() let controller = new AbortController()
const openLongReply = import.meta.env.VITE_GLOB_OPEN_LONG_REPLY === 'true' // const openLongReply = import.meta.env.VITE_GLOB_OPEN_LONG_REPLY === 'true'
const route = useRoute() const route = useRoute()
const dialog = useDialog() const dialog = useDialog()
const ms = useMessage() const ms = useMessage()
const chatStore = useChatStore() const chatStore = useChatStore()
const history = ref<any>([])
const { isMobile } = useBasicLayout() const { isMobile } = useBasicLayout()
const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat() const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat()
const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll() const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll()
...@@ -61,7 +60,9 @@ function handleSubmit() { ...@@ -61,7 +60,9 @@ function handleSubmit() {
async function onConversation() { async function onConversation() {
const message = prompt.value const message = prompt.value
dataSources.value.forEach((item) => {
console.log(item)
})
if (loading.value) if (loading.value)
return return
...@@ -110,13 +111,13 @@ async function onConversation() { ...@@ -110,13 +111,13 @@ async function onConversation() {
const lastText = '' const lastText = ''
const fetchChatAPIOnce = async () => { const fetchChatAPIOnce = async () => {
const res = active.value const res = active.value
? await chatfile({ message }) ? await chatfile({
question: message,
history: history.value,
})
: await chat({ : await chat({
question: message, question: message,
history: [[ history: history.value,
'工伤保险是什么?',
'工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。',
]],
}) })
const result = active.value ? res.data.response.text : res.data.response const result = active.value ? res.data.response.text : res.data.response
updateChat( updateChat(
...@@ -230,7 +231,6 @@ async function onConversation() { ...@@ -230,7 +231,6 @@ async function onConversation() {
loading.value = false loading.value = false
} }
} }
async function onRegenerate(index: number) { async function onRegenerate(index: number) {
if (loading.value) if (loading.value)
return return
...@@ -239,7 +239,7 @@ async function onRegenerate(index: number) { ...@@ -239,7 +239,7 @@ async function onRegenerate(index: number) {
const { requestOptions } = dataSources.value[index] const { requestOptions } = dataSources.value[index]
let message = requestOptions?.prompt ?? '' const message = requestOptions?.prompt ?? ''
let options: Chat.ConversationRequest = {} let options: Chat.ConversationRequest = {}
...@@ -263,48 +263,30 @@ async function onRegenerate(index: number) { ...@@ -263,48 +263,30 @@ async function onRegenerate(index: number) {
) )
try { try {
let lastText = '' const lastText = ''
const fetchChatAPIOnce = async () => { const fetchChatAPIOnce = async () => {
await fetchChatAPIProcess<Chat.ConversationResponse>({ const res = active.value
prompt: message, ? await chatfile({ message })
options, : await chat({
signal: controller.signal, question: message,
onDownloadProgress: ({ event }) => { history: history.value,
const xhr = event.target })
const { responseText } = xhr const result = active.value ? res.data.response.text : res.data.response
// Always process the final line
const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
let chunk = responseText
if (lastIndex !== -1)
chunk = responseText.substring(lastIndex)
try {
const data = JSON.parse(chunk)
updateChat( updateChat(
+uuid, +uuid,
index, dataSources.value.length - 1,
{ {
dateTime: new Date().toLocaleString(), dateTime: new Date().toLocaleString(),
text: lastText + (data.text ?? ''), text: lastText + (result ?? ''),
inversion: false, inversion: false,
error: false, error: false,
loading: true, loading: false,
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id }, conversationOptions: null,
requestOptions: { prompt: message, options: { ...options } }, requestOptions: { prompt: message, options: { ...options } },
}, },
) )
scrollToBottomIfAtBottom()
if (openLongReply && data.detail.choices[0].finish_reason === 'length') { loading.value = false
options.parentMessageId = data.id
lastText = data.text
message = ''
return fetchChatAPIOnce()
}
}
catch (error) {
//
}
},
})
updateChatSome(+uuid, index, { loading: false }) updateChatSome(+uuid, index, { loading: false })
} }
await fetchChatAPIOnce() await fetchChatAPIOnce()
......
<script setup lang='ts'> <script setup lang='ts'>
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { NButton, NLayoutSider, NUpload } from 'naive-ui' import { NButton, NLayoutSider, NRadioButton, NRadioGroup } from 'naive-ui'
import List from './List.vue' import List from './List.vue'
import filelist from './filelist.vue' import Knowledge from './knowledge-base/index.vue'
import Footer from './Footer.vue' import Footer from './Footer.vue'
import { useAppStore, useChatStore } from '@/store' import { useAppStore, useChatStore } from '@/store'
import { useBasicLayout } from '@/hooks/useBasicLayout' import { useBasicLayout } from '@/hooks/useBasicLayout'
...@@ -46,6 +46,25 @@ const mobileSafeArea = computed(() => { ...@@ -46,6 +46,25 @@ const mobileSafeArea = computed(() => {
} }
return {} return {}
}) })
const songs = [
{
value: 1,
label: '会话',
},
{
value: 2,
label: '模型',
},
{
value: 3,
label: '知识库',
},
{
value: 4,
label: '提示词',
},
]
// //
watch( watch(
isMobile, isMobile,
...@@ -75,39 +94,20 @@ watch( ...@@ -75,39 +94,20 @@ watch(
<div class="flex flex-col h-full " :style="mobileSafeArea"> <div class="flex flex-col h-full " :style="mobileSafeArea">
<main class="flex flex-col flex-1 min-h-0"> <main class="flex flex-col flex-1 min-h-0">
<div class=" flex justify-between"> <div class=" flex justify-between">
<NButton dashed @click="menu = 1"> <NRadioGroup v-model:value="menu" name="radiobuttongroup1">
会话 <NRadioButton
</NButton> v-for="song in songs"
<NButton dashed @click="menu = 2"> :key="song.value"
模型 :value="song.value"
</NButton> :label="song.label"
<NButton dashed @click="menu = 3"> />
知识库 </NRadioGroup>
</NButton>
<NButton dashed @click="menu = 4">
提示词
</NButton>
</div> </div>
<!-- 知识库界面 --> <!-- 知识库界面 -->
<div v-if="menu === 3"> <div v-if="menu === 3">
<div class="p-4"> <div class="p-4">
<NUpload <Knowledge />
action="http://127.0.0.1:1002/api/chat-docs/uploadone"
:headers="{
'naive-info': 'hello!',
}"
:data="{
knowledge_base_id: '123',
}"
>
<NButton block>
文件上传
</NButton>
</NUpload>
</div>
<div class="p-2 flex-1 min-h-0 pb-4 overflow-hidden">
<filelist />
</div> </div>
</div> </div>
<!-- 会话界面 --> <!-- 会话界面 -->
...@@ -121,11 +121,6 @@ watch( ...@@ -121,11 +121,6 @@ watch(
<List /> <List />
</div> </div>
</div> </div>
<!-- <div class="p-4">
<NButton block @click="show = true">
{{ $t('store.siderButton') }}
</NButton>
</div> -->
</main> </main>
</div> </div>
</NLayoutSider> </NLayoutSider>
......
<script setup lang='ts'> <script setup lang='ts'>
import { onMounted, ref } from 'vue' import { onMounted, ref, toRef } from 'vue'
import { NInput, NPopconfirm, NScrollbar } from 'naive-ui' import { NInput, NP, NPopconfirm, NScrollbar, NText, NUpload, NUploadDragger } from 'naive-ui'
import { SvgIcon } from '@/components/common' import { SvgIcon } from '@/components/common'
import { useAppStore, useChatStore } from '@/store' import { useChatStore } from '@/store'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { deletefile, getfilelist } from '@/api/chat' import { deletefile, getfilelist } from '@/api/chat'
const knowledge = defineProps({
knowledgebaseid: {
type: String, // 类型字符串
},
})
const knowledge_base_id = toRef(knowledge, 'knowledgebaseid')
const { isMobile } = useBasicLayout()
const appStore = useAppStore()
const chatStore = useChatStore() const chatStore = useChatStore()
const dataSources = ref<any[]>([]) const dataSources = ref<any>([])
onMounted(async () => { onMounted(async () => {
const res = await getfilelist() const res = await getfilelist(knowledge_base_id.value)
dataSources.value = res.data.data dataSources.value = res.data.data
}) })
async function handleSelect({ uuid }: Chat.History) {
if (isActive(uuid))
return
if (chatStore.active)
chatStore.updateHistory(chatStore.active, { isEdit: false })
await chatStore.setActive(uuid)
if (isMobile.value)
appStore.setSiderCollapsed(true)
}
/* function handleEdit({ uuid }: Chat.History, isEdit: boolean, event?: MouseEvent) { /* function handleEdit({ uuid }: Chat.History, isEdit: boolean, event?: MouseEvent) {
event?.stopPropagation() event?.stopPropagation()
chatStore.updateHistory(uuid, { isEdit }) chatStore.updateHistory(uuid, { isEdit })
} */ } */
async function handleDelete(item: any) { async function handleDelete(item: any) {
/* const mid = */await deletefile({ knowledge_base_id: '123', doc_name: item }) /* const mid = */await deletefile({ knowledge_base_id: knowledge_base_id.value, doc_name: item })
const res = await getfilelist() const res = await getfilelist(knowledge_base_id.value)
dataSources.value = res.data.data dataSources.value = res.data.data
} }
...@@ -45,13 +35,34 @@ function handleEnter({ uuid }: Chat.History, isEdit: boolean, event: KeyboardEve ...@@ -45,13 +35,34 @@ function handleEnter({ uuid }: Chat.History, isEdit: boolean, event: KeyboardEve
if (event.key === 'Enter') if (event.key === 'Enter')
chatStore.updateHistory(uuid, { isEdit }) chatStore.updateHistory(uuid, { isEdit })
} }
function isActive(uuid: number) {
return chatStore.active === uuid
}
</script> </script>
<template> <template>
<NUpload
multiple
directory-dnd
action="http://192.168.1.128:1002/api/local_doc_qa/upload_file"
:headers="{
'naive-info': 'hello!',
}"
:data="{
knowledge_base_id: knowledge.knowledgebaseid as string,
}"
>
<NUploadDragger>
<!-- <div style="margin-bottom: 12px">
<NIcon size="48" :depth="3">
<archive-icon />
</NIcon>
</div> -->
<NText style="font-size: 16px">
点击或者拖动文件到该区域来上传
</NText>
<NP depth="3" style="margin: 8px 0 0 0">
在弹出的文件选择框,按住ctrl或shift进行多选
</NP>
</NUploadDragger>
</NUpload>
<NScrollbar class="px-4"> <NScrollbar class="px-4">
<div class="flex flex-col gap-2 text-sm"> <div class="flex flex-col gap-2 text-sm">
<template v-if="!dataSources.length"> <template v-if="!dataSources.length">
...@@ -64,8 +75,6 @@ function isActive(uuid: number) { ...@@ -64,8 +75,6 @@ function isActive(uuid: number) {
<div v-for="(item, index) of dataSources" :key="index"> <div v-for="(item, index) of dataSources" :key="index">
<a <a
class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group dark:border-neutral-800 dark:hover:bg-[#24272e]" class="relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group dark:border-neutral-800 dark:hover:bg-[#24272e]"
:class="isActive(item.uuid) && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'dark:bg-[#24272e]', 'dark:border-[#4b9e5f]', 'pr-14']"
@click="handleSelect(item)"
> >
<span> <span>
<SvgIcon icon="ri:message-3-line" /> <SvgIcon icon="ri:message-3-line" />
...@@ -91,10 +100,10 @@ function isActive(uuid: number) { ...@@ -91,10 +100,10 @@ function isActive(uuid: number) {
<NPopconfirm placement="bottom" @positive-click="handleDelete(item)"> <NPopconfirm placement="bottom" @positive-click="handleDelete(item)">
<template #trigger> <template #trigger>
<button class="p-1"> <button class="p-1">
<SvgIcon icon="ri:delete-bin-line" /> <!-- <SvgIcon icon="ri:delete-bin-line" /> -->
</button> </button>
</template> </template>
{{ $t('chat.deleteHistoryConfirm') }} 确定删除此文件?
</NPopconfirm> </NPopconfirm>
</template> </template>
</div> </div>
......
<script setup lang='ts'>
import { NButton, NForm, NFormItem, NInput } from 'naive-ui'
import { onMounted, ref } from 'vue'
import filelist from './filelist.vue'
import { getfilelist } from '@/api/chat'
const items = ref<any>([])
onMounted(async () => {
const res = await getfilelist({})
res.data.data.forEach((item: any) => {
items.value.push({
value: item,
show: false,
})
})
})
const formValue = ref({
user: {
name: '',
},
})
const rules = {
user: {
name: {
required: true,
message: '请输入名称',
trigger: 'blur',
},
},
}
const handleValidateClick = (item: any) => {
items.value.forEach((res: { value: any; show: boolean }) => {
if (res.value === item)
res.show = !res.show
},
)
}
const handleClick = () => {
if (formValue.value.user.name.trim() !== '') {
items.value.push({
value: formValue.value.user.name.trim(),
show: false,
})
}
}
</script>
<template>
<NForm
ref="formRef"
inline
:label-width="80"
:model="formValue"
:rules="rules"
>
<NFormItem label="" path="user.name">
<NInput v-model:value="formValue.user.name" placeholder="起个知识库名吧!" />
</NFormItem>
<NFormItem>
<NButton attr-type="button" @click="handleClick">
新增
</NButton>
</NFormItem>
</NForm>
<div v-for="item in items" :key="item.value">
<NButton block size="large" @click="handleValidateClick(item.value)">
{{ item.value }}
</NButton>
<div v-if="item.show" class="p-2 flex-1 min-h-0 pb-4 overflow-hidden">
<filelist v-if="item.value" :knowledgebaseid="item.value" />
</div>
<br>
</div>
</template>
...@@ -37,7 +37,8 @@ export default defineConfig((env) => { ...@@ -37,7 +37,8 @@ export default defineConfig((env) => {
open: false, open: false,
proxy: { proxy: {
'/api': { '/api': {
target: 'http://127.0.0.1:7861', target: 'http://146.56.190.29',
// target: 'http://127.0.0.1:7861',
changeOrigin: true, // 允许跨域 changeOrigin: true, // 允许跨域
rewrite: path => path.replace('/api/', ''), rewrite: path => path.replace('/api/', ''),
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论