diff --git a/web/app/components/evaluation/components/config-actions.tsx b/web/app/components/evaluation/components/config-actions.tsx
new file mode 100644
index 0000000000..8c1e769045
--- /dev/null
+++ b/web/app/components/evaluation/components/config-actions.tsx
@@ -0,0 +1,80 @@
+'use client'
+
+import type { EvaluationResourceProps } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
+import { useTranslation } from 'react-i18next'
+import { useSaveEvaluationConfigMutation } from '@/service/use-evaluation'
+import {
+ isEvaluationRunnable,
+ useEvaluationResource,
+ useEvaluationStore,
+ useIsEvaluationConfigDirty,
+} from '../store'
+import { buildEvaluationConfigPayload } from '../store-utils'
+
+const EvaluationConfigActions = ({
+ resourceType,
+ resourceId,
+}: EvaluationResourceProps) => {
+ const { t } = useTranslation('evaluation')
+ const { t: tCommon } = useTranslation('common')
+ const resource = useEvaluationResource(resourceType, resourceId)
+ const isDirty = useIsEvaluationConfigDirty(resourceType, resourceId)
+ const resetResourceConfig = useEvaluationStore(state => state.resetResourceConfig)
+ const markResourceConfigSaved = useEvaluationStore(state => state.markResourceConfigSaved)
+ const saveConfigMutation = useSaveEvaluationConfigMutation()
+ const isRunnable = isEvaluationRunnable(resource)
+
+ const handleSave = () => {
+ if (!isRunnable) {
+ toast.warning(t('batch.validation'))
+ return
+ }
+
+ const body = buildEvaluationConfigPayload(resource, resourceType)
+
+ if (!body) {
+ toast.warning(t('batch.validation'))
+ return
+ }
+
+ saveConfigMutation.mutate({
+ params: {
+ targetType: resourceType,
+ targetId: resourceId,
+ },
+ body,
+ }, {
+ onSuccess: () => {
+ markResourceConfigSaved(resourceType, resourceId)
+ toast.success(tCommon('api.saved'))
+ },
+ onError: () => {
+ toast.error(t('config.saveFailed'))
+ },
+ })
+ }
+
+ return (
+
+
+
+
+ )
+}
+
+export default EvaluationConfigActions
diff --git a/web/app/components/evaluation/components/layout/non-pipeline-evaluation.tsx b/web/app/components/evaluation/components/layout/non-pipeline-evaluation.tsx
index 5d47a754ff..5c6acf289d 100644
--- a/web/app/components/evaluation/components/layout/non-pipeline-evaluation.tsx
+++ b/web/app/components/evaluation/components/layout/non-pipeline-evaluation.tsx
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'
import { useDocLink } from '@/context/i18n'
import BatchTestPanel from '../batch-test-panel'
import ConditionsSection from '../conditions-section'
+import EvaluationConfigActions from '../config-actions'
import JudgeModelSelector from '../judge-model-selector'
import MetricSection from '../metric-section'
import SectionHeader, { InlineSectionHeader } from '../section-header'
@@ -38,6 +39,7 @@ const NonPipelineEvaluation = ({
>
)}
descriptionClassName="max-w-[700px]"
+ action={
}
/>
diff --git a/web/app/components/evaluation/components/layout/pipeline-evaluation.tsx b/web/app/components/evaluation/components/layout/pipeline-evaluation.tsx
index 9fb6c70a90..102360f526 100644
--- a/web/app/components/evaluation/components/layout/pipeline-evaluation.tsx
+++ b/web/app/components/evaluation/components/layout/pipeline-evaluation.tsx
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'
import { useDocLink } from '@/context/i18n'
import { useEvaluationStore } from '../../store'
import HistoryTab from '../batch-test-panel/history-tab'
+import EvaluationConfigActions from '../config-actions'
import JudgeModelSelector from '../judge-model-selector'
import PipelineBatchActions from '../pipeline/pipeline-batch-actions'
import PipelineMetricsSection from '../pipeline/pipeline-metrics-section'
@@ -45,6 +46,7 @@ const PipelineEvaluation = ({
>
)}
+ action={}
/>
diff --git a/web/app/components/evaluation/store.ts b/web/app/components/evaluation/store.ts
index 4e2df1bb00..9f94e594b0 100644
--- a/web/app/components/evaluation/store.ts
+++ b/web/app/components/evaluation/store.ts
@@ -4,6 +4,7 @@ import type {
EvaluationResourceType,
} from './types'
import type { EvaluationConfig, NodeInfo } from '@/types/evaluation'
+import { isEqual } from 'es-toolkit/predicate'
import { create } from 'zustand'
import { getEvaluationMockConfig } from './mock'
import {
@@ -28,8 +29,11 @@ import { buildConditionMetricOptions } from './utils'
type EvaluationStore = {
resources: Record