mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-15 03:04:44 +08:00
fix: presever the local setting between frontend and backend (#670)
* fix: presever the local setting between frontend and backend * Added unit test for the state preservation * fix: passing the locale to the agent call * fix: apply the fix after code review
This commit is contained in:
@@ -84,6 +84,31 @@ def needs_clarification(state: dict) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def preserve_state_meta_fields(state: State) -> dict:
|
||||
"""
|
||||
Extract meta/config fields that should be preserved across state transitions.
|
||||
|
||||
These fields are critical for workflow continuity and should be explicitly
|
||||
included in all Command.update dicts to prevent them from reverting to defaults.
|
||||
|
||||
Args:
|
||||
state: Current state object
|
||||
|
||||
Returns:
|
||||
Dict of meta fields to preserve
|
||||
"""
|
||||
return {
|
||||
"locale": state.get("locale", "en-US"),
|
||||
"research_topic": state.get("research_topic", ""),
|
||||
"clarified_research_topic": state.get("clarified_research_topic", ""),
|
||||
"clarification_history": state.get("clarification_history", []),
|
||||
"enable_clarification": state.get("enable_clarification", False),
|
||||
"max_clarification_rounds": state.get("max_clarification_rounds", 3),
|
||||
"clarification_rounds": state.get("clarification_rounds", 0),
|
||||
"resources": state.get("resources", []),
|
||||
}
|
||||
|
||||
|
||||
def validate_and_fix_plan(plan: dict, enforce_web_search: bool = False) -> dict:
|
||||
"""
|
||||
Validate and fix a plan to ensure it meets requirements.
|
||||
@@ -217,7 +242,7 @@ def planner_node(
|
||||
state: State, config: RunnableConfig
|
||||
) -> Command[Literal["human_feedback", "reporter"]]:
|
||||
"""Planner node that generate the full plan."""
|
||||
logger.info("Planner generating full plan")
|
||||
logger.info("Planner generating full plan with locale: %s", state.get("locale", "en-US"))
|
||||
configurable = Configuration.from_runnable_config(config)
|
||||
plan_iterations = state["plan_iterations"] if state.get("plan_iterations", 0) else 0
|
||||
|
||||
@@ -266,7 +291,10 @@ def planner_node(
|
||||
|
||||
# if the plan iterations is greater than the max plan iterations, return the reporter node
|
||||
if plan_iterations >= configurable.max_plan_iterations:
|
||||
return Command(goto="reporter")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="reporter"
|
||||
)
|
||||
|
||||
full_response = ""
|
||||
if AGENT_LLM_MAP["planner"] == "basic" and not configurable.enable_deep_thinking:
|
||||
@@ -284,9 +312,15 @@ def planner_node(
|
||||
except json.JSONDecodeError:
|
||||
logger.warning("Planner response is not a valid JSON")
|
||||
if plan_iterations > 0:
|
||||
return Command(goto="reporter")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="reporter"
|
||||
)
|
||||
else:
|
||||
return Command(goto="__end__")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="__end__"
|
||||
)
|
||||
|
||||
# Validate and fix plan to ensure web search requirements are met
|
||||
if isinstance(curr_plan, dict):
|
||||
@@ -299,6 +333,7 @@ def planner_node(
|
||||
update={
|
||||
"messages": [AIMessage(content=full_response, name="planner")],
|
||||
"current_plan": new_plan,
|
||||
**preserve_state_meta_fields(state),
|
||||
},
|
||||
goto="reporter",
|
||||
)
|
||||
@@ -306,6 +341,7 @@ def planner_node(
|
||||
update={
|
||||
"messages": [AIMessage(content=full_response, name="planner")],
|
||||
"current_plan": full_response,
|
||||
**preserve_state_meta_fields(state),
|
||||
},
|
||||
goto="human_feedback",
|
||||
)
|
||||
@@ -323,7 +359,10 @@ def human_feedback_node(
|
||||
# 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")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="planner"
|
||||
)
|
||||
|
||||
# Normalize feedback string
|
||||
feedback_normalized = str(feedback).strip().upper()
|
||||
@@ -336,6 +375,7 @@ def human_feedback_node(
|
||||
"messages": [
|
||||
HumanMessage(content=feedback, name="feedback"),
|
||||
],
|
||||
**preserve_state_meta_fields(state),
|
||||
},
|
||||
goto="planner",
|
||||
)
|
||||
@@ -343,7 +383,10 @@ def human_feedback_node(
|
||||
logger.info("Plan is accepted by user.")
|
||||
else:
|
||||
logger.warning(f"Unsupported feedback format: {feedback}. Please use '[ACCEPTED]' to accept or '[EDIT_PLAN]' to edit.")
|
||||
return Command(goto="planner")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="planner"
|
||||
)
|
||||
|
||||
# if the plan is accepted, run the following node
|
||||
plan_iterations = state["plan_iterations"] if state.get("plan_iterations", 0) else 0
|
||||
@@ -360,16 +403,29 @@ def human_feedback_node(
|
||||
except json.JSONDecodeError:
|
||||
logger.warning("Planner response is not a valid JSON")
|
||||
if plan_iterations > 1: # the plan_iterations is increased before this check
|
||||
return Command(goto="reporter")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="reporter"
|
||||
)
|
||||
else:
|
||||
return Command(goto="__end__")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="__end__"
|
||||
)
|
||||
|
||||
# Build update dict with safe locale handling
|
||||
update_dict = {
|
||||
"current_plan": Plan.model_validate(new_plan),
|
||||
"plan_iterations": plan_iterations,
|
||||
**preserve_state_meta_fields(state),
|
||||
}
|
||||
|
||||
# Only override locale if new_plan provides a valid value, otherwise use preserved locale
|
||||
if new_plan.get("locale"):
|
||||
update_dict["locale"] = new_plan["locale"]
|
||||
|
||||
return Command(
|
||||
update={
|
||||
"current_plan": Plan.model_validate(new_plan),
|
||||
"plan_iterations": plan_iterations,
|
||||
"locale": new_plan["locale"],
|
||||
},
|
||||
update=update_dict,
|
||||
goto=goto,
|
||||
)
|
||||
|
||||
@@ -408,6 +464,7 @@ def coordinator_node(
|
||||
|
||||
goto = "__end__"
|
||||
locale = state.get("locale", "en-US")
|
||||
logger.info(f"Coordinator locale: {locale}")
|
||||
research_topic = state.get("research_topic", "")
|
||||
|
||||
# Process tool calls for legacy mode
|
||||
@@ -421,9 +478,8 @@ def coordinator_node(
|
||||
logger.info("Handing off to planner")
|
||||
goto = "planner"
|
||||
|
||||
# Extract locale and research_topic if provided
|
||||
if tool_args.get("locale") and tool_args.get("research_topic"):
|
||||
locale = tool_args.get("locale")
|
||||
# Extract research_topic if provided
|
||||
if tool_args.get("research_topic"):
|
||||
research_topic = tool_args.get("research_topic")
|
||||
break
|
||||
|
||||
@@ -587,8 +643,6 @@ def coordinator_node(
|
||||
logger.info("Handing off to planner")
|
||||
goto = "planner"
|
||||
|
||||
# Extract locale if provided
|
||||
locale = tool_args.get("locale", locale)
|
||||
if not enable_clarification and tool_args.get("research_topic"):
|
||||
research_topic = tool_args["research_topic"]
|
||||
|
||||
@@ -725,7 +779,10 @@ async def _execute_agent_step(
|
||||
|
||||
if not current_step:
|
||||
logger.warning(f"[_execute_agent_step] No unexecuted step found in {len(current_plan.steps)} total steps")
|
||||
return Command(goto="research_team")
|
||||
return Command(
|
||||
update=preserve_state_meta_fields(state),
|
||||
goto="research_team"
|
||||
)
|
||||
|
||||
logger.info(f"[_execute_agent_step] Executing step: {current_step.title}, agent: {agent_name}")
|
||||
logger.debug(f"[_execute_agent_step] Completed steps so far: {len(completed_steps)}")
|
||||
@@ -834,6 +891,7 @@ async def _execute_agent_step(
|
||||
)
|
||||
],
|
||||
"observations": observations + [detailed_error],
|
||||
**preserve_state_meta_fields(state),
|
||||
},
|
||||
goto="research_team",
|
||||
)
|
||||
@@ -859,6 +917,7 @@ async def _execute_agent_step(
|
||||
)
|
||||
],
|
||||
"observations": observations + [response_content],
|
||||
**preserve_state_meta_fields(state),
|
||||
},
|
||||
goto="research_team",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user