mirror of
https://github.com/langgenius/dify.git
synced 2026-09-24 23:22:26 +08:00
feat: agent slash menu backend (#37331)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Fable 5
autofix-ci[bot]
parent
aff8f82bc0
commit
6cfd96ccd6
@@ -45,11 +45,27 @@ class AgentToolRuntimeProvider(Protocol):
|
||||
) -> Tool: ...
|
||||
|
||||
|
||||
class ProviderToolsLister(Protocol):
|
||||
def __call__(self, *, tenant_id: str, provider_id: str) -> list[str]: ...
|
||||
|
||||
|
||||
def _list_provider_tool_names(*, tenant_id: str, provider_id: str) -> list[str]:
|
||||
"""Tool names a provider currently declares (provider-level config entries)."""
|
||||
provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
|
||||
return [tool.entity.identity.name for tool in provider.get_tools() or []]
|
||||
|
||||
|
||||
class WorkflowAgentPluginToolsBuilder:
|
||||
"""Prepare Agent Soul Dify Plugin Tools for the public Agent backend DTO."""
|
||||
|
||||
def __init__(self, *, tool_runtime_provider: AgentToolRuntimeProvider | None = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
tool_runtime_provider: AgentToolRuntimeProvider | None = None,
|
||||
provider_tools_lister: ProviderToolsLister | None = None,
|
||||
) -> None:
|
||||
self._tool_runtime_provider = tool_runtime_provider or ToolManager
|
||||
self._provider_tools_lister = provider_tools_lister or _list_provider_tool_names
|
||||
|
||||
def build(
|
||||
self,
|
||||
@@ -73,7 +89,7 @@ class WorkflowAgentPluginToolsBuilder:
|
||||
|
||||
prepared: list[DifyPluginToolConfig] = []
|
||||
seen_names: set[str] = set()
|
||||
for tool_config in enabled_tools:
|
||||
for tool_config in self._expand_provider_entries(tenant_id=tenant_id, enabled_tools=enabled_tools):
|
||||
agent_tool = self._to_agent_tool_entity(tool_config)
|
||||
tool_runtime = self._fetch_tool_runtime(
|
||||
tenant_id=tenant_id,
|
||||
@@ -96,6 +112,48 @@ class WorkflowAgentPluginToolsBuilder:
|
||||
|
||||
return DifyPluginToolsLayerConfig(tools=prepared)
|
||||
|
||||
def _expand_provider_entries(
|
||||
self,
|
||||
*,
|
||||
tenant_id: str,
|
||||
enabled_tools: list[AgentSoulDifyToolConfig],
|
||||
) -> list[AgentSoulDifyToolConfig]:
|
||||
"""Expand provider-level entries (``tool_name`` omitted = all tools).
|
||||
|
||||
An explicit per-tool entry of the same provider wins over the expansion
|
||||
(it may carry its own ``runtime_parameters``); expanded clones share the
|
||||
provider entry's ``credential_ref`` and start with default parameters.
|
||||
"""
|
||||
explicit_by_provider: dict[str, set[str]] = {}
|
||||
for tool_config in enabled_tools:
|
||||
if tool_config.tool_name is not None:
|
||||
explicit_by_provider.setdefault(self._provider_id(tool_config), set()).add(tool_config.tool_name)
|
||||
|
||||
expanded: list[AgentSoulDifyToolConfig] = []
|
||||
for tool_config in enabled_tools:
|
||||
if tool_config.tool_name is not None:
|
||||
expanded.append(tool_config)
|
||||
continue
|
||||
provider_id = self._provider_id(tool_config)
|
||||
try:
|
||||
tool_names = self._provider_tools_lister(tenant_id=tenant_id, provider_id=provider_id)
|
||||
except ToolProviderNotFoundError as exc:
|
||||
raise WorkflowAgentPluginToolsBuildError(
|
||||
"agent_tool_declaration_not_found",
|
||||
f"Dify Plugin Tool provider {provider_id!r} declaration not found: {exc}",
|
||||
) from exc
|
||||
if not tool_names:
|
||||
raise WorkflowAgentPluginToolsBuildError(
|
||||
"agent_tool_declaration_not_found",
|
||||
f"Dify Plugin Tool provider {provider_id!r} declares no tools.",
|
||||
)
|
||||
already_explicit = explicit_by_provider.get(provider_id, set())
|
||||
for tool_name in tool_names:
|
||||
if tool_name in already_explicit:
|
||||
continue
|
||||
expanded.append(tool_config.model_copy(update={"tool_name": tool_name, "runtime_parameters": {}}))
|
||||
return expanded
|
||||
|
||||
def _fetch_tool_runtime(
|
||||
self,
|
||||
*,
|
||||
@@ -141,6 +199,8 @@ class WorkflowAgentPluginToolsBuilder:
|
||||
|
||||
@staticmethod
|
||||
def _to_agent_tool_entity(tool_config: AgentSoulDifyToolConfig) -> AgentToolEntity:
|
||||
# Provider-level entries are expanded into per-tool clones before this point.
|
||||
assert tool_config.tool_name is not None
|
||||
return AgentToolEntity(
|
||||
provider_type=ToolProviderType.value_of(tool_config.provider_type),
|
||||
provider_id=WorkflowAgentPluginToolsBuilder._provider_id(tool_config),
|
||||
@@ -160,7 +220,9 @@ class WorkflowAgentPluginToolsBuilder:
|
||||
@staticmethod
|
||||
def _exposed_tool_name(tool_config: AgentSoulDifyToolConfig) -> str:
|
||||
# Stage 3.1 decision: no user rename yet. Keep the model-visible tool
|
||||
# name aligned with the plugin declaration identity.
|
||||
# name aligned with the plugin declaration identity. Provider-level
|
||||
# entries are expanded into per-tool clones before this point.
|
||||
assert tool_config.tool_name is not None
|
||||
return tool_config.tool_name
|
||||
|
||||
def _to_backend_tool_config(
|
||||
@@ -190,11 +252,11 @@ class WorkflowAgentPluginToolsBuilder:
|
||||
return DifyPluginToolConfig(
|
||||
plugin_id=plugin_id,
|
||||
provider=provider,
|
||||
tool_name=tool_config.tool_name,
|
||||
tool_name=exposed_name,
|
||||
credential_type=self._credential_type(tool_config, runtime.credentials),
|
||||
name=exposed_name,
|
||||
description=description,
|
||||
credentials=self._normalize_credentials(runtime.credentials, tool_name=tool_config.tool_name),
|
||||
credentials=self._normalize_credentials(runtime.credentials, tool_name=exposed_name),
|
||||
runtime_parameters=runtime_parameters,
|
||||
parameters=parameters,
|
||||
parameters_json_schema=tool_runtime.get_llm_parameters_json_schema(),
|
||||
|
||||
@@ -322,7 +322,11 @@ class WorkflowAgentNodeValidator:
|
||||
for tool in agent_soul.tools.dify_tools:
|
||||
if not tool.enabled:
|
||||
continue
|
||||
exposed_name = tool.tool_name
|
||||
# Provider-level entries (tool_name omitted = all tools of the
|
||||
# provider) are deduped per provider here; the names they expand to
|
||||
# are checked at runtime by the plugin tools builder.
|
||||
provider_key = tool.provider_id or f"{tool.plugin_id}/{tool.provider}"
|
||||
exposed_name = tool.tool_name or f"{provider_key}/*"
|
||||
if exposed_name in exposed_names:
|
||||
raise WorkflowAgentNodeValidationError(
|
||||
f"Workflow Agent node {binding.node_id} has duplicate Dify Plugin Tool name {exposed_name}."
|
||||
|
||||
Reference in New Issue
Block a user