mirror of
https://github.com/langgenius/dify.git
synced 2026-04-10 12:00:26 -04:00
feat(web): switch warining dialog
This commit is contained in:
@@ -16,6 +16,7 @@ const mockFetchInstalledAppList = vi.fn()
|
||||
const mockFetchAppDetailDirect = vi.fn()
|
||||
const mockToastError = vi.fn()
|
||||
const mockConvertWorkflowType = vi.fn()
|
||||
const mockRefetchEvaluationWorkflowAssociatedTargets = vi.fn()
|
||||
|
||||
const sectionProps = vi.hoisted(() => ({
|
||||
summary: null as null | Record<string, any>,
|
||||
@@ -27,6 +28,7 @@ const ahooksMocks = vi.hoisted(() => ({
|
||||
}))
|
||||
|
||||
let mockAppDetail: Record<string, any> | null = null
|
||||
let mockEvaluationWorkflowAssociatedTargets: Record<string, any> | undefined
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
@@ -96,6 +98,14 @@ vi.mock('@/service/use-apps', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-evaluation', () => ({
|
||||
useEvaluationWorkflowAssociatedTargets: () => ({
|
||||
data: mockEvaluationWorkflowAssociatedTargets,
|
||||
refetch: mockRefetchEvaluationWorkflowAssociatedTargets,
|
||||
isFetching: false,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
error: (...args: unknown[]) => mockToastError(...args),
|
||||
@@ -198,6 +208,11 @@ describe('AppPublisher', () => {
|
||||
access_mode: AccessMode.PUBLIC,
|
||||
})
|
||||
mockConvertWorkflowType.mockResolvedValue({})
|
||||
mockEvaluationWorkflowAssociatedTargets = { items: [] }
|
||||
mockRefetchEvaluationWorkflowAssociatedTargets.mockResolvedValue({
|
||||
data: { items: [] },
|
||||
isError: false,
|
||||
})
|
||||
mockOpenAsyncWindow.mockImplementation(async (resolver: () => Promise<string>) => {
|
||||
await resolver()
|
||||
})
|
||||
@@ -518,6 +533,106 @@ describe('AppPublisher', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should confirm before switching an evaluation workflow with associated targets to a standard workflow', async () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
type: AppTypeEnum.EVALUATION,
|
||||
}
|
||||
mockEvaluationWorkflowAssociatedTargets = {
|
||||
items: [
|
||||
{
|
||||
target_type: 'app',
|
||||
target_id: 'dependent-app-1',
|
||||
target_name: 'Dependent App',
|
||||
},
|
||||
{
|
||||
target_type: 'knowledge_base',
|
||||
target_id: 'knowledge-1',
|
||||
target_name: 'Knowledge Base',
|
||||
},
|
||||
],
|
||||
}
|
||||
mockRefetchEvaluationWorkflowAssociatedTargets.mockResolvedValueOnce({
|
||||
data: mockEvaluationWorkflowAssociatedTargets,
|
||||
isError: false,
|
||||
})
|
||||
|
||||
render(
|
||||
<AppPublisher
|
||||
publishedAt={Date.now()}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByText('common.publish'))
|
||||
fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockRefetchEvaluationWorkflowAssociatedTargets).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
expect(mockConvertWorkflowType).not.toHaveBeenCalled()
|
||||
expect(screen.getByText('Dependent App')).toBeInTheDocument()
|
||||
expect(screen.getByText('Knowledge Base')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'common.switchToStandardWorkflowConfirm.switch' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockConvertWorkflowType).toHaveBeenCalledWith({
|
||||
params: { appId: 'app-1' },
|
||||
query: { target_type: AppTypeEnum.WORKFLOW },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should switch an evaluation workflow directly when there are no associated targets', async () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
type: AppTypeEnum.EVALUATION,
|
||||
}
|
||||
|
||||
render(
|
||||
<AppPublisher
|
||||
publishedAt={Date.now()}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByText('common.publish'))
|
||||
fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockRefetchEvaluationWorkflowAssociatedTargets).toHaveBeenCalledTimes(1)
|
||||
expect(mockConvertWorkflowType).toHaveBeenCalledWith({
|
||||
params: { appId: 'app-1' },
|
||||
query: { target_type: AppTypeEnum.WORKFLOW },
|
||||
})
|
||||
})
|
||||
expect(screen.queryByText('common.switchToStandardWorkflowConfirm.title')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should block switching an evaluation workflow when associated targets fail to load', async () => {
|
||||
mockAppDetail = {
|
||||
...mockAppDetail,
|
||||
type: AppTypeEnum.EVALUATION,
|
||||
}
|
||||
mockRefetchEvaluationWorkflowAssociatedTargets.mockResolvedValueOnce({
|
||||
data: undefined,
|
||||
isError: true,
|
||||
})
|
||||
|
||||
render(
|
||||
<AppPublisher
|
||||
publishedAt={Date.now()}
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByText('common.publish'))
|
||||
fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastError).toHaveBeenCalledWith('common.switchToStandardWorkflowConfirm.loadFailed')
|
||||
})
|
||||
expect(mockConvertWorkflowType).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should block switching to evaluation workflow when restricted nodes exist', async () => {
|
||||
render(
|
||||
<AppPublisher
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
'use client'
|
||||
|
||||
import type { EvaluationWorkflowAssociatedTarget, EvaluationWorkflowAssociatedTargetType } from '@/types/evaluation'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogActions,
|
||||
AlertDialogCancelButton,
|
||||
AlertDialogConfirmButton,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogTitle,
|
||||
} from '@/app/components/base/ui/alert-dialog'
|
||||
import Link from '@/next/link'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type EvaluationWorkflowSwitchConfirmDialogProps = {
|
||||
open: boolean
|
||||
targets: EvaluationWorkflowAssociatedTarget[]
|
||||
loading?: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
onConfirm: () => void
|
||||
}
|
||||
|
||||
const TARGET_TYPE_META: Record<EvaluationWorkflowAssociatedTargetType, {
|
||||
icon: string
|
||||
iconClassName: string
|
||||
labelKey: I18nKeysWithPrefix<'workflow', 'common.switchToStandardWorkflowConfirm.targetTypes.'>
|
||||
href: (targetId: string) => string
|
||||
}> = {
|
||||
app: {
|
||||
icon: 'i-ri-flow-chart',
|
||||
iconClassName: 'bg-components-icon-bg-teal-soft text-util-colors-teal-teal-600',
|
||||
labelKey: 'common.switchToStandardWorkflowConfirm.targetTypes.app',
|
||||
href: targetId => `/app/${targetId}/workflow`,
|
||||
},
|
||||
snippets: {
|
||||
icon: 'i-ri-edit-2-line',
|
||||
iconClassName: 'bg-components-icon-bg-violet-soft text-util-colors-violet-violet-600',
|
||||
labelKey: 'common.switchToStandardWorkflowConfirm.targetTypes.snippets',
|
||||
href: targetId => `/snippets/${targetId}/orchestrate`,
|
||||
},
|
||||
knowledge_base: {
|
||||
icon: 'i-ri-book-2-line',
|
||||
iconClassName: 'bg-components-icon-bg-indigo-soft text-util-colors-blue-blue-600',
|
||||
labelKey: 'common.switchToStandardWorkflowConfirm.targetTypes.knowledge_base',
|
||||
href: targetId => `/datasets/${targetId}/documents`,
|
||||
},
|
||||
}
|
||||
|
||||
const getTargetMeta = (targetType: EvaluationWorkflowAssociatedTargetType) => {
|
||||
return TARGET_TYPE_META[targetType] ?? TARGET_TYPE_META.app
|
||||
}
|
||||
|
||||
const DependentTargetItem = ({
|
||||
target,
|
||||
}: {
|
||||
target: EvaluationWorkflowAssociatedTarget
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const meta = getTargetMeta(target.target_type)
|
||||
const targetName = target.target_name || target.target_id
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={meta.href(target.target_id)}
|
||||
className="group flex w-full items-center gap-3 rounded-lg bg-background-section p-2 hover:bg-background-section-burn"
|
||||
title={targetName}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'flex size-10 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-regular',
|
||||
meta.iconClassName,
|
||||
)}
|
||||
>
|
||||
<span className={cn(meta.icon, 'size-5')} />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-1 flex-col gap-1 py-px">
|
||||
<span className="truncate system-md-semibold text-text-secondary">
|
||||
{targetName}
|
||||
</span>
|
||||
<span className="system-2xs-medium-uppercase text-text-tertiary">
|
||||
{t(meta.labelKey, { ns: 'workflow' })}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="i-ri-arrow-right-up-line size-3.5 shrink-0 text-text-quaternary opacity-0 transition-opacity group-hover:opacity-100"
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const EvaluationWorkflowSwitchConfirmDialog = ({
|
||||
open,
|
||||
targets,
|
||||
loading = false,
|
||||
onOpenChange,
|
||||
onConfirm,
|
||||
}: EvaluationWorkflowSwitchConfirmDialogProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={onOpenChange}>
|
||||
<AlertDialogContent className="w-[480px]">
|
||||
<div className="flex flex-col gap-2 px-6 pt-6 pb-4">
|
||||
<AlertDialogTitle className="w-full title-2xl-semi-bold text-text-primary">
|
||||
{t('common.switchToStandardWorkflowConfirm.title', { ns: 'workflow' })}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription className="w-full system-md-regular text-text-secondary">
|
||||
<span className="block">
|
||||
{t('common.switchToStandardWorkflowConfirm.activeIn', { ns: 'workflow', count: targets.length })}
|
||||
</span>
|
||||
<span className="block">
|
||||
{t('common.switchToStandardWorkflowConfirm.description', { ns: 'workflow' })}
|
||||
</span>
|
||||
</AlertDialogDescription>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 px-6 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="shrink-0 system-xs-medium-uppercase text-text-quaternary">
|
||||
{t('common.switchToStandardWorkflowConfirm.dependentWorkflows', { ns: 'workflow' })}
|
||||
</span>
|
||||
<span className="h-px min-w-0 flex-1 bg-divider-subtle" />
|
||||
</div>
|
||||
<div className="flex max-h-[188px] flex-col gap-1 overflow-y-auto">
|
||||
{targets.map(target => (
|
||||
<DependentTargetItem
|
||||
key={`${target.target_type}:${target.target_id}`}
|
||||
target={target}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AlertDialogActions>
|
||||
<AlertDialogCancelButton disabled={loading}>
|
||||
{t('operation.cancel', { ns: 'common' })}
|
||||
</AlertDialogCancelButton>
|
||||
<AlertDialogConfirmButton
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{t('common.switchToStandardWorkflowConfirm.switch', { ns: 'workflow' })}
|
||||
</AlertDialogConfirmButton>
|
||||
</AlertDialogActions>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default EvaluationWorkflowSwitchConfirmDialog
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ModelAndParameter } from '../configuration/debug/types'
|
||||
import type { InputVar, Variable } from '@/app/components/workflow/types'
|
||||
import type { EvaluationWorkflowAssociatedTarget } from '@/types/evaluation'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import type { PublishWorkflowParams, WorkflowTypeConversionTarget } from '@/types/workflow'
|
||||
import { useKeyPress } from 'ahooks'
|
||||
@@ -28,11 +29,13 @@ import { useAppWhiteListSubjects, useGetUserCanAccessApp } from '@/service/acces
|
||||
import { fetchAppDetailDirect } from '@/service/apps'
|
||||
import { fetchInstalledAppList } from '@/service/explore'
|
||||
import { useConvertWorkflowTypeMutation } from '@/service/use-apps'
|
||||
import { useEvaluationWorkflowAssociatedTargets } from '@/service/use-evaluation'
|
||||
import { AppModeEnum, AppTypeEnum } from '@/types/app'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { toast } from '../../base/ui/toast'
|
||||
import { getKeyboardKeyCodeBySystem } from '../../workflow/utils'
|
||||
import AccessControl from '../app-access-control'
|
||||
import EvaluationWorkflowSwitchConfirmDialog from './evaluation-workflow-switch-confirm-dialog'
|
||||
import {
|
||||
PublisherAccessSection,
|
||||
PublisherActionsSection,
|
||||
@@ -122,6 +125,8 @@ const AppPublisher = ({
|
||||
const [published, setPublished] = useState(false)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [showAppAccessControl, setShowAppAccessControl] = useState(false)
|
||||
const [showEvaluationWorkflowSwitchConfirm, setShowEvaluationWorkflowSwitchConfirm] = useState(false)
|
||||
const [evaluationWorkflowSwitchTargets, setEvaluationWorkflowSwitchTargets] = useState<EvaluationWorkflowAssociatedTarget[]>([])
|
||||
|
||||
const [embeddingModalOpen, setEmbeddingModalOpen] = useState(false)
|
||||
|
||||
@@ -138,6 +143,10 @@ const AppPublisher = ({
|
||||
? WORKFLOW_TYPE_SWITCH_CONFIG[appDetail.type]
|
||||
: undefined
|
||||
const isEvaluationWorkflowType = appDetail?.type === AppTypeEnum.EVALUATION
|
||||
const {
|
||||
refetch: refetchEvaluationWorkflowAssociatedTargets,
|
||||
isFetching: isFetchingEvaluationWorkflowAssociatedTargets,
|
||||
} = useEvaluationWorkflowAssociatedTargets(appDetail?.id, { enabled: false })
|
||||
const workflowTypeSwitchDisabledReason = useMemo(() => {
|
||||
if (workflowTypeSwitchConfig?.targetType !== AppTypeEnum.EVALUATION)
|
||||
return undefined
|
||||
@@ -234,13 +243,9 @@ const AppPublisher = ({
|
||||
}
|
||||
}, [appDetail, setAppDetail])
|
||||
|
||||
const handleWorkflowTypeSwitch = useCallback(async () => {
|
||||
const performWorkflowTypeSwitch = useCallback(async () => {
|
||||
if (!appDetail?.id || !workflowTypeSwitchConfig)
|
||||
return
|
||||
if (workflowTypeSwitchDisabledReason) {
|
||||
toast.error(workflowTypeSwitchDisabledReason)
|
||||
return
|
||||
}
|
||||
return false
|
||||
|
||||
try {
|
||||
await convertWorkflowType({
|
||||
@@ -263,9 +268,57 @@ const AppPublisher = ({
|
||||
|
||||
if (publishedAt)
|
||||
setOpen(false)
|
||||
|
||||
setShowEvaluationWorkflowSwitchConfirm(false)
|
||||
setEvaluationWorkflowSwitchTargets([])
|
||||
return true
|
||||
}
|
||||
catch { }
|
||||
}, [appDetail?.id, convertWorkflowType, handlePublish, publishedAt, setAppDetail, workflowTypeSwitchConfig, workflowTypeSwitchDisabledReason])
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
}, [appDetail?.id, convertWorkflowType, handlePublish, publishedAt, setAppDetail, workflowTypeSwitchConfig])
|
||||
|
||||
const handleWorkflowTypeSwitch = useCallback(async () => {
|
||||
if (!appDetail?.id || !workflowTypeSwitchConfig)
|
||||
return
|
||||
if (workflowTypeSwitchDisabledReason) {
|
||||
toast.error(workflowTypeSwitchDisabledReason)
|
||||
return
|
||||
}
|
||||
|
||||
if (appDetail.type === AppTypeEnum.EVALUATION && workflowTypeSwitchConfig.targetType === AppTypeEnum.WORKFLOW) {
|
||||
const associatedTargetsResult = await refetchEvaluationWorkflowAssociatedTargets()
|
||||
|
||||
if (associatedTargetsResult.isError) {
|
||||
toast.error(t('common.switchToStandardWorkflowConfirm.loadFailed', { ns: 'workflow' }))
|
||||
return
|
||||
}
|
||||
|
||||
const associatedTargets = associatedTargetsResult.data?.items ?? []
|
||||
if (associatedTargets.length > 0) {
|
||||
setEvaluationWorkflowSwitchTargets(associatedTargets)
|
||||
setShowEvaluationWorkflowSwitchConfirm(true)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
await performWorkflowTypeSwitch()
|
||||
}, [
|
||||
appDetail?.id,
|
||||
appDetail?.type,
|
||||
performWorkflowTypeSwitch,
|
||||
refetchEvaluationWorkflowAssociatedTargets,
|
||||
t,
|
||||
workflowTypeSwitchConfig,
|
||||
workflowTypeSwitchDisabledReason,
|
||||
])
|
||||
|
||||
const handleEvaluationWorkflowSwitchConfirmOpenChange = useCallback((nextOpen: boolean) => {
|
||||
setShowEvaluationWorkflowSwitchConfirm(nextOpen)
|
||||
|
||||
if (!nextOpen)
|
||||
setEvaluationWorkflowSwitchTargets([])
|
||||
}, [])
|
||||
|
||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => {
|
||||
e.preventDefault()
|
||||
@@ -323,7 +376,7 @@ const AppPublisher = ({
|
||||
startNodeLimitExceeded={startNodeLimitExceeded}
|
||||
upgradeHighlightStyle={upgradeHighlightStyle}
|
||||
workflowTypeSwitchConfig={workflowTypeSwitchConfig}
|
||||
workflowTypeSwitchDisabled={publishDisabled || published || isConvertingWorkflowType || Boolean(workflowTypeSwitchDisabledReason)}
|
||||
workflowTypeSwitchDisabled={publishDisabled || published || isConvertingWorkflowType || isFetchingEvaluationWorkflowAssociatedTargets || Boolean(workflowTypeSwitchDisabledReason)}
|
||||
workflowTypeSwitchDisabledReason={workflowTypeSwitchDisabledReason}
|
||||
onWorkflowTypeSwitch={handleWorkflowTypeSwitch}
|
||||
/>
|
||||
@@ -372,6 +425,13 @@ const AppPublisher = ({
|
||||
/>
|
||||
{showAppAccessControl && <AccessControl app={appDetail!} onConfirm={handleAccessControlUpdate} onClose={() => { setShowAppAccessControl(false) }} />}
|
||||
</PortalToFollowElem>
|
||||
<EvaluationWorkflowSwitchConfirmDialog
|
||||
open={showEvaluationWorkflowSwitchConfirm}
|
||||
targets={evaluationWorkflowSwitchTargets}
|
||||
loading={isConvertingWorkflowType}
|
||||
onOpenChange={handleEvaluationWorkflowSwitchConfirmOpenChange}
|
||||
onConfirm={() => void performWorkflowTypeSwitch()}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
EvaluationRunRequest,
|
||||
EvaluationTargetType,
|
||||
EvaluationVersionDetailResponse,
|
||||
EvaluationWorkflowAssociatedTargetsResponse,
|
||||
} from '@/types/evaluation'
|
||||
import { type } from '@orpc/contract'
|
||||
import { base } from '../base'
|
||||
@@ -290,6 +291,18 @@ export const availableEvaluationWorkflowsContract = base
|
||||
}>())
|
||||
.output(type<AvailableEvaluationWorkflowsResponse>())
|
||||
|
||||
export const evaluationWorkflowAssociatedTargetsContract = base
|
||||
.route({
|
||||
path: '/workspaces/current/evaluation-workflows/{workflowId}/associated-targets',
|
||||
method: 'GET',
|
||||
})
|
||||
.input(type<{
|
||||
params: {
|
||||
workflowId: string
|
||||
}
|
||||
}>())
|
||||
.output(type<EvaluationWorkflowAssociatedTargetsResponse>())
|
||||
|
||||
export const evaluationFileContract = base
|
||||
.route({
|
||||
path: '/{targetType}/{targetId}/evaluation/files/{fileId}',
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
evaluationRunDetailContract,
|
||||
evaluationTemplateDownloadContract,
|
||||
evaluationVersionDetailContract,
|
||||
evaluationWorkflowAssociatedTargetsContract,
|
||||
saveDatasetEvaluationConfigContract,
|
||||
saveEvaluationConfigContract,
|
||||
startDatasetEvaluationRunContract,
|
||||
@@ -134,6 +135,7 @@ export const consoleRouterContract = {
|
||||
nodeInfo: evaluationNodeInfoContract,
|
||||
availableMetrics: availableEvaluationMetricsContract,
|
||||
availableWorkflows: availableEvaluationWorkflowsContract,
|
||||
associatedTargets: evaluationWorkflowAssociatedTargetsContract,
|
||||
file: evaluationFileContract,
|
||||
versionDetail: evaluationVersionDetailContract,
|
||||
},
|
||||
|
||||
@@ -587,9 +587,6 @@
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 5
|
||||
}
|
||||
@@ -5556,6 +5553,11 @@
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"app/components/evaluation/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/explore/app-card/index.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 5
|
||||
@@ -5566,11 +5568,6 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/evaluation/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/explore/banner/banner-item.tsx": {
|
||||
"react-hooks-extra/no-direct-set-state-in-use-effect": {
|
||||
"count": 1
|
||||
@@ -7665,13 +7662,13 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/splash.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"app/components/snippets/components/snippet-main.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/snippets/components/snippet-main.tsx": {
|
||||
"ts/no-explicit-any": {
|
||||
"app/components/splash.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
@@ -11915,11 +11912,6 @@
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"service/use-evaluation.ts": {
|
||||
"no-barrel-files/no-barrel-files": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"service/use-flow.ts": {
|
||||
"react/no-unnecessary-use-prefix": {
|
||||
"count": 1
|
||||
|
||||
@@ -219,6 +219,16 @@
|
||||
"common.switchToEvaluationWorkflowDisabledTip": "Evaluation workflows do not support Human Input nodes or Trigger start nodes.",
|
||||
"common.switchToEvaluationWorkflowTip": "Turns this workflow into a custom evaluator for batch testing. Disables public Web App access.",
|
||||
"common.switchToStandardWorkflow": "Switch to Standard Workflow",
|
||||
"common.switchToStandardWorkflowConfirm.activeIn_one": "This evaluator is currently active in {{count}} configuration.",
|
||||
"common.switchToStandardWorkflowConfirm.activeIn_other": "This evaluator is currently active in {{count}} configurations.",
|
||||
"common.switchToStandardWorkflowConfirm.dependentWorkflows": "Dependent workflows",
|
||||
"common.switchToStandardWorkflowConfirm.description": "Switching to a standard workflow will break these dependencies and may cause active batch tests to fail.",
|
||||
"common.switchToStandardWorkflowConfirm.loadFailed": "Failed to load dependent workflows.",
|
||||
"common.switchToStandardWorkflowConfirm.switch": "Switch",
|
||||
"common.switchToStandardWorkflowConfirm.targetTypes.app": "Workflow",
|
||||
"common.switchToStandardWorkflowConfirm.targetTypes.knowledge_base": "Knowledge Base",
|
||||
"common.switchToStandardWorkflowConfirm.targetTypes.snippets": "Snippet",
|
||||
"common.switchToStandardWorkflowConfirm.title": "Switch to Standard Workflow?",
|
||||
"common.switchToStandardWorkflowTip": "Turns this evaluator back into a standard workflow and restores public Web App access.",
|
||||
"common.syncingData": "Syncing data, just a few seconds.",
|
||||
"common.tagBound": "Number of apps using this tag",
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { EvaluationResourceType } from '@/app/components/evaluation/types'
|
||||
import type { AvailableEvaluationWorkflowsResponse, EvaluationConfig } from '@/types/evaluation'
|
||||
import {
|
||||
keepPreviousData,
|
||||
skipToken,
|
||||
useInfiniteQuery,
|
||||
useMutation,
|
||||
useQuery,
|
||||
@@ -59,6 +60,24 @@ export const useAvailableEvaluationMetrics = (enabled = true) => {
|
||||
}))
|
||||
}
|
||||
|
||||
export const useEvaluationWorkflowAssociatedTargets = (
|
||||
workflowId: string | undefined,
|
||||
options?: { enabled?: boolean },
|
||||
) => {
|
||||
return useQuery(consoleQuery.evaluation.associatedTargets.queryOptions({
|
||||
input: workflowId
|
||||
? {
|
||||
params: {
|
||||
workflowId,
|
||||
},
|
||||
}
|
||||
: skipToken,
|
||||
enabled: Boolean(workflowId) && (options?.enabled ?? true),
|
||||
refetchOnWindowFocus: false,
|
||||
retry: false,
|
||||
}))
|
||||
}
|
||||
|
||||
export const useEvaluationNodeInfoMutation = () => {
|
||||
return useMutation(consoleQuery.evaluation.nodeInfo.mutationOptions())
|
||||
}
|
||||
|
||||
@@ -167,6 +167,18 @@ export type AvailableEvaluationWorkflowsResponse = {
|
||||
has_more: boolean
|
||||
}
|
||||
|
||||
export type EvaluationWorkflowAssociatedTargetType = 'app' | 'snippets' | 'knowledge_base'
|
||||
|
||||
export type EvaluationWorkflowAssociatedTarget = {
|
||||
target_type: EvaluationWorkflowAssociatedTargetType
|
||||
target_id: string
|
||||
target_name: string
|
||||
}
|
||||
|
||||
export type EvaluationWorkflowAssociatedTargetsResponse = {
|
||||
items: EvaluationWorkflowAssociatedTarget[]
|
||||
}
|
||||
|
||||
export type EvaluationNodeInfoRequest = {
|
||||
metrics?: string[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user