From 9b432d0d29c63bcd5d4aa6fc8c10814ad0f2d9df Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Wed, 22 Jul 2026 09:30:55 +0800 Subject: [PATCH] fix: enforce tool provider type contracts (#39380) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../nodes/agent_v2/dify_tools_builder.py | 31 +++++---- api/models/agent_config_entities.py | 7 +- api/openapi/markdown/console-openapi.md | 2 +- .../nodes/agent_v2/test_dify_tools_builder.py | 65 +++++++++++++++++-- .../agent_v2/test_runtime_request_builder.py | 1 + .../nodes/agent_v2/test_validators.py | 19 +++++- .../services/agent/test_agent_dsl_service.py | 9 ++- .../agent/test_composer_mention_validation.py | 1 + .../services/agent/test_prompt_mentions.py | 1 + e2e/features/agent-v2/support/seed.ts | 18 ++--- oxlint-suppressions.json | 2 +- .../generated/api/console/agent/types.gen.ts | 11 +++- .../generated/api/console/agent/zod.gen.ts | 17 ++++- .../generated/api/console/apps/types.gen.ts | 11 +++- .../generated/api/console/apps/zod.gen.ts | 17 ++++- .../api/console/snippets/types.gen.ts | 11 +++- .../generated/api/console/snippets/zod.gen.ts | 17 ++++- .../config/agent/agent-tools/index.tsx | 6 +- .../hooks/__tests__/use-credential.spec.ts | 3 +- .../components/plugins/plugin-auth/types.ts | 4 +- .../detail-header/index.tsx | 2 +- .../tool-authorization-section.spec.tsx | 3 +- web/app/components/tools/provider-type.ts | 5 ++ web/app/components/tools/types.ts | 30 +++++---- .../__tests__/data-sources.spec.tsx | 3 +- .../__tests__/tool-browser.spec.tsx | 4 +- .../__tests__/tool-list-data.spec.ts | 12 ++-- .../block-selector/__tests__/utils.spec.ts | 4 +- .../workflow/block-selector/data-sources.tsx | 3 +- .../workflow/block-selector/tool-list-data.ts | 14 +++- .../__tests__/list.spec.tsx | 4 +- .../trigger-plugin/action-item.tsx | 2 +- .../workflow/block-selector/types.ts | 8 +-- .../use-node-plugin-installation.spec.ts | 8 ++- .../_base/components/form-input-item.tsx | 2 +- .../workflow-panel/__tests__/helpers.spec.tsx | 2 +- .../workflow-panel/__tests__/index.spec.tsx | 2 +- .../_base/components/workflow-panel/index.tsx | 4 +- .../__tests__/index.spec.tsx | 4 +- .../nodes/data-source/__tests__/node.spec.tsx | 2 +- .../data-source/__tests__/panel.spec.tsx | 2 +- .../workflow/nodes/data-source/types.ts | 3 +- .../utils/__tests__/data-source.spec.ts | 5 +- .../components/workflow/utils/data-source.ts | 6 +- .../agent-composer/__tests__/store.spec.ts | 1 + .../agent-v2/agent-composer/conversions.ts | 2 +- .../agent-v2/agent-composer/form-state.ts | 3 +- .../store-modules/__tests__/tools.spec.ts | 1 + .../agent-composer/store-modules/tools.ts | 4 +- .../__tests__/agent-prompt-editor.spec.tsx | 1 + .../orchestrate/prompt-editor/slash.tsx | 3 +- .../tools/__tests__/index.spec.tsx | 4 ++ .../components/orchestrate/tools/index.tsx | 4 +- .../tools/provider-tool/dialog.tsx | 3 +- .../orchestrate/tools/provider-tool/item.tsx | 7 +- .../preview/__tests__/chat.spec.tsx | 33 ++++++++++ .../components/preview/chat-config.ts | 2 +- web/service/use-plugins.ts | 2 +- web/service/use-tools.ts | 5 +- web/types/app.ts | 4 +- 60 files changed, 342 insertions(+), 124 deletions(-) create mode 100644 web/app/components/tools/provider-type.ts diff --git a/api/core/workflow/nodes/agent_v2/dify_tools_builder.py b/api/core/workflow/nodes/agent_v2/dify_tools_builder.py index 0e6fb3d1830..bf384a42482 100644 --- a/api/core/workflow/nodes/agent_v2/dify_tools_builder.py +++ b/api/core/workflow/nodes/agent_v2/dify_tools_builder.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Mapping from dataclasses import dataclass -from typing import Any, Literal, Protocol, cast +from typing import Any, Final, Literal, Protocol from dify_agent.layers.dify_core_tools import DifyCoreToolConfig, DifyCoreToolProviderType, DifyCoreToolsLayerConfig from dify_agent.layers.dify_plugin import ( @@ -29,6 +29,13 @@ from models.provider_ids import ToolProviderID from models.tools import WorkflowToolProvider from services.tools.mcp_tools_manage_service import MCPToolManageService +_CORE_TOOL_PROVIDER_TYPES: Final[dict[ToolProviderType, DifyCoreToolProviderType]] = { + ToolProviderType.BUILT_IN: "builtin", + ToolProviderType.API: "api", + ToolProviderType.WORKFLOW: "workflow", + ToolProviderType.MCP: "mcp", +} + class WorkflowAgentDifyToolsBuildError(ValueError): """Raised when Agent Soul tools cannot be prepared for Agent backend.""" @@ -235,7 +242,7 @@ class WorkflowAgentDifyToolsBuilder: if tool_config.tool_name is not None: expanded.append(tool_config) continue - provider_type = ToolProviderType.value_of(tool_config.provider_type) + provider_type = tool_config.provider_type provider_id = self._provider_id(tool_config) try: tool_names = self._provider_declared_tool_names( @@ -279,7 +286,7 @@ class WorkflowAgentDifyToolsBuilder: tenant_id: str, tool_config: AgentSoulDifyToolConfig, ) -> AgentSoulDifyToolConfig: - if tool_config.provider_type != ToolProviderType.MCP.value: + if tool_config.provider_type is not ToolProviderType.MCP: return tool_config provider_id = self._mcp_provider_id_resolver(tenant_id=tenant_id, provider_id=self._provider_id(tool_config)) return tool_config.model_copy(update={"provider_id": provider_id, "plugin_id": None, "provider": None}) @@ -326,7 +333,7 @@ class WorkflowAgentDifyToolsBuilder: def _to_agent_tool_entity(tool_config: AgentSoulDifyToolConfig) -> AgentToolEntity: assert tool_config.tool_name is not None return AgentToolEntity( - provider_type=ToolProviderType.value_of(tool_config.provider_type), + provider_type=tool_config.provider_type, provider_id=WorkflowAgentDifyToolsBuilder._provider_id(tool_config), tool_name=tool_config.tool_name, tool_parameters=dict(tool_config.runtime_parameters), @@ -343,24 +350,16 @@ class WorkflowAgentDifyToolsBuilder: @staticmethod def _provider_key(tool_config: AgentSoulDifyToolConfig) -> tuple[ToolProviderType, str]: - return ( - ToolProviderType.value_of(tool_config.provider_type), - WorkflowAgentDifyToolsBuilder._provider_id(tool_config), - ) + return (tool_config.provider_type, WorkflowAgentDifyToolsBuilder._provider_id(tool_config)) @staticmethod def _tool_layer_destination(tool_config: AgentSoulDifyToolConfig) -> Literal["plugin", "core"]: - provider_type = ToolProviderType.value_of(tool_config.provider_type) + provider_type = tool_config.provider_type if provider_type is ToolProviderType.PLUGIN or ( provider_type is ToolProviderType.BUILT_IN and _is_plugin_provider_id(tool_config.provider_id) ): return "plugin" - if provider_type in { - ToolProviderType.BUILT_IN, - ToolProviderType.API, - ToolProviderType.WORKFLOW, - ToolProviderType.MCP, - }: + if provider_type in _CORE_TOOL_PROVIDER_TYPES: return "core" if provider_type is ToolProviderType.DATASET_RETRIEVAL: raise WorkflowAgentDifyToolsBuildError( @@ -417,7 +416,7 @@ class WorkflowAgentDifyToolsBuilder: ) -> DifyCoreToolConfig: parameters = self._prepared_parameters(tool_runtime) return DifyCoreToolConfig( - provider_type=cast(DifyCoreToolProviderType, tool_config.provider_type), + provider_type=_CORE_TOOL_PROVIDER_TYPES[tool_config.provider_type], provider_id=self._provider_id(tool_config), tool_name=tool_config.tool_name or exposed_name, credential_id=tool_config.credential_ref.id if tool_config.credential_ref else None, diff --git a/api/models/agent_config_entities.py b/api/models/agent_config_entities.py index c4428b716bf..a47129dcc20 100644 --- a/api/models/agent_config_entities.py +++ b/api/models/agent_config_entities.py @@ -7,6 +7,7 @@ from typing import Annotated, Any, Final, Literal, Self from pydantic import BaseModel, ConfigDict, Field, WithJsonSchema, field_validator, model_validator from core.rag.entities.metadata_entities import ConditionValue, SupportedComparisonOperator +from core.tools.entities.tool_entities import ToolProviderType from core.workflow.file_reference import is_canonical_file_reference from graphon.file import FileTransferMethod, FileType @@ -653,11 +654,7 @@ class AgentSoulDifyToolConfig(BaseModel): model_config = ConfigDict(extra="ignore") enabled: bool = True - # ``plugin`` remains the default for legacy Agent Soul payloads. The runtime - # now also accepts ``builtin`` / ``api`` / ``workflow`` / ``mcp`` here and - # routes them through ``dify.core.tools``; keeping the default narrow still - # makes a missing field resolve against the plugin provider table. - provider_type: str = "plugin" + provider_type: ToolProviderType provider_id: str | None = Field(default=None, max_length=255) plugin_id: str | None = Field(default=None, max_length=255) provider: str | None = Field(default=None, max_length=255) diff --git a/api/openapi/markdown/console-openapi.md b/api/openapi/markdown/console-openapi.md index 41b486f21a3..ecefaac1954 100644 --- a/api/openapi/markdown/console-openapi.md +++ b/api/openapi/markdown/console-openapi.md @@ -14737,7 +14737,7 @@ should send ``plugin_id`` + ``provider`` when available. | plugin_id | string | | No | | provider | string | | No | | provider_id | string | | No | -| provider_type | string,
**Default:** plugin | | No | +| provider_type | [ToolProviderType](#toolprovidertype) | | Yes | | runtime_parameters | object | | No | | tool_name | string | | No | diff --git a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_dify_tools_builder.py b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_dify_tools_builder.py index 9a040926edf..b2abfbf222d 100644 --- a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_dify_tools_builder.py +++ b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_dify_tools_builder.py @@ -235,6 +235,7 @@ def test_builds_dify_plugin_tools_layer_from_existing_tool_runtime(): "dify_tools": [ { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -258,9 +259,6 @@ def test_builds_dify_plugin_tools_layer_from_existing_tool_runtime(): assert "region" not in prepared.parameters_json_schema["properties"] assert runtime_provider.last_agent_tool is not None assert runtime_provider.last_agent_tool.credential_id == "credential-1" - # Default ``provider_type`` is now ``"plugin"`` — the agent tool entity - # must surface that so ToolManager hits the plugin provider table, not the - # built-in legacy table. assert runtime_provider.last_agent_tool.provider_type.value == "plugin" @@ -599,6 +597,7 @@ def test_rejects_duplicate_exposed_tool_names(): "dify_tools": [ { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -606,6 +605,7 @@ def test_rejects_duplicate_exposed_tool_names(): }, { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -629,6 +629,7 @@ def test_rejects_missing_required_runtime_parameter(): "dify_tools": [ { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -663,6 +664,7 @@ def test_invoke_from_is_forwarded_to_tool_runtime_provider(): "dify_tools": [ { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -690,6 +692,7 @@ def test_disabled_tools_are_skipped(): "dify_tools": [ { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -717,6 +720,7 @@ def test_plugin_id_plus_provider_fallback_when_provider_id_missing(): { "plugin_id": "langgenius/search", "provider": "search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -752,6 +756,7 @@ def test_unauthorized_tool_without_credentials(): "dify_tools": [ { "provider_id": "langgenius/time/time", + "provider_type": "plugin", "tool_name": "current_time", "credential_type": "unauthorized", "runtime_parameters": {"region": "us"}, @@ -777,6 +782,7 @@ def _standard_tools_payload() -> AgentSoulToolsConfig: "dify_tools": [ { "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -859,6 +865,7 @@ def test_legacy_provider_name_and_tool_parameters_normalized(): "dify_tools": [ { "provider_name": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_id": "credential-1", @@ -875,6 +882,21 @@ def test_legacy_provider_name_and_tool_parameters_normalized(): assert tool.credential_ref.id == "credential-1" +def test_rejects_unknown_provider_type_at_config_boundary(): + with pytest.raises(ValueError, match="provider_type"): + AgentSoulToolsConfig.model_validate( + { + "dify_tools": [ + { + "provider_id": "future-provider", + "provider_type": "future-provider", + "credential_type": "unauthorized", + } + ] + } + ) + + # ── provider-level entries (tool_name omitted = all tools of the provider) ─── @@ -888,7 +910,15 @@ def test_provider_level_entry_expands_to_all_tools(): builder = WorkflowAgentDifyToolsBuilder(tool_runtime_provider=runtime_provider, provider_tools_lister=lister) tools = AgentSoulToolsConfig.model_validate( - {"dify_tools": [{"provider_id": "langgenius/search/search", "credential_type": "unauthorized"}]} + { + "dify_tools": [ + { + "provider_id": "langgenius/search/search", + "provider_type": "plugin", + "credential_type": "unauthorized", + } + ] + } ) result = _build(builder, tools) @@ -906,9 +936,14 @@ def test_explicit_tool_entry_wins_over_provider_expansion(): tools = AgentSoulToolsConfig.model_validate( { "dify_tools": [ - {"provider_id": "langgenius/search/search", "credential_type": "unauthorized"}, { "provider_id": "langgenius/search/search", + "provider_type": "plugin", + "credential_type": "unauthorized", + }, + { + "provider_id": "langgenius/search/search", + "provider_type": "plugin", "tool_name": "search", "credential_type": "unauthorized", "runtime_parameters": {"region": "eu"}, @@ -930,7 +965,15 @@ def test_provider_level_entry_with_no_tools_maps_to_declaration_not_found(): provider_tools_lister=lambda *, tenant_id, provider_type, provider_id: [], ) tools = AgentSoulToolsConfig.model_validate( - {"dify_tools": [{"provider_id": "langgenius/search/search", "credential_type": "unauthorized"}]} + { + "dify_tools": [ + { + "provider_id": "langgenius/search/search", + "provider_type": "plugin", + "credential_type": "unauthorized", + } + ] + } ) with pytest.raises(WorkflowAgentDifyToolsBuildError) as exc_info: @@ -948,7 +991,15 @@ def test_provider_level_entry_unknown_provider_maps_to_declaration_not_found(): tool_runtime_provider=FakeRuntimeProvider(_tool()), provider_tools_lister=lister ) tools = AgentSoulToolsConfig.model_validate( - {"dify_tools": [{"provider_id": "langgenius/search/search", "credential_type": "unauthorized"}]} + { + "dify_tools": [ + { + "provider_id": "langgenius/search/search", + "provider_type": "plugin", + "credential_type": "unauthorized", + } + ] + } ) with pytest.raises(WorkflowAgentDifyToolsBuildError) as exc_info: diff --git a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_runtime_request_builder.py b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_runtime_request_builder.py index 811620e4005..c66bcf5b6e6 100644 --- a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_runtime_request_builder.py +++ b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_runtime_request_builder.py @@ -667,6 +667,7 @@ def test_builds_workflow_run_request_with_dify_plugin_tools_layer(monkeypatch: p "dify_tools": [ { "provider_id": "langgenius/time/time", + "provider_type": "plugin", "tool_name": "current_time", "credential_type": "unauthorized", } diff --git a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_validators.py b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_validators.py index 237ab8ebe3b..acf27ae2b03 100644 --- a/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_validators.py +++ b/api/tests/unit_tests/core/workflow/nodes/agent_v2/test_validators.py @@ -295,8 +295,16 @@ def test_publish_validation_dedupes_provider_level_tool_entries(): ), tools={ "dify_tools": [ - {"provider_id": "langgenius/duckduckgo/duckduckgo", "credential_type": "unauthorized"}, - {"provider_id": "langgenius/duckduckgo/duckduckgo", "credential_type": "unauthorized"}, + { + "provider_id": "langgenius/duckduckgo/duckduckgo", + "provider_type": "plugin", + "credential_type": "unauthorized", + }, + { + "provider_id": "langgenius/duckduckgo/duckduckgo", + "provider_type": "plugin", + "credential_type": "unauthorized", + }, ] }, ) @@ -321,9 +329,14 @@ def test_publish_validation_accepts_provider_level_plus_explicit_tool_entry(): ), tools={ "dify_tools": [ - {"provider_id": "langgenius/duckduckgo/duckduckgo", "credential_type": "unauthorized"}, { "provider_id": "langgenius/duckduckgo/duckduckgo", + "provider_type": "plugin", + "credential_type": "unauthorized", + }, + { + "provider_id": "langgenius/duckduckgo/duckduckgo", + "provider_type": "plugin", "tool_name": "ddg_search", "credential_type": "unauthorized", }, diff --git a/api/tests/unit_tests/services/agent/test_agent_dsl_service.py b/api/tests/unit_tests/services/agent/test_agent_dsl_service.py index f34bb9ed3ec..4ceecc12a65 100644 --- a/api/tests/unit_tests/services/agent/test_agent_dsl_service.py +++ b/api/tests/unit_tests/services/agent/test_agent_dsl_service.py @@ -78,6 +78,7 @@ def test_make_portable_agent_package_strips_workspace_credentials_and_assets() - "dify_tools": [ { "provider_id": "langgenius/google/google", + "provider_type": "plugin", "tool_name": "search", "credential_type": "api-key", "credential_ref": {"type": "tool", "id": "tool-secret"}, @@ -196,6 +197,7 @@ def test_import_warnings_cover_runtime_setup_removed_from_package(monkeypatch) - "dify_tools": [ { "provider_id": "langgenius/google/google", + "provider_type": "plugin", "tool_name": "search", "credential_type": "unauthorized", } @@ -513,10 +515,15 @@ def test_extract_package_dependencies_covers_model_tools_and_knowledge(monkeypat "model": {"plugin_id": "model-plugin", "model_provider": "provider/model", "model": "model"}, "tools": { "dify_tools": [ - {"provider_id": "provider/tool", "credential_type": "unauthorized"}, + { + "provider_id": "provider/tool", + "provider_type": "plugin", + "credential_type": "unauthorized", + }, { "plugin_id": "plugin-id", "provider": "fallback-provider", + "provider_type": "plugin", "credential_type": "unauthorized", }, ] diff --git a/api/tests/unit_tests/services/agent/test_composer_mention_validation.py b/api/tests/unit_tests/services/agent/test_composer_mention_validation.py index f56ae3751e8..3d28f577137 100644 --- a/api/tests/unit_tests/services/agent/test_composer_mention_validation.py +++ b/api/tests/unit_tests/services/agent/test_composer_mention_validation.py @@ -199,6 +199,7 @@ def test_provider_all_tools_mention_resolves_against_provider_level_entry(): { "plugin_id": "langgenius/duckduckgo", "provider": "duckduckgo", + "provider_type": "plugin", "credential_type": "unauthorized", } ] diff --git a/api/tests/unit_tests/services/agent/test_prompt_mentions.py b/api/tests/unit_tests/services/agent/test_prompt_mentions.py index 6051c96b50b..82c42a5bd86 100644 --- a/api/tests/unit_tests/services/agent/test_prompt_mentions.py +++ b/api/tests/unit_tests/services/agent/test_prompt_mentions.py @@ -140,6 +140,7 @@ def soul() -> AgentSoulConfig: { "plugin_id": "langgenius/tavily", "provider": "tavily", + "provider_type": "plugin", "tool_name": "tavily_search", "credential_type": "unauthorized", }, diff --git a/e2e/features/agent-v2/support/seed.ts b/e2e/features/agent-v2/support/seed.ts index 7c2598db2f7..45bca4df042 100644 --- a/e2e/features/agent-v2/support/seed.ts +++ b/e2e/features/agent-v2/support/seed.ts @@ -1,6 +1,7 @@ import type { AgentKnowledgeDatasetConfig, AgentSoulConfig, + AgentSoulDifyToolConfig, } from '@dify/contracts/api/console/agent/types.gen' import type { ConsoleSegmentListResponse, @@ -713,14 +714,15 @@ const getStableModelResource = (context: SeedContext): StableModel | undefined = const getToolResource = (context: SeedContext, displayName: string) => context.resources.get(`tool:${displayName}`) as ToolResource | undefined -const toolConfig = (tool: ToolResource) => ({ - credential_type: 'unauthorized' as const, - enabled: true, - provider_id: tool.providerName, - provider_type: 'builtin', - runtime_parameters: {}, - tool_name: tool.toolName, -}) +const toolConfig = (tool: ToolResource) => + ({ + credential_type: 'unauthorized', + enabled: true, + provider_id: tool.providerName, + provider_type: 'builtin', + runtime_parameters: {}, + tool_name: tool.toolName, + }) satisfies AgentSoulDifyToolConfig const saveSeededAgentComposer = async ({ agentId, diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 2fde102ff63..c9e6e2ff30e 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -3875,7 +3875,7 @@ }, "web/app/components/tools/types.ts": { "erasable-syntax-only/enums": { - "count": 4 + "count": 3 }, "typescript/no-explicit-any": { "count": 4 diff --git a/packages/contracts/generated/api/console/agent/types.gen.ts b/packages/contracts/generated/api/console/agent/types.gen.ts index 794f5687d84..d26268d5f7b 100644 --- a/packages/contracts/generated/api/console/agent/types.gen.ts +++ b/packages/contracts/generated/api/console/agent/types.gen.ts @@ -1579,7 +1579,7 @@ export type AgentSoulDifyToolConfig = { plugin_id?: string | null provider?: string | null provider_id?: string | null - provider_type?: string + provider_type: ToolProviderType runtime_parameters?: { [key: string]: | string @@ -1744,6 +1744,15 @@ export type AgentSoulDifyToolCredentialRef = { type?: 'provider' | 'tool' } +export type ToolProviderType = + | 'api' + | 'app' + | 'builtin' + | 'dataset-retrieval' + | 'mcp' + | 'plugin' + | 'workflow' + export type OutputErrorStrategy = 'default_value' | 'fail_branch' | 'stop' export type DeclaredOutputRetryConfig = { diff --git a/packages/contracts/generated/api/console/agent/zod.gen.ts b/packages/contracts/generated/api/console/agent/zod.gen.ts index a7d905f1fb4..cae2f7f9eb3 100644 --- a/packages/contracts/generated/api/console/agent/zod.gen.ts +++ b/packages/contracts/generated/api/console/agent/zod.gen.ts @@ -2065,6 +2065,21 @@ export const zAgentSoulDifyToolCredentialRef = z.object({ type: z.enum(['provider', 'tool']).optional().default('tool'), }) +/** + * ToolProviderType + * + * Enum class for tool provider + */ +export const zToolProviderType = z.enum([ + 'api', + 'app', + 'builtin', + 'dataset-retrieval', + 'mcp', + 'plugin', + 'workflow', +]) + /** * AgentSoulDifyToolConfig * @@ -2087,7 +2102,7 @@ export const zAgentSoulDifyToolConfig = z.object({ plugin_id: z.string().max(255).nullish(), provider: z.string().max(255).nullish(), provider_id: z.string().max(255).nullish(), - provider_type: z.string().optional().default('plugin'), + provider_type: zToolProviderType, runtime_parameters: z .record( z.string(), diff --git a/packages/contracts/generated/api/console/apps/types.gen.ts b/packages/contracts/generated/api/console/apps/types.gen.ts index b763ef8689a..f6513363093 100644 --- a/packages/contracts/generated/api/console/apps/types.gen.ts +++ b/packages/contracts/generated/api/console/apps/types.gen.ts @@ -2813,7 +2813,7 @@ export type AgentSoulDifyToolConfig = { plugin_id?: string | null provider?: string | null provider_id?: string | null - provider_type?: string + provider_type: ToolProviderType runtime_parameters?: { [key: string]: | string @@ -2964,6 +2964,15 @@ export type AgentSoulDifyToolCredentialRef = { type?: 'provider' | 'tool' } +export type ToolProviderType = + | 'api' + | 'app' + | 'builtin' + | 'dataset-retrieval' + | 'mcp' + | 'plugin' + | 'workflow' + export type StringSource = { selector?: Array type: ValueSourceType diff --git a/packages/contracts/generated/api/console/apps/zod.gen.ts b/packages/contracts/generated/api/console/apps/zod.gen.ts index 4e80ca63f33..9cc33be2af6 100644 --- a/packages/contracts/generated/api/console/apps/zod.gen.ts +++ b/packages/contracts/generated/api/console/apps/zod.gen.ts @@ -3682,6 +3682,21 @@ export const zAgentSoulDifyToolCredentialRef = z.object({ type: z.enum(['provider', 'tool']).optional().default('tool'), }) +/** + * ToolProviderType + * + * Enum class for tool provider + */ +export const zToolProviderType = z.enum([ + 'api', + 'app', + 'builtin', + 'dataset-retrieval', + 'mcp', + 'plugin', + 'workflow', +]) + /** * AgentSoulDifyToolConfig * @@ -3704,7 +3719,7 @@ export const zAgentSoulDifyToolConfig = z.object({ plugin_id: z.string().max(255).nullish(), provider: z.string().max(255).nullish(), provider_id: z.string().max(255).nullish(), - provider_type: z.string().optional().default('plugin'), + provider_type: zToolProviderType, runtime_parameters: z .record( z.string(), diff --git a/packages/contracts/generated/api/console/snippets/types.gen.ts b/packages/contracts/generated/api/console/snippets/types.gen.ts index 4776fda89f5..28cb3250d68 100644 --- a/packages/contracts/generated/api/console/snippets/types.gen.ts +++ b/packages/contracts/generated/api/console/snippets/types.gen.ts @@ -943,7 +943,7 @@ export type AgentSoulDifyToolConfig = { plugin_id?: string | null provider?: string | null provider_id?: string | null - provider_type?: string + provider_type: ToolProviderType runtime_parameters?: { [key: string]: | string @@ -1063,6 +1063,15 @@ export type AgentSoulDifyToolCredentialRef = { type?: 'provider' | 'tool' } +export type ToolProviderType = + | 'api' + | 'app' + | 'builtin' + | 'dataset-retrieval' + | 'mcp' + | 'plugin' + | 'workflow' + export type AgentModerationIoConfig = { enabled?: boolean preset_response?: string | null diff --git a/packages/contracts/generated/api/console/snippets/zod.gen.ts b/packages/contracts/generated/api/console/snippets/zod.gen.ts index d0b1a2b6169..491b9acdada 100644 --- a/packages/contracts/generated/api/console/snippets/zod.gen.ts +++ b/packages/contracts/generated/api/console/snippets/zod.gen.ts @@ -1261,6 +1261,21 @@ export const zAgentSoulDifyToolCredentialRef = z.object({ type: z.enum(['provider', 'tool']).optional().default('tool'), }) +/** + * ToolProviderType + * + * Enum class for tool provider + */ +export const zToolProviderType = z.enum([ + 'api', + 'app', + 'builtin', + 'dataset-retrieval', + 'mcp', + 'plugin', + 'workflow', +]) + /** * AgentSoulDifyToolConfig * @@ -1283,7 +1298,7 @@ export const zAgentSoulDifyToolConfig = z.object({ plugin_id: z.string().max(255).nullish(), provider: z.string().max(255).nullish(), provider_id: z.string().max(255).nullish(), - provider_type: z.string().optional().default('plugin'), + provider_type: zToolProviderType, runtime_parameters: z .record( z.string(), diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx index 76336ff8027..08473d761cd 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx @@ -22,6 +22,7 @@ import AppIcon from '@/app/components/base/app-icon' import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { Infotip } from '@/app/components/base/infotip' +import { parseToolProviderType } from '@/app/components/tools/provider-type' import { CollectionType } from '@/app/components/tools/types' import { addDefaultValue, @@ -115,16 +116,17 @@ const AgentTools: FC = () => { ? toolParametersToFormSchemas(currToolWithConfigs.parameters) : [] const paramsWithDefaultValue = addDefaultValue(tool.params, formSchemas) + const providerType = parseToolProviderType(tool.provider_type) return { provider_id: tool.provider_id, - provider_type: tool.provider_type as CollectionType, + provider_type: providerType, provider_name: tool.provider_name, tool_name: tool.tool_name, tool_label: tool.tool_label, tool_parameters: paramsWithDefaultValue, notAuthor: !tool.is_team_authorization, enabled: true, - type: tool.provider_type as CollectionType, + type: providerType, } } const handleSelectTool = (tool: ToolDefaultValue) => { diff --git a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts index 83b8ab024b5..fc915120f9a 100644 --- a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts +++ b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-credential.spec.ts @@ -1,3 +1,4 @@ +import type { PluginPayload } from '../../types' import { renderHook } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { AuthCategory, CredentialTypeEnum } from '../../types' @@ -57,7 +58,7 @@ const toolPayload = { category: AuthCategory.tool, provider: 'test-provider', providerType: 'builtin', -} +} satisfies PluginPayload describe('use-credential hooks', () => { beforeEach(() => { diff --git a/web/app/components/plugins/plugin-auth/types.ts b/web/app/components/plugins/plugin-auth/types.ts index 5d697c7ba93..d66d09a9eb8 100644 --- a/web/app/components/plugins/plugin-auth/types.ts +++ b/web/app/components/plugins/plugin-auth/types.ts @@ -1,4 +1,4 @@ -import type { CollectionType } from '../../tools/types' +import type { CollectionProviderType } from '../../tools/types' import type { PluginDetail } from '../types' export type { AddApiKeyButtonProps } from './authorize/add-api-key-button' @@ -14,7 +14,7 @@ export enum AuthCategory { export type PluginPayload = { category: AuthCategory provider: string - providerType?: CollectionType | string + providerType?: CollectionProviderType detail?: PluginDetail } diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx index b79b0566347..56419558441 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx @@ -346,7 +346,7 @@ const DetailHeader = ({ pluginPayload={{ provider: provider?.name || '', category: AuthCategory.tool, - providerType: provider?.type || '', + providerType: provider?.type, detail, }} /> diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-authorization-section.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-authorization-section.spec.tsx index 12667970c34..a81cdb52b2a 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-authorization-section.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-authorization-section.spec.tsx @@ -1,3 +1,4 @@ +import type { PluginPayload } from '@/app/components/plugins/plugin-auth/types' import type { ToolWithProvider } from '@/app/components/workflow/types' import { render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' @@ -12,7 +13,7 @@ vi.mock('@/app/components/plugins/plugin-auth', () => ({ pluginPayload, credentialId, }: { - pluginPayload: { provider: string; providerType: string } + pluginPayload: PluginPayload credentialId?: string }) => (
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