mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-14 03:15:58 +00:00
* fix(agents): sync agent_name across context/configurable and reject empty soul (#3549) Two independent issues caused custom agent creation to silently fail: 1. build_run_config only wrote agent_name into one container (configurable or context), so setup_agent — which reads ToolRuntime.context exclusively since LangGraph >=1.1.9 — saw agent_name=None and wrote SOUL.md to the global base_dir instead of users/{user_id}/agents/{name}/. Mirror the dual-write pattern already used by merge_run_context_overrides and naming.py so both containers always carry the same value. 2. setup_agent persisted whatever soul string it received, including empty or whitespace-only content, and still reported success. The frontend then surfaced an unusable agent and the global default SOUL.md could be silently overwritten with empty content. Reject empty soul before any filesystem operation so the model can retry. Tests: - test_gateway_services.py: dual-write regressions for both configurable and context entry paths, explicit-agent-name precedence on both sides, and a shape-parity test against merge_run_context_overrides. - test_setup_agent_tool.py: empty/whitespace soul rejection, plus no-overwrite guarantees for existing global and per-agent SOUL.md. * Update services.py
This commit is contained in:
@@ -148,3 +148,58 @@ class TestSetupAgentNoDataLoss:
|
||||
default_dir = tmp_path / "users" / "default" / "agents" / "test-agent"
|
||||
assert (expected_dir / "SOUL.md").read_text() == "# My Agent"
|
||||
assert not default_dir.exists()
|
||||
|
||||
|
||||
# --- Empty soul guard tests ---
|
||||
|
||||
|
||||
class TestSetupAgentEmptySoulGuard:
|
||||
"""The tool must refuse to persist an empty / whitespace-only SOUL.md and
|
||||
must not touch the filesystem at all, so an existing SOUL.md (per-agent or
|
||||
global default) cannot be silently overwritten with empty content.
|
||||
"""
|
||||
|
||||
def test_empty_soul_returns_error_and_does_not_write(self, tmp_path: Path):
|
||||
result = _call_setup_agent(tmp_path, soul="", description="desc")
|
||||
|
||||
messages = result.update["messages"]
|
||||
assert len(messages) == 1
|
||||
assert "soul content is empty" in messages[0].content
|
||||
assert "created_agent_name" not in result.update
|
||||
agent_dir = tmp_path / "users" / "test-user-autouse" / "agents" / "test-agent"
|
||||
assert not agent_dir.exists()
|
||||
|
||||
def test_whitespace_only_soul_returns_error_and_does_not_write(self, tmp_path: Path):
|
||||
result = _call_setup_agent(tmp_path, soul=" \n\t ", description="desc")
|
||||
|
||||
messages = result.update["messages"]
|
||||
assert len(messages) == 1
|
||||
assert "soul content is empty" in messages[0].content
|
||||
agent_dir = tmp_path / "users" / "test-user-autouse" / "agents" / "test-agent"
|
||||
assert not agent_dir.exists()
|
||||
|
||||
def test_empty_soul_does_not_overwrite_existing_global_soul(self, tmp_path: Path):
|
||||
"""If agent_name resolution would have fallen back to base_dir, an
|
||||
empty soul must not clobber a pre-existing global SOUL.md.
|
||||
"""
|
||||
global_soul = tmp_path / "SOUL.md"
|
||||
global_soul.write_text("original global soul", encoding="utf-8")
|
||||
|
||||
with patch("deerflow.tools.builtins.setup_agent_tool.get_paths", return_value=_make_paths_mock(tmp_path)):
|
||||
setup_agent.func(
|
||||
soul="",
|
||||
description="desc",
|
||||
runtime=_DummyRuntime(context={"agent_name": None}, tool_call_id="tool-empty"),
|
||||
)
|
||||
|
||||
assert global_soul.read_text(encoding="utf-8") == "original global soul"
|
||||
|
||||
def test_empty_soul_does_not_overwrite_existing_per_agent_soul(self, tmp_path: Path):
|
||||
agent_dir = tmp_path / "users" / "test-user-autouse" / "agents" / "test-agent"
|
||||
agent_dir.mkdir(parents=True)
|
||||
existing_soul = agent_dir / "SOUL.md"
|
||||
existing_soul.write_text("original per-agent soul", encoding="utf-8")
|
||||
|
||||
_call_setup_agent(tmp_path, soul=" ", description="desc")
|
||||
|
||||
assert existing_soul.read_text(encoding="utf-8") == "original per-agent soul"
|
||||
|
||||
Reference in New Issue
Block a user