= ({
onClick={() => handleSelect({
version: version.version,
unique_identifier: version.unique_identifier,
+ isDowngrade: lt(version.version, currentVersion),
})}
>
diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx
index 4be6b18958..ae6e733e49 100644
--- a/web/app/components/share/text-generation/index.tsx
+++ b/web/app/components/share/text-generation/index.tsx
@@ -7,16 +7,14 @@ import {
RiErrorWarningFill,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
-import { usePathname, useRouter, useSearchParams } from 'next/navigation'
+import { useSearchParams } from 'next/navigation'
import TabHeader from '../../base/tab-header'
-import { checkOrSetAccessToken, removeAccessToken } from '../utils'
import MenuDropdown from './menu-dropdown'
import RunBatch from './run-batch'
import ResDownload from './run-batch/res-download'
-import AppUnavailable from '../../base/app-unavailable'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import RunOnce from '@/app/components/share/text-generation/run-once'
-import { fetchSavedMessage as doFetchSavedMessage, fetchAppInfo, fetchAppParams, removeMessage, saveMessage } from '@/service/share'
+import { fetchSavedMessage as doFetchSavedMessage, removeMessage, saveMessage } from '@/service/share'
import type { SiteInfo } from '@/models/share'
import type {
MoreLikeThisConfig,
@@ -39,10 +37,10 @@ import { Resolution, TransferMethod } from '@/types/app'
import { useAppFavicon } from '@/hooks/use-app-favicon'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import cn from '@/utils/classnames'
-import { useGetAppAccessMode, useGetUserCanAccessApp } from '@/service/access-control'
import { AccessMode } from '@/models/access-control'
import { useGlobalPublicStore } from '@/context/global-public-context'
import useDocumentTitle from '@/hooks/use-document-title'
+import { useWebAppStore } from '@/context/web-app-context'
const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group.
enum TaskStatus {
@@ -83,9 +81,6 @@ const TextGeneration: FC = ({
const mode = searchParams.get('mode') || 'create'
const [currentTab, setCurrentTab] = useState(['create', 'batch'].includes(mode) ? mode : 'create')
- const router = useRouter()
- const pathname = usePathname()
-
// Notice this situation isCallBatchAPI but not in batch tab
const [isCallBatchAPI, setIsCallBatchAPI] = useState(false)
const isInBatchTab = currentTab === 'batch'
@@ -103,30 +98,19 @@ const TextGeneration: FC = ({
const [moreLikeThisConfig, setMoreLikeThisConfig] = useState(null)
const [textToSpeechConfig, setTextToSpeechConfig] = useState(null)
- const { isPending: isGettingAccessMode, data: appAccessMode } = useGetAppAccessMode({
- appId,
- isInstalledApp,
- enabled: systemFeatures.webapp_auth.enabled,
- })
- const { isPending: isCheckingPermission, data: userCanAccessResult } = useGetUserCanAccessApp({
- appId,
- isInstalledApp,
- enabled: systemFeatures.webapp_auth.enabled,
- })
-
// save message
const [savedMessages, setSavedMessages] = useState([])
- const fetchSavedMessage = async () => {
- const res: any = await doFetchSavedMessage(isInstalledApp, installedAppInfo?.id)
+ const fetchSavedMessage = useCallback(async () => {
+ const res: any = await doFetchSavedMessage(isInstalledApp, appId)
setSavedMessages(res.data)
- }
+ }, [isInstalledApp, appId])
const handleSaveMessage = async (messageId: string) => {
- await saveMessage(messageId, isInstalledApp, installedAppInfo?.id)
+ await saveMessage(messageId, isInstalledApp, appId)
notify({ type: 'success', message: t('common.api.saved') })
fetchSavedMessage()
}
const handleRemoveSavedMessage = async (messageId: string) => {
- await removeMessage(messageId, isInstalledApp, installedAppInfo?.id)
+ await removeMessage(messageId, isInstalledApp, appId)
notify({ type: 'success', message: t('common.api.remove') })
fetchSavedMessage()
}
@@ -375,34 +359,14 @@ const TextGeneration: FC = ({
}
}
- const fetchInitData = async () => {
- if (!isInstalledApp)
- await checkOrSetAccessToken()
-
- return Promise.all([
- isInstalledApp
- ? {
- app_id: installedAppInfo?.id,
- site: {
- title: installedAppInfo?.app.name,
- prompt_public: false,
- copyright: '',
- icon: installedAppInfo?.app.icon,
- icon_background: installedAppInfo?.app.icon_background,
- },
- plan: 'basic',
- }
- : fetchAppInfo(),
- fetchAppParams(isInstalledApp, installedAppInfo?.id),
- !isWorkflow
- ? fetchSavedMessage()
- : {},
- ])
- }
-
+ const appData = useWebAppStore(s => s.appInfo)
+ const appParams = useWebAppStore(s => s.appParams)
+ const accessMode = useWebAppStore(s => s.webAppAccessMode)
useEffect(() => {
(async () => {
- const [appData, appParams]: any = await fetchInitData()
+ if (!appData || !appParams)
+ return
+ !isWorkflow && fetchSavedMessage()
const { app_id: appId, site: siteInfo, custom_config } = appData
setAppId(appId)
setSiteInfo(siteInfo as SiteInfo)
@@ -413,11 +377,11 @@ const TextGeneration: FC = ({
setVisionConfig({
// legacy of image upload compatible
...file_upload,
- transfer_methods: file_upload.allowed_file_upload_methods || file_upload.allowed_upload_methods,
+ transfer_methods: file_upload?.allowed_file_upload_methods || file_upload?.allowed_upload_methods,
// legacy of image upload compatible
- image_file_size_limit: appParams?.system_parameters?.image_file_size_limit,
+ image_file_size_limit: appParams?.system_parameters.image_file_size_limit,
fileUploadConfig: appParams?.system_parameters,
- })
+ } as any)
const prompt_variables = userInputsFormToPromptVariables(user_input_form)
setPromptConfig({
prompt_template: '', // placeholder for future
@@ -426,7 +390,7 @@ const TextGeneration: FC = ({
setMoreLikeThisConfig(more_like_this)
setTextToSpeechConfig(text_to_speech)
})()
- }, [])
+ }, [appData, appParams, fetchSavedMessage, isWorkflow])
// Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client.
useDocumentTitle(siteInfo?.title || t('share.generation.title'))
@@ -528,32 +492,12 @@ const TextGeneration: FC = ({
)
- const getSigninUrl = useCallback(() => {
- const params = new URLSearchParams(searchParams)
- params.delete('message')
- params.set('redirect_url', pathname)
- return `/webapp-signin?${params.toString()}`
- }, [searchParams, pathname])
-
- const backToHome = useCallback(() => {
- removeAccessToken()
- const url = getSigninUrl()
- router.replace(url)
- }, [getSigninUrl, router])
-
- if (!appId || !siteInfo || !promptConfig || (systemFeatures.webapp_auth.enabled && (isGettingAccessMode || isCheckingPermission))) {
+ if (!appId || !siteInfo || !promptConfig) {
return (
)
}
- if (systemFeatures.webapp_auth.enabled && !userCanAccessResult?.result) {
- return
-
- {!isInstalledApp &&
{t('common.userProfile.logout')}}
-
- }
-
return (
= ({
imageUrl={siteInfo.icon_url}
/>
{siteInfo.title}
-
+
{siteInfo.description && (
{siteInfo.description}
diff --git a/web/app/components/share/text-generation/menu-dropdown.tsx b/web/app/components/share/text-generation/menu-dropdown.tsx
index adb926c7ca..1c1b6adfe8 100644
--- a/web/app/components/share/text-generation/menu-dropdown.tsx
+++ b/web/app/components/share/text-generation/menu-dropdown.tsx
@@ -18,8 +18,8 @@ import {
import ThemeSwitcher from '@/app/components/base/theme-switcher'
import type { SiteInfo } from '@/models/share'
import cn from '@/utils/classnames'
-import { useGlobalPublicStore } from '@/context/global-public-context'
import { AccessMode } from '@/models/access-control'
+import { useWebAppStore } from '@/context/web-app-context'
type Props = {
data?: SiteInfo
@@ -32,7 +32,7 @@ const MenuDropdown: FC = ({
placement,
hideLogout,
}) => {
- const webAppAccessMode = useGlobalPublicStore(s => s.webAppAccessMode)
+ const webAppAccessMode = useWebAppStore(s => s.webAppAccessMode)
const router = useRouter()
const pathname = usePathname()
const { t } = useTranslation()
diff --git a/web/app/components/share/utils.ts b/web/app/components/share/utils.ts
index 8a897ab59a..0c6457fb0c 100644
--- a/web/app/components/share/utils.ts
+++ b/web/app/components/share/utils.ts
@@ -10,7 +10,7 @@ export const getInitialTokenV2 = (): Record => ({
version: 2,
})
-export const checkOrSetAccessToken = async (appCode?: string) => {
+export const checkOrSetAccessToken = async (appCode?: string | null) => {
const sharedToken = appCode || globalThis.location.pathname.split('/').slice(-1)[0]
const userId = (await getProcessedSystemVariablesFromUrlParams()).user_id
const accessToken = localStorage.getItem('token') || JSON.stringify(getInitialTokenV2())
diff --git a/web/app/components/tools/mcp/create-card.tsx b/web/app/components/tools/mcp/create-card.tsx
index 7416f85a2f..0f48dfca92 100644
--- a/web/app/components/tools/mcp/create-card.tsx
+++ b/web/app/components/tools/mcp/create-card.tsx
@@ -33,10 +33,10 @@ const NewMCPCard = ({ handleCreate }: Props) => {
const linkUrl = useMemo(() => {
if (language.startsWith('zh_'))
- return 'https://docs.dify.ai/zh-hans/guides/tools/mcp'
+ return 'https://docs.dify.ai/zh-hans/guides/tools/integrate-tool/mcp'
if (language.startsWith('ja_jp'))
- return 'https://docs.dify.ai/ja_jp/guides/tools/mcp'
- return 'https://docs.dify.ai/en/guides/tools/mcp'
+ return 'https://docs.dify.ai/ja_jp/guides/tools/integrate-tool/mcp'
+ return 'https://docs.dify.ai/en/guides/tools/integrate-tool/mcp'
}, [language])
const [showModal, setShowModal] = useState(false)
diff --git a/web/app/components/workflow-app/components/workflow-main.tsx b/web/app/components/workflow-app/components/workflow-main.tsx
index f0b9bab2cf..d425e6f595 100644
--- a/web/app/components/workflow-app/components/workflow-main.tsx
+++ b/web/app/components/workflow-app/components/workflow-main.tsx
@@ -15,7 +15,7 @@ import {
useWorkflowRun,
useWorkflowStartRun,
} from '../hooks'
-import { useWorkflowStore } from '@/app/components/workflow/store'
+import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
type WorkflowMainProps = Pick
const WorkflowMain = ({
@@ -64,7 +64,11 @@ const WorkflowMain = ({
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
} = useWorkflowStartRun()
- const { fetchInspectVars } = useSetWorkflowVarsWithValue()
+ const appId = useStore(s => s.appId)
+ const { fetchInspectVars } = useSetWorkflowVarsWithValue({
+ flowId: appId,
+ ...useConfigsMap(),
+ })
const {
hasNodeInspectVars,
hasSetInspectVar,
diff --git a/web/app/components/workflow-app/hooks/index.ts b/web/app/components/workflow-app/hooks/index.ts
index 1ee7c030b9..9e4f94965b 100644
--- a/web/app/components/workflow-app/hooks/index.ts
+++ b/web/app/components/workflow-app/hooks/index.ts
@@ -5,6 +5,6 @@ export * from './use-workflow-run'
export * from './use-workflow-start-run'
export * from './use-is-chat-mode'
export * from './use-workflow-refresh-draft'
-export * from './use-fetch-workflow-inspect-vars'
+export * from '../../workflow/hooks/use-fetch-workflow-inspect-vars'
export * from './use-inspect-vars-crud'
export * from './use-configs-map'
diff --git a/web/app/components/workflow-app/hooks/use-inspect-vars-crud.ts b/web/app/components/workflow-app/hooks/use-inspect-vars-crud.ts
index ce052b7ed4..109ee85f96 100644
--- a/web/app/components/workflow-app/hooks/use-inspect-vars-crud.ts
+++ b/web/app/components/workflow-app/hooks/use-inspect-vars-crud.ts
@@ -1,234 +1,16 @@
-import { fetchNodeInspectVars } from '@/service/workflow'
-import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
-import type { ValueSelector } from '@/app/components/workflow/types'
-import type { VarInInspect } from '@/types/workflow'
-import { VarInInspectType } from '@/types/workflow'
-import {
- useDeleteAllInspectorVars,
- useDeleteInspectVar,
- useDeleteNodeInspectorVars,
- useEditInspectorVar,
- useInvalidateConversationVarValues,
- useInvalidateSysVarValues,
- useResetConversationVar,
- useResetToLastRunValue,
-} from '@/service/use-workflow'
-import { useCallback } from 'react'
-import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
-import produce from 'immer'
-import type { Node } from '@/app/components/workflow/types'
-import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
-import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync'
+import { useStore } from '@/app/components/workflow/store'
+import { useInspectVarsCrudCommon } from '../../workflow/hooks/use-inspect-vars-crud-common'
import { useConfigsMap } from './use-configs-map'
export const useInspectVarsCrud = () => {
- const workflowStore = useWorkflowStore()
const appId = useStore(s => s.appId)
- const { conversationVarsUrl, systemVarsUrl } = useConfigsMap()
- const invalidateConversationVarValues = useInvalidateConversationVarValues(conversationVarsUrl)
- const { mutateAsync: doResetConversationVar } = useResetConversationVar(appId)
- const { mutateAsync: doResetToLastRunValue } = useResetToLastRunValue(appId)
- const invalidateSysVarValues = useInvalidateSysVarValues(systemVarsUrl)
-
- const { mutateAsync: doDeleteAllInspectorVars } = useDeleteAllInspectorVars(appId)
- const { mutate: doDeleteNodeInspectorVars } = useDeleteNodeInspectorVars(appId)
- const { mutate: doDeleteInspectVar } = useDeleteInspectVar(appId)
-
- const { mutateAsync: doEditInspectorVar } = useEditInspectorVar(appId)
- const { handleCancelNodeSuccessStatus } = useNodesInteractionsWithoutSync()
- const { handleEdgeCancelRunningStatus } = useEdgesInteractionsWithoutSync()
- const getNodeInspectVars = useCallback((nodeId: string) => {
- const { nodesWithInspectVars } = workflowStore.getState()
- const node = nodesWithInspectVars.find(node => node.nodeId === nodeId)
- return node
- }, [workflowStore])
-
- const getVarId = useCallback((nodeId: string, varName: string) => {
- const node = getNodeInspectVars(nodeId)
- if (!node)
- return undefined
- const varId = node.vars.find((varItem) => {
- return varItem.selector[1] === varName
- })?.id
- return varId
- }, [getNodeInspectVars])
-
- const getInspectVar = useCallback((nodeId: string, name: string): VarInInspect | undefined => {
- const node = getNodeInspectVars(nodeId)
- if (!node)
- return undefined
-
- const variable = node.vars.find((varItem) => {
- return varItem.name === name
- })
- return variable
- }, [getNodeInspectVars])
-
- const hasSetInspectVar = useCallback((nodeId: string, name: string, sysVars: VarInInspect[], conversationVars: VarInInspect[]) => {
- const isEnv = isENV([nodeId])
- if (isEnv) // always have value
- return true
- const isSys = isSystemVar([nodeId])
- if (isSys)
- return sysVars.some(varItem => varItem.selector?.[1]?.replace('sys.', '') === name)
- const isChatVar = isConversationVar([nodeId])
- if (isChatVar)
- return conversationVars.some(varItem => varItem.selector?.[1] === name)
- return getInspectVar(nodeId, name) !== undefined
- }, [getInspectVar])
-
- const hasNodeInspectVars = useCallback((nodeId: string) => {
- return !!getNodeInspectVars(nodeId)
- }, [getNodeInspectVars])
-
- const fetchInspectVarValue = useCallback(async (selector: ValueSelector) => {
- const {
- appId,
- setNodeInspectVars,
- } = workflowStore.getState()
- const nodeId = selector[0]
- const isSystemVar = nodeId === 'sys'
- const isConversationVar = nodeId === 'conversation'
- if (isSystemVar) {
- invalidateSysVarValues()
- return
- }
- if (isConversationVar) {
- invalidateConversationVarValues()
- return
- }
- const vars = await fetchNodeInspectVars(appId, nodeId)
- setNodeInspectVars(nodeId, vars)
- }, [workflowStore, invalidateSysVarValues, invalidateConversationVarValues])
-
- // after last run would call this
- const appendNodeInspectVars = useCallback((nodeId: string, payload: VarInInspect[], allNodes: Node[]) => {
- const {
- nodesWithInspectVars,
- setNodesWithInspectVars,
- } = workflowStore.getState()
- const nodes = produce(nodesWithInspectVars, (draft) => {
- const nodeInfo = allNodes.find(node => node.id === nodeId)
- if (nodeInfo) {
- const index = draft.findIndex(node => node.nodeId === nodeId)
- if (index === -1) {
- draft.unshift({
- nodeId,
- nodeType: nodeInfo.data.type,
- title: nodeInfo.data.title,
- vars: payload,
- nodePayload: nodeInfo.data,
- })
- }
- else {
- draft[index].vars = payload
- // put the node to the topAdd commentMore actions
- draft.unshift(draft.splice(index, 1)[0])
- }
- }
- })
- setNodesWithInspectVars(nodes)
- handleCancelNodeSuccessStatus(nodeId)
- }, [workflowStore, handleCancelNodeSuccessStatus])
-
- const hasNodeInspectVar = useCallback((nodeId: string, varId: string) => {
- const { nodesWithInspectVars } = workflowStore.getState()
- const targetNode = nodesWithInspectVars.find(item => item.nodeId === nodeId)
- if(!targetNode || !targetNode.vars)
- return false
- return targetNode.vars.some(item => item.id === varId)
- }, [workflowStore])
-
- const deleteInspectVar = useCallback(async (nodeId: string, varId: string) => {
- const { deleteInspectVar } = workflowStore.getState()
- if(hasNodeInspectVar(nodeId, varId)) {
- await doDeleteInspectVar(varId)
- deleteInspectVar(nodeId, varId)
- }
- }, [doDeleteInspectVar, workflowStore, hasNodeInspectVar])
-
- const resetConversationVar = useCallback(async (varId: string) => {
- await doResetConversationVar(varId)
- invalidateConversationVarValues()
- }, [doResetConversationVar, invalidateConversationVarValues])
-
- const deleteNodeInspectorVars = useCallback(async (nodeId: string) => {
- const { deleteNodeInspectVars } = workflowStore.getState()
- if (hasNodeInspectVars(nodeId)) {
- await doDeleteNodeInspectorVars(nodeId)
- deleteNodeInspectVars(nodeId)
- }
- }, [doDeleteNodeInspectorVars, workflowStore, hasNodeInspectVars])
-
- const deleteAllInspectorVars = useCallback(async () => {
- const { deleteAllInspectVars } = workflowStore.getState()
- await doDeleteAllInspectorVars()
- await invalidateConversationVarValues()
- await invalidateSysVarValues()
- deleteAllInspectVars()
- handleEdgeCancelRunningStatus()
- }, [doDeleteAllInspectorVars, invalidateConversationVarValues, invalidateSysVarValues, workflowStore, handleEdgeCancelRunningStatus])
-
- const editInspectVarValue = useCallback(async (nodeId: string, varId: string, value: any) => {
- const { setInspectVarValue } = workflowStore.getState()
- await doEditInspectorVar({
- varId,
- value,
- })
- setInspectVarValue(nodeId, varId, value)
- if (nodeId === VarInInspectType.conversation)
- invalidateConversationVarValues()
- if (nodeId === VarInInspectType.system)
- invalidateSysVarValues()
- }, [doEditInspectorVar, invalidateConversationVarValues, invalidateSysVarValues, workflowStore])
-
- const renameInspectVarName = useCallback(async (nodeId: string, oldName: string, newName: string) => {
- const { renameInspectVarName } = workflowStore.getState()
- const varId = getVarId(nodeId, oldName)
- if (!varId)
- return
-
- const newSelector = [nodeId, newName]
- await doEditInspectorVar({
- varId,
- name: newName,
- })
- renameInspectVarName(nodeId, varId, newSelector)
- }, [doEditInspectorVar, getVarId, workflowStore])
-
- const isInspectVarEdited = useCallback((nodeId: string, name: string) => {
- const inspectVar = getInspectVar(nodeId, name)
- if (!inspectVar)
- return false
-
- return inspectVar.edited
- }, [getInspectVar])
-
- const resetToLastRunVar = useCallback(async (nodeId: string, varId: string) => {
- const { resetToLastRunVar } = workflowStore.getState()
- const isSysVar = nodeId === 'sys'
- const data = await doResetToLastRunValue(varId)
-
- if(isSysVar)
- invalidateSysVarValues()
- else
- resetToLastRunVar(nodeId, varId, data.value)
- }, [doResetToLastRunValue, invalidateSysVarValues, workflowStore])
+ const configsMap = useConfigsMap()
+ const apis = useInspectVarsCrudCommon({
+ flowId: appId,
+ ...configsMap,
+ })
return {
- hasNodeInspectVars,
- hasSetInspectVar,
- fetchInspectVarValue,
- editInspectVarValue,
- renameInspectVarName,
- appendNodeInspectVars,
- deleteInspectVar,
- deleteNodeInspectorVars,
- deleteAllInspectorVars,
- isInspectVarEdited,
- resetToLastRunVar,
- invalidateSysVarValues,
- resetConversationVar,
- invalidateConversationVarValues,
+ ...apis,
}
}
diff --git a/web/app/components/workflow-app/hooks/use-workflow-run.ts b/web/app/components/workflow-app/hooks/use-workflow-run.ts
index 4c34d2ffb1..c303211715 100644
--- a/web/app/components/workflow-app/hooks/use-workflow-run.ts
+++ b/web/app/components/workflow-app/hooks/use-workflow-run.ts
@@ -20,7 +20,8 @@ import type { VersionHistory } from '@/types/workflow'
import { noop } from 'lodash-es'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useInvalidAllLastRun } from '@/service/use-workflow'
-import { useSetWorkflowVarsWithValue } from './use-fetch-workflow-inspect-vars'
+import { useSetWorkflowVarsWithValue } from '../../workflow/hooks/use-fetch-workflow-inspect-vars'
+import { useConfigsMap } from './use-configs-map'
export const useWorkflowRun = () => {
const store = useStoreApi()
@@ -32,7 +33,11 @@ export const useWorkflowRun = () => {
const pathname = usePathname()
const appId = useAppStore.getState().appDetail?.id
const invalidAllLastRun = useInvalidAllLastRun(appId as string)
- const { fetchInspectVars } = useSetWorkflowVarsWithValue()
+ const configsMap = useConfigsMap()
+ const { fetchInspectVars } = useSetWorkflowVarsWithValue({
+ flowId: appId as string,
+ ...configsMap,
+ })
const {
handleWorkflowStarted,
diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts
index c96a60f674..361d4ccc9d 100644
--- a/web/app/components/workflow/block-selector/types.ts
+++ b/web/app/components/workflow/block-selector/types.ts
@@ -33,6 +33,7 @@ export type ToolDefaultValue = {
params: Record
paramSchemas: Record[]
output_schema: Record
+ credential_id?: string
meta?: PluginMeta
}
@@ -46,4 +47,5 @@ export type ToolValue = {
parameters?: Record
enabled?: boolean
extra?: Record
+ credential_id?: string
}
diff --git a/web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts b/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts
similarity index 85%
rename from web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts
rename to web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts
index 07580c097e..27a9ea9d2d 100644
--- a/web/app/components/workflow-app/hooks/use-fetch-workflow-inspect-vars.ts
+++ b/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts
@@ -6,12 +6,20 @@ import type { Node } from '@/app/components/workflow/types'
import { fetchAllInspectVars } from '@/service/workflow'
import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow'
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
-import { useConfigsMap } from './use-configs-map'
-export const useSetWorkflowVarsWithValue = () => {
+type Params = {
+ flowId: string
+ conversationVarsUrl: string
+ systemVarsUrl: string
+}
+
+export const useSetWorkflowVarsWithValue = ({
+ flowId,
+ conversationVarsUrl,
+ systemVarsUrl,
+}: Params) => {
const workflowStore = useWorkflowStore()
const store = useStoreApi()
- const { conversationVarsUrl, systemVarsUrl } = useConfigsMap()
const invalidateConversationVarValues = useInvalidateConversationVarValues(conversationVarsUrl)
const invalidateSysVarValues = useInvalidateSysVarValues(systemVarsUrl)
const { handleCancelAllNodeSuccessStatus } = useNodesInteractionsWithoutSync()
@@ -58,13 +66,12 @@ export const useSetWorkflowVarsWithValue = () => {
}, [workflowStore, store])
const fetchInspectVars = useCallback(async () => {
- const { appId } = workflowStore.getState()
invalidateConversationVarValues()
invalidateSysVarValues()
- const data = await fetchAllInspectVars(appId)
+ const data = await fetchAllInspectVars(flowId)
setInspectVarsToStore(data)
handleCancelAllNodeSuccessStatus() // to make sure clear node output show the unset status
- }, [workflowStore, invalidateConversationVarValues, invalidateSysVarValues, setInspectVarsToStore, handleCancelAllNodeSuccessStatus])
+ }, [invalidateConversationVarValues, invalidateSysVarValues, flowId, setInspectVarsToStore, handleCancelAllNodeSuccessStatus])
return {
fetchInspectVars,
}
diff --git a/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts b/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts
new file mode 100644
index 0000000000..ffcfd81666
--- /dev/null
+++ b/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts
@@ -0,0 +1,240 @@
+import { fetchNodeInspectVars } from '@/service/workflow'
+import { useWorkflowStore } from '@/app/components/workflow/store'
+import type { ValueSelector } from '@/app/components/workflow/types'
+import type { VarInInspect } from '@/types/workflow'
+import { VarInInspectType } from '@/types/workflow'
+import {
+ useDeleteAllInspectorVars,
+ useDeleteInspectVar,
+ useDeleteNodeInspectorVars,
+ useEditInspectorVar,
+ useInvalidateConversationVarValues,
+ useInvalidateSysVarValues,
+ useResetConversationVar,
+ useResetToLastRunValue,
+} from '@/service/use-workflow'
+import { useCallback } from 'react'
+import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
+import produce from 'immer'
+import type { Node } from '@/app/components/workflow/types'
+import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
+import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync'
+
+type Params = {
+ flowId: string
+ conversationVarsUrl: string
+ systemVarsUrl: string
+}
+export const useInspectVarsCrudCommon = ({
+ flowId,
+ conversationVarsUrl,
+ systemVarsUrl,
+}: Params) => {
+ const workflowStore = useWorkflowStore()
+ const invalidateConversationVarValues = useInvalidateConversationVarValues(conversationVarsUrl!)
+ const { mutateAsync: doResetConversationVar } = useResetConversationVar(flowId)
+ const { mutateAsync: doResetToLastRunValue } = useResetToLastRunValue(flowId)
+ const invalidateSysVarValues = useInvalidateSysVarValues(systemVarsUrl!)
+
+ const { mutateAsync: doDeleteAllInspectorVars } = useDeleteAllInspectorVars(flowId)
+ const { mutate: doDeleteNodeInspectorVars } = useDeleteNodeInspectorVars(flowId)
+ const { mutate: doDeleteInspectVar } = useDeleteInspectVar(flowId)
+
+ const { mutateAsync: doEditInspectorVar } = useEditInspectorVar(flowId)
+ const { handleCancelNodeSuccessStatus } = useNodesInteractionsWithoutSync()
+ const { handleEdgeCancelRunningStatus } = useEdgesInteractionsWithoutSync()
+ const getNodeInspectVars = useCallback((nodeId: string) => {
+ const { nodesWithInspectVars } = workflowStore.getState()
+ const node = nodesWithInspectVars.find(node => node.nodeId === nodeId)
+ return node
+ }, [workflowStore])
+
+ const getVarId = useCallback((nodeId: string, varName: string) => {
+ const node = getNodeInspectVars(nodeId)
+ if (!node)
+ return undefined
+ const varId = node.vars.find((varItem) => {
+ return varItem.selector[1] === varName
+ })?.id
+ return varId
+ }, [getNodeInspectVars])
+
+ const getInspectVar = useCallback((nodeId: string, name: string): VarInInspect | undefined => {
+ const node = getNodeInspectVars(nodeId)
+ if (!node)
+ return undefined
+
+ const variable = node.vars.find((varItem) => {
+ return varItem.name === name
+ })
+ return variable
+ }, [getNodeInspectVars])
+
+ const hasSetInspectVar = useCallback((nodeId: string, name: string, sysVars: VarInInspect[], conversationVars: VarInInspect[]) => {
+ const isEnv = isENV([nodeId])
+ if (isEnv) // always have value
+ return true
+ const isSys = isSystemVar([nodeId])
+ if (isSys)
+ return sysVars.some(varItem => varItem.selector?.[1]?.replace('sys.', '') === name)
+ const isChatVar = isConversationVar([nodeId])
+ if (isChatVar)
+ return conversationVars.some(varItem => varItem.selector?.[1] === name)
+ return getInspectVar(nodeId, name) !== undefined
+ }, [getInspectVar])
+
+ const hasNodeInspectVars = useCallback((nodeId: string) => {
+ return !!getNodeInspectVars(nodeId)
+ }, [getNodeInspectVars])
+
+ const fetchInspectVarValue = useCallback(async (selector: ValueSelector) => {
+ const {
+ appId,
+ setNodeInspectVars,
+ } = workflowStore.getState()
+ const nodeId = selector[0]
+ const isSystemVar = nodeId === 'sys'
+ const isConversationVar = nodeId === 'conversation'
+ if (isSystemVar) {
+ invalidateSysVarValues()
+ return
+ }
+ if (isConversationVar) {
+ invalidateConversationVarValues()
+ return
+ }
+ const vars = await fetchNodeInspectVars(appId, nodeId)
+ setNodeInspectVars(nodeId, vars)
+ }, [workflowStore, invalidateSysVarValues, invalidateConversationVarValues])
+
+ // after last run would call this
+ const appendNodeInspectVars = useCallback((nodeId: string, payload: VarInInspect[], allNodes: Node[]) => {
+ const {
+ nodesWithInspectVars,
+ setNodesWithInspectVars,
+ } = workflowStore.getState()
+ const nodes = produce(nodesWithInspectVars, (draft) => {
+ const nodeInfo = allNodes.find(node => node.id === nodeId)
+ if (nodeInfo) {
+ const index = draft.findIndex(node => node.nodeId === nodeId)
+ if (index === -1) {
+ draft.unshift({
+ nodeId,
+ nodeType: nodeInfo.data.type,
+ title: nodeInfo.data.title,
+ vars: payload,
+ nodePayload: nodeInfo.data,
+ })
+ }
+ else {
+ draft[index].vars = payload
+ // put the node to the topAdd commentMore actions
+ draft.unshift(draft.splice(index, 1)[0])
+ }
+ }
+ })
+ setNodesWithInspectVars(nodes)
+ handleCancelNodeSuccessStatus(nodeId)
+ }, [workflowStore, handleCancelNodeSuccessStatus])
+
+ const hasNodeInspectVar = useCallback((nodeId: string, varId: string) => {
+ const { nodesWithInspectVars } = workflowStore.getState()
+ const targetNode = nodesWithInspectVars.find(item => item.nodeId === nodeId)
+ if(!targetNode || !targetNode.vars)
+ return false
+ return targetNode.vars.some(item => item.id === varId)
+ }, [workflowStore])
+
+ const deleteInspectVar = useCallback(async (nodeId: string, varId: string) => {
+ const { deleteInspectVar } = workflowStore.getState()
+ if(hasNodeInspectVar(nodeId, varId)) {
+ await doDeleteInspectVar(varId)
+ deleteInspectVar(nodeId, varId)
+ }
+ }, [doDeleteInspectVar, workflowStore, hasNodeInspectVar])
+
+ const resetConversationVar = useCallback(async (varId: string) => {
+ await doResetConversationVar(varId)
+ invalidateConversationVarValues()
+ }, [doResetConversationVar, invalidateConversationVarValues])
+
+ const deleteNodeInspectorVars = useCallback(async (nodeId: string) => {
+ const { deleteNodeInspectVars } = workflowStore.getState()
+ if (hasNodeInspectVars(nodeId)) {
+ await doDeleteNodeInspectorVars(nodeId)
+ deleteNodeInspectVars(nodeId)
+ }
+ }, [doDeleteNodeInspectorVars, workflowStore, hasNodeInspectVars])
+
+ const deleteAllInspectorVars = useCallback(async () => {
+ const { deleteAllInspectVars } = workflowStore.getState()
+ await doDeleteAllInspectorVars()
+ await invalidateConversationVarValues()
+ await invalidateSysVarValues()
+ deleteAllInspectVars()
+ handleEdgeCancelRunningStatus()
+ }, [doDeleteAllInspectorVars, invalidateConversationVarValues, invalidateSysVarValues, workflowStore, handleEdgeCancelRunningStatus])
+
+ const editInspectVarValue = useCallback(async (nodeId: string, varId: string, value: any) => {
+ const { setInspectVarValue } = workflowStore.getState()
+ await doEditInspectorVar({
+ varId,
+ value,
+ })
+ setInspectVarValue(nodeId, varId, value)
+ if (nodeId === VarInInspectType.conversation)
+ invalidateConversationVarValues()
+ if (nodeId === VarInInspectType.system)
+ invalidateSysVarValues()
+ }, [doEditInspectorVar, invalidateConversationVarValues, invalidateSysVarValues, workflowStore])
+
+ const renameInspectVarName = useCallback(async (nodeId: string, oldName: string, newName: string) => {
+ const { renameInspectVarName } = workflowStore.getState()
+ const varId = getVarId(nodeId, oldName)
+ if (!varId)
+ return
+
+ const newSelector = [nodeId, newName]
+ await doEditInspectorVar({
+ varId,
+ name: newName,
+ })
+ renameInspectVarName(nodeId, varId, newSelector)
+ }, [doEditInspectorVar, getVarId, workflowStore])
+
+ const isInspectVarEdited = useCallback((nodeId: string, name: string) => {
+ const inspectVar = getInspectVar(nodeId, name)
+ if (!inspectVar)
+ return false
+
+ return inspectVar.edited
+ }, [getInspectVar])
+
+ const resetToLastRunVar = useCallback(async (nodeId: string, varId: string) => {
+ const { resetToLastRunVar } = workflowStore.getState()
+ const isSysVar = nodeId === 'sys'
+ const data = await doResetToLastRunValue(varId)
+
+ if(isSysVar)
+ invalidateSysVarValues()
+ else
+ resetToLastRunVar(nodeId, varId, data.value)
+ }, [doResetToLastRunValue, invalidateSysVarValues, workflowStore])
+
+ return {
+ hasNodeInspectVars,
+ hasSetInspectVar,
+ fetchInspectVarValue,
+ editInspectVarValue,
+ renameInspectVarName,
+ appendNodeInspectVars,
+ deleteInspectVar,
+ deleteNodeInspectorVars,
+ deleteAllInspectorVars,
+ isInspectVarEdited,
+ resetToLastRunVar,
+ invalidateSysVarValues,
+ resetConversationVar,
+ invalidateConversationVarValues,
+ }
+}
diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
index 31aa91cfdb..ce9fbb77e1 100644
--- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
+++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
@@ -20,7 +20,7 @@ import { useRenderI18nObject } from '@/hooks/use-i18n'
import type { NodeOutPutVar } from '../../../types'
import type { Node } from 'reactflow'
import type { PluginMeta } from '@/app/components/plugins/types'
-import { noop } from 'lodash'
+import { noop } from 'lodash-es'
import { useDocLink } from '@/context/i18n'
export type Strategy = {
diff --git a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx
index 6f8bd17a96..316a5c9819 100644
--- a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx
+++ b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx
@@ -242,7 +242,7 @@ const FormInputItem: FC = ({
)}
@@ -251,7 +251,7 @@ const FormInputItem: FC = ({
popupClassName='!w-[387px]'
isAdvancedMode
isInWorkflow
- value={varInput?.value as any}
+ value={varInput}
setModel={handleAppOrModelSelect}
readonly={readOnly}
scope={scope}
diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts
index ac95f54757..db56feaacc 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts
+++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts
@@ -612,6 +612,7 @@ const getIterationItemType = ({
}): VarType => {
const outputVarNodeId = valueSelector[0]
const isSystem = isSystemVar(valueSelector)
+ const isChatVar = isConversationVar(valueSelector)
const targetVar = isSystem ? beforeNodesOutputVars.find(v => v.isStartNode) : beforeNodesOutputVars.find(v => v.nodeId === outputVarNodeId)
@@ -621,7 +622,7 @@ const getIterationItemType = ({
let arrayType: VarType = VarType.string
let curr: any = targetVar.vars
- if (isSystem) {
+ if (isSystem || isChatVar) {
arrayType = curr.find((v: any) => v.variable === (valueSelector).join('.'))?.type
}
else {
diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx
index 164369e64c..01d8e95272 100644
--- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx
+++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx
@@ -59,6 +59,12 @@ import { useLogs } from '@/app/components/workflow/run/hooks'
import PanelWrap from '../before-run-form/panel-wrap'
import SpecialResultPanel from '@/app/components/workflow/run/special-result-panel'
import { Stop } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
+import {
+ AuthorizedInNode,
+ PluginAuth,
+} from '@/app/components/plugins/plugin-auth'
+import { AuthCategory } from '@/app/components/plugins/plugin-auth'
+import { canFindTool } from '@/utils'
type BasePanelProps = {
children: ReactNode
@@ -83,18 +89,19 @@ const BasePanel: FC = ({
const otherPanelWidth = useStore(s => s.otherPanelWidth)
const setNodePanelWidth = useStore(s => s.setNodePanelWidth)
+ const reservedCanvasWidth = 400 // Reserve the minimum visible width for the canvas
+
const maxNodePanelWidth = useMemo(() => {
if (!workflowCanvasWidth)
return 720
- if (!otherPanelWidth)
- return workflowCanvasWidth - 400
- return workflowCanvasWidth - otherPanelWidth - 400
+ const available = workflowCanvasWidth - (otherPanelWidth || 0) - reservedCanvasWidth
+ return Math.max(available, 400)
}, [workflowCanvasWidth, otherPanelWidth])
const updateNodePanelWidth = useCallback((width: number) => {
// Ensure the width is within the min and max range
- const newValue = Math.min(Math.max(width, 400), maxNodePanelWidth)
+ const newValue = Math.max(400, Math.min(width, maxNodePanelWidth))
localStorage.setItem('workflow-node-panel-width', `${newValue}`)
setNodePanelWidth(newValue)
}, [maxNodePanelWidth, setNodePanelWidth])
@@ -118,8 +125,13 @@ const BasePanel: FC = ({
useEffect(() => {
if (!workflowCanvasWidth)
return
- if (workflowCanvasWidth - 400 <= nodePanelWidth + otherPanelWidth)
- debounceUpdate(workflowCanvasWidth - 400 - otherPanelWidth)
+
+ // If the total width of the three exceeds the canvas, shrink the node panel to the available range (at least 400px)
+ const total = nodePanelWidth + otherPanelWidth + reservedCanvasWidth
+ if (total > workflowCanvasWidth) {
+ const target = Math.max(workflowCanvasWidth - otherPanelWidth - reservedCanvasWidth, 400)
+ debounceUpdate(target)
+ }
}, [nodePanelWidth, otherPanelWidth, workflowCanvasWidth, updateNodePanelWidth])
const { handleNodeSelect } = useNodesInteractions()
@@ -215,6 +227,22 @@ const BasePanel: FC = ({
return {}
})()
+ const buildInTools = useStore(s => s.buildInTools)
+ const currCollection = useMemo(() => {
+ return buildInTools.find(item => canFindTool(item.id, data.provider_id))
+ }, [buildInTools, data.provider_id])
+ const showPluginAuth = useMemo(() => {
+ return data.type === BlockEnum.Tool && currCollection?.allow_delete && !currCollection.is_team_authorization
+ }, [currCollection, data.type])
+ const handleAuthorizationItemClick = useCallback((credential_id: string) => {
+ handleNodeDataUpdate({
+ id,
+ data: {
+ credential_id: credential_id === '__workspace_default__' ? undefined : credential_id,
+ },
+ })
+ }, [handleNodeDataUpdate, id])
+
if(logParams.showSpecialResultPanel) {
return (
= ({
onChange={handleDescriptionChange}
/>
-
-
-
+ {
+ showPluginAuth && (
+
+
+
+
+
+ )
+ }
+ {
+ !showPluginAuth && (
+
+
+ {
+ currCollection?.allow_delete && (
+
+ )
+ }
+
+ )
+ }