mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-03 06:12:14 +08:00
fix: handle [ACCEPTED] feedback gracefully without TypeError in plan review (#657)
* fix: handle [ACCEPTED] feedback gracefully without TypeError in plan review (#607) - Add explicit None/empty feedback check to prevent processing None values - Normalize feedback string once using strip().upper() instead of repeated calls - Replace TypeError exception with graceful fallback to planner node - Handle invalid feedback formats by logging warning and returning to planner - Maintain backward compatibility for '[ACCEPTED]' and '[EDIT_PLAN]' formats - Add test cases for None feedback, empty string feedback, and invalid formats - Update existing test to verify graceful handling instead of exception raising * Update src/graph/nodes.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -320,8 +320,17 @@ def human_feedback_node(
|
||||
if not auto_accepted_plan:
|
||||
feedback = interrupt("Please Review the Plan.")
|
||||
|
||||
# Handle None or empty feedback
|
||||
if not feedback:
|
||||
logger.warning(f"Received empty or None feedback: {feedback}. Returning to planner for new plan.")
|
||||
return Command(goto="planner")
|
||||
|
||||
# Normalize feedback string
|
||||
feedback_normalized = str(feedback).strip().upper()
|
||||
|
||||
# if the feedback is not accepted, return the planner node
|
||||
if feedback and str(feedback).upper().startswith("[EDIT_PLAN]"):
|
||||
if feedback_normalized.startswith("[EDIT_PLAN]"):
|
||||
logger.info(f"Plan edit requested by user: {feedback}")
|
||||
return Command(
|
||||
update={
|
||||
"messages": [
|
||||
@@ -330,10 +339,11 @@ def human_feedback_node(
|
||||
},
|
||||
goto="planner",
|
||||
)
|
||||
elif feedback and str(feedback).upper().startswith("[ACCEPTED]"):
|
||||
elif feedback_normalized.startswith("[ACCEPTED]"):
|
||||
logger.info("Plan is accepted by user.")
|
||||
else:
|
||||
raise TypeError(f"Interrupt value of {feedback} is not supported.")
|
||||
logger.warning(f"Unsupported feedback format: {feedback}. Please use '[ACCEPTED]' to accept or '[EDIT_PLAN]' to edit.")
|
||||
return Command(goto="planner")
|
||||
|
||||
# if the plan is accepted, run the following node
|
||||
plan_iterations = state["plan_iterations"] if state.get("plan_iterations", 0) else 0
|
||||
|
||||
@@ -454,12 +454,37 @@ def test_human_feedback_node_accepted(monkeypatch, mock_state_base, mock_config)
|
||||
def test_human_feedback_node_invalid_interrupt(
|
||||
monkeypatch, mock_state_base, mock_config
|
||||
):
|
||||
# interrupt returns something else, should raise TypeError
|
||||
# interrupt returns something else, should gracefully return to planner (not raise TypeError)
|
||||
state = dict(mock_state_base)
|
||||
state["auto_accepted_plan"] = False
|
||||
with patch("src.graph.nodes.interrupt", return_value="RANDOM_FEEDBACK"):
|
||||
with pytest.raises(TypeError):
|
||||
human_feedback_node(state, mock_config)
|
||||
result = human_feedback_node(state, mock_config)
|
||||
assert isinstance(result, Command)
|
||||
assert result.goto == "planner"
|
||||
|
||||
|
||||
def test_human_feedback_node_none_feedback(
|
||||
monkeypatch, mock_state_base, mock_config
|
||||
):
|
||||
# interrupt returns None, should gracefully return to planner
|
||||
state = dict(mock_state_base)
|
||||
state["auto_accepted_plan"] = False
|
||||
with patch("src.graph.nodes.interrupt", return_value=None):
|
||||
result = human_feedback_node(state, mock_config)
|
||||
assert isinstance(result, Command)
|
||||
assert result.goto == "planner"
|
||||
|
||||
|
||||
def test_human_feedback_node_empty_feedback(
|
||||
monkeypatch, mock_state_base, mock_config
|
||||
):
|
||||
# interrupt returns empty string, should gracefully return to planner
|
||||
state = dict(mock_state_base)
|
||||
state["auto_accepted_plan"] = False
|
||||
with patch("src.graph.nodes.interrupt", return_value=""):
|
||||
result = human_feedback_node(state, mock_config)
|
||||
assert isinstance(result, Command)
|
||||
assert result.goto == "planner"
|
||||
|
||||
|
||||
def test_human_feedback_node_json_decode_error_first_iteration(
|
||||
|
||||
Reference in New Issue
Block a user