feat: add index management interface
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { buildIndex, getActiveIndex, getIndexVersions, publishIndex, type ActiveIndex, type IndexVersion } from '../services/indexes'
|
||||
import { buildVectorIndex, getActiveVectors, getVectorVersions, publishVectorIndex, type ActiveVectors, type VectorVersion } from '../services/vectors'
|
||||
|
||||
const textActive = ref<ActiveIndex>()
|
||||
const vectorActive = ref<ActiveVectors>()
|
||||
const textVersions = ref<IndexVersion[]>([])
|
||||
const vectorVersions = ref<VectorVersion[]>([])
|
||||
const loading = ref(true)
|
||||
const buildingText = ref(false)
|
||||
const buildingVector = ref<string>()
|
||||
const publishing = ref<string>()
|
||||
const error = ref('')
|
||||
const notice = ref('')
|
||||
const config = ref({ chunk_size: 800, overlap: 120 })
|
||||
|
||||
async function refresh() {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
const [text, texts, vectors, vectorList] = await Promise.all([getActiveIndex(), getIndexVersions(), getActiveVectors(), getVectorVersions()])
|
||||
textActive.value = text
|
||||
textVersions.value = texts.items
|
||||
vectorActive.value = vectors
|
||||
vectorVersions.value = vectorList.items
|
||||
} catch (cause) {
|
||||
error.value = cause instanceof Error ? cause.message : '无法读取索引状态'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function buildText() {
|
||||
error.value = ''; notice.value = ''
|
||||
if (config.value.chunk_size <= 0 || config.value.overlap < 0 || config.value.overlap >= config.value.chunk_size) {
|
||||
error.value = '切分长度必须大于 0,且重叠长度必须小于切分长度。'
|
||||
return
|
||||
}
|
||||
buildingText.value = true
|
||||
try {
|
||||
const version = await buildIndex(config.value)
|
||||
notice.value = `文本索引 ${version.id} 已构建为 ready,需确认后再发布。`
|
||||
await refresh()
|
||||
} catch (cause) { error.value = cause instanceof Error ? cause.message : '构建文本索引失败' } finally { buildingText.value = false }
|
||||
}
|
||||
|
||||
async function publishText(version: IndexVersion) {
|
||||
publishing.value = `text:${version.id}`; error.value = ''; notice.value = ''
|
||||
try { await publishIndex(version.id, textActive.value?.generation ?? 0); notice.value = '文本索引已发布。'; await refresh() }
|
||||
catch (cause) { error.value = cause instanceof Error ? cause.message : '发布文本索引失败' } finally { publishing.value = undefined }
|
||||
}
|
||||
|
||||
async function buildVector(version: IndexVersion) {
|
||||
buildingVector.value = version.id; error.value = ''; notice.value = ''
|
||||
try { const created = await buildVectorIndex(version.id); notice.value = `向量索引 ${created.id} 已构建为 ready。`; await refresh() }
|
||||
catch (cause) { error.value = cause instanceof Error ? cause.message : '构建向量索引失败' } finally { buildingVector.value = undefined }
|
||||
}
|
||||
|
||||
async function publishVector(version: VectorVersion) {
|
||||
publishing.value = `vector:${version.id}`; error.value = ''; notice.value = ''
|
||||
try { await publishVectorIndex(version.id, vectorActive.value?.generation ?? 0); notice.value = '向量索引已发布。'; await refresh() }
|
||||
catch (cause) { error.value = cause instanceof Error ? cause.message : '发布向量索引失败' } finally { publishing.value = undefined }
|
||||
}
|
||||
|
||||
onMounted(refresh)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<RouterLink to="/">返回工作台</RouterLink>
|
||||
<h1>索引管理</h1>
|
||||
<p>操作直接调用管理员受保护接口;构建与发布会以服务端当前审核语料和代数校验为准。</p>
|
||||
<p v-if="error" role="alert">{{ error }}</p><p v-if="notice" role="status">{{ notice }}</p>
|
||||
<p v-if="loading">正在读取索引状态…</p>
|
||||
<template v-else>
|
||||
<section><h2>文本索引</h2><p>当前版本:{{ textActive?.active_version?.id || '未发布' }};代数:{{ textActive?.generation ?? 0 }}</p>
|
||||
<form @submit.prevent="buildText"><label>切分长度<input v-model.number="config.chunk_size" type="number" min="1" required /></label><label>重叠长度<input v-model.number="config.overlap" type="number" min="0" required /></label><button :disabled="buildingText">{{ buildingText ? '构建中…' : '构建文本索引' }}</button></form>
|
||||
<table><thead><tr><th>版本</th><th>状态</th><th>条目/片段</th><th>创建时间</th><th>操作</th></tr></thead><tbody><tr v-for="version in textVersions" :key="version.id"><td>{{ version.id }}</td><td>{{ version.status }}</td><td>{{ version.item_count }} / {{ version.chunk_count }}</td><td>{{ new Date(version.created_at).toLocaleString() }}</td><td><button v-if="version.status==='ready'" :disabled="publishing===`text:${version.id}`" @click="publishText(version)">发布</button><button v-if="version.status==='published'" :disabled="buildingVector===version.id" @click="buildVector(version)">{{ buildingVector===version.id?'构建中…':'构建向量索引' }}</button></td></tr><tr v-if="!textVersions.length"><td colspan="5">暂无文本索引。</td></tr></tbody></table>
|
||||
</section>
|
||||
<section><h2>向量索引</h2><p>当前版本:{{ vectorActive?.active_version?.id || '未发布' }};代数:{{ vectorActive?.generation ?? 0 }}</p>
|
||||
<table><thead><tr><th>版本</th><th>文本版本</th><th>状态</th><th>片段数</th><th>操作</th></tr></thead><tbody><tr v-for="version in vectorVersions" :key="version.id"><td>{{ version.id }}</td><td>{{ version.text_index_version_id }}</td><td>{{ version.status }}</td><td>{{ version.chunk_count }}</td><td><button v-if="version.status==='ready'" :disabled="publishing===`vector:${version.id}`" @click="publishVector(version)">发布</button></td></tr><tr v-if="!vectorVersions.length"><td colspan="5">暂无向量索引;需先发布文本索引并启用本地 Embedding。</td></tr></tbody></table>
|
||||
</section>
|
||||
</template>
|
||||
</main>
|
||||
</template>
|
||||
Reference in New Issue
Block a user