mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 08:58:09 +08:00
feat: fix i18n missing keys and merge upstream/main (#24615)
Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: kenwoodjw <blackxin55+@gmail.com> Signed-off-by: Yongtao Huang <yongtaoh2022@gmail.com> Signed-off-by: yihong0618 <zouzou0208@gmail.com> Signed-off-by: zhanluxianshen <zhanluxianshen@163.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: GuanMu <ballmanjq@gmail.com> Co-authored-by: Davide Delbianco <davide.delbianco@outlook.com> Co-authored-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com> Co-authored-by: kenwoodjw <blackxin55+@gmail.com> Co-authored-by: Yongtao Huang <yongtaoh2022@gmail.com> Co-authored-by: Yongtao Huang <99629139+hyongtao-db@users.noreply.github.com> Co-authored-by: Qiang Lee <18018968632@163.com> Co-authored-by: 李强04 <liqiang04@gaotu.cn> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: Matri Qi <matrixdom@126.com> Co-authored-by: huayaoyue6 <huayaoyue@163.com> Co-authored-by: Bowen Liang <liangbowen@gf.com.cn> Co-authored-by: znn <jubinkumarsoni@gmail.com> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: yihong <zouzou0208@gmail.com> Co-authored-by: Muke Wang <shaodwaaron@gmail.com> Co-authored-by: wangmuke <wangmuke@kingsware.cn> Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Co-authored-by: quicksand <quicksandzn@gmail.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: Eric Guo <eric.guocz@gmail.com> Co-authored-by: Zhedong Cen <cenzhedong2@126.com> Co-authored-by: jiangbo721 <jiangbo721@163.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: hjlarry <25834719+hjlarry@users.noreply.github.com> Co-authored-by: lxsummer <35754229+lxjustdoit@users.noreply.github.com> Co-authored-by: 湛露先生 <zhanluxianshen@163.com> Co-authored-by: Guangdong Liu <liugddx@gmail.com> Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Yessenia-d <yessenia.contact@gmail.com> Co-authored-by: huangzhuo1949 <167434202+huangzhuo1949@users.noreply.github.com> Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com> Co-authored-by: 17hz <0x149527@gmail.com> Co-authored-by: Amy <1530140574@qq.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: Nite Knite <nkCoding@gmail.com> Co-authored-by: Yeuoly <45712896+Yeuoly@users.noreply.github.com> Co-authored-by: Petrus Han <petrus.hanks@gmail.com> Co-authored-by: iamjoel <2120155+iamjoel@users.noreply.github.com> Co-authored-by: Kalo Chin <frog.beepers.0n@icloud.com> Co-authored-by: Ujjwal Maurya <ujjwalsbx@gmail.com> Co-authored-by: Maries <xh001x@hotmail.com>
This commit is contained in:
@ -0,0 +1,38 @@
|
||||
'use client'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type Props = {
|
||||
name: string
|
||||
value: boolean
|
||||
required?: boolean
|
||||
onChange: (value: boolean) => void
|
||||
}
|
||||
|
||||
const BoolInput: FC<Props> = ({
|
||||
value,
|
||||
onChange,
|
||||
name,
|
||||
required,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const handleChange = useCallback(() => {
|
||||
onChange(!value)
|
||||
}, [value, onChange])
|
||||
return (
|
||||
<div className='flex h-6 items-center gap-2'>
|
||||
<Checkbox
|
||||
className='!h-4 !w-4'
|
||||
checked={!!value}
|
||||
onCheck={handleChange}
|
||||
/>
|
||||
<div className='system-sm-medium flex items-center gap-1 text-text-secondary'>
|
||||
{name}
|
||||
{!required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(BoolInput)
|
||||
@ -25,6 +25,7 @@ import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||
import BoolInput from './bool-input'
|
||||
|
||||
type Props = {
|
||||
payload: InputVar
|
||||
@ -92,6 +93,7 @@ const FormItem: FC<Props> = ({
|
||||
return ''
|
||||
})()
|
||||
|
||||
const isBooleanType = type === InputVarType.checkbox
|
||||
const isArrayLikeType = [InputVarType.contexts, InputVarType.iterator].includes(type)
|
||||
const isContext = type === InputVarType.contexts
|
||||
const isIterator = type === InputVarType.iterator
|
||||
@ -113,7 +115,7 @@ const FormItem: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
{!isArrayLikeType && (
|
||||
{!isArrayLikeType && !isBooleanType && (
|
||||
<div className='system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary'>
|
||||
<div className='truncate'>{typeof payload.label === 'object' ? nodeKey : payload.label}</div>
|
||||
{!payload.required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>}
|
||||
@ -166,6 +168,15 @@ const FormItem: FC<Props> = ({
|
||||
)
|
||||
}
|
||||
|
||||
{isBooleanType && (
|
||||
<BoolInput
|
||||
name={payload.label as string}
|
||||
value={!!value}
|
||||
required={payload.required}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
type === InputVarType.json && (
|
||||
<CodeEditor
|
||||
@ -176,6 +187,18 @@ const FormItem: FC<Props> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
{ type === InputVarType.jsonObject && (
|
||||
<CodeEditor
|
||||
value={value}
|
||||
language={CodeLanguage.json}
|
||||
onChange={onChange}
|
||||
noWrapper
|
||||
className='bg h-[80px] overflow-y-auto rounded-[10px] bg-components-input-bg-normal p-1'
|
||||
placeholder={
|
||||
<div className='whitespace-pre'>{payload.json_schema}</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{(type === InputVarType.singleFile) && (
|
||||
<FileUploaderInAttachmentWrapper
|
||||
value={singleFileValue}
|
||||
|
||||
@ -32,6 +32,8 @@ export type BeforeRunFormProps = {
|
||||
} & Partial<SpecialResultPanelProps>
|
||||
|
||||
function formatValue(value: string | any, type: InputVarType) {
|
||||
if(type === InputVarType.checkbox)
|
||||
return !!value
|
||||
if(value === undefined || value === null)
|
||||
return value
|
||||
if (type === InputVarType.number)
|
||||
@ -87,7 +89,7 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
|
||||
|
||||
form.inputs.forEach((input) => {
|
||||
const value = form.values[input.variable] as any
|
||||
if (!errMsg && input.required && !(input.variable in existVarValuesInForm) && (value === '' || value === undefined || value === null || (input.type === InputVarType.files && value.length === 0)))
|
||||
if (!errMsg && input.required && (input.type !== InputVarType.checkbox) && !(input.variable in existVarValuesInForm) && (value === '' || value === undefined || value === null || (input.type === InputVarType.files && value.length === 0)))
|
||||
errMsg = t('workflow.errorMsg.fieldRequired', { field: typeof input.label === 'object' ? input.label.variable : input.label })
|
||||
|
||||
if (!errMsg && (input.type === InputVarType.singleFile || input.type === InputVarType.multiFiles) && value) {
|
||||
|
||||
@ -64,7 +64,7 @@ const FormInputItem: FC<Props> = ({
|
||||
const isSelect = type === FormTypeEnum.select || type === FormTypeEnum.dynamicSelect
|
||||
const isAppSelector = type === FormTypeEnum.appSelector
|
||||
const isModelSelector = type === FormTypeEnum.modelSelector
|
||||
const showTypeSwitch = isNumber || isObject || isArray
|
||||
const showTypeSwitch = isNumber || isBoolean || isObject || isArray
|
||||
const isConstant = varInput?.type === VarKindType.constant || !varInput?.type
|
||||
const showVariableSelector = isFile || varInput?.type === VarKindType.variable
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { RiAlignLeft, RiCheckboxMultipleLine, RiFileCopy2Line, RiFileList2Line, RiHashtag, RiTextSnippet } from '@remixicon/react'
|
||||
import { RiAlignLeft, RiBracesLine, RiCheckboxLine, RiCheckboxMultipleLine, RiFileCopy2Line, RiFileList2Line, RiHashtag, RiTextSnippet } from '@remixicon/react'
|
||||
import { InputVarType } from '../../../types'
|
||||
|
||||
type Props = {
|
||||
@ -15,6 +15,8 @@ const getIcon = (type: InputVarType) => {
|
||||
[InputVarType.paragraph]: RiAlignLeft,
|
||||
[InputVarType.select]: RiCheckboxMultipleLine,
|
||||
[InputVarType.number]: RiHashtag,
|
||||
[InputVarType.checkbox]: RiCheckboxLine,
|
||||
[InputVarType.jsonObject]: RiBracesLine,
|
||||
[InputVarType.singleFile]: RiFileList2Line,
|
||||
[InputVarType.multiFiles]: RiFileCopy2Line,
|
||||
} as any)[type] || RiTextSnippet
|
||||
|
||||
@ -24,6 +24,7 @@ import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/v
|
||||
import type { Field as StructField } from '@/app/components/workflow/nodes/llm/types'
|
||||
|
||||
import {
|
||||
AGENT_OUTPUT_STRUCT,
|
||||
HTTP_REQUEST_OUTPUT_STRUCT,
|
||||
KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT,
|
||||
LLM_OUTPUT_STRUCT,
|
||||
@ -56,11 +57,13 @@ export const hasValidChildren = (children: any): boolean => {
|
||||
)
|
||||
}
|
||||
|
||||
const inputVarTypeToVarType = (type: InputVarType): VarType => {
|
||||
export const inputVarTypeToVarType = (type: InputVarType): VarType => {
|
||||
return ({
|
||||
[InputVarType.number]: VarType.number,
|
||||
[InputVarType.checkbox]: VarType.boolean,
|
||||
[InputVarType.singleFile]: VarType.file,
|
||||
[InputVarType.multiFiles]: VarType.arrayFile,
|
||||
[InputVarType.jsonObject]: VarType.object,
|
||||
} as any)[type] || VarType.string
|
||||
}
|
||||
|
||||
@ -227,14 +230,27 @@ const formatItem = (
|
||||
variables,
|
||||
} = data as StartNodeType
|
||||
res.vars = variables.map((v) => {
|
||||
return {
|
||||
const type = inputVarTypeToVarType(v.type)
|
||||
const varRes: Var = {
|
||||
variable: v.variable,
|
||||
type: inputVarTypeToVarType(v.type),
|
||||
type,
|
||||
isParagraph: v.type === InputVarType.paragraph,
|
||||
isSelect: v.type === InputVarType.select,
|
||||
options: v.options,
|
||||
required: v.required,
|
||||
}
|
||||
try {
|
||||
if(type === VarType.object && v.json_schema) {
|
||||
varRes.children = {
|
||||
schema: JSON.parse(v.json_schema),
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error formatting variable:', error)
|
||||
}
|
||||
|
||||
return varRes
|
||||
})
|
||||
if (isChatMode) {
|
||||
res.vars.push({
|
||||
@ -498,6 +514,7 @@ const formatItem = (
|
||||
res.vars = [
|
||||
...outputs,
|
||||
...TOOL_OUTPUT_STRUCT,
|
||||
...AGENT_OUTPUT_STRUCT,
|
||||
]
|
||||
break
|
||||
}
|
||||
@ -688,6 +705,8 @@ const getIterationItemType = ({
|
||||
return VarType.string
|
||||
case VarType.arrayNumber:
|
||||
return VarType.number
|
||||
case VarType.arrayBoolean:
|
||||
return VarType.boolean
|
||||
case VarType.arrayObject:
|
||||
return VarType.object
|
||||
case VarType.array:
|
||||
@ -741,6 +760,8 @@ const getLoopItemType = ({
|
||||
return VarType.number
|
||||
case VarType.arrayObject:
|
||||
return VarType.object
|
||||
case VarType.arrayBoolean:
|
||||
return VarType.boolean
|
||||
case VarType.array:
|
||||
return VarType.any
|
||||
case VarType.arrayFile:
|
||||
|
||||
@ -18,7 +18,7 @@ type Props = {
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const TYPES = [VarType.string, VarType.number, VarType.arrayNumber, VarType.arrayString, VarType.arrayObject, VarType.object]
|
||||
const TYPES = [VarType.string, VarType.number, VarType.boolean, VarType.arrayNumber, VarType.arrayString, VarType.arrayBoolean, VarType.arrayObject, VarType.object]
|
||||
const VarReferencePicker: FC<Props> = ({
|
||||
readonly,
|
||||
className,
|
||||
|
||||
@ -475,7 +475,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
isRunAfterSingleRun={isRunAfterSingleRun}
|
||||
updateNodeRunningStatus={updateNodeRunningStatus}
|
||||
onSingleRunClicked={handleSingleRun}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeInfo={nodeInfo!}
|
||||
singleRunResult={runResult!}
|
||||
isPaused={isPaused}
|
||||
{...passedLogParams}
|
||||
|
||||
@ -67,7 +67,6 @@ const LastRun: FC<Props> = ({
|
||||
updateNodeRunningStatus(hidePageOneStepFinishedStatus)
|
||||
resetHidePageStatus()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isOneStepRunSucceed, isOneStepRunFailed, oneStepRunRunningStatus])
|
||||
|
||||
useEffect(() => {
|
||||
@ -77,7 +76,6 @@ const LastRun: FC<Props> = ({
|
||||
|
||||
useEffect(() => {
|
||||
resetHidePageStatus()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [nodeId])
|
||||
|
||||
const handlePageVisibilityChange = useCallback(() => {
|
||||
@ -117,7 +115,7 @@ const LastRun: FC<Props> = ({
|
||||
status={isPaused ? NodeRunningStatus.Stopped : ((runResult as any).status || otherResultPanelProps.status)}
|
||||
total_tokens={(runResult as any)?.execution_metadata?.total_tokens || otherResultPanelProps?.total_tokens}
|
||||
created_by={(runResult as any)?.created_by_account?.created_by || otherResultPanelProps?.created_by}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeInfo={runResult as NodeTracing}
|
||||
showSteps={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -146,8 +146,8 @@ const useLastRun = <T>({
|
||||
checkValid,
|
||||
} = oneStepRunRes
|
||||
|
||||
const nodeInfo = runResult
|
||||
const {
|
||||
nodeInfo,
|
||||
...singleRunParams
|
||||
} = useSingleRunFormParamsHooks(blockType)({
|
||||
id,
|
||||
@ -197,7 +197,6 @@ const useLastRun = <T>({
|
||||
setTabType(TabType.lastRun)
|
||||
|
||||
setInitShowLastRunTab(false)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [initShowLastRunTab])
|
||||
const invalidLastRun = useInvalidLastRun(appId!, id)
|
||||
|
||||
|
||||
@ -94,6 +94,8 @@ const varTypeToInputVarType = (type: VarType, {
|
||||
return InputVarType.paragraph
|
||||
if (type === VarType.number)
|
||||
return InputVarType.number
|
||||
if (type === VarType.boolean)
|
||||
return InputVarType.checkbox
|
||||
if ([VarType.object, VarType.array, VarType.arrayNumber, VarType.arrayString, VarType.arrayObject].includes(type))
|
||||
return InputVarType.json
|
||||
if (type === VarType.file)
|
||||
@ -185,11 +187,11 @@ const useOneStepRun = <T>({
|
||||
const isPaused = isPausedRef.current
|
||||
|
||||
// The backend don't support pause the single run, so the frontend handle the pause state.
|
||||
if(isPaused)
|
||||
if (isPaused)
|
||||
return
|
||||
|
||||
const canRunLastRun = !isRunAfterSingleRun || runningStatus === NodeRunningStatus.Succeeded
|
||||
if(!canRunLastRun) {
|
||||
if (!canRunLastRun) {
|
||||
doSetRunResult(data)
|
||||
return
|
||||
}
|
||||
@ -199,9 +201,9 @@ const useOneStepRun = <T>({
|
||||
const { getNodes } = store.getState()
|
||||
const nodes = getNodes()
|
||||
appendNodeInspectVars(id, vars, nodes)
|
||||
if(data?.status === NodeRunningStatus.Succeeded) {
|
||||
if (data?.status === NodeRunningStatus.Succeeded) {
|
||||
invalidLastRun()
|
||||
if(isStartNode)
|
||||
if (isStartNode)
|
||||
invalidateSysVarValues()
|
||||
invalidateConversationVarValues() // loop, iteration, variable assigner node can update the conversation variables, but to simple the logic(some nodes may also can update in the future), all nodes refresh.
|
||||
}
|
||||
@ -218,21 +220,21 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
}
|
||||
const checkValidWrap = () => {
|
||||
if(!checkValid)
|
||||
if (!checkValid)
|
||||
return { isValid: true, errorMessage: '' }
|
||||
const res = checkValid(data, t, moreDataForCheckValid)
|
||||
if(!res.isValid) {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
},
|
||||
})
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: res.errorMessage,
|
||||
})
|
||||
if (!res.isValid) {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
},
|
||||
})
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: res.errorMessage,
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -251,7 +253,6 @@ const useOneStepRun = <T>({
|
||||
const { isValid } = checkValidWrap()
|
||||
setCanShowSingleRun(isValid)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data._isSingleRun])
|
||||
|
||||
useEffect(() => {
|
||||
@ -293,9 +294,9 @@ const useOneStepRun = <T>({
|
||||
if (!isIteration && !isLoop) {
|
||||
const isStartNode = data.type === BlockEnum.Start
|
||||
const postData: Record<string, any> = {}
|
||||
if(isStartNode) {
|
||||
if (isStartNode) {
|
||||
const { '#sys.query#': query, '#sys.files#': files, ...inputs } = submitData
|
||||
if(isChatMode)
|
||||
if (isChatMode)
|
||||
postData.conversation_id = ''
|
||||
|
||||
postData.inputs = inputs
|
||||
@ -317,7 +318,7 @@ const useOneStepRun = <T>({
|
||||
{
|
||||
onWorkflowStarted: noop,
|
||||
onWorkflowFinished: (params) => {
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
@ -396,7 +397,7 @@ const useOneStepRun = <T>({
|
||||
setIterationRunResult(newIterationRunResult)
|
||||
},
|
||||
onError: () => {
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
@ -420,7 +421,7 @@ const useOneStepRun = <T>({
|
||||
{
|
||||
onWorkflowStarted: noop,
|
||||
onWorkflowFinished: (params) => {
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
@ -500,7 +501,7 @@ const useOneStepRun = <T>({
|
||||
setLoopRunResult(newLoopRunResult)
|
||||
},
|
||||
onError: () => {
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
@ -522,7 +523,7 @@ const useOneStepRun = <T>({
|
||||
hasError = true
|
||||
invalidLastRun()
|
||||
if (!isIteration && !isLoop) {
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
@ -544,11 +545,11 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
}
|
||||
}
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
|
||||
if (!isIteration && !isLoop && !hasError) {
|
||||
if(isPausedRef.current)
|
||||
if (isPausedRef.current)
|
||||
return
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
@ -587,7 +588,7 @@ const useOneStepRun = <T>({
|
||||
}
|
||||
}
|
||||
return {
|
||||
label: item.label || item.variable,
|
||||
label: (typeof item.label === 'object' ? item.label.variable : item.label) || item.variable,
|
||||
variable: item.variable,
|
||||
type: varTypeToInputVarType(originalVar.type, {
|
||||
isSelect: !!originalVar.isSelect,
|
||||
|
||||
Reference in New Issue
Block a user