mirror of
https://github.com/langgenius/dify.git
synced 2026-06-24 13:01:16 +08:00
fix(agent): switch roster node to inline (#37754)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
82d08851be
commit
99010dab3e
@ -830,6 +830,16 @@ class AgentComposerService:
|
||||
) -> WorkflowAgentNodeBinding:
|
||||
node_job = payload.node_job or WorkflowNodeJobConfig()
|
||||
if binding:
|
||||
if cls._is_start_from_scratch_request(binding=binding, payload=payload):
|
||||
return cls._switch_roster_binding_to_inline_agent(
|
||||
tenant_id=tenant_id,
|
||||
app_id=app_id,
|
||||
workflow_id=workflow_id,
|
||||
node_id=node_id,
|
||||
account_id=account_id,
|
||||
binding=binding,
|
||||
payload=payload,
|
||||
)
|
||||
binding.node_job_config = node_job
|
||||
if payload.agent_soul is not None and binding.binding_type == WorkflowAgentBindingType.INLINE_AGENT:
|
||||
current_snapshot = cls._require_version(
|
||||
@ -880,6 +890,46 @@ class AgentComposerService:
|
||||
db.session.flush()
|
||||
return binding
|
||||
|
||||
@classmethod
|
||||
def _is_start_from_scratch_request(cls, *, binding: WorkflowAgentNodeBinding, payload: ComposerSavePayload) -> bool:
|
||||
return (
|
||||
binding.binding_type == WorkflowAgentBindingType.ROSTER_AGENT
|
||||
and payload.binding is not None
|
||||
and payload.binding.binding_type == WorkflowAgentBindingType.INLINE_AGENT.value
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _switch_roster_binding_to_inline_agent(
|
||||
cls,
|
||||
*,
|
||||
tenant_id: str,
|
||||
app_id: str,
|
||||
workflow_id: str,
|
||||
node_id: str,
|
||||
account_id: str,
|
||||
binding: WorkflowAgentNodeBinding,
|
||||
payload: ComposerSavePayload,
|
||||
) -> WorkflowAgentNodeBinding:
|
||||
if payload.binding and (payload.binding.agent_id or payload.binding.current_snapshot_id):
|
||||
raise ValueError("Start from Scratch must not provide an existing inline agent binding.")
|
||||
|
||||
agent_soul = payload.agent_soul or AgentSoulConfig()
|
||||
agent = cls._create_workflow_only_agent(
|
||||
tenant_id=tenant_id,
|
||||
app_id=app_id,
|
||||
workflow_id=workflow_id,
|
||||
node_id=node_id,
|
||||
account_id=account_id,
|
||||
agent_soul=agent_soul,
|
||||
)
|
||||
binding.binding_type = WorkflowAgentBindingType.INLINE_AGENT
|
||||
binding.agent_id = agent.id
|
||||
binding.current_snapshot_id = agent.active_config_snapshot_id
|
||||
binding.node_job_config = payload.node_job or binding.node_job_config
|
||||
binding.updated_by = account_id
|
||||
db.session.flush()
|
||||
return binding
|
||||
|
||||
@classmethod
|
||||
def _save_to_current_version(
|
||||
cls,
|
||||
|
||||
@ -533,6 +533,100 @@ def test_node_job_only_updates_inline_agent_soul(monkeypatch: pytest.MonkeyPatch
|
||||
assert inline_agent.updated_by == "account-1"
|
||||
|
||||
|
||||
def test_node_job_only_switches_roster_binding_to_inline_agent(monkeypatch: pytest.MonkeyPatch):
|
||||
fake_session = FakeSession()
|
||||
monkeypatch.setattr(composer_service.db, "session", fake_session)
|
||||
created_agent = SimpleNamespace(id="inline-agent-1", active_config_snapshot_id="inline-version-1")
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fake_create_workflow_only_agent(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return created_agent
|
||||
|
||||
monkeypatch.setattr(AgentComposerService, "_create_workflow_only_agent", fake_create_workflow_only_agent)
|
||||
existing_node_job = WorkflowNodeJobConfig(workflow_prompt="keep the existing task")
|
||||
binding = WorkflowAgentNodeBinding(
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
workflow_id="workflow-1",
|
||||
workflow_version="draft",
|
||||
node_id="node-1",
|
||||
binding_type=WorkflowAgentBindingType.ROSTER_AGENT,
|
||||
agent_id="roster-agent-1",
|
||||
current_snapshot_id="roster-version-1",
|
||||
node_job_config=existing_node_job,
|
||||
created_by="account-1",
|
||||
updated_by="account-1",
|
||||
)
|
||||
payload = ComposerSavePayload.model_validate(
|
||||
{
|
||||
"variant": ComposerVariant.WORKFLOW.value,
|
||||
"save_strategy": ComposerSaveStrategy.NODE_JOB_ONLY.value,
|
||||
"binding": {"binding_type": WorkflowAgentBindingType.INLINE_AGENT.value},
|
||||
"agent_soul": {"prompt": {"system_prompt": "start from scratch"}},
|
||||
}
|
||||
)
|
||||
|
||||
updated_binding = AgentComposerService._save_node_job_only(
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
workflow_id="workflow-1",
|
||||
node_id="node-1",
|
||||
account_id="account-1",
|
||||
binding=binding,
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
assert updated_binding is binding
|
||||
assert binding.binding_type == WorkflowAgentBindingType.INLINE_AGENT
|
||||
assert binding.agent_id == "inline-agent-1"
|
||||
assert binding.current_snapshot_id == "inline-version-1"
|
||||
assert binding.node_job_config is existing_node_job
|
||||
assert binding.updated_by == "account-1"
|
||||
assert captured["tenant_id"] == "tenant-1"
|
||||
assert captured["app_id"] == "app-1"
|
||||
assert captured["workflow_id"] == "workflow-1"
|
||||
assert captured["node_id"] == "node-1"
|
||||
assert captured["account_id"] == "account-1"
|
||||
assert captured["agent_soul"].prompt.system_prompt == "start from scratch"
|
||||
assert fake_session.flushes == 1
|
||||
|
||||
|
||||
def test_node_job_only_rejects_start_from_scratch_with_existing_inline_binding_id():
|
||||
binding = WorkflowAgentNodeBinding(
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
workflow_id="workflow-1",
|
||||
workflow_version="draft",
|
||||
node_id="node-1",
|
||||
binding_type=WorkflowAgentBindingType.ROSTER_AGENT,
|
||||
agent_id="roster-agent-1",
|
||||
current_snapshot_id="roster-version-1",
|
||||
node_job_config=WorkflowNodeJobConfig(),
|
||||
)
|
||||
payload = ComposerSavePayload.model_validate(
|
||||
{
|
||||
"variant": ComposerVariant.WORKFLOW.value,
|
||||
"save_strategy": ComposerSaveStrategy.NODE_JOB_ONLY.value,
|
||||
"binding": {
|
||||
"binding_type": WorkflowAgentBindingType.INLINE_AGENT.value,
|
||||
"agent_id": "existing-inline-agent",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="Start from Scratch"):
|
||||
AgentComposerService._save_node_job_only(
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
workflow_id="workflow-1",
|
||||
node_id="node-1",
|
||||
account_id="account-1",
|
||||
binding=binding,
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
|
||||
def test_node_job_only_rejects_inline_binding_pointing_to_roster_agent(monkeypatch: pytest.MonkeyPatch):
|
||||
fake_session = FakeSession()
|
||||
monkeypatch.setattr(composer_service.db, "session", fake_session)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user