mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 11:04:27 +08:00
feat(web): add condition
This commit is contained in:
parent
8dc6d736ee
commit
5efe8b8bd7
@ -1,5 +1,6 @@
|
|||||||
import { act, fireEvent, render, screen } from '@testing-library/react'
|
import { act, fireEvent, render, screen } from '@testing-library/react'
|
||||||
import Evaluation from '..'
|
import Evaluation from '..'
|
||||||
|
import ConditionsSection from '../components/conditions-section'
|
||||||
import { useEvaluationStore } from '../store'
|
import { useEvaluationStore } from '../store'
|
||||||
|
|
||||||
const mockUseAvailableEvaluationMetrics = vi.hoisted(() => vi.fn())
|
const mockUseAvailableEvaluationMetrics = vi.hoisted(() => vi.fn())
|
||||||
@ -150,6 +151,50 @@ describe('Evaluation', () => {
|
|||||||
expect(screen.queryByPlaceholderText('evaluation.conditions.valuePlaceholder')).not.toBeInTheDocument()
|
expect(screen.queryByPlaceholderText('evaluation.conditions.valuePlaceholder')).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should add a condition from grouped metric dropdown items', () => {
|
||||||
|
const resourceType = 'apps'
|
||||||
|
const resourceId = 'app-conditions-dropdown'
|
||||||
|
const store = useEvaluationStore.getState()
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
store.ensureResource(resourceType, resourceId)
|
||||||
|
store.setJudgeModel(resourceType, resourceId, 'openai::gpt-4o-mini')
|
||||||
|
store.addBuiltinMetric(resourceType, resourceId, 'faithfulness', [
|
||||||
|
{ node_id: 'node-faithfulness', title: 'Retriever Node', type: 'retriever' },
|
||||||
|
])
|
||||||
|
store.addCustomMetric(resourceType, resourceId)
|
||||||
|
|
||||||
|
const customMetric = useEvaluationStore.getState().resources['apps:app-conditions-dropdown'].metrics.find(metric => metric.kind === 'custom-workflow')!
|
||||||
|
store.setCustomMetricWorkflow(resourceType, resourceId, customMetric.id, {
|
||||||
|
workflowId: 'workflow-1',
|
||||||
|
workflowAppId: 'workflow-app-1',
|
||||||
|
workflowName: 'Review Workflow',
|
||||||
|
})
|
||||||
|
store.syncCustomMetricOutputs(resourceType, resourceId, customMetric.id, [{
|
||||||
|
id: 'reason',
|
||||||
|
valueType: 'string',
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
|
render(<ConditionsSection resourceType={resourceType} resourceId={resourceId} />)
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('combobox', { name: 'evaluation.conditions.addCondition' }))
|
||||||
|
|
||||||
|
expect(screen.getByText('Faithfulness')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('Review Workflow')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('Retriever Node')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('reason')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('evaluation.conditions.valueTypes.number')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('evaluation.conditions.valueTypes.string')).toBeInTheDocument()
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('option', { name: /reason/i }))
|
||||||
|
|
||||||
|
const condition = useEvaluationStore.getState().resources['apps:app-conditions-dropdown'].judgmentConfig.conditions[0]
|
||||||
|
|
||||||
|
expect(condition.variableSelector).toEqual(['workflow-1', 'reason'])
|
||||||
|
expect(screen.getAllByText('Review Workflow').length).toBeGreaterThan(0)
|
||||||
|
})
|
||||||
|
|
||||||
it('should render the metric no-node empty state', () => {
|
it('should render the metric no-node empty state', () => {
|
||||||
mockUseAvailableEvaluationMetrics.mockReturnValue({
|
mockUseAvailableEvaluationMetrics.mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@ -132,6 +132,35 @@ describe('evaluation store', () => {
|
|||||||
expect(getAllowedOperators(state.metrics, condition.variableSelector)).toEqual(['=', '≠', '>', '<', '≥', '≤', 'is null', 'is not null'])
|
expect(getAllowedOperators(state.metrics, condition.variableSelector)).toEqual(['=', '≠', '>', '<', '≥', '≤', 'is null', 'is not null'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should add a condition from the selected custom metric output', () => {
|
||||||
|
const resourceType = 'apps'
|
||||||
|
const resourceId = 'app-condition-selector'
|
||||||
|
const store = useEvaluationStore.getState()
|
||||||
|
const config = getEvaluationMockConfig(resourceType)
|
||||||
|
|
||||||
|
store.ensureResource(resourceType, resourceId)
|
||||||
|
store.addCustomMetric(resourceType, resourceId)
|
||||||
|
|
||||||
|
const customMetric = useEvaluationStore.getState().resources['apps:app-condition-selector'].metrics.find(metric => metric.kind === 'custom-workflow')!
|
||||||
|
store.setCustomMetricWorkflow(resourceType, resourceId, customMetric.id, {
|
||||||
|
workflowId: config.workflowOptions[0].id,
|
||||||
|
workflowAppId: 'custom-workflow-app-id',
|
||||||
|
workflowName: config.workflowOptions[0].label,
|
||||||
|
})
|
||||||
|
store.syncCustomMetricOutputs(resourceType, resourceId, customMetric.id, [{
|
||||||
|
id: 'reason',
|
||||||
|
valueType: 'string',
|
||||||
|
}])
|
||||||
|
|
||||||
|
store.addCondition(resourceType, resourceId, [config.workflowOptions[0].id, 'reason'])
|
||||||
|
|
||||||
|
const condition = useEvaluationStore.getState().resources['apps:app-condition-selector'].judgmentConfig.conditions[0]
|
||||||
|
|
||||||
|
expect(condition.variableSelector).toEqual([config.workflowOptions[0].id, 'reason'])
|
||||||
|
expect(condition.comparisonOperator).toBe('contains')
|
||||||
|
expect(condition.value).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
it('should clear values for operators without values', () => {
|
it('should clear values for operators without values', () => {
|
||||||
const resourceType = 'apps'
|
const resourceType = 'apps'
|
||||||
const resourceId = 'app-3'
|
const resourceId = 'app-3'
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import type { ConditionMetricOptionGroup, EvaluationResourceProps } from '../../types'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectGroupLabel,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
} from '@/app/components/base/ui/select'
|
||||||
|
import { cn } from '@/utils/classnames'
|
||||||
|
import { useEvaluationStore } from '../../store'
|
||||||
|
import { getConditionMetricValueTypeTranslationKey } from '../../utils'
|
||||||
|
|
||||||
|
type AddConditionSelectProps = EvaluationResourceProps & {
|
||||||
|
metricOptionGroups: ConditionMetricOptionGroup[]
|
||||||
|
disabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddConditionSelect = ({
|
||||||
|
resourceType,
|
||||||
|
resourceId,
|
||||||
|
metricOptionGroups,
|
||||||
|
disabled,
|
||||||
|
}: AddConditionSelectProps) => {
|
||||||
|
const { t } = useTranslation('evaluation')
|
||||||
|
const addCondition = useEvaluationStore(state => state.addCondition)
|
||||||
|
const [selectKey, setSelectKey] = useState(0)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select key={selectKey}>
|
||||||
|
<SelectTrigger
|
||||||
|
aria-label={t('conditions.addCondition')}
|
||||||
|
className={cn(
|
||||||
|
'inline-flex w-auto min-w-0 border-none bg-transparent px-0 py-0 text-text-accent shadow-none hover:bg-transparent focus-visible:bg-transparent',
|
||||||
|
disabled && 'cursor-not-allowed text-components-button-secondary-accent-text-disabled',
|
||||||
|
)}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<span aria-hidden="true" className="i-ri-add-line h-4 w-4" />
|
||||||
|
{t('conditions.addCondition')}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent placement="bottom-start" popupClassName="w-[320px]">
|
||||||
|
{metricOptionGroups.map(group => (
|
||||||
|
<SelectGroup key={group.label}>
|
||||||
|
<SelectGroupLabel className="px-3 pt-2 pb-1 system-xs-medium-uppercase text-text-tertiary">{group.label}</SelectGroupLabel>
|
||||||
|
{group.options.map(option => (
|
||||||
|
<SelectItem
|
||||||
|
key={option.id}
|
||||||
|
value={option.id}
|
||||||
|
className="h-auto gap-0 px-3 py-2"
|
||||||
|
onClick={() => {
|
||||||
|
addCondition(resourceType, resourceId, option.variableSelector)
|
||||||
|
setSelectKey(current => current + 1)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||||
|
<span className="truncate system-sm-medium text-text-secondary">{option.itemLabel}</span>
|
||||||
|
<span className="ml-auto shrink-0 system-xs-medium text-text-tertiary">
|
||||||
|
{t(getConditionMetricValueTypeTranslationKey(option.valueType))}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddConditionSelect
|
||||||
@ -24,6 +24,8 @@ import { getAllowedOperators, requiresConditionValue, useEvaluationResource, use
|
|||||||
import {
|
import {
|
||||||
buildConditionMetricOptions,
|
buildConditionMetricOptions,
|
||||||
getComparisonOperatorLabel,
|
getComparisonOperatorLabel,
|
||||||
|
getConditionMetricValueTypeTranslationKey,
|
||||||
|
groupConditionMetricOptions,
|
||||||
isSelectorEqual,
|
isSelectorEqual,
|
||||||
serializeVariableSelector,
|
serializeVariableSelector,
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
@ -75,9 +77,9 @@ const ConditionMetricLabel = ({
|
|||||||
<div className="flex min-w-0 items-center gap-2 px-1">
|
<div className="flex min-w-0 items-center gap-2 px-1">
|
||||||
<div className="inline-flex h-6 min-w-0 items-center gap-1 rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pr-1.5 pl-[5px] shadow-xs">
|
<div className="inline-flex h-6 min-w-0 items-center gap-1 rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pr-1.5 pl-[5px] shadow-xs">
|
||||||
<span className={cn(getMetricValueTypeIconClassName(metric.valueType), 'h-3 w-3 shrink-0 text-text-secondary')} />
|
<span className={cn(getMetricValueTypeIconClassName(metric.valueType), 'h-3 w-3 shrink-0 text-text-secondary')} />
|
||||||
<span className="truncate system-xs-medium text-text-secondary">{metric.label}</span>
|
<span className="truncate system-xs-medium text-text-secondary">{metric.itemLabel}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="shrink-0 system-xs-regular text-text-tertiary">{metric.group}</span>
|
<span className="shrink-0 system-xs-regular text-text-tertiary">{metric.groupLabel}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -88,11 +90,9 @@ const ConditionMetricSelect = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
onChange,
|
onChange,
|
||||||
}: ConditionMetricSelectProps) => {
|
}: ConditionMetricSelectProps) => {
|
||||||
|
const { t } = useTranslation('evaluation')
|
||||||
const groupedMetricOptions = useMemo(() => {
|
const groupedMetricOptions = useMemo(() => {
|
||||||
return Object.entries(metricOptions.reduce<Record<string, ConditionMetricOption[]>>((acc, option) => {
|
return groupConditionMetricOptions(metricOptions)
|
||||||
acc[option.group] = [...(acc[option.group] ?? []), option]
|
|
||||||
return acc
|
|
||||||
}, {}))
|
|
||||||
}, [metricOptions])
|
}, [metricOptions])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -108,15 +108,17 @@ const ConditionMetricSelect = ({
|
|||||||
<ConditionMetricLabel metric={metric} placeholder={placeholder} />
|
<ConditionMetricLabel metric={metric} placeholder={placeholder} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent popupClassName="w-[360px]">
|
<SelectContent popupClassName="w-[360px]">
|
||||||
{groupedMetricOptions.map(([groupName, options]) => (
|
{groupedMetricOptions.map(group => (
|
||||||
<SelectGroup key={groupName}>
|
<SelectGroup key={group.label}>
|
||||||
<SelectGroupLabel className="px-3 pt-2 pb-1 system-xs-medium-uppercase text-text-tertiary">{groupName}</SelectGroupLabel>
|
<SelectGroupLabel className="px-3 pt-2 pb-1 system-xs-medium-uppercase text-text-tertiary">{group.label}</SelectGroupLabel>
|
||||||
{options.map(option => (
|
{group.options.map(option => (
|
||||||
<SelectItem key={option.id} value={serializeVariableSelector(option.variableSelector)}>
|
<SelectItem key={option.id} value={serializeVariableSelector(option.variableSelector)}>
|
||||||
<div className="flex min-w-0 items-center gap-2">
|
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||||
<span className={cn(getMetricValueTypeIconClassName(option.valueType), 'h-3.5 w-3.5 shrink-0 text-text-tertiary')} />
|
<span className={cn(getMetricValueTypeIconClassName(option.valueType), 'h-3.5 w-3.5 shrink-0 text-text-tertiary')} />
|
||||||
<span className="truncate">{option.label}</span>
|
<span className="truncate">{option.itemLabel}</span>
|
||||||
<span className="shrink-0 text-text-quaternary">{option.description}</span>
|
<span className="ml-auto shrink-0 system-xs-medium text-text-quaternary">
|
||||||
|
{t(getConditionMetricValueTypeTranslationKey(option.valueType))}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
import type { EvaluationResourceProps } from '../../types'
|
import type { EvaluationResourceProps } from '../../types'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { cn } from '@/utils/classnames'
|
import { useEvaluationResource } from '../../store'
|
||||||
import { useEvaluationResource, useEvaluationStore } from '../../store'
|
import { buildConditionMetricOptions, groupConditionMetricOptions } from '../../utils'
|
||||||
import { buildConditionMetricOptions } from '../../utils'
|
|
||||||
import { InlineSectionHeader } from '../section-header'
|
import { InlineSectionHeader } from '../section-header'
|
||||||
|
import AddConditionSelect from './add-condition-select'
|
||||||
import ConditionGroup from './condition-group'
|
import ConditionGroup from './condition-group'
|
||||||
|
|
||||||
const ConditionsSection = ({
|
const ConditionsSection = ({
|
||||||
@ -15,8 +15,8 @@ const ConditionsSection = ({
|
|||||||
}: EvaluationResourceProps) => {
|
}: EvaluationResourceProps) => {
|
||||||
const { t } = useTranslation('evaluation')
|
const { t } = useTranslation('evaluation')
|
||||||
const resource = useEvaluationResource(resourceType, resourceId)
|
const resource = useEvaluationResource(resourceType, resourceId)
|
||||||
const addCondition = useEvaluationStore(state => state.addCondition)
|
|
||||||
const conditionMetricOptions = useMemo(() => buildConditionMetricOptions(resource.metrics), [resource.metrics])
|
const conditionMetricOptions = useMemo(() => buildConditionMetricOptions(resource.metrics), [resource.metrics])
|
||||||
|
const groupedConditionMetricOptions = useMemo(() => groupConditionMetricOptions(conditionMetricOptions), [conditionMetricOptions])
|
||||||
const canAddCondition = conditionMetricOptions.length > 0
|
const canAddCondition = conditionMetricOptions.length > 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -37,18 +37,12 @@ const ConditionsSection = ({
|
|||||||
resourceId={resourceId}
|
resourceId={resourceId}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<button
|
<AddConditionSelect
|
||||||
type="button"
|
resourceType={resourceType}
|
||||||
className={cn(
|
resourceId={resourceId}
|
||||||
'inline-flex items-center system-sm-medium text-text-accent',
|
metricOptionGroups={groupedConditionMetricOptions}
|
||||||
!canAddCondition && 'cursor-not-allowed text-components-button-secondary-accent-text-disabled',
|
|
||||||
)}
|
|
||||||
disabled={!canAddCondition}
|
disabled={!canAddCondition}
|
||||||
onClick={() => addCondition(resourceType, resourceId)}
|
/>
|
||||||
>
|
|
||||||
<span aria-hidden="true" className="mr-1 i-ri-add-line h-4 w-4" />
|
|
||||||
{t('conditions.addCondition')}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -330,8 +330,17 @@ export function createCustomMetric(): EvaluationMetric {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buildConditionItem = (metrics: EvaluationMetric[]): JudgmentConditionItem => {
|
export const buildConditionItem = (
|
||||||
const metricOption = buildConditionMetricOptions(metrics)[0]
|
metrics: EvaluationMetric[],
|
||||||
|
variableSelector?: [string, string] | null,
|
||||||
|
): JudgmentConditionItem => {
|
||||||
|
const metricOptions = buildConditionMetricOptions(metrics)
|
||||||
|
const metricOption = variableSelector
|
||||||
|
? metricOptions.find(option =>
|
||||||
|
option.variableSelector[0] === variableSelector[0]
|
||||||
|
&& option.variableSelector[1] === variableSelector[1],
|
||||||
|
) ?? metricOptions[0]
|
||||||
|
: metricOptions[0]
|
||||||
const comparisonOperator = metricOption ? getDefaultComparisonOperator(metricOption.valueType) : 'is'
|
const comparisonOperator = metricOption ? getDefaultComparisonOperator(metricOption.valueType) : 'is'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -61,7 +61,11 @@ type EvaluationStore = {
|
|||||||
patch: { inputVariableId?: string | null, outputVariableId?: string | null },
|
patch: { inputVariableId?: string | null, outputVariableId?: string | null },
|
||||||
) => void
|
) => void
|
||||||
setConditionLogicalOperator: (resourceType: EvaluationResourceType, resourceId: string, logicalOperator: 'and' | 'or') => void
|
setConditionLogicalOperator: (resourceType: EvaluationResourceType, resourceId: string, logicalOperator: 'and' | 'or') => void
|
||||||
addCondition: (resourceType: EvaluationResourceType, resourceId: string) => void
|
addCondition: (
|
||||||
|
resourceType: EvaluationResourceType,
|
||||||
|
resourceId: string,
|
||||||
|
variableSelector?: [string, string] | null,
|
||||||
|
) => void
|
||||||
removeCondition: (resourceType: EvaluationResourceType, resourceId: string, conditionId: string) => void
|
removeCondition: (resourceType: EvaluationResourceType, resourceId: string, conditionId: string) => void
|
||||||
updateConditionMetric: (resourceType: EvaluationResourceType, resourceId: string, conditionId: string, variableSelector: [string, string]) => void
|
updateConditionMetric: (resourceType: EvaluationResourceType, resourceId: string, conditionId: string, variableSelector: [string, string]) => void
|
||||||
updateConditionOperator: (resourceType: EvaluationResourceType, resourceId: string, conditionId: string, operator: ComparisonOperator) => void
|
updateConditionOperator: (resourceType: EvaluationResourceType, resourceId: string, conditionId: string, operator: ComparisonOperator) => void
|
||||||
@ -270,13 +274,13 @@ export const useEvaluationStore = create<EvaluationStore>((set, get) => ({
|
|||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
addCondition: (resourceType, resourceId) => {
|
addCondition: (resourceType, resourceId, variableSelector) => {
|
||||||
set(state => ({
|
set(state => ({
|
||||||
resources: updateResourceState(state.resources, resourceType, resourceId, resource => ({
|
resources: updateResourceState(state.resources, resourceType, resourceId, resource => ({
|
||||||
...resource,
|
...resource,
|
||||||
judgmentConfig: {
|
judgmentConfig: {
|
||||||
...resource.judgmentConfig,
|
...resource.judgmentConfig,
|
||||||
conditions: [...resource.judgmentConfig.conditions, buildConditionItem(resource.metrics)],
|
conditions: [...resource.judgmentConfig.conditions, buildConditionItem(resource.metrics, variableSelector)],
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -112,13 +112,17 @@ export type JudgmentConfig = {
|
|||||||
|
|
||||||
export type ConditionMetricOption = {
|
export type ConditionMetricOption = {
|
||||||
id: string
|
id: string
|
||||||
group: string
|
groupLabel: string
|
||||||
label: string
|
itemLabel: string
|
||||||
description: string
|
|
||||||
valueType: ConditionMetricValueType
|
valueType: ConditionMetricValueType
|
||||||
variableSelector: [string, string]
|
variableSelector: [string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ConditionMetricOptionGroup = {
|
||||||
|
label: string
|
||||||
|
options: ConditionMetricOption[]
|
||||||
|
}
|
||||||
|
|
||||||
export type BatchTestRecord = {
|
export type BatchTestRecord = {
|
||||||
id: string
|
id: string
|
||||||
fileName: string
|
fileName: string
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { TFunction } from 'i18next'
|
|||||||
import type {
|
import type {
|
||||||
ComparisonOperator,
|
ComparisonOperator,
|
||||||
ConditionMetricOption,
|
ConditionMetricOption,
|
||||||
|
ConditionMetricOptionGroup,
|
||||||
ConditionMetricValueType,
|
ConditionMetricValueType,
|
||||||
EvaluationMetric,
|
EvaluationMetric,
|
||||||
} from './types'
|
} from './types'
|
||||||
@ -69,9 +70,8 @@ export const buildConditionMetricOptions = (metrics: EvaluationMetric[]): Condit
|
|||||||
return (metric.nodeInfoList ?? []).map((nodeInfo) => {
|
return (metric.nodeInfoList ?? []).map((nodeInfo) => {
|
||||||
return {
|
return {
|
||||||
id: `${nodeInfo.node_id}:${metric.optionId}`,
|
id: `${nodeInfo.node_id}:${metric.optionId}`,
|
||||||
group: nodeInfo.title,
|
groupLabel: metric.label,
|
||||||
label: metric.label,
|
itemLabel: nodeInfo.title || nodeInfo.node_id,
|
||||||
description: nodeInfo.type,
|
|
||||||
valueType: metric.valueType,
|
valueType: metric.valueType,
|
||||||
variableSelector: [nodeInfo.node_id, metric.optionId] as [string, string],
|
variableSelector: [nodeInfo.node_id, metric.optionId] as [string, string],
|
||||||
}
|
}
|
||||||
@ -86,9 +86,8 @@ export const buildConditionMetricOptions = (metrics: EvaluationMetric[]): Condit
|
|||||||
return customConfig.outputs.map((output) => {
|
return customConfig.outputs.map((output) => {
|
||||||
return {
|
return {
|
||||||
id: `${customConfig.workflowId}:${output.id}`,
|
id: `${customConfig.workflowId}:${output.id}`,
|
||||||
group: customConfig.workflowName ?? metric.label,
|
groupLabel: customConfig.workflowName ?? metric.label,
|
||||||
label: output.id,
|
itemLabel: output.id,
|
||||||
description: customConfig.workflowName ?? metric.label,
|
|
||||||
valueType: getMetricValueType(output.valueType),
|
valueType: getMetricValueType(output.valueType),
|
||||||
variableSelector: [customConfig.workflowId, output.id] as [string, string],
|
variableSelector: [customConfig.workflowId, output.id] as [string, string],
|
||||||
}
|
}
|
||||||
@ -96,6 +95,30 @@ export const buildConditionMetricOptions = (metrics: EvaluationMetric[]): Condit
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const groupConditionMetricOptions = (metricOptions: ConditionMetricOption[]): ConditionMetricOptionGroup[] => {
|
||||||
|
const groups = metricOptions.reduce<Map<string, ConditionMetricOption[]>>((acc, option) => {
|
||||||
|
acc.set(option.groupLabel, [...(acc.get(option.groupLabel) ?? []), option])
|
||||||
|
return acc
|
||||||
|
}, new Map())
|
||||||
|
|
||||||
|
return Array.from(groups.entries()).map(([label, options]) => ({
|
||||||
|
label,
|
||||||
|
options,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditionMetricValueTypeTranslationKeys = {
|
||||||
|
string: 'conditions.valueTypes.string',
|
||||||
|
number: 'conditions.valueTypes.number',
|
||||||
|
boolean: 'conditions.valueTypes.boolean',
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const getConditionMetricValueTypeTranslationKey = (
|
||||||
|
valueType: ConditionMetricValueType,
|
||||||
|
) => {
|
||||||
|
return conditionMetricValueTypeTranslationKeys[valueType]
|
||||||
|
}
|
||||||
|
|
||||||
export const serializeVariableSelector = (value: [string, string] | null | undefined) => {
|
export const serializeVariableSelector = (value: [string, string] | null | undefined) => {
|
||||||
return value ? JSON.stringify(value) : ''
|
return value ? JSON.stringify(value) : ''
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,9 @@
|
|||||||
"conditions.selectValue": "Choose a value",
|
"conditions.selectValue": "Choose a value",
|
||||||
"conditions.title": "Judgment Conditions",
|
"conditions.title": "Judgment Conditions",
|
||||||
"conditions.valuePlaceholder": "Enter a value",
|
"conditions.valuePlaceholder": "Enter a value",
|
||||||
|
"conditions.valueTypes.boolean": "Boolean",
|
||||||
|
"conditions.valueTypes.number": "Number",
|
||||||
|
"conditions.valueTypes.string": "String",
|
||||||
"description": "Configure automated testing to grade your application's performance.",
|
"description": "Configure automated testing to grade your application's performance.",
|
||||||
"history.columns.creator": "Creator",
|
"history.columns.creator": "Creator",
|
||||||
"history.columns.status": "Status",
|
"history.columns.status": "Status",
|
||||||
|
|||||||
@ -43,6 +43,9 @@
|
|||||||
"conditions.selectValue": "选择值",
|
"conditions.selectValue": "选择值",
|
||||||
"conditions.title": "判定条件",
|
"conditions.title": "判定条件",
|
||||||
"conditions.valuePlaceholder": "输入值",
|
"conditions.valuePlaceholder": "输入值",
|
||||||
|
"conditions.valueTypes.boolean": "布尔",
|
||||||
|
"conditions.valueTypes.number": "数值",
|
||||||
|
"conditions.valueTypes.string": "文本",
|
||||||
"description": "配置自动化测试,对应用表现进行评分。",
|
"description": "配置自动化测试,对应用表现进行评分。",
|
||||||
"judgeModel.description": "选择用于打分和判定评测结果的模型。",
|
"judgeModel.description": "选择用于打分和判定评测结果的模型。",
|
||||||
"judgeModel.title": "判定模型",
|
"judgeModel.title": "判定模型",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user