diff --git a/api/libs/oauth_data_source.py b/api/libs/oauth_data_source.py index 48249e4a353e10..1d39abd8fa7886 100644 --- a/api/libs/oauth_data_source.py +++ b/api/libs/oauth_data_source.py @@ -253,6 +253,8 @@ def notion_block_parent_page_id(self, access_token: str, block_id: str): } response = requests.get(url=f"{self._NOTION_BLOCK_SEARCH}/{block_id}", headers=headers) response_json = response.json() + if response.status_code != 200: + raise ValueError(f"Error fetching block parent page ID: {response_json.message}") parent = response_json["parent"] parent_type = parent["type"] if parent_type == "block_id": diff --git a/api/tests/unit_tests/configs/test_dify_config.py b/api/tests/unit_tests/configs/test_dify_config.py index 0eb310a51a335b..385eb08c3681ff 100644 --- a/api/tests/unit_tests/configs/test_dify_config.py +++ b/api/tests/unit_tests/configs/test_dify_config.py @@ -37,7 +37,11 @@ def test_dify_config_undefined_entry(example_env_file): assert config["LOG_LEVEL"] == "INFO" +# NOTE: If there is a `.env` file in your Workspace, this test might not succeed as expected. +# This is due to `pymilvus` loading all the variables from the `.env` file into `os.environ`. def test_dify_config(example_env_file): + # clear system environment variables + os.environ.clear() # load dotenv file with pydantic-settings config = DifyConfig(_env_file=example_env_file) diff --git a/web/app/components/base/mermaid/index.tsx b/web/app/components/base/mermaid/index.tsx index 88c5386fb7a664..bcc30ca9393623 100644 --- a/web/app/components/base/mermaid/index.tsx +++ b/web/app/components/base/mermaid/index.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import mermaid from 'mermaid' import { usePrevious } from 'ahooks' -import CryptoJS from 'crypto-js' import { useTranslation } from 'react-i18next' import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' import LoadingAnim from '@/app/components/base/chat/chat/loading-anim' @@ -14,12 +13,6 @@ mermaidAPI = null if (typeof window !== 'undefined') mermaidAPI = mermaid.mermaidAPI -const style = { - minWidth: '480px', - height: 'auto', - overflow: 'auto', -} - const svgToBase64 = (svgGraph: string) => { const svgBytes = new TextEncoder().encode(svgGraph) const blob = new Blob([svgBytes], { type: 'image/svg+xml;charset=utf-8' }) @@ -38,7 +31,6 @@ const Flowchart = React.forwardRef((props: { const [svgCode, setSvgCode] = useState(null) const [look, setLook] = useState<'classic' | 'handDrawn'>('classic') - const chartId = useRef(`flowchart_${CryptoJS.MD5(props.PrimitiveCode).toString()}`) const prevPrimitiveCode = usePrevious(props.PrimitiveCode) const [isLoading, setIsLoading] = useState(true) const timeRef = useRef() @@ -51,12 +43,10 @@ const Flowchart = React.forwardRef((props: { try { if (typeof window !== 'undefined' && mermaidAPI) { - const svgGraph = await mermaidAPI.render(chartId.current, PrimitiveCode) + const svgGraph = await mermaidAPI.render('flowchart', PrimitiveCode) const base64Svg: any = await svgToBase64(svgGraph.svg) setSvgCode(base64Svg) setIsLoading(false) - if (chartId.current && base64Svg) - localStorage.setItem(chartId.current, base64Svg) } } catch (error) { @@ -79,19 +69,11 @@ const Flowchart = React.forwardRef((props: { }, }) - localStorage.removeItem(chartId.current) renderFlowchart(props.PrimitiveCode) } }, [look]) useEffect(() => { - const cachedSvg: any = localStorage.getItem(chartId.current) - - if (cachedSvg) { - setSvgCode(cachedSvg) - setIsLoading(false) - return - } if (timeRef.current) clearTimeout(timeRef.current) @@ -130,8 +112,8 @@ const Flowchart = React.forwardRef((props: { { svgCode - &&
setImagePreviewUrl(svgCode)}> - {svgCode && mermaid_chart} + &&
setImagePreviewUrl(svgCode)}> + {svgCode && mermaid_chart}
} {isLoading diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx index 65f3dad3a21f6e..0073ac300b7a90 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx @@ -26,6 +26,7 @@ import { VarBlockIcon } from '@/app/components/workflow/block-icon' import { Line3 } from '@/app/components/base/icons/src/public/common' import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import Tooltip from '@/app/components/base/tooltip' +import { isExceptionVariable } from '@/app/components/workflow/utils' type WorkflowVariableBlockComponentProps = { nodeKey: string @@ -53,6 +54,7 @@ const WorkflowVariableBlockComponent = ({ const node = localWorkflowNodesMap![variables[0]] const isEnv = isENV(variables) const isChatVar = isConversationVar(variables) + const isException = isExceptionVariable(varName, node?.type) useEffect(() => { if (!editor.hasNodes([WorkflowVariableBlockNode])) @@ -98,10 +100,10 @@ const WorkflowVariableBlockComponent = ({
)}
- {!isEnv && !isChatVar && } + {!isEnv && !isChatVar && } {isEnv && } {isChatVar && } -
{varName}
+
{varName}
{ !node && !isEnv && !isChatVar && ( diff --git a/web/app/components/workflow/nodes/_base/components/variable-tag.tsx b/web/app/components/workflow/nodes/_base/components/variable-tag.tsx index 6e1b1ed1437c5d..0c5c3bde4bf171 100644 --- a/web/app/components/workflow/nodes/_base/components/variable-tag.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable-tag.tsx @@ -17,6 +17,7 @@ import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others import { getNodeInfoById, isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import Tooltip from '@/app/components/base/tooltip' import cn from '@/utils/classnames' +import { isExceptionVariable } from '@/app/components/workflow/utils' type VariableTagProps = { valueSelector: ValueSelector @@ -45,6 +46,7 @@ const VariableTag = ({ const isValid = Boolean(node) || isEnv || isChatVar const variableName = isSystemVar(valueSelector) ? valueSelector.slice(0).join('.') : valueSelector.slice(1).join('.') + const isException = isExceptionVariable(variableName, node?.data.type) const { t } = useTranslation() return ( @@ -67,12 +69,12 @@ const VariableTag = ({ )} - + )} {isEnv && } {isChatVar && }
{variableName} diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx index e4d354a615fe9a..3a4cece35c9863 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx @@ -36,6 +36,7 @@ import TypeSelector from '@/app/components/workflow/nodes/_base/components/selec import AddButton from '@/app/components/base/button/add-button' import Badge from '@/app/components/base/badge' import Tooltip from '@/app/components/base/tooltip' +import { isExceptionVariable } from '@/app/components/workflow/utils' const TRIGGER_DEFAULT_WIDTH = 227 @@ -224,16 +225,18 @@ const VarReferencePicker: FC = ({ isConstant: !!isConstant, }) - const { isEnv, isChatVar, isValidVar } = useMemo(() => { + const { isEnv, isChatVar, isValidVar, isException } = useMemo(() => { const isEnv = isENV(value as ValueSelector) const isChatVar = isConversationVar(value as ValueSelector) const isValidVar = Boolean(outputVarNode) || isEnv || isChatVar + const isException = isExceptionVariable(varName, outputVarNode?.type) return { isEnv, isChatVar, isValidVar, + isException, } - }, [value, outputVarNode]) + }, [value, outputVarNode, varName]) // 8(left/right-padding) + 14(icon) + 4 + 14 + 2 = 42 + 17 buff const availableWidth = triggerWidth - 56 @@ -335,7 +338,7 @@ const VarReferencePicker: FC = ({ {!hasValue && } {isEnv && } {isChatVar && } -
{varName}
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index 5ae3f6ed1bc86a..6c1bb1e719a89c 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -23,16 +23,10 @@ import { comparisonOperatorNotRequireValue, getOperators } from '../../utils' import ConditionNumberInput from '../condition-number-input' import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../default' import ConditionWrap from '../condition-wrap' -import VarReferenceVars from '../../../_base/components/variable/var-reference-vars' import ConditionOperator from './condition-operator' import ConditionInput from './condition-input' -import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' +import ConditionVarSelector from './condition-var-selector' import type { Node, NodeOutPutVar, @@ -147,7 +141,7 @@ const ConditionItem = ({ value: isArrayValue ? [value] : value, } doUpdateCondition(newCondition) - }, [condition, doUpdateCondition, fileAttr]) + }, [condition, doUpdateCondition, isArrayValue]) const isSelect = condition.comparison_operator && [ComparisonOperator.in, ComparisonOperator.notIn].includes(condition.comparison_operator) const selectOptions = useMemo(() => { @@ -241,35 +235,15 @@ const ConditionItem = ({ /> ) : ( - - setOpen(!open)}> -
- -
-
- -
- -
-
-
+ valueSelector={condition.variable_selector || []} + varType={condition.varType} + availableNodes={availableNodes} + nodesOutputVars={nodesOutputVars} + onChange={handleVarChange} + /> )}
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx new file mode 100644 index 00000000000000..68a012d1a0a3c0 --- /dev/null +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx @@ -0,0 +1,58 @@ +import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' +import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' +import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' +import type { Node, NodeOutPutVar, ValueSelector, Var, VarType } from '@/app/components/workflow/types' + +type ConditionVarSelectorProps = { + open: boolean + onOpenChange: (open: boolean) => void + valueSelector: ValueSelector + varType: VarType + availableNodes: Node[] + nodesOutputVars: NodeOutPutVar[] + onChange: (valueSelector: ValueSelector, varItem: Var) => void +} + +const ConditionVarSelector = ({ + open, + onOpenChange, + valueSelector, + varType, + availableNodes, + nodesOutputVars, + onChange, +}: ConditionVarSelectorProps) => { + return ( + + onOpenChange(!open)}> +
+ +
+
+ +
+ +
+
+
+ ) +} + +export default ConditionVarSelector diff --git a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx index 182e38f71ed412..e997c2cbd2df51 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx @@ -3,6 +3,7 @@ import { useMemo, } from 'react' import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' import { ComparisonOperator } from '../types' import { comparisonOperatorNotRequireValue, @@ -13,6 +14,11 @@ import { Variable02 } from '@/app/components/base/icons/src/vender/solid/develop import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import cn from '@/utils/classnames' import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { isExceptionVariable } from '@/app/components/workflow/utils' +import type { + CommonNodeType, + Node, +} from '@/app/components/workflow/types' type ConditionValueProps = { variableSelector: string[] @@ -27,11 +33,14 @@ const ConditionValue = ({ value, }: ConditionValueProps) => { const { t } = useTranslation() + const nodes = useNodes() const variableName = labelName || (isSystemVar(variableSelector) ? variableSelector.slice(0).join('.') : variableSelector.slice(1).join('.')) const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${operator}`) : operator const notHasValue = comparisonOperatorNotRequireValue(operator) const isEnvVar = isENV(variableSelector) const isChatVar = isConversationVar(variableSelector) + const node: Node | undefined = nodes.find(n => n.id === variableSelector[0]) as Node + const isException = isExceptionVariable(variableName, node?.data.type) const formatValue = useMemo(() => { if (notHasValue) return '' @@ -67,14 +76,15 @@ const ConditionValue = ({ return (
- {!isEnvVar && !isChatVar && } + {!isEnvVar && !isChatVar && } {isEnvVar && } {isChatVar && }
diff --git a/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx index e0c15e396bbd3c..a58acb5e921b9a 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx @@ -21,6 +21,7 @@ import AddVariable from './add-variable' import NodeVariableItem from './node-variable-item' import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import cn from '@/utils/classnames' +import { isExceptionVariable } from '@/app/components/workflow/utils' const i18nPrefix = 'workflow.nodes.variableAssigner' type GroupItem = { @@ -128,12 +129,14 @@ const NodeGroupItem = ({ const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.') + const isException = isExceptionVariable(varName, node?.data.type) return ( { const { t } = useTranslation() return ( @@ -50,14 +52,14 @@ const NodeVariableItem = ({
)}
- {!isEnv && !isChatVar && } + {!isEnv && !isChatVar && } {isEnv && } - {!isChatVar &&
{varName}
} + {!isChatVar &&
{varName}
} {isChatVar &&
-
{varName}
+
{varName}
{writeMode && }
diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts index e1dea4f9bd9bdd..f5c112d6e8ef7f 100644 --- a/web/app/components/workflow/utils.ts +++ b/web/app/components/workflow/utils.ts @@ -766,7 +766,7 @@ export const getParallelInfo = (nodes: Node[], edges: Edge[], parentNodeId?: str } } -export const hasErrorHandleNode = (nodeType: BlockEnum) => { +export const hasErrorHandleNode = (nodeType?: BlockEnum) => { return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code } @@ -789,3 +789,10 @@ export const getEdgeColor = (nodeRunningStatus?: NodeRunningStatus, isFailBranch return 'var(--color-workflow-link-line-normal)' } + +export const isExceptionVariable = (variable: string, nodeType?: BlockEnum) => { + if ((variable === 'error_message' || variable === 'error_type') && hasErrorHandleNode(nodeType)) + return true + + return false +}