diff --git a/web/app/components/tools/provider-type.ts b/web/app/components/tools/provider-type.ts
new file mode 100644
index 00000000000..443827530fd
--- /dev/null
+++ b/web/app/components/tools/provider-type.ts
@@ -0,0 +1,5 @@
+import type { ToolProviderType } from '@dify/contracts/api/console/workspaces/types.gen'
+import { zToolProviderType } from '@dify/contracts/api/console/workspaces/zod.gen'
+
+export const parseToolProviderType = (providerType: unknown): ToolProviderType =>
+ zToolProviderType.parse(providerType)
diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts
index d820719aeeb..a1e5ef098a1 100644
--- a/web/app/components/tools/types.ts
+++ b/web/app/components/tools/types.ts
@@ -1,3 +1,7 @@
+import type {
+ DatasourceProviderType,
+ ToolProviderType,
+} from '@dify/contracts/api/console/workspaces/types.gen'
import type { VarType } from '../workflow/types'
type LocalizedText
= {
@@ -27,16 +31,20 @@ export type Credential = {
api_key_query_param?: string
}
-export enum CollectionType {
- all = 'all',
- builtIn = 'builtin',
- custom = 'api',
- model = 'model',
- workflow = 'workflow',
- mcp = 'mcp',
- datasource = 'datasource',
- trigger = 'trigger',
-}
+export const CollectionType = {
+ all: 'all',
+ builtIn: 'builtin',
+ custom: 'api',
+ model: 'model',
+ workflow: 'workflow',
+ mcp: 'mcp',
+ datasource: 'datasource',
+ trigger: 'trigger',
+} as const
+
+export type CollectionType = (typeof CollectionType)[keyof typeof CollectionType]
+
+export type CollectionProviderType = CollectionType | DatasourceProviderType | ToolProviderType
export type Emoji = {
background: string
@@ -51,7 +59,7 @@ export type Collection = {
icon: string | Emoji
icon_dark?: string | Emoji
label: LocalizedText
- type: CollectionType | string
+ type: CollectionProviderType
team_credentials: Record
is_team_authorization: boolean
allow_delete: boolean
diff --git a/web/app/components/workflow/block-selector/__tests__/data-sources.spec.tsx b/web/app/components/workflow/block-selector/__tests__/data-sources.spec.tsx
index 6f192ce1e2e..4fb296eb9a4 100644
--- a/web/app/components/workflow/block-selector/__tests__/data-sources.spec.tsx
+++ b/web/app/components/workflow/block-selector/__tests__/data-sources.spec.tsx
@@ -4,7 +4,6 @@ import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/query'
import { PluginCategoryEnum } from '@/app/components/plugins/types'
-import { CollectionType } from '@/app/components/tools/types'
import { useGetLanguage } from '@/context/i18n'
import useTheme from '@/hooks/use-theme'
import { renderWithConsoleQuery } from '@/test/console/query-data'
@@ -43,7 +42,7 @@ const createToolProvider = (overrides: Partial = {}): ToolWith
description: { en_US: 'desc', zh_Hans: '描述' },
icon: 'icon',
label: { en_US: 'File Source', zh_Hans: '文件源' },
- type: CollectionType.datasource,
+ type: 'local_file',
team_credentials: {},
is_team_authorization: false,
allow_delete: false,
diff --git a/web/app/components/workflow/block-selector/__tests__/tool-browser.spec.tsx b/web/app/components/workflow/block-selector/__tests__/tool-browser.spec.tsx
index 91f104966ad..0ca184ebe46 100644
--- a/web/app/components/workflow/block-selector/__tests__/tool-browser.spec.tsx
+++ b/web/app/components/workflow/block-selector/__tests__/tool-browser.spec.tsx
@@ -77,7 +77,7 @@ describe('ToolBrowser', () => {
customTools={[
createToolProvider({
id: 'provider-custom',
- type: 'custom',
+ type: CollectionType.custom,
label: { en_US: 'Custom Provider', zh_Hans: 'Custom Provider' },
}),
]}
@@ -89,7 +89,7 @@ describe('ToolBrowser', () => {
expect(screen.getByText('Built In Provider')).toBeInTheDocument()
expect(screen.getByText('Custom Provider')).toBeInTheDocument()
- await user.click(screen.getByText('workflow.tabs.customTool'))
+ await user.click(screen.getByRole('button', { name: 'workflow.tabs.customTool' }))
expect(screen.getByText('Custom Provider')).toBeInTheDocument()
expect(screen.queryByText('Built In Provider')).not.toBeInTheDocument()
diff --git a/web/app/components/workflow/block-selector/__tests__/tool-list-data.spec.ts b/web/app/components/workflow/block-selector/__tests__/tool-list-data.spec.ts
index 4a7726f41ea..f30d4d5dde3 100644
--- a/web/app/components/workflow/block-selector/__tests__/tool-list-data.spec.ts
+++ b/web/app/components/workflow/block-selector/__tests__/tool-list-data.spec.ts
@@ -71,7 +71,7 @@ describe('createToolListData', () => {
const result = createToolListData(
[
createToolProvider({ id: 'workflow', type: CollectionType.workflow }),
- createToolProvider({ id: 'data-source', type: CollectionType.datasource }),
+ createToolProvider({ id: 'data-source', type: 'local_file' }),
],
() => 'A',
)
@@ -90,19 +90,19 @@ describe('createToolListData', () => {
])
})
- it('merges providers across letters and keeps MCP and unknown provider identities explicit', () => {
+ it('merges providers across letters and keeps MCP and plugin provider identities explicit', () => {
const lettersByToolId: Record = {
'author-a': 'A',
'author-b': 'B',
mcp: 'C',
- unknown: 'D',
+ plugin: 'D',
}
const result = createToolListData(
[
createToolProvider({ id: 'author-a', author: 'Dify' }),
createToolProvider({ id: 'author-b', author: 'Dify' }),
createToolProvider({ id: 'mcp', type: CollectionType.mcp }),
- createToolProvider({ id: 'unknown', author: 'Future', type: 'future-provider' }),
+ createToolProvider({ id: 'plugin', author: 'Plugin Author', type: 'plugin' }),
],
(tool) => lettersByToolId[tool.id] ?? '#',
)
@@ -123,8 +123,8 @@ describe('createToolListData', () => {
},
{
kind: 'author',
- author: 'Future',
- tools: [expect.objectContaining({ id: 'unknown' })],
+ author: 'Plugin Author',
+ tools: [expect.objectContaining({ id: 'plugin' })],
},
])
})
diff --git a/web/app/components/workflow/block-selector/__tests__/utils.spec.ts b/web/app/components/workflow/block-selector/__tests__/utils.spec.ts
index 8f3249a8259..18c76ac035b 100644
--- a/web/app/components/workflow/block-selector/__tests__/utils.spec.ts
+++ b/web/app/components/workflow/block-selector/__tests__/utils.spec.ts
@@ -12,7 +12,7 @@ const createDataSourceItem = (overrides: Partial = {}): DataSour
provider: 'provider-a',
declaration: {
credentials_schema: [{ name: 'api_key' }],
- provider_type: 'hosted',
+ provider_type: 'local_file',
identity: {
author: 'Dify',
description: createLocalizedText('Datasource provider'),
@@ -58,7 +58,7 @@ describe('transformDataSourceToTool', () => {
description: createLocalizedText('Datasource provider'),
icon: 'provider-icon',
label: createLocalizedText('Provider A'),
- type: 'hosted',
+ type: 'local_file',
allow_delete: true,
is_authorized: true,
is_team_authorization: true,
diff --git a/web/app/components/workflow/block-selector/data-sources.tsx b/web/app/components/workflow/block-selector/data-sources.tsx
index 5c5e075dd6b..a7ebcba5f17 100644
--- a/web/app/components/workflow/block-selector/data-sources.tsx
+++ b/web/app/components/workflow/block-selector/data-sources.tsx
@@ -1,6 +1,7 @@
import type { OnSelectBlock, ToolWithProvider } from '../types'
import type { DataSourceDefaultValue, ToolDefaultValue } from './types'
import type { ListRef } from '@/app/components/workflow/block-selector/marketplace-plugin/list'
+import { zDatasourceProviderType } from '@dify/contracts/api/console/workspaces/zod.gen'
import { cn } from '@langgenius/dify-ui/cn'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useDebounce } from 'ahooks'
@@ -60,7 +61,7 @@ function DataSources({
(_: BlockEnum, toolDefaultValue: ToolDefaultValue) => {
let defaultValue: DataSourceDefaultValue = {
plugin_id: toolDefaultValue?.provider_id,
- provider_type: toolDefaultValue?.provider_type,
+ provider_type: zDatasourceProviderType.parse(toolDefaultValue.provider_type),
provider_name: toolDefaultValue?.provider_name,
datasource_name: toolDefaultValue?.tool_name,
datasource_label: toolDefaultValue?.tool_label,
diff --git a/web/app/components/workflow/block-selector/tool-list-data.ts b/web/app/components/workflow/block-selector/tool-list-data.ts
index 13e21c301e2..3b9a7e03707 100644
--- a/web/app/components/workflow/block-selector/tool-list-data.ts
+++ b/web/app/components/workflow/block-selector/tool-list-data.ts
@@ -1,9 +1,17 @@
+import type { DatasourceProviderType } from '@dify/contracts/api/console/workspaces/types.gen'
import type { ToolWithProvider } from '../types'
import { pinyin } from 'pinyin-pro'
import { CollectionType } from '../../tools/types'
type ToolCategoryGroup = 'custom' | 'data-source' | 'mcp' | 'workflow'
+const datasourceProviderTypes: Record = {
+ local_file: true,
+ online_document: true,
+ online_drive: true,
+ website_crawl: true,
+}
+
type AuthorToolGroup = {
kind: 'author'
author: string
@@ -114,10 +122,14 @@ function addToolToAuthorGroup(bucket: LetterBucket, tool: ToolWithProvider) {
function getToolCategoryGroup(type: ToolWithProvider['type']): ToolCategoryGroup | undefined {
if (type === CollectionType.custom) return 'custom'
if (type === CollectionType.workflow) return 'workflow'
- if (type === CollectionType.datasource) return 'data-source'
+ if (isDatasourceProviderType(type)) return 'data-source'
if (type === CollectionType.mcp) return 'mcp'
}
+function isDatasourceProviderType(type: ToolWithProvider['type']): type is DatasourceProviderType {
+ return Object.hasOwn(datasourceProviderTypes, type)
+}
+
function mergeGroupsByProvider(buckets: LetterBucket[]) {
const groups: ToolGroup[] = []
const authorGroups = new Map()
diff --git a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/__tests__/list.spec.tsx b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/__tests__/list.spec.tsx
index c84b9ffd896..f4b25998b81 100644
--- a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/__tests__/list.spec.tsx
+++ b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/__tests__/list.spec.tsx
@@ -50,7 +50,7 @@ describe('ToolListTreeView', () => {
tools: [
createToolProvider({
id: 'custom-provider',
- type: 'custom',
+ type: CollectionType.custom,
label: { en_US: 'Custom Provider', zh_Hans: 'Custom Provider' },
}),
],
@@ -83,7 +83,7 @@ describe('ToolListTreeView', () => {
tools: [
createToolProvider({
id: 'data-source-provider',
- type: CollectionType.datasource,
+ type: 'local_file',
label: { en_US: 'Data Source Provider', zh_Hans: 'Data Source Provider' },
}),
],
diff --git a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx
index d0b17b57f46..7d70a2b95e4 100644
--- a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx
+++ b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx
@@ -62,7 +62,7 @@ const TriggerPluginActionItem: FC = ({
onSelect(BlockEnum.TriggerPlugin, {
plugin_id: provider.plugin_id,
provider_id: provider.name,
- provider_type: provider.type as string,
+ provider_type: provider.type,
provider_name: provider.name,
event_name: payload.name,
event_label: payload.label[language]!,
diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts
index 011c2fff6fb..6d224866d9c 100644
--- a/web/app/components/workflow/block-selector/types.ts
+++ b/web/app/components/workflow/block-selector/types.ts
@@ -1,4 +1,5 @@
import type { AgentInviteOptionResponse } from '@dify/contracts/api/console/agent/types.gen'
+import type { DatasourceProviderType } from '@dify/contracts/api/console/workspaces/types.gen'
import type {
ParametersSchema,
PluginMeta,
@@ -6,7 +7,7 @@ import type {
SupportedCreationMethods,
TriggerEvent,
} from '../../plugins/types'
-import type { Collection, Event } from '../../tools/types'
+import type { Collection, CollectionProviderType, Event } from '../../tools/types'
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
export const TabType = {
@@ -48,7 +49,7 @@ export type BlockClassification = (typeof BlockClassification)[keyof typeof Bloc
type PluginCommonDefaultValue = {
provider_id: string
- provider_type: string
+ provider_type: CollectionProviderType
provider_name: string
}
@@ -87,7 +88,6 @@ export type ToolDefaultValue = PluginCommonDefaultValue & {
export type DataSourceDefaultValue = Omit & {
plugin_id: string
- provider_type: string
provider_name: string
datasource_name: string
datasource_label: string
@@ -145,7 +145,7 @@ export type DataSourceItem = {
provider: string
declaration: {
credentials_schema: unknown[]
- provider_type: string
+ provider_type: DatasourceProviderType
identity: {
author: string
description: TypeWithI18N
diff --git a/web/app/components/workflow/hooks/__tests__/use-node-plugin-installation.spec.ts b/web/app/components/workflow/hooks/__tests__/use-node-plugin-installation.spec.ts
index c2444e4c4d5..7c8b6d211b1 100644
--- a/web/app/components/workflow/hooks/__tests__/use-node-plugin-installation.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-node-plugin-installation.spec.ts
@@ -1,4 +1,5 @@
import type { CommonNodeType } from '../../types'
+import type { CollectionProviderType } from '@/app/components/tools/types'
import { act } from '@testing-library/react'
import { CollectionType } from '@/app/components/tools/types'
import { renderWorkflowHook } from '../../__tests__/workflow-test-env'
@@ -27,7 +28,8 @@ vi.mock('@/service/use-tools', () => ({
useAllCustomTools: (enabled: boolean) => mockCustomTools(enabled),
useAllWorkflowTools: (enabled: boolean) => mockWorkflowTools(enabled),
useAllMCPTools: (enabled: boolean) => mockMcpTools(enabled),
- useInvalidToolsByType: (providerType?: string) => mockInvalidToolsByType(providerType),
+ useInvalidToolsByType: (providerType?: CollectionProviderType) =>
+ mockInvalidToolsByType(providerType),
}))
vi.mock('@/service/use-triggers', () => ({
@@ -164,11 +166,11 @@ describe('useNodePluginInstallation', () => {
expect(result.current.shouldDim).toBe(false)
})
- it('should keep unknown tool collection types installable without collection state', () => {
+ it('should keep plugin tool collections installable without collection state', () => {
const { result } = renderWorkflowHook(() =>
useNodePluginInstallation(
makeToolNode({
- provider_type: 'unknown' as CollectionType,
+ provider_type: 'plugin',
plugin_unique_identifier: undefined,
plugin_id: undefined,
provider_id: 'legacy-provider',
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 dc0e8b909e0..e1354c0c985 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
@@ -61,7 +61,7 @@ type Props = Readonly<{
showManageInputField?: boolean
onManageInputField?: () => void
extraParams?: Record
- providerType?: string
+ providerType?: 'tool' | 'trigger'
disableVariableInsertion?: boolean
}>
diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/helpers.spec.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/helpers.spec.tsx
index 54c19df4f19..59e6b4b2923 100644
--- a/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/helpers.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/helpers.spec.tsx
@@ -73,7 +73,7 @@ describe('workflow-panel helpers', () => {
const dataSourceData = asNodeData({
type: BlockEnum.DataSource,
plugin_id: 'source-1',
- provider_type: 'remote',
+ provider_type: 'online_document',
})
const triggerPlugins = [{ plugin_id: 'trigger-1', id: '1' }]
const dataSources = [{ plugin_id: 'source-1' }]
diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/index.spec.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/index.spec.tsx
index d8959247c25..7aebb57ca17 100644
--- a/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/__tests__/index.spec.tsx
@@ -592,7 +592,7 @@ describe('workflow-panel index', () => {
createData({
type: BlockEnum.DataSource,
plugin_id: 'source-1',
- provider_type: 'remote',
+ provider_type: 'online_document',
}) as never
}
>
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 9c845a89eed..8a714bb1b4e 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
@@ -637,7 +637,7 @@ const BasePanel: FC = ({ id, data, children }) => {
className="px-4 pb-2"
pluginPayload={{
provider: currToolCollection?.name || '',
- providerType: currToolCollection?.type || '',
+ providerType: currToolCollection?.type,
category: AuthCategory.tool,
detail: currToolCollection as any,
}}
@@ -647,7 +647,7 @@ const BasePanel: FC = ({ id, data, children }) => {
({
onClick={() =>
onSelect(BlockEnum.DataSource, {
plugin_id: 'plugin-id',
- provider_type: 'datasource',
+ provider_type: 'local_file',
provider_name: 'file',
datasource_name: 'local-file',
datasource_label: 'Local File',
@@ -89,7 +89,7 @@ describe('DataSourceEmptyNode', () => {
expect(handleReplaceNode).toHaveBeenCalledWith(BlockEnum.DataSource, {
plugin_id: 'plugin-id',
- provider_type: 'datasource',
+ provider_type: 'local_file',
provider_name: 'file',
datasource_name: 'local-file',
datasource_label: 'Local File',
diff --git a/web/app/components/workflow/nodes/data-source/__tests__/node.spec.tsx b/web/app/components/workflow/nodes/data-source/__tests__/node.spec.tsx
index 0208448c7ea..a7b708d1d16 100644
--- a/web/app/components/workflow/nodes/data-source/__tests__/node.spec.tsx
+++ b/web/app/components/workflow/nodes/data-source/__tests__/node.spec.tsx
@@ -25,7 +25,7 @@ const createNodeData = (overrides: Partial = {}): DataSource
desc: '',
type: BlockEnum.DataSource,
plugin_id: 'plugin-id',
- provider_type: 'datasource',
+ provider_type: 'local_file',
provider_name: 'file',
datasource_name: 'local-file',
datasource_label: 'Local File',
diff --git a/web/app/components/workflow/nodes/data-source/__tests__/panel.spec.tsx b/web/app/components/workflow/nodes/data-source/__tests__/panel.spec.tsx
index 0e69c3a21f9..93484f38bd2 100644
--- a/web/app/components/workflow/nodes/data-source/__tests__/panel.spec.tsx
+++ b/web/app/components/workflow/nodes/data-source/__tests__/panel.spec.tsx
@@ -106,7 +106,7 @@ const createData = (overrides: Partial = {}): DataSourceNode
desc: '',
type: BlockEnum.DataSource,
plugin_id: 'plugin-1',
- provider_type: 'remote',
+ provider_type: 'online_document',
provider_name: 'provider',
datasource_name: 'source-a',
datasource_label: 'Source A',
diff --git a/web/app/components/workflow/nodes/data-source/types.ts b/web/app/components/workflow/nodes/data-source/types.ts
index c51bca8a1b3..a4d7f2601ef 100644
--- a/web/app/components/workflow/nodes/data-source/types.ts
+++ b/web/app/components/workflow/nodes/data-source/types.ts
@@ -1,3 +1,4 @@
+import type { DatasourceProviderType } from '@dify/contracts/api/console/workspaces/types.gen'
import type { Dispatch, SetStateAction } from 'react'
import type { ResourceVarInputs } from '../_base/types'
import type { CommonNodeType, Node } from '@/app/components/workflow/types'
@@ -13,7 +14,7 @@ export type ToolVarInputs = ResourceVarInputs
export type DataSourceNodeType = CommonNodeType & {
fileExtensions?: string[]
plugin_id: string
- provider_type: string
+ provider_type: DatasourceProviderType
provider_name: string
datasource_name: string
datasource_label: string
diff --git a/web/app/components/workflow/utils/__tests__/data-source.spec.ts b/web/app/components/workflow/utils/__tests__/data-source.spec.ts
index 22b31d7e2bd..300ad3196af 100644
--- a/web/app/components/workflow/utils/__tests__/data-source.spec.ts
+++ b/web/app/components/workflow/utils/__tests__/data-source.spec.ts
@@ -1,6 +1,5 @@
import type { DataSourceNodeType } from '../../nodes/data-source/types'
import type { ToolWithProvider } from '../../types'
-import { CollectionType } from '@/app/components/tools/types'
import { BlockEnum } from '../../types'
import { getDataSourceCheckParams } from '../data-source'
@@ -23,7 +22,7 @@ function createDataSourceData(overrides: Partial = {}): Data
desc: '',
type: BlockEnum.DataSource,
plugin_id: 'plugin-ds-1',
- provider_type: CollectionType.builtIn,
+ provider_type: 'local_file',
datasource_name: 'mysql_query',
datasource_parameters: {},
datasource_configurations: {},
@@ -70,7 +69,7 @@ describe('getDataSourceCheckParams', () => {
])
})
- it('should mark notAuthed for builtin datasource without authorization', () => {
+ it('should mark an unauthorized datasource as not authed', () => {
const result = getDataSourceCheckParams(
createDataSourceData(),
[createDataSourceCollection()],
diff --git a/web/app/components/workflow/utils/data-source.ts b/web/app/components/workflow/utils/data-source.ts
index a15c5e0f106..3151dd784fa 100644
--- a/web/app/components/workflow/utils/data-source.ts
+++ b/web/app/components/workflow/utils/data-source.ts
@@ -1,6 +1,5 @@
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { InputVar, ToolWithProvider } from '../types'
-import { CollectionType } from '@/app/components/tools/types'
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
export const getDataSourceCheckParams = (
@@ -8,8 +7,7 @@ export const getDataSourceCheckParams = (
dataSourceList: ToolWithProvider[],
language: string,
) => {
- const { plugin_id, provider_type, datasource_name } = toolData
- const isBuiltIn = provider_type === CollectionType.builtIn
+ const { plugin_id, datasource_name } = toolData
const currentDataSource = dataSourceList.find((item) => item.plugin_id === plugin_id)
const currentDataSourceItem = currentDataSource?.tools.find(
(tool) => tool.name === datasource_name,
@@ -32,7 +30,7 @@ export const getDataSourceCheckParams = (
})
return formInputs
})(),
- notAuthed: isBuiltIn && !!currentDataSource?.allow_delete && !currentDataSource?.is_authorized,
+ notAuthed: !!currentDataSource?.allow_delete && !currentDataSource?.is_authorized,
language,
}
}
diff --git a/web/features/agent-v2/agent-composer/__tests__/store.spec.ts b/web/features/agent-v2/agent-composer/__tests__/store.spec.ts
index 68b64d59474..c16105c6d2e 100644
--- a/web/features/agent-v2/agent-composer/__tests__/store.spec.ts
+++ b/web/features/agent-v2/agent-composer/__tests__/store.spec.ts
@@ -428,6 +428,7 @@ describe('agent composer store conversions', () => {
kind: 'provider',
name: 'duckduckgo',
iconClassName: 'i-custom-public-other-default-tool-icon text-text-tertiary',
+ providerType: 'builtin',
credentialVariant: 'none',
actions: [
{
diff --git a/web/features/agent-v2/agent-composer/conversions.ts b/web/features/agent-v2/agent-composer/conversions.ts
index da3a3f02994..4b79f162fce 100644
--- a/web/features/agent-v2/agent-composer/conversions.ts
+++ b/web/features/agent-v2/agent-composer/conversions.ts
@@ -287,7 +287,7 @@ const toDifyToolConfigs = (
enabled: true,
provider: tool.name,
provider_id: tool.id,
- provider_type: tool.providerType ?? 'builtin',
+ provider_type: tool.providerType,
tool_name: action.toolName,
runtime_parameters: toToolRuntimeParameters(toolSettings[action.id]),
credential_type: credentialType,
diff --git a/web/features/agent-v2/agent-composer/form-state.ts b/web/features/agent-v2/agent-composer/form-state.ts
index adaec226a0f..76c6e7f3fd5 100644
--- a/web/features/agent-v2/agent-composer/form-state.ts
+++ b/web/features/agent-v2/agent-composer/form-state.ts
@@ -2,6 +2,7 @@ import type {
AgentKnowledgeDatasetConfig,
AgentSoulAppFeaturesConfig,
AgentSoulModelConfig,
+ ToolProviderType,
} from '@dify/contracts/api/console/agent/types.gen'
import type { FileTreeIconType } from '@langgenius/dify-ui/file-tree'
import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
@@ -95,7 +96,7 @@ export type AgentProviderTool = AgentToolBase & {
iconClassName: string
icon?: ToolDefaultValue['provider_icon']
iconDark?: ToolDefaultValue['provider_icon_dark']
- providerType?: string
+ providerType: ToolProviderType
allowDelete?: boolean
credentialId?: string
credentialKey?: I18nKeysWithPrefix<'agentV2', 'agentDetail.configure.tools.'>
diff --git a/web/features/agent-v2/agent-composer/store-modules/__tests__/tools.spec.ts b/web/features/agent-v2/agent-composer/store-modules/__tests__/tools.spec.ts
index 06723304a3f..3e0216456ea 100644
--- a/web/features/agent-v2/agent-composer/store-modules/__tests__/tools.spec.ts
+++ b/web/features/agent-v2/agent-composer/store-modules/__tests__/tools.spec.ts
@@ -151,6 +151,7 @@ describe('agent composer tools store', () => {
kind: 'provider',
name: 'DuckDuckGo',
iconClassName: 'i-simple-icons-duckduckgo',
+ providerType: 'builtin',
credentialVariant: 'none',
actions: [
{
diff --git a/web/features/agent-v2/agent-composer/store-modules/tools.ts b/web/features/agent-v2/agent-composer/store-modules/tools.ts
index 22d9beffb05..9cbc98b3722 100644
--- a/web/features/agent-v2/agent-composer/store-modules/tools.ts
+++ b/web/features/agent-v2/agent-composer/store-modules/tools.ts
@@ -1,3 +1,4 @@
+import type { ToolProviderType } from '@dify/contracts/api/console/agent/types.gen'
import type {
AgentCliTool,
AgentProviderTool,
@@ -11,7 +12,8 @@ import { syncCliToolReferenceLabels } from '../reference-labels'
import { agentComposerDraftAtom } from '../store'
import { resolveDraftFieldUpdate } from './utils'
-export type AgentProviderToolDefaultValue = ToolDefaultValue & {
+export type AgentProviderToolDefaultValue = Omit & {
+ provider_type: ToolProviderType
allowDelete?: boolean
credentialType?: AgentProviderTool['credentialType']
credentialRequired?: boolean
diff --git a/web/features/agent-v2/agent-detail/configure/components/__tests__/agent-prompt-editor.spec.tsx b/web/features/agent-v2/agent-detail/configure/components/__tests__/agent-prompt-editor.spec.tsx
index f4376642ad1..b5db3f0c203 100644
--- a/web/features/agent-v2/agent-detail/configure/components/__tests__/agent-prompt-editor.spec.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/__tests__/agent-prompt-editor.spec.tsx
@@ -198,6 +198,7 @@ const duckDuckGoProviderTool: AgentTool = {
name: 'DuckDuckGo',
kind: 'provider',
iconClassName: 'i-simple-icons-duckduckgo',
+ providerType: 'builtin',
credentialKey: 'agentDetail.configure.tools.credential.authOne',
credentialVariant: 'authorized',
actions: [duckDuckGoSearchAction],
diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/slash.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/slash.tsx
index 7181ec21674..02ea70542a8 100644
--- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/slash.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/prompt-editor/slash.tsx
@@ -19,6 +19,7 @@ import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { getMarketplaceCategoryUrl } from '@/app/components/plugins/marketplace/utils'
import { PluginCategoryEnum } from '@/app/components/plugins/types'
+import { parseToolProviderType } from '@/app/components/tools/provider-type'
import { CollectionType } from '@/app/components/tools/types'
import BlockIcon from '@/app/components/workflow/block-icon'
import { ToolType } from '@/app/components/workflow/block-selector/types'
@@ -493,7 +494,7 @@ function toToolDefaultValue(
return {
provider_id: provider.id,
- provider_type: provider.type,
+ provider_type: parseToolProviderType(provider.type),
provider_name: provider.name,
provider_show_name: providerLabel,
plugin_id: provider.plugin_id,
diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/__tests__/index.spec.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/__tests__/index.spec.tsx
index 9e8f8184dbb..472f3a91044 100644
--- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/__tests__/index.spec.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/__tests__/index.spec.tsx
@@ -111,6 +111,7 @@ const agentToolsDraft = {
kind: 'provider',
name: 'DuckDuckGo',
iconClassName: 'i-simple-icons-duckduckgo',
+ providerType: 'builtin',
credentialKey: 'agentDetail.configure.tools.credential.authOne',
credentialVariant: 'none',
actions: [
@@ -144,6 +145,7 @@ const reflectedAgentToolsDraft = {
kind: 'provider',
name: 'google',
iconClassName: 'i-custom-public-other-default-tool-icon',
+ providerType: 'builtin',
credentialVariant: 'none',
actions: [
{
@@ -165,6 +167,7 @@ const reflectedUnauthorizedNoCredentialDraft = {
kind: 'provider',
name: 'duckduckgo',
iconClassName: 'i-custom-public-other-default-tool-icon',
+ providerType: 'builtin',
credentialType: 'unauthorized',
credentialVariant: 'unauthorized',
actions: [
@@ -187,6 +190,7 @@ const reflectedUnauthorizedOAuthCredentialTypeDraft = {
kind: 'provider',
name: 'google',
iconClassName: 'i-custom-public-other-default-tool-icon',
+ providerType: 'builtin',
credentialType: 'unauthorized',
credentialVariant: 'none',
actions: [
diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/index.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/index.tsx
index 157e747b58b..52d1f11025c 100644
--- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/index.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/index.tsx
@@ -15,6 +15,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/pop
import { useAtomValue, useSetAtom } from 'jotai'
import { memo, useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
+import { parseToolProviderType } from '@/app/components/tools/provider-type'
import { CollectionType } from '@/app/components/tools/types'
import { ToolPickerContent } from '@/app/components/workflow/block-selector/tool-picker'
import { useGetLanguage } from '@/context/i18n'
@@ -211,7 +212,7 @@ function useDisplayTools(tools: AgentTool[], providerById: Map ({
...tool,
+ provider_type: parseToolProviderType(tool.provider_type),
allowDelete: (
providerById.get(tool.provider_id) ??
providerById.get(tool.provider_name) ??
diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/dialog.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/dialog.tsx
index c81d8309e97..5849b7ca620 100644
--- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/dialog.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/dialog.tsx
@@ -7,7 +7,6 @@ import type { AgentProviderTool } from '@/features/agent-v2/agent-composer/form-
import { useAtomValue, useSetAtom } from 'jotai'
import { useCallback, useMemo } from 'react'
import SettingBuiltInTool from '@/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool'
-import { CollectionType } from '@/app/components/tools/types'
import {
agentComposerToolsAtom,
agentComposerToolSettingsAtom,
@@ -28,7 +27,7 @@ const createFallbackToolCollection = (tool: AgentProviderTool): ToolWithProvider
icon: tool.icon ?? '',
icon_dark: tool.iconDark,
label: localize(tool.displayName ?? tool.name),
- type: (tool.providerType as CollectionType | undefined) ?? CollectionType.builtIn,
+ type: tool.providerType,
team_credentials: {},
is_team_authorization: true,
allow_delete: tool.allowDelete ?? false,
diff --git a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/item.tsx b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/item.tsx
index 74b179a3efe..ab60f638d66 100644
--- a/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/item.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/orchestrate/tools/provider-tool/item.tsx
@@ -60,7 +60,7 @@ function UnauthorizedCredentialStatus({
() => ({
provider: tool.id,
category: AuthCategory.tool,
- providerType: tool.providerType ?? CollectionType.builtIn,
+ providerType: tool.providerType,
}),
[tool.id, tool.providerType],
)
@@ -129,8 +129,7 @@ function CredentialStatus({
credentialType?: AgentProviderTool['credentialType'],
) => void
}) {
- const canSwitchCredential =
- (tool.providerType ?? CollectionType.builtIn) === CollectionType.builtIn && tool.allowDelete
+ const canSwitchCredential = tool.providerType === CollectionType.builtIn && tool.allowDelete
const handleAuthorizationItemClick = useCallback(
(id: string) => {
onCredentialChange(
@@ -161,7 +160,7 @@ function CredentialStatus({
pluginPayload={{
provider: tool.id,
category: AuthCategory.tool,
- providerType: tool.providerType ?? CollectionType.builtIn,
+ providerType: tool.providerType,
}}
credentialId={tool.credentialId}
onAuthorizationItemClick={handleAuthorizationItemClick}
diff --git a/web/features/agent-v2/agent-detail/configure/components/preview/__tests__/chat.spec.tsx b/web/features/agent-v2/agent-detail/configure/components/preview/__tests__/chat.spec.tsx
index 9fed430c123..3910d61db88 100644
--- a/web/features/agent-v2/agent-detail/configure/components/preview/__tests__/chat.spec.tsx
+++ b/web/features/agent-v2/agent-detail/configure/components/preview/__tests__/chat.spec.tsx
@@ -1195,6 +1195,39 @@ describe('AgentPreviewChat', () => {
)
})
+ it('should preserve the tool provider type in the preview runtime config', async () => {
+ renderPreviewChat({
+ agentSoulConfig: {
+ tools: {
+ dify_tools: [
+ {
+ provider_id: 'workflow-provider',
+ provider_type: 'workflow',
+ tool_name: 'search',
+ credential_type: 'unauthorized',
+ },
+ ],
+ },
+ },
+ })
+
+ await waitFor(() => expect(useChatMock).toHaveBeenCalled())
+
+ const config = useChatMock.mock.calls.at(-1)?.[0]
+ expect(config.agent_mode).toEqual(
+ expect.objectContaining({
+ enabled: true,
+ tools: [
+ expect.objectContaining({
+ provider_id: 'workflow-provider',
+ provider_type: 'workflow',
+ tool_name: 'search',
+ }),
+ ],
+ }),
+ )
+ })
+
it('should enable build chat file upload when chat features file upload is enabled', async () => {
renderPreviewChat({
agentSoulConfig: {
diff --git a/web/features/agent-v2/agent-detail/configure/components/preview/chat-config.ts b/web/features/agent-v2/agent-detail/configure/components/preview/chat-config.ts
index 766493e9c1d..b39af5f27d8 100644
--- a/web/features/agent-v2/agent-detail/configure/components/preview/chat-config.ts
+++ b/web/features/agent-v2/agent-detail/configure/components/preview/chat-config.ts
@@ -132,7 +132,7 @@ export const getAgentSoulInputs = (inputsForm: InputForm[]) => {
const toAgentTool = (tool: AgentSoulDifyToolConfig) => ({
provider_id: tool.provider_id ?? tool.provider ?? tool.plugin_id ?? '',
- provider_type: tool.provider_type ?? 'builtin',
+ provider_type: tool.provider_type,
provider_name: tool.provider ?? '',
tool_name: tool.tool_name,
tool_label: tool.name ?? tool.tool_name,
diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts
index 854b0494067..baf40bb0a36 100644
--- a/web/service/use-plugins.ts
+++ b/web/service/use-plugins.ts
@@ -1430,7 +1430,7 @@ export const useFetchDynamicOptions = (
provider: string,
action: string,
parameter: string,
- provider_type?: string,
+ provider_type?: 'tool' | 'trigger',
extra?: Record,
) => {
return useMutation({
diff --git a/web/service/use-tools.ts b/web/service/use-tools.ts
index 6e7488ad0e7..772e813df51 100644
--- a/web/service/use-tools.ts
+++ b/web/service/use-tools.ts
@@ -1,6 +1,7 @@
import type { QueryKey, UseQueryOptions } from '@tanstack/react-query'
import type {
Collection,
+ CollectionProviderType,
MCPServerDetail,
Tool,
WorkflowToolProviderResponse,
@@ -79,13 +80,13 @@ export const useInvalidateAllMCPTools = () => {
return useInvalid(useAllMCPToolsKey)
}
-const useInvalidToolsKeyMap: Record = {
+const useInvalidToolsKeyMap: Partial> = {
[CollectionType.builtIn]: useAllBuiltInToolsKey,
[CollectionType.custom]: useAllCustomToolsKey,
[CollectionType.workflow]: useAllWorkflowToolsKey,
[CollectionType.mcp]: useAllMCPToolsKey,
}
-export const useInvalidToolsByType = (type?: CollectionType | string) => {
+export const useInvalidToolsByType = (type?: CollectionProviderType) => {
const queryKey = type ? useInvalidToolsKeyMap[type] : undefined
return useInvalid(queryKey)
}
diff --git a/web/types/app.ts b/web/types/app.ts
index 81de2bcec0d..f5d510194a5 100644
--- a/web/types/app.ts
+++ b/web/types/app.ts
@@ -1,5 +1,5 @@
+import type { ToolProviderType } from '@dify/contracts/api/console/agent/types.gen'
import type { TagResponse as Tag } from '@dify/contracts/api/console/tags/types.gen'
-import type { CollectionType } from '@/app/components/tools/types'
import type { UploadFileSetting } from '@/app/components/workflow/types'
import type { LanguagesSupported } from '@/i18n-config/language'
import type { AccessMode } from '@/models/access-control'
@@ -172,7 +172,7 @@ export type UserInputFormItem =
export type AgentTool = {
provider_id: string
- provider_type: CollectionType
+ provider_type: ToolProviderType
provider_name: string
tool_name: string
tool_label: string