Files
dify/api/services/dify_builder/agent/graph_translate.py
T
Charles YaoandClaude Opus 4.8 cd5fb6bbf0 fix(dify-builder): ground model on all model-bearing nodes, carry branch handles, clean test lint
Final consolidated fix wave from whole-branch review of the Real Build +
Edit Cognition slice:

- _ground now overwrites the model dict on ANY create_node whose config
  carries a model dict (llm, question-classifier, parameter-extractor),
  not just "llm", so a drifted/hallucinated provider/name on those other
  node types no longer flows through ungrounded.
- to_intents now carries a generator edge's sourceHandle/targetHandle
  into the connect intent's args (source_handle/target_handle) so
  branching nodes (if-else, question-classifier) wire onto the correct
  port instead of collapsing onto the default handle; edges without
  handles still produce bare {from_node, to_node} args.
- Cleared the 17 ruff errors (PT018/E302/E306/E501) across the slice's
  test files by splitting composite asserts, adding missing blank
  lines, and wrapping long lines -- no assertions weakened, no noqa.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-29 14:22:19 -07:00

61 lines
2.3 KiB
Python

"""Translate a WorkflowGenerator graph into Dify Builder MutationIntents.
The generator emits ReactFlow nodes ({id, type:"custom", data:{type:<real>,...}})
and edges. We emit one create_node per node (config = node.data minus the
redundant "type" key -- apply_create_node re-sets it) followed by one connect
per edge, so every connect endpoint already exists at apply time. The start
node is renamed to the conventional id "start" so it reconciles with a draft
that already has one (the handler's present-filter drops the duplicate)."""
from typing import Any
from core.dify_builder.models import MutationIntent
_START_ID = "start"
def to_intents(graph: dict[str, Any]) -> list[MutationIntent]:
nodes = graph.get("nodes") or []
edges = graph.get("edges") or []
# rename map: the generator start id -> canonical "start"
# Only map the first start node; additional starts keep their original ids
remap: dict[str, str] = {}
for node in nodes:
data = node.get("data") or {}
if data.get("type") == "start" and node.get("id") and not remap:
remap[str(node["id"])] = _START_ID
creates: list[MutationIntent] = []
for node in nodes:
data = node.get("data") or {}
node_type = data.get("type")
node_id = node.get("id")
if not node_type or not node_id:
continue
config = {k: v for k, v in data.items() if k != "type"}
creates.append(MutationIntent(op="create_node", args={
"node_type": str(node_type),
"config": config,
"node_id": remap.get(str(node_id), str(node_id)),
}))
connects: list[MutationIntent] = []
for edge in edges:
src, tgt = edge.get("source"), edge.get("target")
if not src or not tgt:
continue
args: dict[str, Any] = {
"from_node": remap.get(str(src), str(src)),
"to_node": remap.get(str(tgt), str(tgt)),
}
source_handle = edge.get("sourceHandle")
if source_handle:
args["source_handle"] = source_handle
target_handle = edge.get("targetHandle")
if target_handle:
args["target_handle"] = target_handle
connects.append(MutationIntent(op="connect", args=args))
return creates + connects