This commit is contained in:
Wenjie Zhang 2024-08-25 20:29:24 +08:00
parent def68e8a3e
commit 0b2dd0179b
17 changed files with 405 additions and 169 deletions

View File

@ -51,7 +51,7 @@ class Config(SimpleConfig):
## 如果需要自定义路径,则在 config/base.yaml 中配置 model_local_paths
self.add_item("model_provider", default="zhipu", des="模型提供商", choices=["qianfan", "vllm", "zhipu", "deepseek", "dashscope"])
self.add_item("model_name", default=None, des="模型名称")
self.add_item("embed_model", default="bge-large-zh-v1.5", des="Embedding 模型", choices=["bge-large-zh-v1.5", "zhipu"])
self.add_item("embed_model", default="zhipu-embedding-3", des="Embedding 模型", choices=list(EMBED_MODEL_INFO.keys()))
self.add_item("reranker", default="bge-reranker-v2-m3", des="Re-Ranker 模型", choices=["bge-reranker-v2-m3"])
self.add_item("model_local_paths", default={}, des="本地模型路径")
### <<< 默认配置结束
@ -92,22 +92,29 @@ class Config(SimpleConfig):
"""根据传入的文件覆盖掉默认配置"""
logger.info(f"Loading config from {self.filename}")
if self.filename is not None and os.path.exists(self.filename):
if self.filename.endswith(".json"):
with open(self.filename, 'r') as f:
content = f.read()
if content:
self.update(json.loads(content))
local_config = json.loads(content)
local_config.pop("_config_items")
self.update(local_config)
else:
print(f"{self.filename} is empty.")
elif self.filename.endswith(".yaml"):
with open(self.filename, 'r') as f:
content = f.read()
if content:
self.update(yaml.safe_load(content))
local_config = yaml.safe_load(content)
local_config.pop("_config_items")
self.update(local_config)
else:
print(f"{self.filename} is empty.")
else:
logger.warning(f"Unknown config file type {self.filename}")
else:
logger.warning(f"\n\n{'='*70}\n{'Config file not found':^70}\n{'You can custum your config in `' + self.filename + '`':^70}\n{'='*70}\n\n")
@ -170,7 +177,30 @@ MODEL_NAMES = {
"llama3.1-8b-instruct",
"llama3-8b-instruct",
"llama3.1-405b-instruct",
"baichuan2-7b-chat-v1",
"qwen2-0.5b-instruct"
]
}
EMBED_MODEL_INFO = {
"bge-large-zh-v1.5": SimpleConfig({
"name": "bge-large-zh-v1.5",
"default_path": "BAAI/bge-large-zh-v1.5",
"dimension": 1024,
"query_instruction": "为这个句子生成表示以用于检索相关文章:",
}),
"zhipu-embedding-2": SimpleConfig({
"name": "zhipu-embedding-2",
"default_path": "embedding-2",
"dimension": 1024,
}),
"zhipu-embedding-3": SimpleConfig({
"name": "zhipu-embedding-3",
"default_path": "embedding-3",
"dimension": 2048,
}),
}
RERANKER_LIST = {
"bge-reranker-v2-m3": "BAAI/bge-reranker-v2-m3",
}

View File

@ -9,10 +9,11 @@ logger = setup_logger("DataBaseManager")
class DataBaseLite:
def __init__(self, name, description, db_type, **kwargs) -> None:
def __init__(self, name, description, db_type, dimension=None, **kwargs) -> None:
self.name = name
self.description = description
self.db_type = db_type
self.dimension = dimension
self.db_id = kwargs.get("db_id", hashstr(name))
self.metaname = kwargs.get("metaname", f"{db_type[:1]}{hashstr(name)}")
self.metadata = kwargs.get("metaname", {})
@ -37,7 +38,8 @@ class DataBaseLite:
"embed_model": self.embed_model,
"metaname": self.metaname,
"metadata": self.metadata,
"files": self.files
"files": self.files,
"dimension": self.dimension
}
def to_json(self):
@ -115,10 +117,14 @@ class DataBaseManager:
else:
return {"message": "Graph base not enabled", "graph": {}}
def create_database(self, database_name, description, db_type):
new_database = DataBaseLite(database_name, description, db_type, embed_model=self.config.embed_model)
def create_database(self, database_name, description, db_type, dimension):
new_database = DataBaseLite(database_name,
description,
db_type,
embed_model=self.config.embed_model,
dimension=dimension)
self.knowledge_base.add_collection(new_database.metaname)
self.knowledge_base.add_collection(new_database.metaname, dimension)
self.data["databases"].append(new_database)
self._save_databases()
return self.get_databases()

View File

@ -18,7 +18,6 @@ class KnowledgeBase:
self.client = MilvusClient(self.milvus_path)
def _init_config(self, config):
self.vector_dim = 1024 # 暂时不知道这个和 embedding model 的 embedding 大小有什么关系
self.milvus_path = os.path.join(config.save_dir, "data/vector_base/milvus.db")
os.makedirs(os.path.dirname(self.milvus_path), exist_ok=True)
@ -40,14 +39,14 @@ class KnowledgeBase:
# collection["id"] = hashstr(collection_name)
return collection
def add_collection(self, collection_name):
def add_collection(self, collection_name, dimension=None):
if self.client.has_collection(collection_name=collection_name):
logger.warning(f"Collection {collection_name} already exists, drop it")
self.client.drop_collection(collection_name=collection_name)
self.client.create_collection(
collection_name=collection_name,
dimension=self.vector_dim, # The vectors we will use in this demo has 768 dimensions
dimension= dimension, # The vectors we will use in this demo has 768 dimensions
)
def add_documents(self, docs, collection_name, **kwargs):
@ -55,8 +54,8 @@ class KnowledgeBase:
# 检查 collection 是否存在
import random
if not self.client.has_collection(collection_name=collection_name):
logger.warning(f"Collection {collection_name} not found, create it")
self.add_collection(collection_name)
logger.error(f"Collection {collection_name} not found, create it")
# self.add_collection(collection_name)
vectors = self.embed_model.encode(docs)

View File

@ -66,29 +66,31 @@ class Retriever:
def query_knowledgebase(self, query, history, refs):
"""查询知识库"""
rw_query = self.rewrite_query(query, history, refs)
kb_res = []
final_res = []
if refs["meta"].get("db_name") and self.config.enable_knowledge_base:
if not refs["meta"].get("db_name") or not self.config.enable_knowledge_base:
return {"results": final_res, "all_results": kb_res, "rw_query": query, "message": "Knowledge base is disabled"}
db_name = refs["meta"]["db_name"]
kb = self.dbm.metaname2db[db_name]
limit = refs["meta"].get("queryCount", 10)
rw_query = self.rewrite_query(query, history, refs)
kb_res = self.dbm.knowledge_base.search(rw_query, db_name, limit=limit)
db_name = refs["meta"]["db_name"]
kb = self.dbm.metaname2db[db_name]
limit = refs["meta"].get("queryCount", 10)
kb_res = self.dbm.knowledge_base.search(rw_query, db_name, limit=limit)
for r in kb_res:
r["file"] = kb.id2file(r["entity"]["file_id"])
if self.config.enable_reranker:
RERANK_THRESHOLD = 0.1
for r in kb_res:
r["file"] = kb.id2file(r["entity"]["file_id"])
r["rerank_score"] = self.reranker.compute_score([query, r["entity"]["text"]], normalize=True)
kb_res.sort(key=lambda x: x["rerank_score"], reverse=True)
final_res = [_res for _res in kb_res if _res["rerank_score"] > RERANK_THRESHOLD]
if self.config.enable_reranker:
RERANK_THRESHOLD = 0.1
for r in kb_res:
r["rerank_score"] = self.reranker.compute_score([query, r["entity"]["text"]], normalize=True)
kb_res.sort(key=lambda x: x["rerank_score"], reverse=True)
final_res = [_res for _res in kb_res if _res["rerank_score"] > RERANK_THRESHOLD]
else:
final_res = kb_res[:5]
else:
final_res = kb_res[:5]
return {"results": final_res, "all_results": kb_res, "rw_query": rw_query}

View File

@ -11,8 +11,6 @@ class OpenAIBase():
self.model_name = model_name
def predict(self, message, stream=False):
logger.debug(message)
if isinstance(message, str):
messages=[{"role": "user", "content": message}]
else:

View File

@ -1,37 +1,24 @@
import os
from FlagEmbedding import FlagModel, FlagReranker
from src.config import EMBED_MODEL_INFO, RERANKER_LIST
from src.utils.logging_config import setup_logger
logger = setup_logger("EmbeddingModel")
SUPPORT_LIST = {
"bge-large-zh-v1.5": "BAAI/bge-large-zh-v1.5",
"zhipu": "embedding-3",
}
RERANKER_LIST = {
"bge-reranker-v2-m3": "BAAI/bge-reranker-v2-m3",
}
QUERY_INSTRUCTION = {
"bge-large-zh-v1.5": "为这个句子生成表示以用于检索相关文章:",
}
class EmbeddingModel(FlagModel):
def __init__(self, config, **kwargs):
assert config.embed_model in SUPPORT_LIST.keys(), f"Unsupported embed model: {config.embed_model}, only support {SUPPORT_LIST}"
model_name_or_path = config.model_local_paths.get(config.embed_model, SUPPORT_LIST[config.embed_model])
logger.info(f"Loading embedding model {config.embed_model} from {model_name_or_path}")
def __init__(self, model_info, config, **kwargs):
self.info = model_info
model_name_or_path = config.model_local_paths.get(model_info.name, model_info.default_path)
logger.info(f"Loading embedding model {model_info.name} from {model_name_or_path}")
super().__init__(model_name_or_path,
query_instruction_for_retrieval=QUERY_INSTRUCTION[config.embed_model],
query_instruction_for_retrieval=model_info.get("query_instruction", None),
use_fp16=False, **kwargs)
logger.info(f"Embedding model {config.embed_model} loaded")
logger.info(f"Embedding model {model_info.name} loaded")
class Reranker(FlagReranker):
@ -50,8 +37,9 @@ from zhipuai import ZhipuAI
class ZhipuEmbedding:
def __init__(self, config) -> None:
def __init__(self, model_info, config) -> None:
self.config = config
self.model_info = model_info
self.client = ZhipuAI(api_key=os.getenv("ZHIPUAPI"))
logger.info("Zhipu Embedding model loaded")
self.query_instruction_for_retrieval = "为这个句子生成表示以用于检索相关文章:"
@ -63,8 +51,8 @@ class ZhipuEmbedding:
for i in range(0, len(message), 10):
group_msg = message[i:i+10]
response = self.client.embeddings.create(
model=SUPPORT_LIST[self.config.embed_model],
input=group_msg
model=self.model_info.default_path,
input=group_msg,
)
data.extend([a.embedding for a in response.data])
@ -83,7 +71,12 @@ def get_embedding_model(config):
if not config.enable_knowledge_base:
return None
if config.embed_model == "zhipu":
return ZhipuEmbedding(config)
else:
return EmbeddingModel(config)
assert config.embed_model in EMBED_MODEL_INFO.keys(), f"Unsupported embed model: {config.embed_model}, only support {EMBED_MODEL_INFO.keys()}"
if config.embed_model in ["bge-large-zh-v1.5"]:
model = EmbeddingModel(EMBED_MODEL_INFO[config.embed_model], config)
if config.embed_model in ["zhipu-embedding-2", "zhipu-embedding-3"]:
model = ZhipuEmbedding(EMBED_MODEL_INFO[config.embed_model], config)
return model

View File

@ -3,21 +3,23 @@ import os
from datetime import datetime
# DATETIME = datetime.now().strftime('%Y-%m-%d-%H%M%S')
DATETIME = "debug" # 为了方便,调试的时候输出到 debug.log 文件
DATETIME = datetime.now().strftime('%Y-%m-%d-%H%M%S')
# DATETIME = "debug" # 为了方便,调试的时候输出到 debug.log 文件
LOG_FILE = f'log/project-{DATETIME}.log'
def setup_logger(name, log_file=None, level=logging.DEBUG, console=False):
if log_file is None:
log_file = f'log/project-{DATETIME}.log'
def setup_logger(name, level=logging.DEBUG, console=False):
os.makedirs("log", exist_ok=True)
"""Function to setup logger with the given name and log file."""
logger = logging.getLogger(name)
logger.setLevel(level)
# 清除已有的 Handler防止重复添加
if logger.hasHandlers():
logger.handlers.clear()
# File handler for logging to a file
file_handler = logging.FileHandler(log_file)
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setLevel(level)
# Formatter for the logs
@ -34,6 +36,7 @@ def setup_logger(name, log_file=None, level=logging.DEBUG, console=False):
return logger
# Setup the root logger
logger = setup_logger('Athena')

View File

@ -29,7 +29,6 @@ def chat():
request_data = json.loads(request.data)
query = request_data['query']
meta = request_data.get('meta')
logger.debug(f"Web query: {query}")
history_manager = HistoryManager(request_data['history'])
new_query, refs = startup.retriever(query, history_manager.messages, meta)
@ -63,7 +62,7 @@ def call():
request_data = json.loads(request.data)
query = request_data['query']
response = startup.model.predict(query)
logger.debug(f"\n\n\nCall query: \n{query} \n\nResponse: \n{response.content}\n\n")
logger.debug({"query": query, "response": response.content})
return jsonify({
"response": response.content,
@ -83,4 +82,13 @@ def update_config():
@common.route('/restart', methods=['POST'])
def restart():
startup.restart()
return jsonify({"message": "Restarted!"})
return jsonify({"message": "Restarted!"})
@common.route('/log', methods=['GET'])
def get_log():
from src.utils.logging_config import LOG_FILE
with open(LOG_FILE, 'r') as f:
log = f.read()
return jsonify({"log": log})

View File

@ -27,8 +27,9 @@ def create_database():
database_name = data.get('database_name')
description = data.get('description')
db_type = data.get('db_type')
dimension = data.get('dimension')
logger.debug(f"Create database {database_name}")
database = startup.dbm.create_database(database_name, description, db_type)
database = startup.dbm.create_database(database_name, description, db_type, dimension=dimension)
return jsonify(database)
@db.route('/', methods=['DELETE'])

View File

@ -10,6 +10,7 @@
--main-200: #8CC6E1;
--main-100: #ABE0F7;
--main-50: #CDF5FF;
--main-25: #E6FAFF;
--c-white: #ffffff;
--c-white-soft: #f8f8f8;

View File

@ -117,49 +117,7 @@
class="message-md"
@click="consoleMsg(message)"></p>
<div class="refs" v-if="message.role=='received' && message.groupedResults && message.status=='finished'">
<a-tag
class="filetag"
v-for="(results, filename) in message.groupedResults"
:key="filename"
@click="opts.openDetail = true"
:bordered="false"
>
{{ filename }}
<a-drawer
v-model:open="opts.openDetail"
title="检索详情"
width="800"
:contentWrapperStyle="{ maxWidth: '100%'}"
placement="right"
class="retrieval-detail"
rootClassName="root"
>
<div class="fileinfo">
<p><strong>文件名:</strong> {{ results[0].file.filename }}</p>
<p><strong>文件类型:</strong> {{ results[0].file.type }}</p>
<p><strong>创建时间:</strong> {{ new Date(results[0].file.created_at * 1000).toLocaleString() }}</p>
</div>
<div v-for="(res, idx) in results" :key="idx" class="result-item">
<p class="result-id"><strong>ID:</strong> #{{ res.id }}</p>
<p class="result-distance">
<strong>相似度距离:</strong>
<div class=scorebar>
<a-progress :percent="(res.distance * 100).toFixed(2)" stroke-color="#1677FF" :size="[200, 10]"/>
</div>
</p>
<p class="result-rerank-score">
<strong>重排序分数:</strong>
<div class=scorebar>
<a-progress :percent="(res.rerank_score * 100).toFixed(2)" stroke-color="#1677FF" :size="[200, 10]"/>
</div>
</p>
<a-divider />
<p class="result-text">{{ res.entity.text }}</p>
</div>
</a-drawer>
</a-tag>
</div>
<RefsComponent v-if="message.role=='received' && message.status=='finished'" :message="message" />
</div>
</div>
<div class="bottom">
@ -196,11 +154,14 @@ import {
PlusCircleOutlined,
FolderOutlined,
FolderOpenOutlined,
GlobalOutlined,
FileTextOutlined,
} from '@ant-design/icons-vue'
import { onClickOutside } from '@vueuse/core'
import { Marked } from 'marked';
import { markedHighlight } from 'marked-highlight';
import { useConfigStore } from '@/stores/config'
import RefsComponent from '@/components/RefsComponent.vue'
import hljs from 'highlight.js';
import 'highlight.js/styles/github.css';
@ -663,57 +624,9 @@ watch(
margin-bottom: 0;
}
.refs {
margin-bottom: 20px;
.filetag:hover {
cursor: pointer;
}
}
}
.retrieval-detail {
.fileinfo {
margin-bottom: 20px;
padding: 1rem;
background: var(--main-50);
color: var(--main-800);
border-radius: 8px;
// border: 1px solid var(--main-100);
p {
margin: 10px;
line-height: 1.5;
}
}
.result-item {
margin-bottom: 20px;
padding: 24px 16px 10px 16px;
border: 1px solid #e8e8e8;
border-radius: 8px;
background: var(--main-light-6);
.result-id,
.result-distance,
.result-rerank-score,
.result-text-label,
.result-text {
margin: 5px 0;
}
.scorebar {
margin-left: 10px;
display: inline-block;
width: 200px;
padding-bottom: 2px;
& > * {
margin: 0;
}
}
}
}

View File

@ -0,0 +1,85 @@
<template>
<div class="log-viewer">
<a-button @click="fetchLogs">刷新</a-button>
<div ref="logContainer" class="log-container">
<pre v-if="logs">{{ logs }}</pre>
</div>
<p v-if="error" class="error">{{ error }}</p>
</div>
</template>
<script setup>
import { ref, onMounted, onActivated, nextTick } from 'vue';
// ref
const logs = ref('');
const error = ref('');
// ref DOM
const logContainer = ref(null);
//
const fetchLogs = async () => {
try {
//
error.value = '';
//
const response = await fetch('/api/log'); // API
if (!response.ok) {
throw new Error('Failed to fetch logs');
}
// JSON
const data = await response.json();
// logs
logs.value = data.log;
// DOM
await nextTick();
if (logContainer.value) {
logContainer.value.scrollTop = logContainer.value.scrollHeight;
}
} catch (err) {
//
error.value = `Error: ${err.message}`;
}
};
//
onMounted(() => {
fetchLogs();
setInterval(fetchLogs, 5000); // 5
});
// keep-alive
onActivated(() => {
fetchLogs();
});
</script>
<style scoped>
.log-viewer {}
.error {
color: red;
}
.log-container {
max-height: 80vh; /* 设置最大高度 */
overflow-y: auto; /* 启用垂直滚动 */
background-color: #f0f0f0;
margin: 20px 0;
padding: 10px;
padding-bottom: 0;
border-radius: 5px;
white-space: pre-wrap; /* 使日志内容自动换行 */
word-wrap: break-word;
background: #0C0C0C;
color: #D1D1D1;
}
</style>

View File

@ -0,0 +1,143 @@
<!-- RefsComponent.vue -->
<template>
<div class="refs" v-if="showRefs">
<span class="item"><GlobalOutlined /> {{ message.model_name }}</span>
<div class="tags">
<span class="filetag item"
v-for="(results, filename) in message.groupedResults"
:key="filename"
@click="openDetail = true"
>
<FileTextOutlined /> {{ filename }}
<a-drawer
v-model:open="openDetail"
title="检索详情"
width="800"
:contentWrapperStyle="{ maxWidth: '100%'}"
placement="right"
class="retrieval-detail"
rootClassName="root"
>
<div class="fileinfo">
<p><strong>文件名:</strong> {{ results[0].file.filename }}</p>
<p><strong>文件类型:</strong> {{ results[0].file.type }}</p>
<p><strong>创建时间:</strong> {{ new Date(results[0].file.created_at * 1000).toLocaleString() }}</p>
</div>
<div v-for="(res, idx) in results" :key="idx" class="result-item">
<p class="result-id"><strong>ID:</strong> #{{ res.id }}</p>
<p class="result-distance">
<strong>相似度距离:</strong>
<div class="scorebar">
<a-progress :percent="(res.distance * 100).toFixed(2)" stroke-color="#1677FF" :size="[200, 10]"/>
</div>
</p>
<p class="result-rerank-score">
<strong>重排序分数:</strong>
<div class="scorebar">
<a-progress :percent="(res.rerank_score * 100).toFixed(2)" stroke-color="#1677FF" :size="[200, 10]"/>
</div>
</p>
<a-divider />
<p class="result-text">{{ res.entity.text }}</p>
</div>
</a-drawer>
</span>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import {
GlobalOutlined,
FileTextOutlined,
} from '@ant-design/icons-vue'
const props = defineProps({
message: Object,
})
const message = ref(props.message)
const openDetail = ref(false)
const showRefs = computed(() => message.value.role=='received' && message.value.status=='finished')
</script>
<style lang="less" scoped>
.refs {
display: flex;
margin-bottom: 20px;
color: var(--c-text-light-4);
font-size: 14px;
gap: 10px;
.item {
background: var(--main-25);
color: var(--main-800);
border: 1px solid var(--main-100);
padding: 2px 8px;
border-radius: 8px;
font-size: 14px;
}
.tags {
display: flex;
gap: 10px;
.filetag {
display: flex;
align-items: center;
gap: 5px;
cursor: pointer;
&:hover {
background: var(--main-100);
}
}
}
}
.retrieval-detail {
.fileinfo {
margin-bottom: 20px;
padding: 1rem;
background: var(--main-25);
color: var(--main-800);
border-radius: 8px;
border: 1px solid var(--main-100);
p {
margin: 10px;
line-height: 1.5;
}
}
.result-item {
margin-bottom: 20px;
padding: 24px 16px 10px 16px;
border: 1px solid #e8e8e8;
border-radius: 8px;
background: var(--main-light-6);
.result-id,
.result-distance,
.result-rerank-score,
.result-text-label,
.result-text {
margin: 5px 0;
}
.scorebar {
margin-left: 10px;
display: inline-block;
width: 200px;
padding-bottom: 2px;
& > * {
margin: 0;
}
}
}
}
</style>

View File

@ -1,5 +1,5 @@
<script setup>
import { KeepAlive, onMounted } from 'vue'
import { ref, KeepAlive, onMounted } from 'vue'
import { RouterLink, RouterView, useRoute } from 'vue-router'
import {
MessageOutlined,
@ -15,10 +15,13 @@ import {
import { themeConfig } from '@/assets/theme'
import { useConfigStore } from '@/stores/config'
import { useDatabaseStore } from '@/stores/database'
import DebugComponent from '@/components/DebugComponent.vue'
const configStore = useConfigStore()
const databaseStore = useDatabaseStore()
const showDebug = ref(false)
const getRemoteConfig = () => {
fetch('/api/config').then(res => res.json()).then(data => {
console.log(data)
@ -48,6 +51,18 @@ console.log(route)
<template>
<div class="app-layout">
<div class="debug-panel">
<div class="shown-btn" @click="showDebug=!showDebug">Debug</div>
<a-drawer
v-model:open="showDebug"
title="调试面板"
width="800"
:contentWrapperStyle="{ maxWidth: '100%'}"
placement="right"
>
<DebugComponent />
</a-drawer>
</div>
<div class="header">
<div class="logo">
<router-link to="/"><img src="/jnu.png"> </router-link>
@ -98,6 +113,19 @@ console.log(route)
.header-mobile {
display: none;
}
.debug-panel {
position: absolute;
z-index: 100;
right: 0;
top: 50px;
border-radius: 16px 0 0 16px;
background-color: var(--main-light-3);
padding: 8px 8px 8px 16px;
box-shadow: 0 0 20px 10px rgba(0, 0, 0, 0.1);
transition: right 0.3s ease-in-out;
cursor: pointer;
}
}
div.header, #app-router-view {

View File

@ -67,6 +67,19 @@ const router = createRouter({
}
]
},
// {
// path: '/monitor',
// name: 'monitor',
// component: AppLayout,
// children: [
// {
// path: '',
// name: 'monitor',
// component: () => import('../views/MonitorView.vue'),
// meta: { keepAlive: true }
// }
// ]
// },
{
path: '/:pathMatch(.*)*',
name: 'NotFound',

View File

@ -11,6 +11,9 @@
placeholder="新建数据库描述"
:auto-size="{ minRows: 2, maxRows: 5 }"
/>
<h3 style="margin-top: 20px;">向量维度</h3>
<p>必须与向量模型 {{ configStore.config.embed_model }} 一致</p>
<a-input v-model:value="newDatabase.dimension" placeholder="向量维度 (e.g. 768, 1024)" />
<template #footer>
<a-button key="back" @click="newDatabase.open=false">取消</a-button>
<a-button key="submit" type="primary" :loading="newDatabase.loading" @click="createDatabase">创建</a-button>
@ -40,7 +43,8 @@
</div>
<p class="description">{{ database.description }}</p>
<div class="tags">
<a-tag color="blue" v-if="database.embed_model">Embed: {{ database.embed_model }}</a-tag>
<a-tag color="blue" v-if="database.embed_model">{{ database.embed_model }}</a-tag>
<a-tag color="green" v-if="database.dimension">{{ database.dimension }}</a-tag>
</div>
<!-- <button @click="deleteDatabase(database.collection_name)">删除</button> -->
</div>
@ -94,6 +98,7 @@ const configStore = useConfigStore()
const newDatabase = reactive({
name: '',
description: '',
dimension: '',
loading: false,
})
@ -123,7 +128,8 @@ const createDatabase = () => {
body: JSON.stringify({
database_name: newDatabase.name,
description: newDatabase.description,
db_type: "knowledge"
db_type: "knowledge",
dimension: newDatabase.dimension,
})
})
.then(response => response.json())
@ -132,7 +138,8 @@ const createDatabase = () => {
loadDatabases()
newDatabase.open = false
newDatabase.name = ''
newDatabase.description = ''
newDatabase.description = '',
newDatabase.dimension = ''
})
.finally(() => {
newDatabase.loading = false
@ -238,6 +245,7 @@ onMounted(() => {
background-color: #F5F8FF;
border-radius: 8px;
border: 1px solid #E0EAFF;
color: var(--main-color);
}
.info {
@ -246,6 +254,11 @@ onMounted(() => {
color: black;
}
h3 {
font-size: 16px;
font-weight: bold;
}
p {
color: var(--c-text-light-1);
font-size: small;

View File

@ -12,7 +12,7 @@
<ReloadOutlined />需要重启
</a-button>
</span>
<a-select ref="select" style="width: 160px"
<a-select ref="select" style="width: 200px"
:value="configStore.config?.model_provider"
@change="handleChange('model_provider', $event)"
>
@ -29,7 +29,7 @@
<ReloadOutlined />需要重启
</a-button>
</span>
<a-select ref="select" style="width: 160px"
<a-select ref="select" style="width: 200px"
:value="configStore.config?.model_name"
@change="handleChange('model_name', $event)"
v-if="configStore.config?.model_names && configStore.config?.model_provider && configStore.config?.model_names[configStore.config?.model_provider]"
@ -47,7 +47,7 @@
<ReloadOutlined />需要重启
</a-button>
</span>
<a-select style="width: 160px"
<a-select style="width: 200px"
:value="configStore.config?.embed_model"
@change="handleChange('embed_model', $event)"
>
@ -63,7 +63,7 @@
<ReloadOutlined />需要重启
</a-button>
</span>
<a-select style="width: 160px"
<a-select style="width: 200px"
:value="configStore.config?.reranker"
@change="handleChange('reranker', $event)"
:disabled="!configStore.config.enable_reranker"