feat(frontend): 新增模型平台管理页面并接入RAG模型配置
This commit is contained in:
@@ -3,6 +3,14 @@ import { CirclePlus, Delete, Edit, FolderAdd, Refresh, Search, UploadFilled } fr
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { loadModelProviderEnumOptions, type EnumOption } from '@/api/modelEnums';
|
||||
import {
|
||||
getRagStoreModelConfig,
|
||||
queryModelConfigs,
|
||||
rebuildRagStoreIndex,
|
||||
saveRagStoreModelConfig,
|
||||
type ModelConfig,
|
||||
} from '@/api/modelProvider';
|
||||
|
||||
import {
|
||||
deleteRagStore,
|
||||
@@ -28,6 +36,20 @@ const activeStoreId = ref<string | null>(null);
|
||||
const activeStore = ref<RagStore | null>(null);
|
||||
const pageOverview = ref<RagStoreOverview | null>(null);
|
||||
const activeStoreDocumentOverview = ref<RagStoreDocumentOverview | null>(null);
|
||||
const embeddingModels = ref<ModelConfig[]>([]);
|
||||
const ragConfigLoading = ref(false);
|
||||
const ragConfigSaving = ref(false);
|
||||
const ragConfig = reactive({
|
||||
id: '',
|
||||
embeddingModelId: '',
|
||||
embeddingDimension: 1024,
|
||||
chunkStrategy: null as number | null,
|
||||
chunkSize: null as number | null,
|
||||
chunkOverlap: null as number | null,
|
||||
delimiter: '',
|
||||
remark: '',
|
||||
});
|
||||
const chunkStrategyOptions = ref<EnumOption[]>([]);
|
||||
|
||||
const queryForm = reactive({
|
||||
storeName: '',
|
||||
@@ -91,6 +113,8 @@ async function loadStores(preferredStoreId?: string | null) {
|
||||
activeStoreId.value = null;
|
||||
activeStore.value = null;
|
||||
activeStoreDocumentOverview.value = null;
|
||||
ragConfig.id = '';
|
||||
ragConfig.embeddingModelId = '';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -123,6 +147,7 @@ async function selectStore(storeId: string) {
|
||||
]);
|
||||
activeStore.value = storeResponse.data ?? null;
|
||||
activeStoreDocumentOverview.value = documentOverviewResponse.data ?? null;
|
||||
await loadRagModelConfig(storeId);
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
@@ -223,10 +248,6 @@ async function removeStore() {
|
||||
await loadStores();
|
||||
}
|
||||
|
||||
function showFutureMessage(actionName: string) {
|
||||
ElMessage.info(`${actionName} 会在下一批接口里补齐`);
|
||||
}
|
||||
|
||||
function openBatchUploadDialog() {
|
||||
if (!activeStore.value?.id) {
|
||||
ElMessage.warning('请选择知识库');
|
||||
@@ -252,13 +273,85 @@ async function refreshAfterUpload() {
|
||||
]);
|
||||
}
|
||||
|
||||
async function loadRagModelConfig(storeId: string) {
|
||||
ragConfigLoading.value = true;
|
||||
try {
|
||||
const response = await getRagStoreModelConfig(storeId);
|
||||
const data = response.data;
|
||||
ragConfig.id = data?.id ?? '';
|
||||
ragConfig.embeddingModelId = data?.embeddingModelId ?? '';
|
||||
ragConfig.embeddingDimension = data?.embeddingDimension ?? 1024;
|
||||
ragConfig.chunkStrategy = data?.chunkStrategy ?? null;
|
||||
ragConfig.chunkSize = data?.chunkSize ?? null;
|
||||
ragConfig.chunkOverlap = data?.chunkOverlap ?? null;
|
||||
ragConfig.delimiter = data?.delimiter ?? '';
|
||||
ragConfig.remark = data?.remark ?? '';
|
||||
} finally {
|
||||
ragConfigLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveStoreModelConfig() {
|
||||
if (!activeStoreId.value) {
|
||||
return;
|
||||
}
|
||||
if (!ragConfig.embeddingModelId) {
|
||||
ElMessage.warning('请选择 Embedding 模型');
|
||||
return;
|
||||
}
|
||||
ragConfigSaving.value = true;
|
||||
try {
|
||||
await saveRagStoreModelConfig({
|
||||
id: ragConfig.id || undefined,
|
||||
storeId: activeStoreId.value,
|
||||
embeddingModelId: ragConfig.embeddingModelId,
|
||||
embeddingDimension: ragConfig.embeddingDimension,
|
||||
chunkStrategy: ragConfig.chunkStrategy ?? undefined,
|
||||
chunkSize: ragConfig.chunkSize ?? undefined,
|
||||
chunkOverlap: ragConfig.chunkOverlap ?? undefined,
|
||||
delimiter: ragConfig.delimiter || undefined,
|
||||
remark: ragConfig.remark || undefined,
|
||||
});
|
||||
ElMessage.success('知识库模型配置已保存');
|
||||
await loadRagModelConfig(activeStoreId.value);
|
||||
} finally {
|
||||
ragConfigSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function triggerRebuildIndex() {
|
||||
if (!activeStoreId.value) {
|
||||
return;
|
||||
}
|
||||
await rebuildRagStoreIndex(activeStoreId.value);
|
||||
ElMessage.success('已触发重建索引');
|
||||
}
|
||||
|
||||
function syncEmbeddingDimensionFromModel() {
|
||||
const model = embeddingModels.value.find((item) => item.id === ragConfig.embeddingModelId);
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
if (model.embeddingDimension) {
|
||||
ragConfig.embeddingDimension = model.embeddingDimension;
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusTagType(status?: string | null) {
|
||||
return status === '启用' ? 'success' : 'info';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadOverview();
|
||||
loadStores();
|
||||
Promise.all([
|
||||
loadOverview(),
|
||||
loadStores(),
|
||||
queryModelConfigs().then((response) => {
|
||||
embeddingModels.value = (response.data ?? []).filter((item) => item.modelType === 'EMBEDDING');
|
||||
}),
|
||||
loadModelProviderEnumOptions().then((enums) => {
|
||||
chunkStrategyOptions.value = enums.chunk_strategy ?? [];
|
||||
}),
|
||||
]);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -351,7 +444,7 @@ onMounted(() => {
|
||||
>
|
||||
批量导入文件
|
||||
</el-button>
|
||||
<el-button :icon="FolderAdd" @click="showFutureMessage('重建索引')">重建索引</el-button>
|
||||
<el-button :icon="FolderAdd" @click="triggerRebuildIndex">重建索引</el-button>
|
||||
<el-button type="danger" :icon="Delete" @click="removeStore">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -415,12 +508,56 @@ onMounted(() => {
|
||||
</el-descriptions>
|
||||
</article>
|
||||
|
||||
<article class="detail-card detail-card--placeholder">
|
||||
<article class="detail-card">
|
||||
<div class="detail-card__header">
|
||||
<h4>检索配置</h4>
|
||||
<span>下一批接口补充</span>
|
||||
<el-button type="primary" link :loading="ragConfigSaving" @click="saveStoreModelConfig">
|
||||
保存配置
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-loading="ragConfigLoading" class="rag-config-form">
|
||||
<el-form :model="ragConfig" label-width="112px">
|
||||
<el-form-item label="Embedding 模型" required>
|
||||
<el-select
|
||||
v-model="ragConfig.embeddingModelId"
|
||||
placeholder="请选择模型"
|
||||
@change="syncEmbeddingDimensionFromModel"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in embeddingModels"
|
||||
:key="item.id"
|
||||
:label="`${item.modelName}(${item.modelCode})`"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="向量维度">
|
||||
<el-input-number v-model="ragConfig.embeddingDimension" :min="1" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="切片策略">
|
||||
<el-select v-model="ragConfig.chunkStrategy" clearable placeholder="可选">
|
||||
<el-option
|
||||
v-for="item in chunkStrategyOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="Number(item.value)"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="切片大小">
|
||||
<el-input-number v-model="ragConfig.chunkSize" :min="1" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="切片重叠">
|
||||
<el-input-number v-model="ragConfig.chunkOverlap" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分隔符">
|
||||
<el-input v-model="ragConfig.delimiter" placeholder="可选,如 \\n\\n" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="ragConfig.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-empty description="检索模式、Embedding 模型、Chunk 参数待后端补充" />
|
||||
</article>
|
||||
|
||||
<article class="detail-card detail-card--placeholder">
|
||||
@@ -721,6 +858,11 @@ onMounted(() => {
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.rag-config-form :deep(.el-select),
|
||||
.rag-config-form :deep(.el-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.overview-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user