feat(data-process): 接入重新生成配置流程
This commit is contained in:
@@ -14,9 +14,12 @@ import { DEFAULT_SOURCE_TEXT, estimateTokenCount } from './create/previewModel'
|
||||
import {
|
||||
createDefaultStructuredOptions,
|
||||
createDefaultUnstructuredOptions,
|
||||
generationAffectingOptionsFor,
|
||||
previewAffectingOptionsFor,
|
||||
} from './create/dataProcessCreateState'
|
||||
import { useDataProcessGeneration } from './create/useDataProcessGeneration'
|
||||
import { useDataProcessPreviewBuild } from './create/useDataProcessPreviewBuild'
|
||||
import { useDataProcessRegeneration } from './create/useDataProcessRegeneration'
|
||||
import {
|
||||
mapDataProcessSourceFile,
|
||||
useDataProcessSourceUpload,
|
||||
@@ -51,7 +54,7 @@ import type {
|
||||
|
||||
const router = useRouter()
|
||||
const modelsStore = useModelsStore()
|
||||
const { list: modelList } = storeToRefs(modelsStore)
|
||||
const { list: modelList, loaded: modelsLoaded } = storeToRefs(modelsStore)
|
||||
// 候选范围与模型管理保持一致,不在数据处理页面重复定义模型过滤规则。
|
||||
const generationModels = computed(() => modelList.value)
|
||||
const taskSetupRef = ref<InstanceType<typeof TaskSetupStep>>()
|
||||
@@ -98,6 +101,7 @@ const selectedPreviewFileId = ref<string | null>(null)
|
||||
const selectedPreviewId = ref<string | null>(null)
|
||||
const selectedPreviewIdsByFile = ref<Record<string, string>>({})
|
||||
const dirty = ref(false)
|
||||
const modelSubmitLoading = ref(false)
|
||||
let allowLeave = false
|
||||
|
||||
const {
|
||||
@@ -236,7 +240,7 @@ function taskPayload() {
|
||||
return {
|
||||
name: task.name.trim(),
|
||||
description: task.description.trim(),
|
||||
process_type: processType.value,
|
||||
process_type: originalProcessType.value || processType.value,
|
||||
config: toBackendConfig(),
|
||||
}
|
||||
}
|
||||
@@ -274,82 +278,11 @@ function mapPreviewItem(item: DataProcessPreviewItem): PreviewItem {
|
||||
}
|
||||
|
||||
function previewAffectingOptions() {
|
||||
if (processType.value === 'structured') {
|
||||
return {
|
||||
preprocessOptions: structuredOptions.value.preprocessOptions,
|
||||
}
|
||||
}
|
||||
if (processType.value === 'unstructured') {
|
||||
const {
|
||||
preprocessOptions,
|
||||
chunkMethod,
|
||||
chunkSize,
|
||||
chunkOverlap,
|
||||
minChunkSize,
|
||||
semanticBreakpointPercentile,
|
||||
preserveTables,
|
||||
preserveCodeBlocks,
|
||||
preserveLists,
|
||||
} = unstructuredOptions.value
|
||||
return {
|
||||
preprocessOptions,
|
||||
chunkMethod,
|
||||
chunkSize,
|
||||
chunkOverlap,
|
||||
minChunkSize,
|
||||
semanticBreakpointPercentile,
|
||||
preserveTables,
|
||||
preserveCodeBlocks,
|
||||
preserveLists,
|
||||
}
|
||||
}
|
||||
return null
|
||||
return previewAffectingOptionsFor(processType.value, structuredOptions.value, unstructuredOptions.value)
|
||||
}
|
||||
|
||||
function generationAffectingOptions() {
|
||||
if (processType.value === 'structured') {
|
||||
const {
|
||||
semanticEnrichment, qaPairsPerRow, datasetSplit, generationModelId, generationPrompt,
|
||||
temperature, maxTokens, jsonMode,
|
||||
qualityFilterEnabled, filterLowQuality, filterShortContent, minOutputLength,
|
||||
} = structuredOptions.value
|
||||
return {
|
||||
semanticEnrichment, qaPairsPerRow, datasetSplit, generationModelId, generationPrompt,
|
||||
temperature, maxTokens, jsonMode,
|
||||
qualityFilterEnabled, filterLowQuality, filterShortContent, minOutputLength,
|
||||
}
|
||||
}
|
||||
if (processType.value === 'unstructured') {
|
||||
const {
|
||||
semanticEnrichment,
|
||||
qaPairsPerChunk,
|
||||
datasetSplit,
|
||||
generationModelId,
|
||||
generationPrompt,
|
||||
temperature,
|
||||
maxTokens,
|
||||
jsonMode,
|
||||
qualityFilterEnabled,
|
||||
filterLowQuality,
|
||||
filterShortContent,
|
||||
minOutputLength,
|
||||
} = unstructuredOptions.value
|
||||
return {
|
||||
semanticEnrichment,
|
||||
qaPairsPerChunk,
|
||||
datasetSplit,
|
||||
generationModelId,
|
||||
generationPrompt,
|
||||
temperature,
|
||||
maxTokens,
|
||||
jsonMode,
|
||||
qualityFilterEnabled,
|
||||
filterLowQuality,
|
||||
filterShortContent,
|
||||
minOutputLength,
|
||||
}
|
||||
}
|
||||
return null
|
||||
return generationAffectingOptionsFor(processType.value, structuredOptions.value, unstructuredOptions.value)
|
||||
}
|
||||
|
||||
const generationOptionsSignature = computed(() => JSON.stringify(generationAffectingOptions()))
|
||||
@@ -365,19 +298,58 @@ function buildPreviewSignature() {
|
||||
return `${buildPreviewConfigSignature()}:${filesSignature}`
|
||||
}
|
||||
|
||||
const {
|
||||
isRegeneration,
|
||||
originalProcessType,
|
||||
regenerationPrepared,
|
||||
hydrating,
|
||||
initializationError,
|
||||
loadSource: loadRegenerationSource,
|
||||
confirmPreviewConfigChange,
|
||||
prepareRegeneration,
|
||||
} = useDataProcessRegeneration({
|
||||
task,
|
||||
processType,
|
||||
structuredOptions,
|
||||
unstructuredOptions,
|
||||
uploadedFiles,
|
||||
previewItems,
|
||||
selectedPreviewFileId,
|
||||
selectedPreviewId,
|
||||
selectedPreviewIdsByFile,
|
||||
previewSignature,
|
||||
dirty,
|
||||
buildPreviewConfigSignature,
|
||||
buildPreviewSignature,
|
||||
mapPreviewItem,
|
||||
resetDownstream,
|
||||
})
|
||||
const leaveWarningMessage = computed(() => regenerationPrepared.value
|
||||
? '仍有尚未完成的本地操作;已提交到服务端的修改不会因离开页面而撤销。'
|
||||
: '当前存在未保存修改,离开后这些修改将不会保留。')
|
||||
const leaveConfirmText = computed(() => regenerationPrepared.value ? '离开页面' : '放弃修改')
|
||||
|
||||
watch(
|
||||
[() => task.name, () => task.description, processType, structuredOptions, unstructuredOptions, externalSource],
|
||||
() => { dirty.value = true },
|
||||
() => {
|
||||
if (!hydrating.value) dirty.value = true
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
watch(processType, (nextType, previousType) => {
|
||||
if (hydrating.value) return
|
||||
if (isRegeneration.value && originalProcessType.value && nextType !== originalProcessType.value) {
|
||||
processType.value = originalProcessType.value
|
||||
return
|
||||
}
|
||||
if (nextType === previousType || uploadedFiles.value.length === 0) return
|
||||
resetSourceDataForProcessTypeChange()
|
||||
ElMessage.info('处理类型已变更,请重新上传或拉取匹配的源数据')
|
||||
})
|
||||
|
||||
watch(generationOptionsSignature, (currentSignature, previousSignature) => {
|
||||
if (hydrating.value) return
|
||||
if (currentSignature === previousSignature) return
|
||||
resetDownstream()
|
||||
})
|
||||
@@ -531,21 +503,50 @@ function resetSourceDataForProcessTypeChange() {
|
||||
async function nextFromCreate() {
|
||||
const valid = await taskSetupRef.value?.validate()
|
||||
if (!valid) return
|
||||
const confirmed = await confirmPreviewConfigChange((options) => (
|
||||
confirmDialogRef.value?.open(options) ?? Promise.resolve(false)
|
||||
))
|
||||
if (!confirmed) return
|
||||
goToStep('model')
|
||||
}
|
||||
|
||||
async function nextFromModel() {
|
||||
const valid = await modelSelectionRef.value?.validate()
|
||||
if (!valid) return
|
||||
if (modelSubmitLoading.value) return
|
||||
modelSubmitLoading.value = true
|
||||
try {
|
||||
const saved = taskId.value
|
||||
? await updateDataProcessTask(taskId.value, taskPayload())
|
||||
: await createDataProcessTask(taskPayload())
|
||||
taskId.value = String(saved.id)
|
||||
await modelsStore.load(true)
|
||||
if (!modelsLoaded.value) {
|
||||
ElMessage.error('模型列表加载失败,未提交任何修改,请稍后重试')
|
||||
return
|
||||
}
|
||||
const modelId = modelSelectionOptions.value.generationModelId
|
||||
const modelExists = generationModels.value.some(model => String(model.id) === String(modelId))
|
||||
if (modelId !== '' && !modelExists) {
|
||||
ElMessage.error(isRegeneration.value ? '原任务使用的模型已删除,请重新选择模型' : '所选模型已删除,请重新选择')
|
||||
return
|
||||
}
|
||||
const valid = await modelSelectionRef.value?.validate()
|
||||
if (!valid) return
|
||||
if (isRegeneration.value) {
|
||||
const regenerated = await prepareRegeneration(taskPayload())
|
||||
taskId.value = String(regenerated.task.id)
|
||||
if (regenerated.preview_invalidated) {
|
||||
ElMessage.info('切分配置已变化,现有源文件将在下一步按新配置重新切分')
|
||||
} else if (regenerated.published_outputs_preserved) {
|
||||
ElMessage.info('已保留原切片;已发布的三个数据集将在重新发布前保持不变')
|
||||
}
|
||||
} else {
|
||||
const saved = taskId.value
|
||||
? await updateDataProcessTask(taskId.value, taskPayload())
|
||||
: await createDataProcessTask(taskPayload())
|
||||
taskId.value = String(saved.id)
|
||||
}
|
||||
dirty.value = true
|
||||
goToStep('upload')
|
||||
} catch {
|
||||
// 请求层已展示名称冲突或配置非法等具体原因。
|
||||
} finally {
|
||||
modelSubmitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -812,8 +813,8 @@ async function handleCancel() {
|
||||
}
|
||||
const confirmed = await confirmDialogRef.value?.open({
|
||||
title: '确认离开当前页面?',
|
||||
message: '当前存在未保存修改,离开后这些修改将不会保留。',
|
||||
confirmText: '放弃修改',
|
||||
message: leaveWarningMessage.value,
|
||||
confirmText: leaveConfirmText.value,
|
||||
cancelText: '继续编辑',
|
||||
tone: 'danger',
|
||||
})
|
||||
@@ -830,8 +831,8 @@ onBeforeRouteLeave(async () => {
|
||||
if (allowLeave || !dirty.value) return true
|
||||
const confirmed = await confirmDialogRef.value?.open({
|
||||
title: '确认离开当前页面?',
|
||||
message: '当前存在未保存修改,离开后这些修改将不会保留。',
|
||||
confirmText: '放弃修改',
|
||||
message: leaveWarningMessage.value,
|
||||
confirmText: leaveConfirmText.value,
|
||||
cancelText: '继续编辑',
|
||||
tone: 'danger',
|
||||
})
|
||||
@@ -845,6 +846,7 @@ onBeforeUnmount(() => {
|
||||
onMounted(() => {
|
||||
localStorage.removeItem('yg-data-process-create-draft')
|
||||
void modelsStore.load(true)
|
||||
if (isRegeneration.value) void loadRegenerationSource()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -882,15 +884,20 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-content">
|
||||
<div class="wizard-content" v-loading="hydrating">
|
||||
<div v-if="initializationError" class="initialization-error" role="alert">
|
||||
<el-alert type="error" :closable="false" :title="initializationError" show-icon />
|
||||
<el-button type="primary" :loading="hydrating" @click="loadRegenerationSource">重试加载原任务</el-button>
|
||||
</div>
|
||||
<TaskSetupStep
|
||||
v-if="currentStepId === 'create'"
|
||||
v-else-if="currentStepId === 'create'"
|
||||
ref="taskSetupRef"
|
||||
v-model:name="task.name"
|
||||
v-model:description="task.description"
|
||||
v-model:process-type="processType"
|
||||
v-model:structured-options="structuredOptions"
|
||||
v-model:unstructured-options="unstructuredOptions"
|
||||
:process-type-locked="isRegeneration"
|
||||
/>
|
||||
|
||||
<ModelSelectionStep
|
||||
@@ -974,8 +981,8 @@ onMounted(() => {
|
||||
<el-button
|
||||
class="wizard-primary-action"
|
||||
type="primary"
|
||||
:loading="generation.status === 'running' || (currentStepId === 'upload' && (sourceUploading || previewBuilding))"
|
||||
:disabled="(currentStepId === 'generate' && generation.status === 'running') || previewBuilding || sourceUploading || (currentStepId === 'upload' && hasUnfinishedUploads)"
|
||||
:loading="modelSubmitLoading || generation.status === 'running' || (currentStepId === 'upload' && (sourceUploading || previewBuilding))"
|
||||
:disabled="hydrating || modelSubmitLoading || Boolean(initializationError) || (currentStepId === 'generate' && generation.status === 'running') || previewBuilding || sourceUploading || (currentStepId === 'upload' && hasUnfinishedUploads)"
|
||||
@click="handlePrimaryAction"
|
||||
>
|
||||
{{ primaryActionLabel }} <i class="fa" :class="primaryActionIcon" style="margin-left: 6px;" />
|
||||
|
||||
Reference in New Issue
Block a user