syncupdate packages

This commit is contained in:
xerrors 2024-09-03 16:37:59 +08:00
parent 8d6107fa0a
commit 4f4928e536
7 changed files with 197 additions and 159 deletions

View File

@ -38,12 +38,12 @@ class Config(SimpleConfig):
### >>> 默认配置 ### >>> 默认配置
# 可以在 config/base.yaml 中覆盖 # 可以在 config/base.yaml 中覆盖
self.add_item("mode", default="cli", des="运行模式", choices=["cli", "api"])
self.add_item("stream", default=True, des="是否开启流式输出") self.add_item("stream", default=True, des="是否开启流式输出")
self.add_item("save_dir", default="saves", des="保存目录") self.add_item("save_dir", default="saves", des="保存目录")
# 功能选项 # 功能选项
self.add_item("enable_reranker", default=False, des="是否开启重排序") self.add_item("enable_reranker", default=False, des="是否开启重排序")
self.add_item("enable_knowledge_base", default=False, des="是否开启知识库") self.add_item("enable_knowledge_base", default=False, des="是否开启知识库")
self.add_item("enable_knowledge_graph", default=False, des="是否开启知识图谱")
self.add_item("enable_search_engine", default=False, des="是否开启搜索引擎") self.add_item("enable_search_engine", default=False, des="是否开启搜索引擎")
# 模型配置 # 模型配置
@ -70,6 +70,13 @@ class Config(SimpleConfig):
"choices": choices "choices": choices
} }
def __dict__(self):
blocklist = [
"_config_items",
"model_names",
]
return {k: v for k, v in self.items() if k not in blocklist}
def handle_self(self): def handle_self(self):
### handle local model ### handle local model
model_root_dir = os.getenv("MODEL_ROOT_DIR", "pretrained_models") model_root_dir = os.getenv("MODEL_ROOT_DIR", "pretrained_models")
@ -98,7 +105,6 @@ class Config(SimpleConfig):
content = f.read() content = f.read()
if content: if content:
local_config = json.loads(content) local_config = json.loads(content)
local_config.pop("_config_items")
self.update(local_config) self.update(local_config)
else: else:
print(f"{self.filename} is empty.") print(f"{self.filename} is empty.")
@ -108,7 +114,6 @@ class Config(SimpleConfig):
content = f.read() content = f.read()
if content: if content:
local_config = yaml.safe_load(content) local_config = yaml.safe_load(content)
local_config.pop("_config_items")
self.update(local_config) self.update(local_config)
else: else:
print(f"{self.filename} is empty.") print(f"{self.filename} is empty.")

View File

@ -7,7 +7,8 @@
/* layout */ /* layout */
.layout-container { .layout-container {
padding: 10px 30px; width: 100%;
padding: 16px 30px;
background-color: #FCFEFF; background-color: #FCFEFF;
h2 { h2 {

View File

@ -329,6 +329,7 @@ const updateStatus = (id, status) => {
return acc; return acc;
}, {}) }, {})
} }
scrollToBottom()
} }
const simpleCall = (message) => { const simpleCall = (message) => {
@ -573,7 +574,7 @@ watch(
max-width: 900px; max-width: 900px;
margin: 0 auto; margin: 0 auto;
flex-grow: 1; flex-grow: 1;
padding: 1rem; padding: 1rem 2rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -28,13 +28,13 @@
<p class="result-distance"> <p class="result-distance">
<strong>相似度距离:</strong> <strong>相似度距离:</strong>
<div class="scorebar"> <div class="scorebar">
<a-progress :percent="(res.distance * 100).toFixed(2)" stroke-color="#1677FF" :size="[200, 10]"/> <a-progress :percent="parseFloat((res.distance * 100).toFixed(2))" stroke-color="#1677FF" :size="[200, 10]"/>
</div> </div>
</p> </p>
<p class="result-rerank-score"> <p class="result-rerank-score" v-if="res.rerank_score">
<strong>重排序分数:</strong> <strong>重排序分数:</strong>
<div class="scorebar"> <div class="scorebar">
<a-progress :percent="(res.rerank_score * 100).toFixed(2)" stroke-color="#1677FF" :size="[200, 10]"/> <a-progress :percent="parseFloat((res.rerank_score * 100).toFixed(2))" stroke-color="#1677FF" :size="[200, 10]"/>
</div> </div>
</p> </p>
<a-divider /> <a-divider />
@ -74,7 +74,6 @@ const showRefs = computed(() => message.value.role=='received' && message.value.
.item { .item {
background: var(--main-10); background: var(--main-10);
color: var(--main-600); color: var(--main-600);
border: 1px solid var(--main-50);
padding: 2px 8px; padding: 2px 8px;
border-radius: 8px; border-radius: 8px;
font-size: 14px; font-size: 14px;

View File

@ -18,14 +18,6 @@
<a-tag color="blue" v-if="database.embed_model">{{ database.embed_model }}</a-tag> <a-tag color="blue" v-if="database.embed_model">{{ database.embed_model }}</a-tag>
</div> </div>
<a-divider/> <a-divider/>
<div class="pagebtns">
<div @click="state.curPage='add'" :class="{ 'active': state.curPage === 'add' }">
<CloudUploadOutlined />添加文件
</div>
<div @click="state.curPage='query-test'" :class="{ 'active': state.curPage === 'query-test' }">
<SearchOutlined />检索测试
</div>
</div>
<div class="query-params" v-if="state.curPage == 'query-test'"> <div class="query-params" v-if="state.curPage == 'query-test'">
<p style="text-align: center; margin: 0;"><strong>参数配置</strong></p> <p style="text-align: center; margin: 0;"><strong>参数配置</strong></p>
<div class="params-item"> <div class="params-item">
@ -46,103 +38,113 @@
<div class="sider-bottom"> <div class="sider-bottom">
</div> </div>
</div> </div>
<div class="db-info-container" v-if="state.curPage == 'add'"> <a-tabs v-model:activeKey="state.curPage" class="atab-container" type="card">
<h3>向知识库中添加文件</h3> <a-tab-pane key="add">
<div class="upload"> <template #tab><span><CloudUploadOutlined />添加文件</span></template>
<a-upload-dragger <div class="db-info-container">
class="upload-dragger" <h3>向知识库中添加文件</h3>
v-model:fileList="fileList" <div class="upload">
name="file" <a-upload-dragger
:multiple="true" class="upload-dragger"
:disabled="state.loading" v-model:fileList="fileList"
action="/api/database/upload" name="file"
@change="handleFileUpload" :multiple="true"
@drop="handleDrop" :disabled="state.loading"
> action="/api/database/upload"
<p class="ant-upload-text">点击或者把文件拖拽到这里上传</p> @change="handleFileUpload"
<p class="ant-upload-hint"> @drop="handleDrop"
目前仅支持上传文本文件 .pdf, .txt, .md且同名文件无法重复添加 >
</p> <p class="ant-upload-text">点击或者把文件拖拽到这里上传</p>
</a-upload-dragger> <p class="ant-upload-hint">
</div> 目前仅支持上传文本文件 .pdf, .txt, .md且同名文件无法重复添加
<a-button </p>
type="primary" </a-upload-dragger>
@click="addDocumentByFile" </div>
:loading="state.loading" <a-button
:disabled="fileList.length === 0" type="primary"
style="margin: 0px 20px 20px 0;" @click="addDocumentByFile"
> :loading="state.loading"
添加到知识库 :disabled="fileList.length === 0"
</a-button> style="margin: 0px 20px 20px 0;"
<a-button @click="handleRefresh" :loading="state.refrashing">刷新状态</a-button> >
<a-table :columns="columns" :data-source="database.files" row-key="file_id" class="my-table"> 添加到知识库
<template #bodyCell="{ column, text, record }"> </a-button>
<template v-if="column.key === 'file_id'"> <a-button @click="handleRefresh" :loading="state.refrashing">刷新状态</a-button>
<a-button class="main-btn" type="link" @click="openFileDetail(record)">{{ text.toUpperCase() }}</a-button> <a-table :columns="columns" :data-source="database.files" row-key="file_id" class="my-table">
</template> <template #bodyCell="{ column, text, record }">
<template v-else-if="column.key === 'type'"><span :class="text">{{ text.toUpperCase() }}</span></template> <template v-if="column.key === 'file_id'">
<template v-else-if="column.key === 'status' && text === 'done'"> <a-button class="main-btn" type="link" @click="openFileDetail(record)">{{ text.toUpperCase() }}</a-button>
<CheckCircleFilled style="color: #41A317;"/> </template>
</template> <template v-else-if="column.key === 'type'"><span :class="text">{{ text.toUpperCase() }}</span></template>
<template v-else-if="column.key === 'status' && text === 'failed'"> <template v-else-if="column.key === 'status' && text === 'done'">
<CloseCircleFilled style="color: #FF4D4F ;"/> <CheckCircleFilled style="color: #41A317;"/>
</template> </template>
<template v-else-if="column.key === 'status' && text === 'processing'"> <template v-else-if="column.key === 'status' && text === 'failed'">
<HourglassFilled style="color: #1677FF;"/> <CloseCircleFilled style="color: #FF4D4F ;"/>
</template> </template>
<template v-else-if="column.key === 'status' && text === 'waiting'"> <template v-else-if="column.key === 'status' && text === 'processing'">
<ClockCircleFilled style="color: #FFCD43;"/> <HourglassFilled style="color: #1677FF;"/>
</template> </template>
<template v-else-if="column.key === 'action'"> <template v-else-if="column.key === 'status' && text === 'waiting'">
<a-button class="del-btn" type="link" <ClockCircleFilled style="color: #FFCD43;"/>
@click="deleteFile(text)" </template>
:disabled="state.lock || record.status == 'processing' || record.status == 'waiting' " <template v-else-if="column.key === 'action'">
>删除 <a-button class="del-btn" type="link"
</a-button> @click="deleteFile(text)"
</template> :disabled="state.lock || record.status == 'processing' || record.status == 'waiting' "
<span v-else-if="column.key === 'created_at'">{{ formatRelativeTime(Math.round(text*1000)) }}</span> >删除
<span v-else>{{ text }}</span> </a-button>
</template> </template>
</a-table> <span v-else-if="column.key === 'created_at'">{{ formatRelativeTime(Math.round(text*1000)) }}</span>
<a-drawer <span v-else>{{ text }}</span>
width="50%" </template>
v-model:open="state.drawer" </a-table>
class="custom-class" <a-drawer
:title="selectedFile?.filename" width="50%"
placement="right" v-model:open="state.drawer"
@after-open-change="afterOpenChange" class="custom-class"
> :title="selectedFile?.filename"
<h2> {{ selectedFile?.lines.length }} 个片段</h2> placement="right"
<p v-for="line in selectedFile?.lines" :key="line.id"> @after-open-change="afterOpenChange"
<strong>Chunk #{{ line.id }}</strong> {{ line.text }} >
</p> <h2> {{ selectedFile?.lines.length }} 个片段</h2>
</a-drawer> <p v-for="line in selectedFile?.lines" :key="line.id">
</div> <strong>Chunk #{{ line.id }}</strong> {{ line.text }}
<div class="db-info-container" v-else-if="state.curPage == 'query-test'"> </p>
<h3>检索测试</h3> </a-drawer>
<div class="query-action">
<a-textarea
v-model:value="queryText"
placeholder="填写需要查询的句子"
:auto-size="{ minRows: 2, maxRows: 10 }"
/>
<!-- :loading="state.searchLoading" -->
<a-button @click="onQuery" :disabled="queryText.length == 0" :loading="state.searchLoading">
<SearchOutlined v-if="!state.searchLoading"/>检索
</a-button>
</div>
<div class="query-test" v-if="queryResult">
<div class="query-card" v-for="(result, idx) in (meta.filter ? queryResult.results : queryResult.all_results)" :key="idx">
<p>
<strong>#{{ idx + 1 }}&nbsp;&nbsp;&nbsp;</strong>
<span>{{ result.file.filename }}&nbsp;&nbsp;&nbsp;</span>
<span><strong>距离</strong>{{ result.distance.toFixed(4) }}&nbsp;&nbsp;&nbsp;</span>
<span v-if="result.rerank_score"><strong>重排序</strong>{{ result.rerank_score.toFixed(4) }}</span>
</p>
<p class="query-text">{{ result.entity.text }}</p>
</div> </div>
</div> </a-tab-pane>
</div> <a-tab-pane key="query-test" force-render>
<template #tab><span><SearchOutlined />检索测试</span></template>
<div class="db-info-container">
<h3>检索测试</h3>
<div class="query-action">
<a-textarea
v-model:value="queryText"
placeholder="填写需要查询的句子"
:auto-size="{ minRows: 2, maxRows: 10 }"
/>
<!-- :loading="state.searchLoading" -->
<a-button class="btn-query" @click="onQuery" :disabled="queryText.length == 0">
<span v-if="!state.searchLoading"><SearchOutlined /> 检索</span>
<span v-else><LoadingOutlined /></span>
</a-button>
</div>
<div class="query-test" v-if="queryResult">
<div class="query-card" v-for="(result, idx) in (meta.filter ? queryResult.results : queryResult.all_results)" :key="idx">
<p>
<strong>#{{ idx + 1 }}&nbsp;&nbsp;&nbsp;</strong>
<span>{{ result.file.filename }}&nbsp;&nbsp;&nbsp;</span>
<span><strong>距离</strong>{{ result.distance.toFixed(4) }}&nbsp;&nbsp;&nbsp;</span>
<span v-if="result.rerank_score"><strong>重排序</strong>{{ result.rerank_score.toFixed(4) }}</span>
</p>
<p class="query-text">{{ result.entity.text }}</p>
</div>
</div>
</div>
</a-tab-pane>
<!-- <a-tab-pane key="3" tab="Tab 3">Content of Tab Pane 3</a-tab-pane> -->
</a-tabs>
</div> </div>
</template> </template>
@ -160,6 +162,7 @@ import {
DeleteOutlined, DeleteOutlined,
CloudUploadOutlined, CloudUploadOutlined,
SearchOutlined, SearchOutlined,
LoadingOutlined
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
@ -490,38 +493,38 @@ onMounted(() => {
} }
} }
.pagebtns { // .pagebtns {
display: flex; // display: flex;
flex-direction: column; // flex-direction: column;
gap: 16px; // gap: 16px;
> div { // > div {
gap: 1rem; // gap: 1rem;
width: 100%; // width: 100%;
display: flex; // display: flex;
justify-content: center; // justify-content: center;
align-items: center; // align-items: center;
padding: 10px 16px; // padding: 10px 16px;
height: auto; // height: auto;
border-radius: 4px; // border-radius: 4px;
border: none; // border: none;
background: var(--main-light-5); // background: var(--main-light-5);
letter-spacing: 4px; // letter-spacing: 4px;
border-radius: 8px; // border-radius: 8px;
border: 1px solid var(--main-light-2); // border: 1px solid var(--main-light-2);
&:hover { // &:hover {
cursor: pointer; // cursor: pointer;
background: var(--main-light-3); // background: var(--main-light-3);
} // }
} // }
.active { // .active {
color: var(--main-color); // color: var(--main-color);
background: var(--main-light-3); // background: var(--main-light-3);
font-weight: bold; // font-weight: bold;
} // }
} // }
.query-params { .query-params {
display: flex; display: flex;
@ -549,10 +552,13 @@ onMounted(() => {
} }
} }
.atab-container {
padding: 12px 16px;
width: 100%;
}
.db-info-container { .db-info-container {
padding: 20px;
flex: 1 1 auto; flex: 1 1 auto;
overflow: scroll;
.query-action { .query-action {
display: flex; display: flex;
@ -564,13 +570,13 @@ onMounted(() => {
border: 1px solid var(--main-light-2); border: 1px solid var(--main-light-2);
} }
button { button.btn-query {
height: auto; height: auto;
width: 120px; width: 100px;
box-shadow: none; box-shadow: none;
border: none; border: none;
font-weight: bold; font-weight: bold;
background: var(--main-light-2); background: var(--main-light-3);
color: var(--main-color); color: var(--main-color);
&:disabled { &:disabled {
@ -627,6 +633,24 @@ onMounted(() => {
margin: 0; margin: 0;
} }
h3 {
font-size: 20px;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: 0px;
width: 100%;
height: 10px;
background: var(--main-color);
display: block;
opacity: 0.5;
z-index: -1;
}
}
p { p {
color: var(--c-text-light-1); color: var(--c-text-light-1);
font-size: small; font-size: small;

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="graph-container layout-container" v-if="configStore.config.enable_knowledge_base"> <div class="graph-container layout-container" v-if="state.showPage">
<div class="info"> <div class="info">
<h2>Neo4j 图数据库</h2> <h2>Neo4j 图数据库</h2>
<p>基于 Neo4j 构建的图数据库</p> <p>基于 Neo4j 构建的图数据库</p>
@ -50,13 +50,12 @@
</div> </div>
</div> </div>
<div class="main" id="container"></div> <div class="main" id="container"></div>
</div> </div>
<div class="database-empty" v-else> <div class="database-empty" v-else>
<a-empty> <a-empty>
<template #description> <template #description>
<span> <span>
前往 <router-link to="/setting" style="color: var(--main-color); font-weight: bold;">设置</router-link> 前往 <router-link to="/setting" style="color: var(--main-color); font-weight: bold;">设置</router-link>
</span> </span>
</template> </template>
</a-empty> </a-empty>
@ -94,10 +93,13 @@ const state = reactive({
searchLoading: false, searchLoading: false,
showModal: false, showModal: false,
precessing: false, precessing: false,
showPage: computed(() => configStore.config.enable_knowledge_base && configStore.config.enable_knowledge_graph),
}) })
const getCurWidth = () => document.getElementById("container").offsetWidth // const showPage = computed(() => {
const getCurHeight = () => document.getElementById("container").offsetHeight // return configStore.config.enable_knowledge_base && configStore.config.enable_knowledge_graph
// })
const graphData = computed(() => { const graphData = computed(() => {
return { return {
@ -174,14 +176,15 @@ const randerGraph = () => {
graphInstance.render(); graphInstance.render();
} }
onMounted(() => { onMounted(() => {
if (!state.showPage) {
return
}
graphInstance = new Graph({ graphInstance = new Graph({
container: document.getElementById("container"), container: document.getElementById("container"),
width: getCurWidth(), width: document.getElementById("container").offsetWidth,
height: getCurHeight(), height: document.getElementById("container").offsetHeight,
autoFit: true, autoFit: true,
autoResize: true, autoResize: true,
layout: { layout: {
@ -273,6 +276,7 @@ const handleDrop = (event) => {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100%;
height: 100%; height: 100%;
flex-direction: column; flex-direction: column;
color: var(--c-text-light-1); color: var(--c-text-light-1);

View File

@ -88,13 +88,13 @@
@change="handleChange('enable_knowledge_base', !configStore.config.enable_knowledge_base)" @change="handleChange('enable_knowledge_base', !configStore.config.enable_knowledge_base)"
/> />
</div> </div>
<!-- <div class="card"> <div class="card">
<span class="label">{{ items?.enable_knowledge_graph.des }}</span> <span class="label">{{ items?.enable_knowledge_graph.des }}</span>
<a-switch <a-switch
:checked="configStore.config.enable_knowledge_graph" :checked="configStore.config.enable_knowledge_graph"
@change="handleChange('enable_knowledge_graph', !configStore.config.enable_knowledge_graph)" @change="handleChange('enable_knowledge_graph', !configStore.config.enable_knowledge_graph)"
/> />
</div> --> </div>
<div class="card"> <div class="card">
<span class="label">{{ items?.enable_search_engine.des }}</span> <span class="label">{{ items?.enable_search_engine.des }}</span>
<a-switch <a-switch
@ -136,6 +136,10 @@ const state = reactive({
}) })
const handleChange = (key, e) => { const handleChange = (key, e) => {
if (key == 'enable_knowledge_graph' && e && !configStore.config.enable_knowledge_base) {
message.error('请先启用知识库功能')
return
}
console.log('Change', key, e) console.log('Change', key, e)
needRestart[key] = true needRestart[key] = true
configStore.setConfigValue(key, e) configStore.setConfigValue(key, e)