> = ({
const { zoom } = useViewport()
const nodesInitialized = useNodesInitialized()
const { handleNodeIterationRerender } = useNodeIterationInteractions()
+ const { t } = useTranslation()
useEffect(() => {
if (nodesInitialized)
handleNodeIterationRerender(id)
- }, [nodesInitialized, id, handleNodeIterationRerender])
+ if (data.is_parallel && data._isShowTips) {
+ Toast.notify({
+ type: 'warning',
+ message: t(`${i18nPrefix}.answerNodeWarningDesc`),
+ duration: 5000,
+ })
+ data._isShowTips = false
+ }
+ }, [nodesInitialized, id, handleNodeIterationRerender, data, t])
return (
> = ({
data,
}) => {
const { t } = useTranslation()
-
+ const responseMethod = [
+ {
+ value: ErrorHandleMode.Terminated,
+ name: t(`${i18nPrefix}.ErrorMethod.operationTerminated`),
+ },
+ {
+ value: ErrorHandleMode.ContinueOnError,
+ name: t(`${i18nPrefix}.ErrorMethod.continueOnError`),
+ },
+ {
+ value: ErrorHandleMode.RemoveAbnormalOutput,
+ name: t(`${i18nPrefix}.ErrorMethod.removeAbnormalOutput`),
+ },
+ ]
const {
readOnly,
inputs,
@@ -47,6 +66,9 @@ const Panel: FC> = ({
setIterator,
iteratorInputKey,
iterationRunResult,
+ changeParallel,
+ changeErrorResponseMode,
+ changeParallelNums,
} = useConfig(id, data)
return (
@@ -87,6 +109,39 @@ const Panel: FC> = ({
/>
+
+ {t(`${i18nPrefix}.parallelPanelDesc`)}
} inline>
+
+
+
+ {
+ inputs.is_parallel && (
+ {t(`${i18nPrefix}.MaxParallelismDesc`)}
}>
+
+ { changeParallelNums(Number(e.target.value)) }} />
+
+
+
+
+ )
+ }
+
+
+
+
+
+
+
+
{isShowSingleRun && (
{
@@ -184,6 +185,25 @@ const useConfig = (id: string, payload: IterationNodeType) => {
})
}, [iteratorInputKey, runInputData, setRunInputData])
+ const changeParallel = useCallback((value: boolean) => {
+ const newInputs = produce(inputs, (draft) => {
+ draft.is_parallel = value
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+
+ const changeErrorResponseMode = useCallback((item: Item) => {
+ const newInputs = produce(inputs, (draft) => {
+ draft.error_handle_mode = item.value as ErrorHandleMode
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
+ const changeParallelNums = useCallback((num: number) => {
+ const newInputs = produce(inputs, (draft) => {
+ draft.parallel_nums = num
+ })
+ setInputs(newInputs)
+ }, [inputs, setInputs])
return {
readOnly,
inputs,
@@ -210,6 +230,9 @@ const useConfig = (id: string, payload: IterationNodeType) => {
setIterator,
iteratorInputKey,
iterationRunResult,
+ changeParallel,
+ changeErrorResponseMode,
+ changeParallelNums,
}
}
diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts
index 58a4561e2c..5d932a1ba2 100644
--- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts
@@ -9,6 +9,8 @@ import { produce, setAutoFreeze } from 'immer'
import { uniqBy } from 'lodash-es'
import { useWorkflowRun } from '../../hooks'
import { NodeRunningStatus, WorkflowRunningStatus } from '../../types'
+import { useWorkflowStore } from '../../store'
+import { DEFAULT_ITER_TIMES } from '../../constants'
import type {
ChatItem,
Inputs,
@@ -43,6 +45,7 @@ export const useChat = (
const { notify } = useToastContext()
const { handleRun } = useWorkflowRun()
const hasStopResponded = useRef(false)
+ const workflowStore = useWorkflowStore()
const conversationId = useRef('')
const taskIdRef = useRef('')
const [chatList, setChatList] = useState(prevChatList || [])
@@ -52,6 +55,9 @@ export const useChat = (
const [suggestedQuestions, setSuggestQuestions] = useState([])
const suggestedQuestionsAbortControllerRef = useRef(null)
+ const {
+ setIterTimes,
+ } = workflowStore.getState()
useEffect(() => {
setAutoFreeze(false)
return () => {
@@ -102,15 +108,16 @@ export const useChat = (
handleResponding(false)
if (stopChat && taskIdRef.current)
stopChat(taskIdRef.current)
-
+ setIterTimes(DEFAULT_ITER_TIMES)
if (suggestedQuestionsAbortControllerRef.current)
suggestedQuestionsAbortControllerRef.current.abort()
- }, [handleResponding, stopChat])
+ }, [handleResponding, setIterTimes, stopChat])
const handleRestart = useCallback(() => {
conversationId.current = ''
taskIdRef.current = ''
handleStop()
+ setIterTimes(DEFAULT_ITER_TIMES)
const newChatList = config?.opening_statement
? [{
id: `${Date.now()}`,
@@ -126,6 +133,7 @@ export const useChat = (
config,
handleStop,
handleUpdateChatList,
+ setIterTimes,
])
const updateCurrentQA = useCallback(({
diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx
index 9e636e902b..89db43fa35 100644
--- a/web/app/components/workflow/run/index.tsx
+++ b/web/app/components/workflow/run/index.tsx
@@ -60,36 +60,67 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe
}, [notify, getResultCallback])
const formatNodeList = useCallback((list: NodeTracing[]) => {
- const allItems = list.reverse()
+ const allItems = [...list].reverse()
const result: NodeTracing[] = []
- allItems.forEach((item) => {
- const { node_type, execution_metadata } = item
- if (node_type !== BlockEnum.Iteration) {
- const isInIteration = !!execution_metadata?.iteration_id
+ const groupMap = new Map()
- if (isInIteration) {
- const iterationNode = result.find(node => node.node_id === execution_metadata?.iteration_id)
- const iterationDetails = iterationNode?.details
- const currentIterationIndex = execution_metadata?.iteration_index ?? 0
-
- if (Array.isArray(iterationDetails)) {
- if (iterationDetails.length === 0 || !iterationDetails[currentIterationIndex])
- iterationDetails[currentIterationIndex] = [item]
- else
- iterationDetails[currentIterationIndex].push(item)
- }
- return
- }
- // not in iteration
- result.push(item)
-
- return
- }
+ const processIterationNode = (item: NodeTracing) => {
result.push({
...item,
details: [],
})
+ }
+ const updateParallelModeGroup = (runId: string, item: NodeTracing, iterationNode: NodeTracing) => {
+ if (!groupMap.has(runId))
+ groupMap.set(runId, [item])
+ else
+ groupMap.get(runId)!.push(item)
+ if (item.status === 'failed') {
+ iterationNode.status = 'failed'
+ iterationNode.error = item.error
+ }
+
+ iterationNode.details = Array.from(groupMap.values())
+ }
+ const updateSequentialModeGroup = (index: number, item: NodeTracing, iterationNode: NodeTracing) => {
+ const { details } = iterationNode
+ if (details) {
+ if (!details[index])
+ details[index] = [item]
+ else
+ details[index].push(item)
+ }
+
+ if (item.status === 'failed') {
+ iterationNode.status = 'failed'
+ iterationNode.error = item.error
+ }
+ }
+ const processNonIterationNode = (item: NodeTracing) => {
+ const { execution_metadata } = item
+ if (!execution_metadata?.iteration_id) {
+ result.push(item)
+ return
+ }
+
+ const iterationNode = result.find(node => node.node_id === execution_metadata.iteration_id)
+ if (!iterationNode || !Array.isArray(iterationNode.details))
+ return
+
+ const { parallel_mode_run_id, iteration_index = 0 } = execution_metadata
+
+ if (parallel_mode_run_id)
+ updateParallelModeGroup(parallel_mode_run_id, item, iterationNode)
+ else
+ updateSequentialModeGroup(iteration_index, item, iterationNode)
+ }
+
+ allItems.forEach((item) => {
+ item.node_type === BlockEnum.Iteration
+ ? processIterationNode(item)
+ : processNonIterationNode(item)
})
+
return result
}, [])
diff --git a/web/app/components/workflow/run/iteration-result-panel.tsx b/web/app/components/workflow/run/iteration-result-panel.tsx
index 7e2f6cbc00..c4cd909f2e 100644
--- a/web/app/components/workflow/run/iteration-result-panel.tsx
+++ b/web/app/components/workflow/run/iteration-result-panel.tsx
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'
import {
RiArrowRightSLine,
RiCloseLine,
+ RiErrorWarningLine,
} from '@remixicon/react'
import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows'
import TracingPanel from './tracing-panel'
@@ -27,7 +28,7 @@ const IterationResultPanel: FC = ({
noWrap,
}) => {
const { t } = useTranslation()
- const [expandedIterations, setExpandedIterations] = useState>([])
+ const [expandedIterations, setExpandedIterations] = useState>({})
const toggleIteration = useCallback((index: number) => {
setExpandedIterations(prev => ({
@@ -71,10 +72,19 @@ const IterationResultPanel: FC = ({
{t(`${i18nPrefix}.iteration`)} {index + 1}
-
+ {
+ iteration.some(item => item.status === 'failed')
+ ? (
+
+ )
+ : (< RiArrowRightSLine className={
+ cn(
+ 'w-4 h-4 text-text-tertiary transition-transform duration-200 flex-shrink-0',
+ expandedIterations[index] && 'transform rotate-90',
+ )} />
+ )
+ }
+
{expandedIterations[index] && = ({
return iteration_length
}
+ const getErrorCount = (details: NodeTracing[][] | undefined) => {
+ if (!details || details.length === 0)
+ return 0
+ return details.reduce((acc, iteration) => {
+ if (iteration.some(item => item.status === 'failed'))
+ acc++
+ return acc
+ }, 0)
+ }
useEffect(() => {
setCollapseState(!nodeInfo.expand)
}, [nodeInfo.expand, setCollapseState])
@@ -136,7 +145,12 @@ const NodePanel: FC
= ({
onClick={handleOnShowIterationDetail}
>
- {t('workflow.nodes.iteration.iteration', { count: getCount(nodeInfo.details?.length, nodeInfo.metadata?.iterator_length) })}
+ {t('workflow.nodes.iteration.iteration', { count: getCount(nodeInfo.details?.length, nodeInfo.metadata?.iterator_length) })}{getErrorCount(nodeInfo.details) > 0 && (
+ <>
+ {t('workflow.nodes.iteration.comma')}
+ {t('workflow.nodes.iteration.error', { count: getErrorCount(nodeInfo.details) })}
+ >
+ )}
{justShowIterationNavArrow
? (
diff --git a/web/app/components/workflow/store.ts b/web/app/components/workflow/store.ts
index c2a6823e6b..c4a625c777 100644
--- a/web/app/components/workflow/store.ts
+++ b/web/app/components/workflow/store.ts
@@ -21,6 +21,7 @@ import type {
WorkflowRunningData,
} from './types'
import { WorkflowContext } from './context'
+import type { NodeTracing } from '@/types/workflow'
// #TODO chatVar#
// const MOCK_DATA = [
@@ -166,6 +167,10 @@ type Shape = {
setShowImportDSLModal: (showImportDSLModal: boolean) => void
showTips: string
setShowTips: (showTips: string) => void
+ iterTimes: number
+ setIterTimes: (iterTimes: number) => void
+ iterParallelLogMap: Map
+ setIterParallelLogMap: (iterParallelLogMap: Map) => void
}
export const createWorkflowStore = () => {
@@ -281,6 +286,11 @@ export const createWorkflowStore = () => {
setShowImportDSLModal: showImportDSLModal => set(() => ({ showImportDSLModal })),
showTips: '',
setShowTips: showTips => set(() => ({ showTips })),
+ iterTimes: 1,
+ setIterTimes: iterTimes => set(() => ({ iterTimes })),
+ iterParallelLogMap: new Map(),
+ setIterParallelLogMap: iterParallelLogMap => set(() => ({ iterParallelLogMap })),
+
}))
}
diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts
index 81bec41eac..9b6ad033bf 100644
--- a/web/app/components/workflow/types.ts
+++ b/web/app/components/workflow/types.ts
@@ -36,7 +36,11 @@ export enum ControlMode {
Pointer = 'pointer',
Hand = 'hand',
}
-
+export enum ErrorHandleMode {
+ Terminated = 'terminated',
+ ContinueOnError = 'continue-on-error',
+ RemoveAbnormalOutput = 'remove-abnormal-output',
+}
export type Branch = {
id: string
name: string
diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts
index 91656e3bbc..aaf333f4d7 100644
--- a/web/app/components/workflow/utils.ts
+++ b/web/app/components/workflow/utils.ts
@@ -19,7 +19,7 @@ import type {
ToolWithProvider,
ValueSelector,
} from './types'
-import { BlockEnum } from './types'
+import { BlockEnum, ErrorHandleMode } from './types'
import {
CUSTOM_NODE,
ITERATION_CHILDREN_Z_INDEX,
@@ -267,8 +267,13 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => {
})
}
- if (node.data.type === BlockEnum.Iteration)
- node.data._children = iterationNodeMap[node.id] || []
+ if (node.data.type === BlockEnum.Iteration) {
+ const iterationNodeData = node.data as IterationNodeType
+ iterationNodeData._children = iterationNodeMap[node.id] || []
+ iterationNodeData.is_parallel = iterationNodeData.is_parallel || false
+ iterationNodeData.parallel_nums = iterationNodeData.parallel_nums || 10
+ iterationNodeData.error_handle_mode = iterationNodeData.error_handle_mode || ErrorHandleMode.Terminated
+ }
return node
})
diff --git a/web/app/signin/normalForm.tsx b/web/app/signin/normalForm.tsx
index c0f2d89b37..f4f46c68ba 100644
--- a/web/app/signin/normalForm.tsx
+++ b/web/app/signin/normalForm.tsx
@@ -12,11 +12,9 @@ import cn from '@/utils/classnames'
import { getSystemFeatures, invitationCheck } from '@/service/common'
import { defaultSystemFeatures } from '@/types/feature'
import Toast from '@/app/components/base/toast'
-import useRefreshToken from '@/hooks/use-refresh-token'
import { IS_CE_EDITION } from '@/config'
const NormalForm = () => {
- const { getNewAccessToken } = useRefreshToken()
const { t } = useTranslation()
const router = useRouter()
const searchParams = useSearchParams()
@@ -38,7 +36,6 @@ const NormalForm = () => {
if (consoleToken && refreshToken) {
localStorage.setItem('console_token', consoleToken)
localStorage.setItem('refresh_token', refreshToken)
- getNewAccessToken()
router.replace('/apps')
return
}
@@ -71,7 +68,7 @@ const NormalForm = () => {
setSystemFeatures(defaultSystemFeatures)
}
finally { setIsLoading(false) }
- }, [consoleToken, refreshToken, message, router, invite_token, isInviteLink, getNewAccessToken])
+ }, [consoleToken, refreshToken, message, router, invite_token, isInviteLink])
useEffect(() => {
init()
}, [init])
diff --git a/web/hooks/use-refresh-token.ts b/web/hooks/use-refresh-token.ts
deleted file mode 100644
index 53dc4faf00..0000000000
--- a/web/hooks/use-refresh-token.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-'use client'
-import { useCallback, useEffect, useRef } from 'react'
-import { jwtDecode } from 'jwt-decode'
-import dayjs from 'dayjs'
-import utc from 'dayjs/plugin/utc'
-import { useRouter } from 'next/navigation'
-import type { CommonResponse } from '@/models/common'
-import { fetchNewToken } from '@/service/common'
-import { fetchWithRetry } from '@/utils'
-
-dayjs.extend(utc)
-
-const useRefreshToken = () => {
- const router = useRouter()
- const timer = useRef()
- const advanceTime = useRef(5 * 60 * 1000)
-
- const getExpireTime = useCallback((token: string) => {
- if (!token)
- return 0
- const decoded = jwtDecode(token)
- return (decoded.exp || 0) * 1000
- }, [])
-
- const getCurrentTimeStamp = useCallback(() => {
- return dayjs.utc().valueOf()
- }, [])
-
- const handleError = useCallback(() => {
- localStorage?.removeItem('is_refreshing')
- localStorage?.removeItem('console_token')
- localStorage?.removeItem('refresh_token')
- router.replace('/signin')
- }, [])
-
- const getNewAccessToken = useCallback(async () => {
- const currentAccessToken = localStorage?.getItem('console_token')
- const currentRefreshToken = localStorage?.getItem('refresh_token')
- if (!currentAccessToken || !currentRefreshToken) {
- handleError()
- return new Error('No access token or refresh token found')
- }
- if (localStorage?.getItem('is_refreshing') === '1') {
- clearTimeout(timer.current)
- timer.current = setTimeout(() => {
- getNewAccessToken()
- }, 1000)
- return null
- }
- const currentTokenExpireTime = getExpireTime(currentAccessToken)
- if (getCurrentTimeStamp() + advanceTime.current > currentTokenExpireTime) {
- localStorage?.setItem('is_refreshing', '1')
- const [e, res] = await fetchWithRetry(fetchNewToken({
- body: { refresh_token: currentRefreshToken },
- }) as Promise)
- if (e) {
- handleError()
- return e
- }
- const { access_token, refresh_token } = res.data
- localStorage?.setItem('is_refreshing', '0')
- localStorage?.setItem('console_token', access_token)
- localStorage?.setItem('refresh_token', refresh_token)
- const newTokenExpireTime = getExpireTime(access_token)
- clearTimeout(timer.current)
- timer.current = setTimeout(() => {
- getNewAccessToken()
- }, newTokenExpireTime - advanceTime.current - getCurrentTimeStamp())
- }
- else {
- const newTokenExpireTime = getExpireTime(currentAccessToken)
- clearTimeout(timer.current)
- timer.current = setTimeout(() => {
- getNewAccessToken()
- }, newTokenExpireTime - advanceTime.current - getCurrentTimeStamp())
- }
- return null
- }, [getExpireTime, getCurrentTimeStamp, handleError])
-
- const handleVisibilityChange = useCallback(() => {
- if (document.visibilityState === 'visible')
- getNewAccessToken()
- }, [])
-
- useEffect(() => {
- window.addEventListener('visibilitychange', handleVisibilityChange)
- return () => {
- window.removeEventListener('visibilitychange', handleVisibilityChange)
- clearTimeout(timer.current)
- localStorage?.removeItem('is_refreshing')
- }
- }, [])
-
- return {
- getNewAccessToken,
- }
-}
-
-export default useRefreshToken
diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts
index bde0250fcc..d05070c308 100644
--- a/web/i18n/de-DE/workflow.ts
+++ b/web/i18n/de-DE/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Iteration',
iteration_other: '{{count}} Iterationen',
currentIteration: 'Aktuelle Iteration',
+ ErrorMethod: {
+ operationTerminated: 'beendet',
+ removeAbnormalOutput: 'remove-abnormale_ausgabe',
+ continueOnError: 'Fehler "Fortfahren bei"',
+ },
+ MaxParallelismTitle: 'Maximale Parallelität',
+ parallelMode: 'Paralleler Modus',
+ errorResponseMethod: 'Methode der Fehlerantwort',
+ error_one: '{{Anzahl}} Fehler',
+ error_other: '{{Anzahl}} Irrtümer',
+ MaxParallelismDesc: 'Die maximale Parallelität wird verwendet, um die Anzahl der Aufgaben zu steuern, die gleichzeitig in einer einzigen Iteration ausgeführt werden.',
+ parallelPanelDesc: 'Im parallelen Modus unterstützen Aufgaben in der Iteration die parallele Ausführung.',
+ parallelModeEnableDesc: 'Im parallelen Modus unterstützen Aufgaben innerhalb von Iterationen die parallele Ausführung. Sie können dies im Eigenschaftenbereich auf der rechten Seite konfigurieren.',
+ answerNodeWarningDesc: 'Warnung im parallelen Modus: Antwortknoten, Zuweisungen von Konversationsvariablen und persistente Lese-/Schreibvorgänge innerhalb von Iterationen können Ausnahmen verursachen.',
+ parallelModeEnableTitle: 'Paralleler Modus aktiviert',
+ parallelModeUpper: 'PARALLELER MODUS',
+ comma: ',',
},
note: {
editor: {
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts
index ea8355500a..1c6639aba0 100644
--- a/web/i18n/en-US/workflow.ts
+++ b/web/i18n/en-US/workflow.ts
@@ -556,6 +556,23 @@ const translation = {
iteration_one: '{{count}} Iteration',
iteration_other: '{{count}} Iterations',
currentIteration: 'Current Iteration',
+ comma: ', ',
+ error_one: '{{count}} Error',
+ error_other: '{{count}} Errors',
+ parallelMode: 'Parallel Mode',
+ parallelModeUpper: 'PARALLEL MODE',
+ parallelModeEnableTitle: 'Parallel Mode Enabled',
+ parallelModeEnableDesc: 'In parallel mode, tasks within iterations support parallel execution. You can configure this in the properties panel on the right.',
+ parallelPanelDesc: 'In parallel mode, tasks in the iteration support parallel execution.',
+ MaxParallelismTitle: 'Maximum parallelism',
+ MaxParallelismDesc: 'The maximum parallelism is used to control the number of tasks executed simultaneously in a single iteration.',
+ errorResponseMethod: 'Error response method',
+ ErrorMethod: {
+ operationTerminated: 'terminated',
+ continueOnError: 'continue-on-error',
+ removeAbnormalOutput: 'remove-abnormal-output',
+ },
+ answerNodeWarningDesc: 'Parallel mode warning: Answer nodes, conversation variable assignments, and persistent read/write operations within iterations may cause exceptions.',
},
note: {
addNote: 'Add Note',
diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts
index 59a330e7f4..6c9af49c4d 100644
--- a/web/i18n/es-ES/workflow.ts
+++ b/web/i18n/es-ES/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Iteración',
iteration_other: '{{count}} Iteraciones',
currentIteration: 'Iteración actual',
+ ErrorMethod: {
+ operationTerminated: 'Terminado',
+ continueOnError: 'Continuar en el error',
+ removeAbnormalOutput: 'eliminar-salida-anormal',
+ },
+ comma: ',',
+ errorResponseMethod: 'Método de respuesta a errores',
+ error_one: '{{conteo}} Error',
+ parallelPanelDesc: 'En el modo paralelo, las tareas de la iteración admiten la ejecución en paralelo.',
+ MaxParallelismTitle: 'Máximo paralelismo',
+ error_other: '{{conteo}} Errores',
+ parallelMode: 'Modo paralelo',
+ parallelModeEnableDesc: 'En el modo paralelo, las tareas dentro de las iteraciones admiten la ejecución en paralelo. Puede configurar esto en el panel de propiedades a la derecha.',
+ parallelModeUpper: 'MODO PARALELO',
+ MaxParallelismDesc: 'El paralelismo máximo se utiliza para controlar el número de tareas ejecutadas simultáneamente en una sola iteración.',
+ answerNodeWarningDesc: 'Advertencia de modo paralelo: Los nodos de respuesta, las asignaciones de variables de conversación y las operaciones de lectura/escritura persistentes dentro de las iteraciones pueden provocar excepciones.',
+ parallelModeEnableTitle: 'Modo paralelo habilitado',
},
note: {
addNote: 'Agregar nota',
diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts
index b1f9384159..4b00390663 100644
--- a/web/i18n/fa-IR/workflow.ts
+++ b/web/i18n/fa-IR/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} تکرار',
iteration_other: '{{count}} تکرارها',
currentIteration: 'تکرار فعلی',
+ ErrorMethod: {
+ continueOnError: 'ادامه در خطا',
+ operationTerminated: 'فسخ',
+ removeAbnormalOutput: 'حذف خروجی غیرطبیعی',
+ },
+ error_one: '{{تعداد}} خطا',
+ error_other: '{{تعداد}} خطاهای',
+ parallelMode: 'حالت موازی',
+ errorResponseMethod: 'روش پاسخ به خطا',
+ parallelModeEnableTitle: 'حالت موازی فعال است',
+ parallelModeUpper: 'حالت موازی',
+ comma: ',',
+ parallelModeEnableDesc: 'در حالت موازی، وظایف درون تکرارها از اجرای موازی پشتیبانی می کنند. می توانید این را در پانل ویژگی ها در سمت راست پیکربندی کنید.',
+ MaxParallelismTitle: 'حداکثر موازی سازی',
+ parallelPanelDesc: 'در حالت موازی، وظایف در تکرار از اجرای موازی پشتیبانی می کنند.',
+ MaxParallelismDesc: 'حداکثر موازی سازی برای کنترل تعداد وظایف اجرا شده به طور همزمان در یک تکرار واحد استفاده می شود.',
+ answerNodeWarningDesc: 'هشدار حالت موازی: گره های پاسخ، تکالیف متغیر مکالمه و عملیات خواندن/نوشتن مداوم در تکرارها ممکن است باعث استثنائات شود.',
},
note: {
addNote: 'افزودن یادداشت',
diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts
index e56932455f..e736e2cb07 100644
--- a/web/i18n/fr-FR/workflow.ts
+++ b/web/i18n/fr-FR/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Itération',
iteration_other: '{{count}} Itérations',
currentIteration: 'Itération actuelle',
+ ErrorMethod: {
+ operationTerminated: 'Terminé',
+ removeAbnormalOutput: 'remove-abnormal-output',
+ continueOnError: 'continuer sur l’erreur',
+ },
+ comma: ',',
+ error_one: '{{compte}} Erreur',
+ error_other: '{{compte}} Erreurs',
+ parallelModeEnableDesc: 'En mode parallèle, les tâches au sein des itérations prennent en charge l’exécution parallèle. Vous pouvez le configurer dans le panneau des propriétés à droite.',
+ parallelModeUpper: 'MODE PARALLÈLE',
+ parallelPanelDesc: 'En mode parallèle, les tâches de l’itération prennent en charge l’exécution parallèle.',
+ MaxParallelismDesc: 'Le parallélisme maximal est utilisé pour contrôler le nombre de tâches exécutées simultanément en une seule itération.',
+ errorResponseMethod: 'Méthode de réponse aux erreurs',
+ MaxParallelismTitle: 'Parallélisme maximal',
+ answerNodeWarningDesc: 'Avertissement en mode parallèle : les nœuds de réponse, les affectations de variables de conversation et les opérations de lecture/écriture persistantes au sein des itérations peuvent provoquer des exceptions.',
+ parallelModeEnableTitle: 'Mode parallèle activé',
+ parallelMode: 'Mode parallèle',
},
note: {
addNote: 'Ajouter note',
diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts
index 1473f78ccd..4112643488 100644
--- a/web/i18n/hi-IN/workflow.ts
+++ b/web/i18n/hi-IN/workflow.ts
@@ -577,6 +577,23 @@ const translation = {
iteration_one: '{{count}} इटरेशन',
iteration_other: '{{count}} इटरेशन्स',
currentIteration: 'वर्तमान इटरेशन',
+ ErrorMethod: {
+ operationTerminated: 'समाप्त',
+ continueOnError: 'जारी रखें-पर-त्रुटि',
+ removeAbnormalOutput: 'निकालें-असामान्य-आउटपुट',
+ },
+ comma: ',',
+ error_other: '{{गिनती}} त्रुटियों',
+ error_one: '{{गिनती}} चूक',
+ parallelMode: 'समानांतर मोड',
+ parallelModeUpper: 'समानांतर मोड',
+ errorResponseMethod: 'त्रुटि प्रतिक्रिया विधि',
+ MaxParallelismTitle: 'अधिकतम समांतरता',
+ parallelModeEnableTitle: 'समानांतर मोड सक्षम किया गया',
+ parallelModeEnableDesc: 'समानांतर मोड में, पुनरावृत्तियों के भीतर कार्य समानांतर निष्पादन का समर्थन करते हैं। आप इसे दाईं ओर गुण पैनल में कॉन्फ़िगर कर सकते हैं।',
+ parallelPanelDesc: 'समानांतर मोड में, पुनरावृत्ति में कार्य समानांतर निष्पादन का समर्थन करते हैं।',
+ MaxParallelismDesc: 'अधिकतम समांतरता का उपयोग एकल पुनरावृत्ति में एक साथ निष्पादित कार्यों की संख्या को नियंत्रित करने के लिए किया जाता है।',
+ answerNodeWarningDesc: 'समानांतर मोड चेतावनी: उत्तर नोड्स, वार्तालाप चर असाइनमेंट, और पुनरावृत्तियों के भीतर लगातार पढ़ने/लिखने की कार्रवाई अपवाद पैदा कर सकती है।',
},
note: {
addNote: 'नोट जोड़ें',
diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts
index 19fa7bfbb5..756fb665af 100644
--- a/web/i18n/it-IT/workflow.ts
+++ b/web/i18n/it-IT/workflow.ts
@@ -584,6 +584,23 @@ const translation = {
iteration_one: '{{count}} Iterazione',
iteration_other: '{{count}} Iterazioni',
currentIteration: 'Iterazione Corrente',
+ ErrorMethod: {
+ operationTerminated: 'Terminato',
+ continueOnError: 'continua sull\'errore',
+ removeAbnormalOutput: 'rimuovi-output-anomalo',
+ },
+ error_one: '{{conteggio}} Errore',
+ parallelMode: 'Modalità parallela',
+ MaxParallelismTitle: 'Parallelismo massimo',
+ error_other: '{{conteggio}} Errori',
+ parallelModeEnableDesc: 'In modalità parallela, le attività all\'interno delle iterazioni supportano l\'esecuzione parallela. È possibile configurare questa opzione nel pannello delle proprietà a destra.',
+ MaxParallelismDesc: 'Il parallelismo massimo viene utilizzato per controllare il numero di attività eseguite contemporaneamente in una singola iterazione.',
+ errorResponseMethod: 'Metodo di risposta all\'errore',
+ parallelModeEnableTitle: 'Modalità parallela abilitata',
+ parallelModeUpper: 'MODALITÀ PARALLELA',
+ comma: ',',
+ parallelPanelDesc: 'In modalità parallela, le attività nell\'iterazione supportano l\'esecuzione parallela.',
+ answerNodeWarningDesc: 'Avviso in modalità parallela: i nodi di risposta, le assegnazioni di variabili di conversazione e le operazioni di lettura/scrittura persistenti all\'interno delle iterazioni possono causare eccezioni.',
},
note: {
addNote: 'Aggiungi Nota',
diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts
index b6c7786081..a82ba71e48 100644
--- a/web/i18n/ja-JP/workflow.ts
+++ b/web/i18n/ja-JP/workflow.ts
@@ -558,6 +558,23 @@ const translation = {
iteration_one: '{{count}} イテレーション',
iteration_other: '{{count}} イテレーション',
currentIteration: '現在のイテレーション',
+ ErrorMethod: {
+ operationTerminated: '終了',
+ continueOnError: 'エラー時に続行',
+ removeAbnormalOutput: 'アブノーマルアウトプットの削除',
+ },
+ comma: ',',
+ error_other: '{{カウント}}エラー',
+ error_one: '{{カウント}}エラー',
+ parallelModeUpper: 'パラレルモード',
+ parallelMode: 'パラレルモード',
+ MaxParallelismTitle: '最大並列処理',
+ errorResponseMethod: 'エラー応答方式',
+ parallelPanelDesc: '並列モードでは、イテレーションのタスクは並列実行をサポートします。',
+ parallelModeEnableDesc: '並列モードでは、イテレーション内のタスクは並列実行をサポートします。これは、右側のプロパティパネルで構成できます。',
+ parallelModeEnableTitle: 'パラレルモード有効',
+ MaxParallelismDesc: '最大並列処理は、1 回の反復で同時に実行されるタスクの数を制御するために使用されます。',
+ answerNodeWarningDesc: '並列モードの警告: 応答ノード、会話変数の割り当て、およびイテレーション内の永続的な読み取り/書き込み操作により、例外が発生する可能性があります。',
},
note: {
addNote: 'コメントを追加',
diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts
index b62aff2068..589831401c 100644
--- a/web/i18n/ko-KR/workflow.ts
+++ b/web/i18n/ko-KR/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} 반복',
iteration_other: '{{count}} 반복',
currentIteration: '현재 반복',
+ ErrorMethod: {
+ operationTerminated: '종료',
+ continueOnError: '오류 발생 시 계속',
+ removeAbnormalOutput: '비정상 출력 제거',
+ },
+ comma: ',',
+ error_one: '{{개수}} 오류',
+ parallelMode: '병렬 모드',
+ errorResponseMethod: '오류 응답 방법',
+ parallelModeUpper: '병렬 모드',
+ MaxParallelismTitle: '최대 병렬 처리',
+ error_other: '{{개수}} 오류',
+ parallelModeEnableTitle: 'Parallel Mode Enabled(병렬 모드 사용)',
+ parallelPanelDesc: '병렬 모드에서 반복의 작업은 병렬 실행을 지원합니다.',
+ parallelModeEnableDesc: '병렬 모드에서는 반복 내의 작업이 병렬 실행을 지원합니다. 오른쪽의 속성 패널에서 이를 구성할 수 있습니다.',
+ MaxParallelismDesc: '최대 병렬 처리는 단일 반복에서 동시에 실행되는 작업 수를 제어하는 데 사용됩니다.',
+ answerNodeWarningDesc: '병렬 모드 경고: 응답 노드, 대화 변수 할당 및 반복 내의 지속적인 읽기/쓰기 작업으로 인해 예외가 발생할 수 있습니다.',
},
note: {
editor: {
diff --git a/web/i18n/pl-PL/app-debug.ts b/web/i18n/pl-PL/app-debug.ts
index 7cf6c77cb4..cf7232e563 100644
--- a/web/i18n/pl-PL/app-debug.ts
+++ b/web/i18n/pl-PL/app-debug.ts
@@ -355,7 +355,7 @@ const translation = {
openingStatement: {
title: 'Wstęp do rozmowy',
add: 'Dodaj',
- writeOpner: 'Napisz wstęp',
+ writeOpener: 'Napisz wstęp',
placeholder:
'Tutaj napisz swoją wiadomość wprowadzającą, możesz użyć zmiennych, spróbuj wpisać {{variable}}.',
openingQuestion: 'Pytania otwierające',
diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts
index aace1b2642..f118f7945c 100644
--- a/web/i18n/pl-PL/workflow.ts
+++ b/web/i18n/pl-PL/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Iteracja',
iteration_other: '{{count}} Iteracje',
currentIteration: 'Bieżąca iteracja',
+ ErrorMethod: {
+ continueOnError: 'kontynuacja w przypadku błędu',
+ operationTerminated: 'Zakończone',
+ removeAbnormalOutput: 'usuń-nieprawidłowe-wyjście',
+ },
+ comma: ',',
+ parallelModeUpper: 'TRYB RÓWNOLEGŁY',
+ parallelModeEnableTitle: 'Włączony tryb równoległy',
+ MaxParallelismTitle: 'Maksymalna równoległość',
+ error_one: '{{liczba}} Błąd',
+ error_other: '{{liczba}} Błędy',
+ parallelPanelDesc: 'W trybie równoległym zadania w iteracji obsługują wykonywanie równoległe.',
+ parallelMode: 'Tryb równoległy',
+ MaxParallelismDesc: 'Maksymalna równoległość służy do kontrolowania liczby zadań wykonywanych jednocześnie w jednej iteracji.',
+ parallelModeEnableDesc: 'W trybie równoległym zadania w iteracjach obsługują wykonywanie równoległe. Możesz to skonfigurować w panelu właściwości po prawej stronie.',
+ answerNodeWarningDesc: 'Ostrzeżenie w trybie równoległym: węzły odpowiedzi, przypisania zmiennych konwersacji i trwałe operacje odczytu/zapisu w iteracjach mogą powodować wyjątki.',
+ errorResponseMethod: 'Metoda odpowiedzi na błąd',
},
note: {
editor: {
diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts
index f0f2fec0e2..44afda5cd4 100644
--- a/web/i18n/pt-BR/workflow.ts
+++ b/web/i18n/pt-BR/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Iteração',
iteration_other: '{{count}} Iterações',
currentIteration: 'Iteração atual',
+ ErrorMethod: {
+ continueOnError: 'continuar em erro',
+ removeAbnormalOutput: 'saída anormal de remoção',
+ operationTerminated: 'Terminada',
+ },
+ MaxParallelismTitle: 'Paralelismo máximo',
+ parallelModeEnableTitle: 'Modo paralelo ativado',
+ errorResponseMethod: 'Método de resposta de erro',
+ error_other: '{{contagem}} Erros',
+ parallelMode: 'Modo paralelo',
+ parallelModeUpper: 'MODO PARALELO',
+ error_one: '{{contagem}} Erro',
+ parallelModeEnableDesc: 'No modo paralelo, as tarefas dentro das iterações dão suporte à execução paralela. Você pode configurar isso no painel de propriedades à direita.',
+ comma: ',',
+ MaxParallelismDesc: 'O paralelismo máximo é usado para controlar o número de tarefas executadas simultaneamente em uma única iteração.',
+ answerNodeWarningDesc: 'Aviso de modo paralelo: nós de resposta, atribuições de variáveis de conversação e operações persistentes de leitura/gravação em iterações podem causar exceções.',
+ parallelPanelDesc: 'No modo paralelo, as tarefas na iteração dão suporte à execução paralela.',
},
note: {
editor: {
diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts
index ab0100d347..d8cd84f730 100644
--- a/web/i18n/ro-RO/workflow.ts
+++ b/web/i18n/ro-RO/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Iterație',
iteration_other: '{{count}} Iterații',
currentIteration: 'Iterație curentă',
+ ErrorMethod: {
+ operationTerminated: 'Încheiată',
+ continueOnError: 'continuare-la-eroare',
+ removeAbnormalOutput: 'elimină-ieșire-anormală',
+ },
+ parallelModeEnableTitle: 'Modul paralel activat',
+ errorResponseMethod: 'Metoda de răspuns la eroare',
+ comma: ',',
+ parallelModeEnableDesc: 'În modul paralel, sarcinile din iterații acceptă execuția paralelă. Puteți configura acest lucru în panoul de proprietăți din dreapta.',
+ parallelModeUpper: 'MOD PARALEL',
+ MaxParallelismTitle: 'Paralelism maxim',
+ parallelMode: 'Mod paralel',
+ error_other: '{{număr}} Erori',
+ error_one: '{{număr}} Eroare',
+ parallelPanelDesc: 'În modul paralel, activitățile din iterație acceptă execuția paralelă.',
+ MaxParallelismDesc: 'Paralelismul maxim este utilizat pentru a controla numărul de sarcini executate simultan într-o singură iterație.',
+ answerNodeWarningDesc: 'Avertisment modul paralel: Nodurile de răspuns, atribuirea variabilelor de conversație și operațiunile persistente de citire/scriere în iterații pot cauza excepții.',
},
note: {
editor: {
diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts
index 27735fbb7d..c822f8c3e5 100644
--- a/web/i18n/ru-RU/workflow.ts
+++ b/web/i18n/ru-RU/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Итерация',
iteration_other: '{{count}} Итераций',
currentIteration: 'Текущая итерация',
+ ErrorMethod: {
+ operationTerminated: 'Прекращено',
+ continueOnError: 'продолжить по ошибке',
+ removeAbnormalOutput: 'удалить аномальный вывод',
+ },
+ comma: ',',
+ error_other: '{{Количество}} Ошибки',
+ errorResponseMethod: 'Метод реагирования на ошибку',
+ MaxParallelismTitle: 'Максимальный параллелизм',
+ parallelModeUpper: 'ПАРАЛЛЕЛЬНЫЙ РЕЖИМ',
+ error_one: '{{Количество}} Ошибка',
+ parallelModeEnableTitle: 'Параллельный режим включен',
+ parallelMode: 'Параллельный режим',
+ parallelPanelDesc: 'В параллельном режиме задачи в итерации поддерживают параллельное выполнение.',
+ parallelModeEnableDesc: 'В параллельном режиме задачи в итерациях поддерживают параллельное выполнение. Вы можете настроить это на панели свойств справа.',
+ MaxParallelismDesc: 'Максимальный параллелизм используется для управления количеством задач, выполняемых одновременно в одной итерации.',
+ answerNodeWarningDesc: 'Предупреждение о параллельном режиме: узлы ответов, присвоение переменных диалога и постоянные операции чтения и записи в итерациях могут вызывать исключения.',
},
note: {
addNote: 'Добавить заметку',
diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts
index 82718ebc03..e6e25f6d0e 100644
--- a/web/i18n/tr-TR/workflow.ts
+++ b/web/i18n/tr-TR/workflow.ts
@@ -558,6 +558,23 @@ const translation = {
iteration_one: '{{count}} Yineleme',
iteration_other: '{{count}} Yineleme',
currentIteration: 'Mevcut Yineleme',
+ ErrorMethod: {
+ operationTerminated: 'Sonlandırıldı',
+ continueOnError: 'Hata Üzerine Devam Et',
+ removeAbnormalOutput: 'anormal çıktıyı kaldır',
+ },
+ parallelModeUpper: 'PARALEL MOD',
+ parallelMode: 'Paralel Mod',
+ MaxParallelismTitle: 'Maksimum paralellik',
+ error_one: '{{sayı}} Hata',
+ errorResponseMethod: 'Hata yanıtı yöntemi',
+ comma: ',',
+ parallelModeEnableTitle: 'Paralel Mod Etkin',
+ error_other: '{{sayı}} Hata',
+ parallelPanelDesc: 'Paralel modda, yinelemedeki görevler paralel yürütmeyi destekler.',
+ answerNodeWarningDesc: 'Paralel mod uyarısı: Yinelemeler içindeki yanıt düğümleri, konuşma değişkeni atamaları ve kalıcı okuma/yazma işlemleri özel durumlara neden olabilir.',
+ parallelModeEnableDesc: 'Paralel modda, yinelemeler içindeki görevler paralel yürütmeyi destekler. Bunu sağdaki özellikler panelinde yapılandırabilirsiniz.',
+ MaxParallelismDesc: 'Maksimum paralellik, tek bir yinelemede aynı anda yürütülen görevlerin sayısını kontrol etmek için kullanılır.',
},
note: {
addNote: 'Not Ekle',
diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts
index 1828b6499f..663b5e4c13 100644
--- a/web/i18n/uk-UA/workflow.ts
+++ b/web/i18n/uk-UA/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Ітерація',
iteration_other: '{{count}} Ітерацій',
currentIteration: 'Поточна ітерація',
+ ErrorMethod: {
+ operationTerminated: 'Припинено',
+ continueOnError: 'Продовжити після помилки',
+ removeAbnormalOutput: 'видалити-ненормальний-вивід',
+ },
+ error_one: '{{count}} Помилка',
+ comma: ',',
+ MaxParallelismTitle: 'Максимальна паралельність',
+ parallelModeUpper: 'ПАРАЛЕЛЬНИЙ РЕЖИМ',
+ error_other: '{{count}} Помилки',
+ parallelMode: 'Паралельний режим',
+ parallelModeEnableTitle: 'Увімкнено паралельний режим',
+ errorResponseMethod: 'Метод реагування на помилку',
+ parallelPanelDesc: 'У паралельному режимі завдання в ітерації підтримують паралельне виконання.',
+ parallelModeEnableDesc: 'У паралельному режимі завдання всередині ітерацій підтримують паралельне виконання. Ви можете налаштувати це на панелі властивостей праворуч.',
+ MaxParallelismDesc: 'Максимальний паралелізм використовується для контролю числа завдань, що виконуються одночасно за одну ітерацію.',
+ answerNodeWarningDesc: 'Попередження в паралельному режимі: вузли відповідей, призначення змінних розмови та постійні операції читання/запису в межах ітерацій можуть спричинити винятки.',
},
note: {
editor: {
diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts
index 2866af8a2a..1176fdd2b5 100644
--- a/web/i18n/vi-VN/workflow.ts
+++ b/web/i18n/vi-VN/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}} Lặp',
iteration_other: '{{count}} Lặp',
currentIteration: 'Lặp hiện tại',
+ ErrorMethod: {
+ operationTerminated: 'Chấm dứt',
+ removeAbnormalOutput: 'loại bỏ-bất thường-đầu ra',
+ continueOnError: 'Tiếp tục lỗi',
+ },
+ comma: ',',
+ error_other: '{{đếm}} Lỗi',
+ error_one: '{{đếm}} Lỗi',
+ MaxParallelismTitle: 'Song song tối đa',
+ parallelPanelDesc: 'Ở chế độ song song, các tác vụ trong quá trình lặp hỗ trợ thực thi song song.',
+ parallelMode: 'Chế độ song song',
+ parallelModeEnableTitle: 'Đã bật Chế độ song song',
+ errorResponseMethod: 'Phương pháp phản hồi lỗi',
+ MaxParallelismDesc: 'Tính song song tối đa được sử dụng để kiểm soát số lượng tác vụ được thực hiện đồng thời trong một lần lặp.',
+ answerNodeWarningDesc: 'Cảnh báo chế độ song song: Các nút trả lời, bài tập biến hội thoại và các thao tác đọc/ghi liên tục trong các lần lặp có thể gây ra ngoại lệ.',
+ parallelModeEnableDesc: 'Trong chế độ song song, các tác vụ trong các lần lặp hỗ trợ thực thi song song. Bạn có thể định cấu hình điều này trong bảng thuộc tính ở bên phải.',
+ parallelModeUpper: 'CHẾ ĐỘ SONG SONG',
},
note: {
editor: {
diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts
index 515d0fe235..1229ba8c03 100644
--- a/web/i18n/zh-Hans/workflow.ts
+++ b/web/i18n/zh-Hans/workflow.ts
@@ -556,6 +556,23 @@ const translation = {
iteration_one: '{{count}}个迭代',
iteration_other: '{{count}}个迭代',
currentIteration: '当前迭代',
+ comma: ',',
+ error_one: '{{count}}个失败',
+ error_other: '{{count}}个失败',
+ parallelMode: '并行模式',
+ parallelModeUpper: '并行模式',
+ parallelModeEnableTitle: '并行模式启用',
+ parallelModeEnableDesc: '启用并行模式时迭代内的任务支持并行执行。你可以在右侧的属性面板中进行配置。',
+ parallelPanelDesc: '在并行模式下,迭代中的任务支持并行执行。',
+ MaxParallelismTitle: '最大并行度',
+ MaxParallelismDesc: '最大并行度用于控制单次迭代中同时执行的任务数量。',
+ errorResponseMethod: '错误响应方法',
+ ErrorMethod: {
+ operationTerminated: '错误时终止',
+ continueOnError: '忽略错误并继续',
+ removeAbnormalOutput: '移除错误输出',
+ },
+ answerNodeWarningDesc: '并行模式警告:在迭代中,回答节点、会话变量赋值和工具持久读/写操作可能会导致异常。',
},
note: {
addNote: '添加注释',
diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts
index d65b3999d2..f3fbfdedc2 100644
--- a/web/i18n/zh-Hant/workflow.ts
+++ b/web/i18n/zh-Hant/workflow.ts
@@ -557,6 +557,23 @@ const translation = {
iteration_one: '{{count}}個迭代',
iteration_other: '{{count}}個迭代',
currentIteration: '當前迭代',
+ ErrorMethod: {
+ operationTerminated: '終止',
+ removeAbnormalOutput: 'remove-abnormal-output',
+ continueOnError: '出錯時繼續',
+ },
+ comma: ',',
+ parallelMode: '並行模式',
+ parallelModeEnableTitle: 'Parallel Mode 已啟用',
+ MaxParallelismTitle: '最大並行度',
+ parallelModeUpper: '並行模式',
+ parallelPanelDesc: '在並行模式下,反覆運算中的任務支援並行執行。',
+ error_one: '{{count}}錯誤',
+ errorResponseMethod: '錯誤回應方法',
+ parallelModeEnableDesc: '在並行模式下,反覆運算中的任務支援並行執行。您可以在右側的 properties 面板中進行配置。',
+ answerNodeWarningDesc: '並行模式警告:反覆運算中的應答節點、對話變數賦值和持久讀/寫操作可能會導致異常。',
+ error_other: '{{count}}錯誤',
+ MaxParallelismDesc: '最大並行度用於控制在單個反覆運算中同時執行的任務數。',
},
note: {
editor: {
diff --git a/web/models/common.ts b/web/models/common.ts
index 9ab27a6018..dc2b1120b9 100644
--- a/web/models/common.ts
+++ b/web/models/common.ts
@@ -216,7 +216,7 @@ export type FileUploadConfigResponse = {
file_size_limit: number // default is 15MB
audio_file_size_limit?: number // default is 50MB
video_file_size_limit?: number // default is 100MB
-
+ workflow_file_upload_limit?: number // default is 10
}
export type InvitationResult = {
diff --git a/web/service/base.ts b/web/service/base.ts
index fbdd5c1fd3..fcf8d8bd7d 100644
--- a/web/service/base.ts
+++ b/web/service/base.ts
@@ -1,3 +1,4 @@
+import { refreshAccessTokenOrRelogin } from './refresh-token'
import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
import Toast from '@/app/components/base/toast'
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
@@ -356,39 +357,8 @@ const baseFetch = (
if (!/^(2|3)\d{2}$/.test(String(res.status))) {
const bodyJson = res.json()
switch (res.status) {
- case 401: {
- if (isPublicAPI) {
- return bodyJson.then((data: ResponseError) => {
- if (data.code === 'web_sso_auth_required')
- requiredWebSSOLogin()
-
- if (data.code === 'unauthorized') {
- removeAccessToken()
- globalThis.location.reload()
- }
-
- return Promise.reject(data)
- })
- }
- const loginUrl = `${globalThis.location.origin}/signin`
- bodyJson.then((data: ResponseError) => {
- if (data.code === 'init_validate_failed' && IS_CE_EDITION && !silent)
- Toast.notify({ type: 'error', message: data.message, duration: 4000 })
- else if (data.code === 'not_init_validated' && IS_CE_EDITION)
- globalThis.location.href = `${globalThis.location.origin}/init`
- else if (data.code === 'not_setup' && IS_CE_EDITION)
- globalThis.location.href = `${globalThis.location.origin}/install`
- else if (location.pathname !== '/signin' || !IS_CE_EDITION)
- globalThis.location.href = loginUrl
- else if (!silent)
- Toast.notify({ type: 'error', message: data.message })
- }).catch(() => {
- // Handle any other errors
- globalThis.location.href = loginUrl
- })
-
- break
- }
+ case 401:
+ return Promise.reject(resClone)
case 403:
bodyJson.then((data: ResponseError) => {
if (!silent)
@@ -484,7 +454,9 @@ export const upload = (options: any, isPublicAPI?: boolean, url?: string, search
export const ssePost = (
url: string,
fetchOptions: FetchOptionType,
- {
+ otherOptions: IOtherOptions,
+) => {
+ const {
isPublicAPI = false,
onData,
onCompleted,
@@ -507,8 +479,7 @@ export const ssePost = (
onTextReplace,
onError,
getAbortController,
- }: IOtherOptions,
-) => {
+ } = otherOptions
const abortController = new AbortController()
const options = Object.assign({}, baseOptions, {
@@ -532,21 +503,29 @@ export const ssePost = (
globalThis.fetch(urlWithPrefix, options as RequestInit)
.then((res) => {
if (!/^(2|3)\d{2}$/.test(String(res.status))) {
- res.json().then((data: any) => {
- if (isPublicAPI) {
- if (data.code === 'web_sso_auth_required')
- requiredWebSSOLogin()
+ if (res.status === 401) {
+ refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
+ ssePost(url, fetchOptions, otherOptions)
+ }).catch(() => {
+ res.json().then((data: any) => {
+ if (isPublicAPI) {
+ if (data.code === 'web_sso_auth_required')
+ requiredWebSSOLogin()
- if (data.code === 'unauthorized') {
- removeAccessToken()
- globalThis.location.reload()
- }
- if (res.status === 401)
- return
- }
- Toast.notify({ type: 'error', message: data.message || 'Server Error' })
- })
- onError?.('Server Error')
+ if (data.code === 'unauthorized') {
+ removeAccessToken()
+ globalThis.location.reload()
+ }
+ }
+ })
+ })
+ }
+ else {
+ res.json().then((data) => {
+ Toast.notify({ type: 'error', message: data.message || 'Server Error' })
+ })
+ onError?.('Server Error')
+ }
return
}
return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => {
@@ -568,7 +547,54 @@ export const ssePost = (
// base request
export const request = (url: string, options = {}, otherOptions?: IOtherOptions) => {
- return baseFetch(url, options, otherOptions || {})
+ return new Promise((resolve, reject) => {
+ const otherOptionsForBaseFetch = otherOptions || {}
+ baseFetch(url, options, otherOptionsForBaseFetch).then(resolve).catch((errResp) => {
+ if (errResp?.status === 401) {
+ return refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
+ baseFetch(url, options, otherOptionsForBaseFetch).then(resolve).catch(reject)
+ }).catch(() => {
+ const {
+ isPublicAPI = false,
+ silent,
+ } = otherOptionsForBaseFetch
+ const bodyJson = errResp.json()
+ if (isPublicAPI) {
+ return bodyJson.then((data: ResponseError) => {
+ if (data.code === 'web_sso_auth_required')
+ requiredWebSSOLogin()
+
+ if (data.code === 'unauthorized') {
+ removeAccessToken()
+ globalThis.location.reload()
+ }
+
+ return Promise.reject(data)
+ })
+ }
+ const loginUrl = `${globalThis.location.origin}/signin`
+ bodyJson.then((data: ResponseError) => {
+ if (data.code === 'init_validate_failed' && IS_CE_EDITION && !silent)
+ Toast.notify({ type: 'error', message: data.message, duration: 4000 })
+ else if (data.code === 'not_init_validated' && IS_CE_EDITION)
+ globalThis.location.href = `${globalThis.location.origin}/init`
+ else if (data.code === 'not_setup' && IS_CE_EDITION)
+ globalThis.location.href = `${globalThis.location.origin}/install`
+ else if (location.pathname !== '/signin' || !IS_CE_EDITION)
+ globalThis.location.href = loginUrl
+ else if (!silent)
+ Toast.notify({ type: 'error', message: data.message })
+ }).catch(() => {
+ // Handle any other errors
+ globalThis.location.href = loginUrl
+ })
+ })
+ }
+ else {
+ reject(errResp)
+ }
+ })
+ })
}
// request methods
diff --git a/web/service/refresh-token.ts b/web/service/refresh-token.ts
new file mode 100644
index 0000000000..8bd2215041
--- /dev/null
+++ b/web/service/refresh-token.ts
@@ -0,0 +1,75 @@
+import { apiPrefix } from '@/config'
+import { fetchWithRetry } from '@/utils'
+
+let isRefreshing = false
+function waitUntilTokenRefreshed() {
+ return new Promise((resolve, reject) => {
+ function _check() {
+ const isRefreshingSign = localStorage.getItem('is_refreshing')
+ if ((isRefreshingSign && isRefreshingSign === '1') || isRefreshing) {
+ setTimeout(() => {
+ _check()
+ }, 1000)
+ }
+ else {
+ resolve()
+ }
+ }
+ _check()
+ })
+}
+
+// only one request can send
+async function getNewAccessToken(): Promise {
+ try {
+ const isRefreshingSign = localStorage.getItem('is_refreshing')
+ if ((isRefreshingSign && isRefreshingSign === '1') || isRefreshing) {
+ await waitUntilTokenRefreshed()
+ }
+ else {
+ globalThis.localStorage.setItem('is_refreshing', '1')
+ isRefreshing = true
+ const refresh_token = globalThis.localStorage.getItem('refresh_token')
+
+ // Do not use baseFetch to refresh tokens.
+ // If a 401 response occurs and baseFetch itself attempts to refresh the token,
+ // it can lead to an infinite loop if the refresh attempt also returns 401.
+ // To avoid this, handle token refresh separately in a dedicated function
+ // that does not call baseFetch and uses a single retry mechanism.
+ const [error, ret] = await fetchWithRetry(globalThis.fetch(`${apiPrefix}/refresh-token`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json;utf-8',
+ },
+ body: JSON.stringify({ refresh_token }),
+ }))
+ if (error) {
+ return Promise.reject(error)
+ }
+ else {
+ if (ret.status === 401)
+ return Promise.reject(ret)
+
+ const { data } = await ret.json()
+ globalThis.localStorage.setItem('console_token', data.access_token)
+ globalThis.localStorage.setItem('refresh_token', data.refresh_token)
+ }
+ }
+ }
+ catch (error) {
+ console.error(error)
+ return Promise.reject(error)
+ }
+ finally {
+ isRefreshing = false
+ globalThis.localStorage.removeItem('is_refreshing')
+ }
+}
+
+export async function refreshAccessTokenOrRelogin(timeout: number) {
+ return Promise.race([new Promise((resolve, reject) => setTimeout(() => {
+ isRefreshing = false
+ globalThis.localStorage.removeItem('is_refreshing')
+ reject(new Error('request timeout'))
+ }, timeout)), getNewAccessToken()])
+}
diff --git a/web/types/workflow.ts b/web/types/workflow.ts
index 810026b084..3c0675b605 100644
--- a/web/types/workflow.ts
+++ b/web/types/workflow.ts
@@ -19,6 +19,7 @@ export type NodeTracing = {
process_data: any
outputs?: any
status: string
+ parallel_run_id?: string
error?: string
elapsed_time: number
execution_metadata: {
@@ -31,6 +32,7 @@ export type NodeTracing = {
parallel_start_node_id?: string
parent_parallel_id?: string
parent_parallel_start_node_id?: string
+ parallel_mode_run_id?: string
}
metadata: {
iterator_length: number
@@ -121,6 +123,7 @@ export type NodeStartedResponse = {
id: string
node_id: string
iteration_id?: string
+ parallel_run_id?: string
node_type: string
index: number
predecessor_node_id?: string
@@ -166,6 +169,7 @@ export type NodeFinishedResponse = {
parallel_start_node_id?: string
iteration_index?: number
iteration_id?: string
+ parallel_mode_run_id: string
}
created_at: number
files?: FileResponse[]
@@ -200,6 +204,7 @@ export type IterationNextResponse = {
output: any
extras?: any
created_at: number
+ parallel_mode_run_id: string
execution_metadata: {
parallel_id?: string
}