提交 01bf39bc 作者: fxjhello

文件接口

上级 a256f25e
......@@ -3,7 +3,7 @@ import { api } from './api'
export const chat = (params: any) => {
return api({
url: '/chat-docs/chatno',
url: '/chat',
method: 'post',
data: JSON.stringify(params),
})
......@@ -17,20 +17,18 @@ export const chatfile = (params: any) => {
})
}
export const getfilelist = () => {
export const getfilelist = (knowledge_base_id: any) => {
return api({
url: '/chat-docs/list',
url: '/local_doc_qa/list_files',
method: 'get',
params: {
knowledge_base_id: '123',
},
params: { knowledge_base_id },
})
}
export const deletefile = (params: any) => {
return api({
url: '/chat-docs/delete',
url: '/local_doc_qa/delete_file',
method: 'post',
data: JSON.stringify(params),
})
......
......@@ -13,19 +13,18 @@ import HeaderComponent from './components/Header/index.vue'
import { HoverButton, SvgIcon } from '@/components/common'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { useChatStore, usePromptStore } from '@/store'
import { fetchChatAPIProcess } from '@/api'
import { t } from '@/locales'
import { chat, chatfile } from '@/api/chat'
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 dialog = useDialog()
const ms = useMessage()
const chatStore = useChatStore()
const history = ref<any>([])
const { isMobile } = useBasicLayout()
const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat()
const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll()
......@@ -61,7 +60,9 @@ function handleSubmit() {
async function onConversation() {
const message = prompt.value
dataSources.value.forEach((item) => {
console.log(item)
})
if (loading.value)
return
......@@ -110,13 +111,13 @@ async function onConversation() {
const lastText = ''
const fetchChatAPIOnce = async () => {
const res = active.value
? await chatfile({ message })
? await chatfile({
question: message,
history: history.value,
})
: await chat({
question: message,
history: [[
'工伤保险是什么?',
'工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。',
]],
history: history.value,
})
const result = active.value ? res.data.response.text : res.data.response
updateChat(
......@@ -230,7 +231,6 @@ async function onConversation() {
loading.value = false
}
}
async function onRegenerate(index: number) {
if (loading.value)
return
......@@ -239,7 +239,7 @@ async function onRegenerate(index: number) {
const { requestOptions } = dataSources.value[index]
let message = requestOptions?.prompt ?? ''
const message = requestOptions?.prompt ?? ''
let options: Chat.ConversationRequest = {}
......@@ -263,48 +263,30 @@ async function onRegenerate(index: number) {
)
try {
let lastText = ''
const lastText = ''
const fetchChatAPIOnce = async () => {
await fetchChatAPIProcess<Chat.ConversationResponse>({
prompt: message,
options,
signal: controller.signal,
onDownloadProgress: ({ event }) => {
const xhr = event.target
const { responseText } = xhr
// 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(
+uuid,
index,
{
dateTime: new Date().toLocaleString(),
text: lastText + (data.text ?? ''),
inversion: false,
error: false,
loading: true,
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
requestOptions: { prompt: message, options: { ...options } },
},
)
if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
options.parentMessageId = data.id
lastText = data.text
message = ''
return fetchChatAPIOnce()
}
}
catch (error) {
//
}
const res = active.value
? await chatfile({ message })
: await chat({
question: message,
history: history.value,
})
const result = active.value ? res.data.response.text : res.data.response
updateChat(
+uuid,
dataSources.value.length - 1,
{
dateTime: new Date().toLocaleString(),
text: lastText + (result ?? ''),
inversion: false,
error: false,
loading: false,
conversationOptions: null,
requestOptions: { prompt: message, options: { ...options } },
},
})
)
scrollToBottomIfAtBottom()
loading.value = false
updateChatSome(+uuid, index, { loading: false })
}
await fetchChatAPIOnce()
......
<script setup lang='ts'>
import type { CSSProperties } 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 filelist from './filelist.vue'
import Knowledge from './knowledge-base/index.vue'
import Footer from './Footer.vue'
import { useAppStore, useChatStore } from '@/store'
import { useBasicLayout } from '@/hooks/useBasicLayout'
......@@ -46,6 +46,25 @@ const mobileSafeArea = computed(() => {
}
return {}
})
const songs = [
{
value: 1,
label: '会话',
},
{
value: 2,
label: '模型',
},
{
value: 3,
label: '知识库',
},
{
value: 4,
label: '提示词',
},
]
//
watch(
isMobile,
......@@ -75,39 +94,20 @@ watch(
<div class="flex flex-col h-full " :style="mobileSafeArea">
<main class="flex flex-col flex-1 min-h-0">
<div class=" flex justify-between">
<NButton dashed @click="menu = 1">
会话
</NButton>
<NButton dashed @click="menu = 2">
模型
</NButton>
<NButton dashed @click="menu = 3">
知识库
</NButton>
<NButton dashed @click="menu = 4">
提示词
</NButton>
<NRadioGroup v-model:value="menu" name="radiobuttongroup1">
<NRadioButton
v-for="song in songs"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</NRadioGroup>
</div>
<!-- 知识库界面 -->
<div v-if="menu === 3">
<div class="p-4">
<NUpload
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 />
<Knowledge />
</div>
</div>
<!-- 会话界面 -->
......@@ -121,11 +121,6 @@ watch(
<List />
</div>
</div>
<!-- <div class="p-4">
<NButton block @click="show = true">
{{ $t('store.siderButton') }}
</NButton>
</div> -->
</main>
</div>
</NLayoutSider>
......
<script setup lang='ts'>
import { onMounted, ref } from 'vue'
import { NInput, NPopconfirm, NScrollbar } from 'naive-ui'
import { onMounted, ref, toRef } from 'vue'
import { NInput, NP, NPopconfirm, NScrollbar, NText, NUpload, NUploadDragger } from 'naive-ui'
import { SvgIcon } from '@/components/common'
import { useAppStore, useChatStore } from '@/store'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { useChatStore } from '@/store'
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 dataSources = ref<any[]>([])
const dataSources = ref<any>([])
onMounted(async () => {
const res = await getfilelist()
const res = await getfilelist(knowledge_base_id.value)
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) {
event?.stopPropagation()
chatStore.updateHistory(uuid, { isEdit })
} */
async function handleDelete(item: any) {
/* const mid = */await deletefile({ knowledge_base_id: '123', doc_name: item })
const res = await getfilelist()
/* const mid = */await deletefile({ knowledge_base_id: knowledge_base_id.value, doc_name: item })
const res = await getfilelist(knowledge_base_id.value)
dataSources.value = res.data.data
}
......@@ -45,13 +35,34 @@ function handleEnter({ uuid }: Chat.History, isEdit: boolean, event: KeyboardEve
if (event.key === 'Enter')
chatStore.updateHistory(uuid, { isEdit })
}
function isActive(uuid: number) {
return chatStore.active === uuid
}
</script>
<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">
<div class="flex flex-col gap-2 text-sm">
<template v-if="!dataSources.length">
......@@ -64,8 +75,6 @@ function isActive(uuid: number) {
<div v-for="(item, index) of dataSources" :key="index">
<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="isActive(item.uuid) && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'dark:bg-[#24272e]', 'dark:border-[#4b9e5f]', 'pr-14']"
@click="handleSelect(item)"
>
<span>
<SvgIcon icon="ri:message-3-line" />
......@@ -91,10 +100,10 @@ function isActive(uuid: number) {
<NPopconfirm placement="bottom" @positive-click="handleDelete(item)">
<template #trigger>
<button class="p-1">
<SvgIcon icon="ri:delete-bin-line" />
<!-- <SvgIcon icon="ri:delete-bin-line" /> -->
</button>
</template>
{{ $t('chat.deleteHistoryConfirm') }}
确定删除此文件?
</NPopconfirm>
</template>
</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) => {
open: false,
proxy: {
'/api': {
target: 'http://127.0.0.1:7861',
target: 'http://146.56.190.29',
// target: 'http://127.0.0.1:7861',
changeOrigin: true, // 允许跨域
rewrite: path => path.replace('/api/', ''),
},
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论