From 458c2d9cabed29296a0a42ced1032faaf3249fae Mon Sep 17 00:00:00 2001 From: chihlasm Date: Wed, 11 Mar 2026 23:46:49 -0400 Subject: [PATCH] fix: prevent circular parent_node_id in KB troubleshooting import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AI-generated trees can have circular next_node_id references (e.g., node A → B → A). The parent mapping now checks for cycles before assigning parent_node_id, preventing FK deadlocks during insert. Co-Authored-By: Claude Opus 4.6 --- backend/app/core/kb_conversion_service.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/backend/app/core/kb_conversion_service.py b/backend/app/core/kb_conversion_service.py index 69d3a4b5..e1655411 100644 --- a/backend/app/core/kb_conversion_service.py +++ b/backend/app/core/kb_conversion_service.py @@ -284,17 +284,30 @@ def _parse_troubleshooting_response( if nid not in node_id_to_parent: node_id_to_parent[nid] = None # default: no parent - # Trace parent relationships + # Trace parent relationships (only set if it won't create a cycle) + def _would_cycle(child: str, parent: str) -> bool: + """Check if setting child's parent to parent creates a cycle.""" + visited: set[str] = set() + cur: str | None = parent + while cur: + if cur == child: + return True + if cur in visited: + break + visited.add(cur) + cur = node_id_to_parent.get(cur) + return False + for node in raw_nodes: nid = node.get("id", "") # Options point to children for opt in node.get("options", []): child_id = opt.get("next_node_id") - if child_id and child_id in node_id_to_data: + if child_id and child_id in node_id_to_data and not _would_cycle(nid, child_id): node_id_to_parent[child_id] = nid # next_node_id points to child next_id = node.get("next_node_id") - if next_id and next_id in node_id_to_data: + if next_id and next_id in node_id_to_data and not _would_cycle(nid, next_id): node_id_to_parent[next_id] = nid # Create import node records preserving order