mirror of
https://github.com/langgenius/dify.git
synced 2026-04-19 18:27:27 +08:00
refactor(web): rag-pipeline evaluation
This commit is contained in:
@ -1,26 +1,17 @@
|
||||
'use client'
|
||||
|
||||
import type { EvaluationResourceProps } from '../../types'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useAvailableEvaluationMetrics } from '@/service/use-evaluation'
|
||||
import { getEvaluationMockConfig } from '../../mock'
|
||||
import { isEvaluationRunnable, useEvaluationResource, useEvaluationStore } from '../../store'
|
||||
import { useEvaluationStore } from '../../store'
|
||||
import HistoryTab from '../batch-test-panel/history-tab'
|
||||
import UploadRunPopover from '../batch-test-panel/input-fields/upload-run-popover'
|
||||
import { useInputFieldsActions } from '../batch-test-panel/input-fields/use-input-fields-actions'
|
||||
import JudgeModelSelector from '../judge-model-selector'
|
||||
import PipelineMetricItem from '../pipeline/pipeline-metric-item'
|
||||
import PipelineBatchActions from '../pipeline/pipeline-batch-actions'
|
||||
import PipelineMetricsSection from '../pipeline/pipeline-metrics-section'
|
||||
import PipelineResultsPanel from '../pipeline/pipeline-results-panel'
|
||||
import SectionHeader, { InlineSectionHeader } from '../section-header'
|
||||
|
||||
const PIPELINE_INPUT_FIELDS = [
|
||||
{ name: 'query', type: 'string' },
|
||||
{ name: 'Expect Results', type: 'string' },
|
||||
]
|
||||
|
||||
const PipelineEvaluation = ({
|
||||
resourceType,
|
||||
resourceId,
|
||||
@ -29,49 +20,11 @@ const PipelineEvaluation = ({
|
||||
const { t: tCommon } = useTranslation('common')
|
||||
const docLink = useDocLink()
|
||||
const ensureResource = useEvaluationStore(state => state.ensureResource)
|
||||
const addBuiltinMetric = useEvaluationStore(state => state.addBuiltinMetric)
|
||||
const removeMetric = useEvaluationStore(state => state.removeMetric)
|
||||
const updateMetricThreshold = useEvaluationStore(state => state.updateMetricThreshold)
|
||||
const { data: availableMetricsData } = useAvailableEvaluationMetrics()
|
||||
const resource = useEvaluationResource(resourceType, resourceId)
|
||||
const config = getEvaluationMockConfig(resourceType)
|
||||
const builtinMetricMap = useMemo(() => new Map(
|
||||
resource.metrics
|
||||
.filter(metric => metric.kind === 'builtin')
|
||||
.map(metric => [metric.optionId, metric]),
|
||||
), [resource.metrics])
|
||||
const availableMetricIds = useMemo(() => new Set(availableMetricsData?.metrics ?? []), [availableMetricsData?.metrics])
|
||||
const availableBuiltinMetrics = useMemo(() => {
|
||||
return config.builtinMetrics.filter(metric =>
|
||||
availableMetricIds.has(metric.id) || builtinMetricMap.has(metric.id),
|
||||
)
|
||||
}, [availableMetricIds, builtinMetricMap, config.builtinMetrics])
|
||||
const isConfigReady = !!resource.judgeModelId && builtinMetricMap.size > 0
|
||||
const isRunnable = isEvaluationRunnable(resource)
|
||||
const actions = useInputFieldsActions({
|
||||
resourceType,
|
||||
resourceId,
|
||||
inputFields: PIPELINE_INPUT_FIELDS,
|
||||
isInputFieldsLoading: false,
|
||||
isPanelReady: isConfigReady,
|
||||
isRunnable,
|
||||
templateFileName: config.templateFileName,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
ensureResource(resourceType, resourceId)
|
||||
}, [ensureResource, resourceId, resourceType])
|
||||
|
||||
const handleToggleMetric = (metricId: string) => {
|
||||
const selectedMetric = builtinMetricMap.get(metricId)
|
||||
if (selectedMetric) {
|
||||
removeMetric(resourceType, resourceId, selectedMetric.id)
|
||||
return
|
||||
}
|
||||
|
||||
addBuiltinMetric(resourceType, resourceId, metricId)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col bg-background-default xl:flex-row">
|
||||
<div className="flex min-h-0 flex-col border-b border-divider-subtle bg-background-default xl:w-[450px] xl:shrink-0 xl:border-r xl:border-b-0">
|
||||
@ -108,57 +61,15 @@ const PipelineEvaluation = ({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<InlineSectionHeader title={t('metrics.title')} tooltip={t('metrics.description')} />
|
||||
<div className="mt-1 space-y-0.5">
|
||||
{availableBuiltinMetrics.map((metric) => {
|
||||
const selectedMetric = builtinMetricMap.get(metric.id)
|
||||
<PipelineMetricsSection
|
||||
resourceType={resourceType}
|
||||
resourceId={resourceId}
|
||||
/>
|
||||
|
||||
return (
|
||||
<PipelineMetricItem
|
||||
key={metric.id}
|
||||
metric={metric}
|
||||
selected={!!selectedMetric}
|
||||
threshold={selectedMetric?.threshold}
|
||||
disabledCondition
|
||||
onToggle={() => handleToggleMetric(metric.id)}
|
||||
onThresholdChange={value => updateMetricThreshold(resourceType, resourceId, selectedMetric?.id ?? '', value)}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="flex gap-2 pt-2">
|
||||
<Button
|
||||
className="flex-1 justify-center"
|
||||
variant="secondary"
|
||||
disabled={!actions.canDownloadTemplate}
|
||||
onClick={actions.handleDownloadTemplate}
|
||||
>
|
||||
<span aria-hidden="true" className="mr-1 i-ri-file-excel-2-line h-4 w-4" />
|
||||
{t('batch.downloadTemplate')}
|
||||
</Button>
|
||||
<div className="flex-1">
|
||||
<UploadRunPopover
|
||||
open={actions.isUploadPopoverOpen}
|
||||
onOpenChange={actions.setIsUploadPopoverOpen}
|
||||
triggerDisabled={actions.uploadButtonDisabled}
|
||||
triggerLabel={t('pipeline.uploadAndRun')}
|
||||
inputFields={PIPELINE_INPUT_FIELDS}
|
||||
currentFileName={actions.currentFileName}
|
||||
currentFileExtension={actions.currentFileExtension}
|
||||
currentFileSize={actions.currentFileSize}
|
||||
isFileUploading={actions.isFileUploading}
|
||||
isRunDisabled={actions.isRunDisabled}
|
||||
isRunning={actions.isRunning}
|
||||
onUploadFile={actions.handleUploadFile}
|
||||
onClearUploadedFile={actions.handleClearUploadedFile}
|
||||
onDownloadTemplate={actions.handleDownloadTemplate}
|
||||
onRun={actions.handleRun}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<PipelineBatchActions
|
||||
resourceType={resourceType}
|
||||
resourceId={resourceId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
'use client'
|
||||
|
||||
import type { EvaluationResourceProps } from '../../types'
|
||||
import type { InputField } from '../batch-test-panel/input-fields/input-fields-utils'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { getEvaluationMockConfig } from '../../mock'
|
||||
import { isEvaluationRunnable, useEvaluationResource } from '../../store'
|
||||
import UploadRunPopover from '../batch-test-panel/input-fields/upload-run-popover'
|
||||
import { useInputFieldsActions } from '../batch-test-panel/input-fields/use-input-fields-actions'
|
||||
|
||||
const PIPELINE_INPUT_FIELDS: InputField[] = [
|
||||
{ name: 'query', type: 'string' },
|
||||
{ name: 'Expect Results', type: 'string' },
|
||||
]
|
||||
|
||||
const PipelineBatchActions = ({
|
||||
resourceType,
|
||||
resourceId,
|
||||
}: EvaluationResourceProps) => {
|
||||
const { t } = useTranslation('evaluation')
|
||||
const resource = useEvaluationResource(resourceType, resourceId)
|
||||
const config = getEvaluationMockConfig(resourceType)
|
||||
const isConfigReady = !!resource.judgeModelId && resource.metrics.some(metric => metric.kind === 'builtin')
|
||||
const isRunnable = isEvaluationRunnable(resource)
|
||||
const actions = useInputFieldsActions({
|
||||
resourceType,
|
||||
resourceId,
|
||||
inputFields: PIPELINE_INPUT_FIELDS,
|
||||
isInputFieldsLoading: false,
|
||||
isPanelReady: isConfigReady,
|
||||
isRunnable,
|
||||
templateFileName: config.templateFileName,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 pt-2">
|
||||
<Button
|
||||
className="flex-1 justify-center"
|
||||
variant="secondary"
|
||||
disabled={!actions.canDownloadTemplate}
|
||||
onClick={actions.handleDownloadTemplate}
|
||||
>
|
||||
<span aria-hidden="true" className="mr-1 i-ri-file-excel-2-line h-4 w-4" />
|
||||
{t('batch.downloadTemplate')}
|
||||
</Button>
|
||||
<div className="flex-1">
|
||||
<UploadRunPopover
|
||||
open={actions.isUploadPopoverOpen}
|
||||
onOpenChange={actions.setIsUploadPopoverOpen}
|
||||
triggerDisabled={actions.uploadButtonDisabled}
|
||||
triggerLabel={t('pipeline.uploadAndRun')}
|
||||
inputFields={PIPELINE_INPUT_FIELDS}
|
||||
currentFileName={actions.currentFileName}
|
||||
currentFileExtension={actions.currentFileExtension}
|
||||
currentFileSize={actions.currentFileSize}
|
||||
isFileUploading={actions.isFileUploading}
|
||||
isRunDisabled={actions.isRunDisabled}
|
||||
isRunning={actions.isRunning}
|
||||
onUploadFile={actions.handleUploadFile}
|
||||
onClearUploadedFile={actions.handleClearUploadedFile}
|
||||
onDownloadTemplate={actions.handleDownloadTemplate}
|
||||
onRun={actions.handleRun}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PipelineBatchActions
|
||||
@ -0,0 +1,69 @@
|
||||
'use client'
|
||||
|
||||
import type { EvaluationResourceProps } from '../../types'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAvailableEvaluationMetrics } from '@/service/use-evaluation'
|
||||
import { getEvaluationMockConfig } from '../../mock'
|
||||
import { useEvaluationResource, useEvaluationStore } from '../../store'
|
||||
import { InlineSectionHeader } from '../section-header'
|
||||
import PipelineMetricItem from './pipeline-metric-item'
|
||||
|
||||
const PipelineMetricsSection = ({
|
||||
resourceType,
|
||||
resourceId,
|
||||
}: EvaluationResourceProps) => {
|
||||
const { t } = useTranslation('evaluation')
|
||||
const addBuiltinMetric = useEvaluationStore(state => state.addBuiltinMetric)
|
||||
const removeMetric = useEvaluationStore(state => state.removeMetric)
|
||||
const updateMetricThreshold = useEvaluationStore(state => state.updateMetricThreshold)
|
||||
const { data: availableMetricsData } = useAvailableEvaluationMetrics()
|
||||
const resource = useEvaluationResource(resourceType, resourceId)
|
||||
const config = getEvaluationMockConfig(resourceType)
|
||||
const builtinMetricMap = useMemo(() => new Map(
|
||||
resource.metrics
|
||||
.filter(metric => metric.kind === 'builtin')
|
||||
.map(metric => [metric.optionId, metric]),
|
||||
), [resource.metrics])
|
||||
const availableMetricIds = useMemo(() => new Set(availableMetricsData?.metrics ?? []), [availableMetricsData?.metrics])
|
||||
const availableBuiltinMetrics = useMemo(() => {
|
||||
return config.builtinMetrics.filter(metric =>
|
||||
availableMetricIds.has(metric.id) || builtinMetricMap.has(metric.id),
|
||||
)
|
||||
}, [availableMetricIds, builtinMetricMap, config.builtinMetrics])
|
||||
|
||||
const handleToggleMetric = (metricId: string) => {
|
||||
const selectedMetric = builtinMetricMap.get(metricId)
|
||||
if (selectedMetric) {
|
||||
removeMetric(resourceType, resourceId, selectedMetric.id)
|
||||
return
|
||||
}
|
||||
|
||||
addBuiltinMetric(resourceType, resourceId, metricId)
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<InlineSectionHeader title={t('metrics.title')} tooltip={t('metrics.description')} />
|
||||
<div className="mt-1 space-y-0.5">
|
||||
{availableBuiltinMetrics.map((metric) => {
|
||||
const selectedMetric = builtinMetricMap.get(metric.id)
|
||||
|
||||
return (
|
||||
<PipelineMetricItem
|
||||
key={metric.id}
|
||||
metric={metric}
|
||||
selected={!!selectedMetric}
|
||||
threshold={selectedMetric?.threshold}
|
||||
disabledCondition
|
||||
onToggle={() => handleToggleMetric(metric.id)}
|
||||
onThresholdChange={value => updateMetricThreshold(resourceType, resourceId, selectedMetric?.id ?? '', value)}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default PipelineMetricsSection
|
||||
Reference in New Issue
Block a user