mirror of
https://github.com/langgenius/dify.git
synced 2026-11-11 05:41:09 +08:00
82 lines
3.4 KiB
Python
82 lines
3.4 KiB
Python
"""Locate and retarget Workflow Tool references inside App DSL data."""
|
|
|
|
from collections.abc import Iterator, Mapping
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
from graphon.enums import BuiltinNodeTypes
|
|
from graphon.nodes.tool.entities import ToolProviderType
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class WorkflowToolReference:
|
|
"""A tool configuration in a DSL whose ``id_key`` holds a Workflow Tool provider id."""
|
|
|
|
config: dict[str, Any]
|
|
id_key: str
|
|
|
|
@property
|
|
def provider_id(self) -> str:
|
|
return self.config[self.id_key]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class WorkflowToolTarget:
|
|
"""The Workflow Tool that a packaged provider id resolves to in the importing workspace."""
|
|
|
|
provider_id: str
|
|
name: str
|
|
create: bool
|
|
|
|
|
|
def iter_workflow_tool_references(data: Mapping[str, Any]) -> Iterator[WorkflowToolReference]:
|
|
"""Yield the Workflow Tool references of tool nodes, legacy Agent nodes and agent-chat configs."""
|
|
tool_configs: list[Any] = []
|
|
workflow = data.get("workflow")
|
|
graph = workflow.get("graph") if isinstance(workflow, Mapping) else None
|
|
nodes = graph.get("nodes") if isinstance(graph, Mapping) else None
|
|
for node in nodes if isinstance(nodes, list) else []:
|
|
node_data = node.get("data") if isinstance(node, Mapping) else None
|
|
if not isinstance(node_data, Mapping):
|
|
continue
|
|
if node_data.get("type") == BuiltinNodeTypes.TOOL:
|
|
tool_configs.append(node_data)
|
|
agent_parameters = node_data.get("agent_parameters")
|
|
if node_data.get("type") == BuiltinNodeTypes.AGENT and isinstance(agent_parameters, Mapping):
|
|
for parameter in agent_parameters.values():
|
|
value = parameter.get("value") if isinstance(parameter, Mapping) else None
|
|
tool_configs.extend(value if isinstance(value, list) else [])
|
|
model_config = data.get("model_config")
|
|
agent_mode = model_config.get("agent_mode") if isinstance(model_config, Mapping) else None
|
|
agent_tools = agent_mode.get("tools") if isinstance(agent_mode, Mapping) else None
|
|
tool_configs.extend(agent_tools if isinstance(agent_tools, list) else [])
|
|
|
|
for config in tool_configs:
|
|
if not isinstance(config, dict):
|
|
continue
|
|
# Agent node tools name the provider type ``type`` and keep the provider id in ``provider_name``.
|
|
if "provider_type" in config:
|
|
provider_type, id_key = config["provider_type"], "provider_id"
|
|
else:
|
|
provider_type, id_key = config.get("type"), "provider_name"
|
|
provider_id = config.get(id_key)
|
|
if provider_type == ToolProviderType.WORKFLOW and isinstance(provider_id, str) and provider_id:
|
|
yield WorkflowToolReference(config=config, id_key=id_key)
|
|
|
|
|
|
def retarget_workflow_tool_dsl(dsl: str, targets: Mapping[str, WorkflowToolTarget]) -> str:
|
|
"""Point the DSL's Workflow Tool references at their targets, keeping unknown providers as they are."""
|
|
data = yaml.safe_load(dsl)
|
|
if not isinstance(data, dict):
|
|
return dsl
|
|
for reference in iter_workflow_tool_references(data):
|
|
target = targets.get(reference.provider_id)
|
|
if target is None:
|
|
continue
|
|
reference.config[reference.id_key] = target.provider_id
|
|
# The editor matches a tool node's outputs by the tool name, which follows a renamed provider.
|
|
reference.config["tool_name"] = target.name
|
|
return yaml.dump(data, allow_unicode=True)
|