mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 11:04:27 +08:00
refactor(web): rag-pipeline evaluation
This commit is contained in:
parent
d1ca468c1e
commit
8b6b3cddea
@ -1,26 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { EvaluationResourceProps } from '../../types'
|
import type { EvaluationResourceProps } from '../../types'
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Button from '@/app/components/base/button'
|
|
||||||
import { useDocLink } from '@/context/i18n'
|
import { useDocLink } from '@/context/i18n'
|
||||||
import { useAvailableEvaluationMetrics } from '@/service/use-evaluation'
|
import { useEvaluationStore } from '../../store'
|
||||||
import { getEvaluationMockConfig } from '../../mock'
|
|
||||||
import { isEvaluationRunnable, useEvaluationResource, useEvaluationStore } from '../../store'
|
|
||||||
import HistoryTab from '../batch-test-panel/history-tab'
|
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 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 PipelineResultsPanel from '../pipeline/pipeline-results-panel'
|
||||||
import SectionHeader, { InlineSectionHeader } from '../section-header'
|
import SectionHeader, { InlineSectionHeader } from '../section-header'
|
||||||
|
|
||||||
const PIPELINE_INPUT_FIELDS = [
|
|
||||||
{ name: 'query', type: 'string' },
|
|
||||||
{ name: 'Expect Results', type: 'string' },
|
|
||||||
]
|
|
||||||
|
|
||||||
const PipelineEvaluation = ({
|
const PipelineEvaluation = ({
|
||||||
resourceType,
|
resourceType,
|
||||||
resourceId,
|
resourceId,
|
||||||
@ -29,49 +20,11 @@ const PipelineEvaluation = ({
|
|||||||
const { t: tCommon } = useTranslation('common')
|
const { t: tCommon } = useTranslation('common')
|
||||||
const docLink = useDocLink()
|
const docLink = useDocLink()
|
||||||
const ensureResource = useEvaluationStore(state => state.ensureResource)
|
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(() => {
|
useEffect(() => {
|
||||||
ensureResource(resourceType, resourceId)
|
ensureResource(resourceType, resourceId)
|
||||||
}, [ensureResource, resourceId, resourceType])
|
}, [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 (
|
return (
|
||||||
<div className="flex h-full min-h-0 flex-col bg-background-default xl:flex-row">
|
<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">
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<PipelineMetricsSection
|
||||||
<InlineSectionHeader title={t('metrics.title')} tooltip={t('metrics.description')} />
|
resourceType={resourceType}
|
||||||
<div className="mt-1 space-y-0.5">
|
resourceId={resourceId}
|
||||||
{availableBuiltinMetrics.map((metric) => {
|
/>
|
||||||
const selectedMetric = builtinMetricMap.get(metric.id)
|
|
||||||
|
|
||||||
return (
|
<PipelineBatchActions
|
||||||
<PipelineMetricItem
|
resourceType={resourceType}
|
||||||
key={metric.id}
|
resourceId={resourceId}
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
</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
|
||||||
Loading…
Reference in New Issue
Block a user