mirror of
https://github.com/langgenius/dify.git
synced 2026-02-09 15:10:13 -05:00
chore: refact tool and filepreveiw context to zustand to reduce rerender
This commit is contained in:
@@ -333,6 +333,10 @@ const PromptEditorContent: FC<PromptEditorContentProps> = ({
|
||||
}
|
||||
}, [availableNodes, nodeId, onToolMetadataChange, toolMetadata, workflowVariableBlock?.variables])
|
||||
|
||||
const filePreviewContextValue = React.useMemo(() => ({
|
||||
enabled: Boolean(isSupportSandbox),
|
||||
}), [isSupportSandbox])
|
||||
|
||||
const sandboxPlaceHolder = React.useMemo(() => {
|
||||
if (!isSupportSandbox)
|
||||
return null
|
||||
@@ -387,7 +391,7 @@ const PromptEditorContent: FC<PromptEditorContentProps> = ({
|
||||
return (
|
||||
<LexicalComposer initialConfig={{ ...initialConfig, editable }}>
|
||||
<ToolBlockContextProvider value={toolBlockContextValue}>
|
||||
<FilePreviewContextProvider value={{ enabled: Boolean(isSupportSandbox) }}>
|
||||
<FilePreviewContextProvider value={filePreviewContextValue}>
|
||||
<div
|
||||
className={cn('relative', wrapperClassName)}
|
||||
data-skill-editor-root={isSupportSandbox ? 'true' : undefined}
|
||||
|
||||
@@ -94,6 +94,8 @@ const SkillEditor = ({
|
||||
onAutoFocus,
|
||||
toolPickerScope = 'all',
|
||||
}: SkillEditorProps) => {
|
||||
const filePreviewContextValue = React.useMemo(() => ({ enabled: false }), [])
|
||||
|
||||
const initialConfig = {
|
||||
namespace: 'skill-editor',
|
||||
nodes: [
|
||||
@@ -123,7 +125,7 @@ const SkillEditor = ({
|
||||
|
||||
return (
|
||||
<LexicalComposer initialConfig={{ ...initialConfig, editable }}>
|
||||
<FilePreviewContextProvider value={{ enabled: false }}>
|
||||
<FilePreviewContextProvider value={filePreviewContextValue}>
|
||||
<div
|
||||
className={cn('relative', showLineNumbers && styles.lineNumbersScope, wrapperClassName)}
|
||||
data-skill-editor-root="true"
|
||||
|
||||
@@ -45,7 +45,7 @@ const FileReferenceBlock = ({ nodeKey, resourceId }: FileReferenceBlockProps) =>
|
||||
const [previewOpen, setPreviewOpen] = useState(false)
|
||||
const [previewStyle, setPreviewStyle] = useState<React.CSSProperties | null>(null)
|
||||
const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const { enabled: isPreviewEnabled } = useFilePreviewContext()
|
||||
const isPreviewEnabled = useFilePreviewContext(context => context.enabled)
|
||||
const { t } = useTranslation()
|
||||
const isInteractive = editor.isEditable()
|
||||
|
||||
|
||||
@@ -1,13 +1,62 @@
|
||||
import * as React from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useRef } from 'react'
|
||||
import { useStore } from 'zustand'
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
type FilePreviewContextValue = {
|
||||
export type FilePreviewContextValue = {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
const FilePreviewContext = React.createContext<FilePreviewContextValue>({ enabled: false })
|
||||
|
||||
export const FilePreviewContextProvider = FilePreviewContext.Provider
|
||||
|
||||
export const useFilePreviewContext = () => {
|
||||
return React.useContext(FilePreviewContext)
|
||||
type FilePreviewStoreState = {
|
||||
enabled: boolean
|
||||
setEnabled: (enabled: boolean) => void
|
||||
}
|
||||
|
||||
const createFilePreviewStore = (enabled: boolean) => createStore<FilePreviewStoreState>(set => ({
|
||||
enabled,
|
||||
setEnabled: nextEnabled => set({ enabled: nextEnabled }),
|
||||
}))
|
||||
|
||||
type FilePreviewStore = ReturnType<typeof createFilePreviewStore>
|
||||
|
||||
const defaultFilePreviewStore = createFilePreviewStore(false)
|
||||
|
||||
const FilePreviewStoreContext = createContext<FilePreviewStore | null>(null)
|
||||
|
||||
type FilePreviewContextProviderProps = {
|
||||
value?: FilePreviewContextValue
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const FilePreviewContextProvider = ({ value, children }: FilePreviewContextProviderProps) => {
|
||||
const storeRef = useRef<FilePreviewStore>(null)
|
||||
if (!storeRef.current)
|
||||
storeRef.current = createFilePreviewStore(value?.enabled ?? false)
|
||||
|
||||
useEffect(() => {
|
||||
storeRef.current?.getState().setEnabled(value?.enabled ?? false)
|
||||
}, [value?.enabled])
|
||||
|
||||
return (
|
||||
<FilePreviewStoreContext.Provider value={storeRef.current}>
|
||||
{children}
|
||||
</FilePreviewStoreContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const useFilePreviewContext = <T = FilePreviewContextValue>(
|
||||
selector?: (context: FilePreviewContextValue) => T,
|
||||
) => {
|
||||
const store = useContext(FilePreviewStoreContext) ?? defaultFilePreviewStore
|
||||
const selectContext = useCallback(
|
||||
(state: FilePreviewStoreState) => {
|
||||
if (selector)
|
||||
return selector({ enabled: state.enabled })
|
||||
return { enabled: state.enabled } as T
|
||||
},
|
||||
[selector],
|
||||
)
|
||||
|
||||
return useStore(store, selectContext)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as React from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
@@ -127,9 +128,25 @@ const ToolBlockComponent = ({
|
||||
const { t } = useTranslation()
|
||||
const authBadgeLabel = t('skillEditor.authorizationBadge', { ns: 'workflow' })
|
||||
const { theme } = useTheme()
|
||||
const toolBlockContext = useToolBlockContext()
|
||||
const isUsingExternalMetadata = Boolean(toolBlockContext?.onMetadataChange)
|
||||
const useModal = Boolean(toolBlockContext?.useModal)
|
||||
const {
|
||||
metadata,
|
||||
onMetadataChange,
|
||||
useModal,
|
||||
nodeId: contextNodeId,
|
||||
nodesOutputVars,
|
||||
availableNodes,
|
||||
} = useToolBlockContext(
|
||||
useShallow(context => ({
|
||||
metadata: context?.metadata,
|
||||
onMetadataChange: context?.onMetadataChange,
|
||||
useModal: context?.useModal,
|
||||
nodeId: context?.nodeId,
|
||||
nodesOutputVars: context?.nodesOutputVars,
|
||||
availableNodes: context?.availableNodes,
|
||||
})),
|
||||
)
|
||||
const isUsingExternalMetadata = Boolean(onMetadataChange)
|
||||
const useModalValue = Boolean(useModal)
|
||||
const [isSettingOpen, setIsSettingOpen] = useState(false)
|
||||
const [toolValue, setToolValue] = useState<ToolValue | null>(null)
|
||||
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)
|
||||
@@ -183,14 +200,14 @@ const ToolBlockComponent = ({
|
||||
|
||||
const toolConfigFromMetadata = useMemo(() => {
|
||||
if (isUsingExternalMetadata) {
|
||||
const metadata = toolBlockContext?.metadata as SkillFileMetadata | undefined
|
||||
return metadata?.tools?.[configId]
|
||||
const externalMetadata = metadata as SkillFileMetadata | undefined
|
||||
return externalMetadata?.tools?.[configId]
|
||||
}
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return undefined
|
||||
const metadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
return metadata?.tools?.[configId]
|
||||
}, [activeTabId, configId, fileMetadata, isUsingExternalMetadata, toolBlockContext?.metadata])
|
||||
const resultMetadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
return resultMetadata?.tools?.[configId]
|
||||
}, [activeTabId, configId, fileMetadata, isUsingExternalMetadata, metadata])
|
||||
|
||||
const isInteractive = editor.isEditable()
|
||||
|
||||
@@ -281,7 +298,7 @@ const ToolBlockComponent = ({
|
||||
}, [configuredToolValue, isSettingOpen])
|
||||
|
||||
useEffect(() => {
|
||||
if (useModal)
|
||||
if (useModalValue)
|
||||
return
|
||||
const containerFromRef = ref.current?.closest('[data-skill-editor-root="true"]') as HTMLElement | null
|
||||
const fallbackContainer = document.querySelector('[data-skill-editor-root="true"]') as HTMLElement | null
|
||||
@@ -289,10 +306,10 @@ const ToolBlockComponent = ({
|
||||
if (container)
|
||||
// eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect
|
||||
setPortalContainer(container)
|
||||
}, [ref, useModal])
|
||||
}, [ref, useModalValue])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSettingOpen || useModal)
|
||||
if (!isSettingOpen || useModalValue)
|
||||
return
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
@@ -316,7 +333,7 @@ const ToolBlockComponent = ({
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside)
|
||||
}, [isSettingOpen, portalContainer, ref, useModal])
|
||||
}, [isSettingOpen, portalContainer, ref, useModalValue])
|
||||
|
||||
const displayLabel = label || toolMeta?.label || tool
|
||||
const resolvedIcon = (() => {
|
||||
@@ -398,7 +415,7 @@ const ToolBlockComponent = ({
|
||||
return
|
||||
const credentialId = normalizeCredentialId(nextValue.credential_id)
|
||||
if (isUsingExternalMetadata) {
|
||||
const metadata = (toolBlockContext?.metadata || {}) as SkillFileMetadata
|
||||
const externalMetadata = (metadata || {}) as SkillFileMetadata
|
||||
const toolType = currentProvider.type === CollectionType.mcp ? 'mcp' : 'builtin'
|
||||
const buildFields = (value: Record<string, unknown> | undefined) => {
|
||||
if (!value)
|
||||
@@ -415,9 +432,9 @@ const ToolBlockComponent = ({
|
||||
...buildFields(nextValue.parameters),
|
||||
]
|
||||
const nextMetadata: SkillFileMetadata = {
|
||||
...metadata,
|
||||
...externalMetadata,
|
||||
tools: {
|
||||
...(metadata.tools || {}),
|
||||
...(externalMetadata.tools || {}),
|
||||
[configId]: {
|
||||
type: toolType,
|
||||
configuration: { fields },
|
||||
@@ -425,12 +442,12 @@ const ToolBlockComponent = ({
|
||||
},
|
||||
},
|
||||
}
|
||||
toolBlockContext?.onMetadataChange?.(nextMetadata)
|
||||
onMetadataChange?.(nextMetadata)
|
||||
return
|
||||
}
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return
|
||||
const metadata = (fileMetadata.get(activeTabId) || {}) as SkillFileMetadata
|
||||
const currentMetadata = (fileMetadata.get(activeTabId) || {}) as SkillFileMetadata
|
||||
const toolType = currentProvider.type === CollectionType.mcp ? 'mcp' : 'builtin'
|
||||
const buildFields = (value: Record<string, unknown> | undefined) => {
|
||||
if (!value)
|
||||
@@ -447,9 +464,9 @@ const ToolBlockComponent = ({
|
||||
...buildFields(nextValue.parameters),
|
||||
]
|
||||
const nextMetadata: SkillFileMetadata = {
|
||||
...metadata,
|
||||
...currentMetadata,
|
||||
tools: {
|
||||
...(metadata.tools || {}),
|
||||
...(currentMetadata.tools || {}),
|
||||
[configId]: {
|
||||
type: toolType,
|
||||
configuration: { fields },
|
||||
@@ -488,13 +505,13 @@ const ToolBlockComponent = ({
|
||||
return nextMetadata
|
||||
}
|
||||
if (isUsingExternalMetadata) {
|
||||
toolBlockContext?.onMetadataChange?.(applyCredential(toolBlockContext?.metadata as SkillFileMetadata | undefined))
|
||||
onMetadataChange?.(applyCredential(metadata as SkillFileMetadata | undefined))
|
||||
return
|
||||
}
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return
|
||||
const metadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
const nextMetadata = applyCredential(metadata)
|
||||
const currentMetadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
const nextMetadata = applyCredential(currentMetadata)
|
||||
storeApi.getState().setDraftMetadata(activeTabId, nextMetadata)
|
||||
storeApi.getState().pinTab(activeTabId)
|
||||
}
|
||||
@@ -535,10 +552,10 @@ const ToolBlockComponent = ({
|
||||
currentTool={currentTool}
|
||||
value={toolValue}
|
||||
onChange={handleToolValueChange}
|
||||
nodeId={toolBlockContext?.nodeId}
|
||||
nodesOutputVars={toolBlockContext?.nodesOutputVars}
|
||||
availableNodes={toolBlockContext?.availableNodes}
|
||||
enableVariableReference={useModal}
|
||||
nodeId={contextNodeId}
|
||||
nodesOutputVars={nodesOutputVars}
|
||||
availableNodes={availableNodes}
|
||||
enableVariableReference={useModalValue}
|
||||
/>
|
||||
{readmeEntrance}
|
||||
</div>
|
||||
@@ -577,7 +594,7 @@ const ToolBlockComponent = ({
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{useModal && (
|
||||
{useModalValue && (
|
||||
<Modal
|
||||
isShow={isSettingOpen}
|
||||
onClose={() => setIsSettingOpen(false)}
|
||||
@@ -589,7 +606,7 @@ const ToolBlockComponent = ({
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
{!useModal && portalContainer && isSettingOpen && createPortal(
|
||||
{!useModalValue && portalContainer && isSettingOpen && createPortal(
|
||||
<div
|
||||
className="absolute bottom-4 right-4 top-4 z-[99]"
|
||||
data-tool-setting-panel="true"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { createContext, useContext } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useRef } from 'react'
|
||||
import { useStore } from 'zustand'
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
type ToolBlockContextValue = {
|
||||
export type ToolBlockContextValue = {
|
||||
metadata?: Record<string, unknown>
|
||||
onMetadataChange?: (metadata: Record<string, unknown>) => void
|
||||
useModal?: boolean
|
||||
@@ -10,8 +13,56 @@ type ToolBlockContextValue = {
|
||||
availableNodes?: Node[]
|
||||
}
|
||||
|
||||
const ToolBlockContext = createContext<ToolBlockContextValue | null>(null)
|
||||
type ToolBlockStoreState = {
|
||||
context: ToolBlockContextValue | null
|
||||
setContext: (context: ToolBlockContextValue | null) => void
|
||||
}
|
||||
|
||||
export const ToolBlockContextProvider = ToolBlockContext.Provider
|
||||
const createToolBlockStore = (initialContext: ToolBlockContextValue | null) => createStore<ToolBlockStoreState>(set => ({
|
||||
context: initialContext,
|
||||
setContext: context => set({ context }),
|
||||
}))
|
||||
|
||||
export const useToolBlockContext = () => useContext(ToolBlockContext)
|
||||
type ToolBlockStore = ReturnType<typeof createToolBlockStore>
|
||||
|
||||
const defaultToolBlockStore = createToolBlockStore(null)
|
||||
|
||||
const ToolBlockStoreContext = createContext<ToolBlockStore | null>(null)
|
||||
|
||||
type ToolBlockContextProviderProps = {
|
||||
value?: ToolBlockContextValue | null
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const ToolBlockContextProvider = ({ value, children }: ToolBlockContextProviderProps) => {
|
||||
const storeRef = useRef<ToolBlockStore>(null)
|
||||
if (!storeRef.current)
|
||||
storeRef.current = createToolBlockStore(value ?? null)
|
||||
|
||||
useEffect(() => {
|
||||
storeRef.current?.getState().setContext(value ?? null)
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<ToolBlockStoreContext.Provider value={storeRef.current}>
|
||||
{children}
|
||||
</ToolBlockStoreContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const useToolBlockContext = <T = ToolBlockContextValue | null,>(
|
||||
selector?: (context: ToolBlockContextValue | null) => T,
|
||||
) => {
|
||||
const store = useContext(ToolBlockStoreContext) ?? defaultToolBlockStore
|
||||
const selectContext = useCallback(
|
||||
(state: ToolBlockStoreState) => {
|
||||
if (selector)
|
||||
return selector(state.context)
|
||||
return state.context as T
|
||||
},
|
||||
[selector],
|
||||
)
|
||||
|
||||
return useStore(store, selectContext)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as React from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { useSelectOrDelete } from '@/app/components/base/prompt-editor/hooks'
|
||||
@@ -112,9 +113,25 @@ const ToolGroupBlockComponent = ({
|
||||
const authBadgeLabel = t('skillEditor.authorizationBadge', { ns: 'workflow' })
|
||||
const language = useGetLanguage()
|
||||
const { theme } = useTheme()
|
||||
const toolBlockContext = useToolBlockContext()
|
||||
const isUsingExternalMetadata = Boolean(toolBlockContext?.onMetadataChange)
|
||||
const useModal = Boolean(toolBlockContext?.useModal)
|
||||
const {
|
||||
metadata,
|
||||
onMetadataChange,
|
||||
useModal,
|
||||
nodeId: contextNodeId,
|
||||
nodesOutputVars,
|
||||
availableNodes,
|
||||
} = useToolBlockContext(
|
||||
useShallow(context => ({
|
||||
metadata: context?.metadata,
|
||||
onMetadataChange: context?.onMetadataChange,
|
||||
useModal: context?.useModal,
|
||||
nodeId: context?.nodeId,
|
||||
nodesOutputVars: context?.nodesOutputVars,
|
||||
availableNodes: context?.availableNodes,
|
||||
})),
|
||||
)
|
||||
const isUsingExternalMetadata = Boolean(onMetadataChange)
|
||||
const useModalValue = Boolean(useModal)
|
||||
const isInteractive = editor.isEditable()
|
||||
const [isSettingOpen, setIsSettingOpen] = useState(false)
|
||||
const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null)
|
||||
@@ -196,22 +213,22 @@ const ToolGroupBlockComponent = ({
|
||||
if (!activeToolItem)
|
||||
return undefined
|
||||
if (isUsingExternalMetadata) {
|
||||
const metadata = toolBlockContext?.metadata as SkillFileMetadata | undefined
|
||||
return metadata?.tools?.[activeToolItem.configId]
|
||||
const externalMetadata = metadata as SkillFileMetadata | undefined
|
||||
return externalMetadata?.tools?.[activeToolItem.configId]
|
||||
}
|
||||
if (!activeTabId)
|
||||
return undefined
|
||||
const metadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
return metadata?.tools?.[activeToolItem.configId]
|
||||
}, [activeTabId, activeToolItem, fileMetadata, isUsingExternalMetadata, toolBlockContext?.metadata])
|
||||
const resultMetadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
return resultMetadata?.tools?.[activeToolItem.configId]
|
||||
}, [activeTabId, activeToolItem, fileMetadata, isUsingExternalMetadata, metadata])
|
||||
|
||||
const metadataTools = useMemo(() => {
|
||||
if (isUsingExternalMetadata)
|
||||
return ((toolBlockContext?.metadata as SkillFileMetadata | undefined)?.tools || {}) as Record<string, ToolConfigMetadata>
|
||||
return ((metadata as SkillFileMetadata | undefined)?.tools || {}) as Record<string, ToolConfigMetadata>
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return {}
|
||||
return ((fileMetadata.get(activeTabId) as SkillFileMetadata | undefined)?.tools || {}) as Record<string, ToolConfigMetadata>
|
||||
}, [activeTabId, fileMetadata, isUsingExternalMetadata, toolBlockContext?.metadata])
|
||||
}, [activeTabId, fileMetadata, isUsingExternalMetadata, metadata])
|
||||
|
||||
const getVarKindType = (type: FormTypeEnum | string) => {
|
||||
if (type === FormTypeEnum.file || type === FormTypeEnum.files)
|
||||
@@ -348,7 +365,7 @@ const ToolGroupBlockComponent = ({
|
||||
}, [configuredToolValue, isSettingOpen])
|
||||
|
||||
useEffect(() => {
|
||||
if (useModal)
|
||||
if (useModalValue)
|
||||
return
|
||||
const containerFromRef = ref.current?.closest('[data-skill-editor-root="true"]') as HTMLElement | null
|
||||
const fallbackContainer = document.querySelector('[data-skill-editor-root="true"]') as HTMLElement | null
|
||||
@@ -356,10 +373,10 @@ const ToolGroupBlockComponent = ({
|
||||
if (container)
|
||||
// eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect
|
||||
setPortalContainer(container)
|
||||
}, [ref, useModal])
|
||||
}, [ref, useModalValue])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSettingOpen || useModal)
|
||||
if (!isSettingOpen || useModalValue)
|
||||
return
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
@@ -384,7 +401,7 @@ const ToolGroupBlockComponent = ({
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside)
|
||||
}, [isSettingOpen, portalContainer, ref, useModal])
|
||||
}, [isSettingOpen, portalContainer, ref, useModalValue])
|
||||
|
||||
const resolvedEnabledByConfigId = useMemo(() => {
|
||||
const next = { ...enabledByConfigId }
|
||||
@@ -413,9 +430,9 @@ const ToolGroupBlockComponent = ({
|
||||
setToolValue(nextValue)
|
||||
const credentialId = normalizeCredentialId(nextValue.credential_id)
|
||||
if (isUsingExternalMetadata) {
|
||||
const metadata = (toolBlockContext?.metadata || {}) as SkillFileMetadata
|
||||
const externalMetadata = (metadata || {}) as SkillFileMetadata
|
||||
const toolType = currentProvider.type === CollectionType.mcp ? 'mcp' : 'builtin'
|
||||
const currentToolMetadata = (metadata.tools || {})[activeToolItem.configId]
|
||||
const currentToolMetadata = (externalMetadata.tools || {})[activeToolItem.configId]
|
||||
const buildFields = (value: Record<string, unknown> | undefined) => {
|
||||
if (!value)
|
||||
return []
|
||||
@@ -431,9 +448,9 @@ const ToolGroupBlockComponent = ({
|
||||
...buildFields(nextValue.parameters),
|
||||
]
|
||||
const nextMetadata: SkillFileMetadata = {
|
||||
...metadata,
|
||||
...externalMetadata,
|
||||
tools: {
|
||||
...(metadata.tools || {}),
|
||||
...(externalMetadata.tools || {}),
|
||||
[activeToolItem.configId]: {
|
||||
type: toolType,
|
||||
configuration: { fields },
|
||||
@@ -442,14 +459,14 @@ const ToolGroupBlockComponent = ({
|
||||
},
|
||||
},
|
||||
}
|
||||
toolBlockContext?.onMetadataChange?.(nextMetadata)
|
||||
onMetadataChange?.(nextMetadata)
|
||||
return
|
||||
}
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return
|
||||
const metadata = (fileMetadata.get(activeTabId) || {}) as SkillFileMetadata
|
||||
const currentMetaData = (fileMetadata.get(activeTabId) || {}) as SkillFileMetadata
|
||||
const toolType = currentProvider.type === CollectionType.mcp ? 'mcp' : 'builtin'
|
||||
const currentToolMetadata = (metadata.tools || {})[activeToolItem.configId]
|
||||
const currentToolMetadata = (currentMetaData.tools || {})[activeToolItem.configId]
|
||||
const buildFields = (value: Record<string, unknown> | undefined) => {
|
||||
if (!value)
|
||||
return []
|
||||
@@ -465,9 +482,9 @@ const ToolGroupBlockComponent = ({
|
||||
...buildFields(nextValue.parameters),
|
||||
]
|
||||
const nextMetadata: SkillFileMetadata = {
|
||||
...metadata,
|
||||
...currentMetaData,
|
||||
tools: {
|
||||
...(metadata.tools || {}),
|
||||
...(currentMetaData.tools || {}),
|
||||
[activeToolItem.configId]: {
|
||||
type: toolType,
|
||||
configuration: { fields },
|
||||
@@ -502,16 +519,16 @@ const ToolGroupBlockComponent = ({
|
||||
return nextMetadata
|
||||
}
|
||||
if (isUsingExternalMetadata) {
|
||||
toolBlockContext?.onMetadataChange?.(applyEnabled(toolBlockContext?.metadata as SkillFileMetadata | undefined))
|
||||
onMetadataChange?.(applyEnabled(metadata as SkillFileMetadata | undefined))
|
||||
return
|
||||
}
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return
|
||||
const metadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
const nextMetadata = applyEnabled(metadata)
|
||||
const currentMetadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
const nextMetadata = applyEnabled(currentMetadata)
|
||||
storeApi.getState().setDraftMetadata(activeTabId, nextMetadata)
|
||||
storeApi.getState().pinTab(activeTabId)
|
||||
}, [activeTabId, currentProvider?.type, fileMetadata, isUsingExternalMetadata, storeApi, toolBlockContext])
|
||||
}, [activeTabId, currentProvider?.type, fileMetadata, isUsingExternalMetadata, metadata, onMetadataChange, storeApi])
|
||||
|
||||
const renderIcon = () => {
|
||||
if (!resolvedIcon)
|
||||
@@ -611,10 +628,10 @@ const ToolGroupBlockComponent = ({
|
||||
currentTool={currentTool}
|
||||
value={toolValue}
|
||||
onChange={handleToolValueChange}
|
||||
nodeId={toolBlockContext?.nodeId}
|
||||
nodesOutputVars={toolBlockContext?.nodesOutputVars}
|
||||
availableNodes={toolBlockContext?.availableNodes}
|
||||
enableVariableReference={useModal}
|
||||
nodeId={contextNodeId}
|
||||
nodesOutputVars={nodesOutputVars}
|
||||
availableNodes={availableNodes}
|
||||
enableVariableReference={useModalValue}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -724,13 +741,13 @@ const ToolGroupBlockComponent = ({
|
||||
return nextMetadata
|
||||
}
|
||||
if (isUsingExternalMetadata) {
|
||||
toolBlockContext?.onMetadataChange?.(applyCredential(toolBlockContext?.metadata as SkillFileMetadata | undefined))
|
||||
onMetadataChange?.(applyCredential(metadata as SkillFileMetadata | undefined))
|
||||
return
|
||||
}
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return
|
||||
const metadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
const nextMetadata = applyCredential(metadata)
|
||||
const currentMetadata = fileMetadata.get(activeTabId) as SkillFileMetadata | undefined
|
||||
const nextMetadata = applyCredential(currentMetadata)
|
||||
storeApi.getState().setDraftMetadata(activeTabId, nextMetadata)
|
||||
storeApi.getState().pinTab(activeTabId)
|
||||
}}
|
||||
@@ -841,7 +858,7 @@ const ToolGroupBlockComponent = ({
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{useModal && (
|
||||
{useModalValue && (
|
||||
<Modal
|
||||
isShow={isSettingOpen}
|
||||
onClose={() => {
|
||||
@@ -856,7 +873,7 @@ const ToolGroupBlockComponent = ({
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
{!useModal && portalContainer && isSettingOpen && createPortal(
|
||||
{!useModalValue && portalContainer && isSettingOpen && createPortal(
|
||||
<div
|
||||
className="absolute bottom-4 right-4 top-4 z-[99]"
|
||||
data-tool-group-setting-panel="true"
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as React from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { useBasicTypeaheadTriggerMatch } from '@/app/components/base/prompt-editor/hooks'
|
||||
import { $splitNodeContainingQuery } from '@/app/components/base/prompt-editor/utils'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
@@ -42,8 +43,13 @@ const ToolPickerBlock = ({ scope = 'all', enableAutoDefault = false }: ToolPicke
|
||||
maxLength: 75,
|
||||
})
|
||||
const storeApi = useWorkflowStore()
|
||||
const toolBlockContext = useToolBlockContext()
|
||||
const isUsingExternalMetadata = Boolean(toolBlockContext?.onMetadataChange)
|
||||
const { metadata, onMetadataChange } = useToolBlockContext(
|
||||
useShallow(context => ({
|
||||
metadata: context?.metadata,
|
||||
onMetadataChange: context?.onMetadataChange,
|
||||
})),
|
||||
)
|
||||
const isUsingExternalMetadata = Boolean(onMetadataChange)
|
||||
const [queryString, setQueryString] = useState('')
|
||||
|
||||
const canUseAutoByType = useCallback(
|
||||
@@ -133,21 +139,21 @@ const ToolPickerBlock = ({ scope = 'all', enableAutoDefault = false }: ToolPicke
|
||||
})
|
||||
|
||||
if (isUsingExternalMetadata) {
|
||||
const metadata = (toolBlockContext?.metadata || {}) as Record<string, unknown>
|
||||
const nextMetadata = buildNextMetadata(metadata, toolEntries)
|
||||
toolBlockContext?.onMetadataChange?.(nextMetadata)
|
||||
const externalMetadata = (metadata || {}) as Record<string, unknown>
|
||||
const nextMetadata = buildNextMetadata(externalMetadata, toolEntries)
|
||||
onMetadataChange?.(nextMetadata)
|
||||
return
|
||||
}
|
||||
const { activeTabId, fileMetadata, setDraftMetadata, pinTab } = storeApi.getState()
|
||||
if (!activeTabId || activeTabId === START_TAB_ID)
|
||||
return
|
||||
const metadata = (fileMetadata.get(activeTabId) || {}) as Record<string, unknown>
|
||||
const nextMetadata = buildNextMetadata(metadata, toolEntries)
|
||||
const currentMetadata = (fileMetadata.get(activeTabId) || {}) as Record<string, unknown>
|
||||
const nextMetadata = buildNextMetadata(currentMetadata, toolEntries)
|
||||
setDraftMetadata(activeTabId, {
|
||||
...nextMetadata,
|
||||
})
|
||||
pinTab(activeTabId)
|
||||
}, [buildNextMetadata, editor, getMatchFromSelection, isUsingExternalMetadata, storeApi, toolBlockContext])
|
||||
}, [buildNextMetadata, editor, getMatchFromSelection, isUsingExternalMetadata, metadata, onMetadataChange, storeApi])
|
||||
|
||||
const renderMenu = useCallback((
|
||||
anchorElementRef: React.RefObject<HTMLElement | null>,
|
||||
|
||||
Reference in New Issue
Block a user