fix(agent): isolate build and preview chat conversations (#39405)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
zyssyz123
2026-07-22 09:17:55 +00:00
committed by GitHub
co-authored by autofix-ci[bot]
parent 187501f53e
commit 0f9fffb7a2
12 changed files with 370 additions and 67 deletions
@@ -53,6 +53,7 @@ from controllers.console.app.message import (
AgentMessageFeedbackApi,
AgentMessageSuggestedQuestionApi,
)
from models.agent import AgentConfigDraftType
from services.entities.agent_entities import ComposerSaveStrategy, ComposerVariant
@@ -371,6 +372,7 @@ def test_agent_app_list_and_create_use_agent_route(
"tenant_id": "tenant-1",
"agent_id": "agent-created",
"account_id": account_id,
"draft_type": AgentConfigDraftType.DEBUG_BUILD,
"commit": False,
}
@@ -544,8 +546,19 @@ def test_agent_app_copy_uses_agent_id_and_returns_agent_detail(
}
def test_agent_debug_conversation_refresh_uses_current_user(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
@pytest.mark.parametrize(
("payload", "expected_draft_type"),
[
(None, AgentConfigDraftType.DEBUG_BUILD),
({"draft_type": "draft"}, AgentConfigDraftType.DRAFT),
],
)
def test_agent_debug_conversation_refresh_uses_current_user_and_draft_type(
app: Flask,
monkeypatch: pytest.MonkeyPatch,
account_id: str,
payload: dict[str, str] | None,
expected_draft_type: AgentConfigDraftType,
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
captured: dict[str, object] = {}
@@ -557,7 +570,9 @@ def test_agent_debug_conversation_refresh_uses_current_user(
monkeypatch.setattr(roster_controller, "_agent_roster_service", lambda *_args: FakeRosterService())
with app.test_request_context(
"/console/api/agent/00000000-0000-0000-0000-000000000001/debug-conversation/refresh", method="POST"
"/console/api/agent/00000000-0000-0000-0000-000000000001/debug-conversation/refresh",
method="POST",
json=payload,
):
response = unwrap(AgentDebugConversationRefreshApi.post)(
AgentDebugConversationRefreshApi(), MagicMock(), "tenant-1", SimpleNamespace(id=account_id), agent_id
@@ -567,7 +582,12 @@ def test_agent_debug_conversation_refresh_uses_current_user(
"debug_conversation_has_messages": False,
"debug_conversation_message_count": 0,
}
assert captured == {"tenant_id": "tenant-1", "agent_id": agent_id, "account_id": account_id}
assert captured == {
"tenant_id": "tenant-1",
"agent_id": agent_id,
"account_id": account_id,
"draft_type": expected_draft_type,
}
def test_agent_publish_and_build_draft_routes_call_composer_service(
@@ -1456,6 +1476,7 @@ def test_build_chat_finalization_helper_forces_debug_build_and_push_prompt(
"current_user": SimpleNamespace(id=account_id),
"app_model": app_model,
"agent_id": "agent-1",
"draft_type": AgentConfigDraftType.DEBUG_BUILD,
}
generate_call = cast(dict[str, object], captured["generate"])
assert generate_call["app_model"] is app_model
@@ -1520,11 +1541,15 @@ def test_agent_chat_helper_forces_agent_streaming_and_external_trace(
captured.update(kwargs)
return {"answer": "ok"}
def resolve_debug_conversation(**kwargs: object) -> str:
captured["resolve_debug_conversation"] = kwargs
return "debug-conversation-1"
monkeypatch.setattr(completion_controller.AppGenerateService, "generate", generate)
monkeypatch.setattr(
completion_controller,
"_resolve_current_user_agent_debug_conversation_id",
lambda **kwargs: "debug-conversation-1",
resolve_debug_conversation,
)
monkeypatch.setattr(
completion_controller.helper, "compact_generate_response", lambda response: {"response": response}
@@ -1544,6 +1569,7 @@ def test_agent_chat_helper_forces_agent_streaming_and_external_trace(
assert args["conversation_id"] == "debug-conversation-1"
assert args["auto_generate_name"] is False
assert args["external_trace_id"] == "trace-1"
assert cast(dict[str, object], captured["resolve_debug_conversation"])["draft_type"] == AgentConfigDraftType.DRAFT
def test_agent_chat_helper_ignores_private_exit_intent_payload_key(
@@ -1642,6 +1668,7 @@ def test_resolve_current_user_agent_debug_conversation_uses_agent_or_backing_app
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
agent_id="agent-1",
draft_type=AgentConfigDraftType.DRAFT,
)
fallback_id = completion_controller._resolve_current_user_agent_debug_conversation_id(
session="session-1", # type: ignore[arg-type]
@@ -1649,13 +1676,26 @@ def test_resolve_current_user_agent_debug_conversation_uses_agent_or_backing_app
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
agent_id=None,
draft_type=AgentConfigDraftType.DEBUG_BUILD,
)
assert explicit_id == "debug-agent-1"
assert fallback_id == "debug-backing-agent"
assert calls[1] == {"get_or_create": {"tenant_id": "tenant-1", "agent_id": "agent-1", "account_id": "account-1"}}
assert calls[1] == {
"get_or_create": {
"tenant_id": "tenant-1",
"agent_id": "agent-1",
"account_id": "account-1",
"draft_type": AgentConfigDraftType.DRAFT,
}
}
assert calls[3] == {"get_app_backing_agent": {"tenant_id": "tenant-1", "app_id": "app-1"}}
assert calls[4] == {
"get_or_create": {"tenant_id": "tenant-1", "agent_id": "backing-agent", "account_id": "account-1"}
"get_or_create": {
"tenant_id": "tenant-1",
"agent_id": "backing-agent",
"account_id": "account-1",
"draft_type": AgentConfigDraftType.DEBUG_BUILD,
}
}
@@ -574,6 +574,27 @@ def test_console_account_avatar_query_param_renders_as_query(monkeypatch: pytest
assert params["avatar"]["required"] is True
def test_console_agent_debug_conversation_refresh_body_is_optional(monkeypatch: pytest.MonkeyPatch):
from configs import dify_config
from controllers.console import bp as console_bp
monkeypatch.setattr(dify_config, "SWAGGER_UI_ENABLED", True)
app = Flask(__name__)
app.config["TESTING"] = True
app.register_blueprint(console_bp)
payload = app.test_client().get("/console/api/openapi.json").get_json()
operation = payload["paths"]["/agent/{agent_id}/debug-conversation/refresh"]["post"]
request_body = operation["requestBody"]
assert request_body["required"] is False
assert request_body["content"]["application/json"]["schema"] == {
"$ref": "#/components/schemas/AgentDebugConversationRefreshPayload"
}
assert "AgentDebugConversationRefreshPayload" in payload["components"]["schemas"]
def test_console_member_invite_documents_bad_request_response(monkeypatch: pytest.MonkeyPatch):
from configs import dify_config
from controllers.console import bp as console_bp
@@ -2624,7 +2624,7 @@ def test_roster_create_detail_and_lookup_helpers(monkeypatch: pytest.MonkeyPatch
monkeypatch.setattr(
AgentRosterService,
"_get_or_create_agent_app_debug_conversation",
lambda self, *, agent, account_id: "debug-conversation-1",
lambda self, *, agent, account_id, draft_type: "debug-conversation-1",
)
payload = roster_service.RosterAgentCreatePayload(
name="Analyst",
@@ -2730,6 +2730,7 @@ def test_agent_app_debug_conversation_create_reuse_and_recreate():
assert created_mapping.tenant_id == "tenant-1"
assert created_mapping.agent_id == "agent-1"
assert created_mapping.account_id == "account-1"
assert created_mapping.draft_type == AgentConfigDraftType.DEBUG_BUILD
assert create_session.commits == 1
existing_mapping = AgentDebugConversation(
@@ -2737,6 +2738,7 @@ def test_agent_app_debug_conversation_create_reuse_and_recreate():
agent_id="agent-1",
app_id="app-1",
account_id="account-1",
draft_type=AgentConfigDraftType.DEBUG_BUILD,
conversation_id="existing-conversation",
)
reuse_session = FakeSession(scalar=[agent, existing_mapping, "existing-conversation"])
@@ -2754,6 +2756,7 @@ def test_agent_app_debug_conversation_create_reuse_and_recreate():
agent_id="agent-1",
app_id="app-1",
account_id="account-1",
draft_type=AgentConfigDraftType.DEBUG_BUILD,
conversation_id="deleted-conversation",
)
recreate_session = FakeSession(scalar=[agent, stale_mapping, None])
@@ -2768,6 +2771,42 @@ def test_agent_app_debug_conversation_create_reuse_and_recreate():
assert recreate_session.commits == 1
def test_agent_app_debug_conversations_are_isolated_by_draft_type():
agent = Agent(
id="agent-1",
tenant_id="tenant-1",
app_id="app-1",
name="Analyst",
description="",
agent_kind=AgentKind.DIFY_AGENT,
scope=AgentScope.ROSTER,
source=AgentSource.AGENT_APP,
status=AgentStatus.ACTIVE,
)
session = FakeSession(scalar=[agent, None, agent, None])
service = AgentRosterService(session)
build_conversation_id = service.get_or_create_agent_app_debug_conversation_id(
tenant_id="tenant-1",
agent_id="agent-1",
account_id="account-1",
draft_type=AgentConfigDraftType.DEBUG_BUILD,
)
preview_conversation_id = service.get_or_create_agent_app_debug_conversation_id(
tenant_id="tenant-1",
agent_id="agent-1",
account_id="account-1",
draft_type=AgentConfigDraftType.DRAFT,
)
mappings = [value for value in session.added if isinstance(value, AgentDebugConversation)]
assert build_conversation_id != preview_conversation_id
assert {mapping.draft_type for mapping in mappings} == {
AgentConfigDraftType.DRAFT,
AgentConfigDraftType.DEBUG_BUILD,
}
def test_agent_app_debug_conversation_message_count():
session = FakeSession(scalar=[3])
@@ -2794,6 +2833,7 @@ def test_agent_app_debug_conversation_requires_app_binding():
AgentRosterService(FakeSession())._get_or_create_agent_app_debug_conversation(
agent=agent,
account_id="account-1",
draft_type=AgentConfigDraftType.DEBUG_BUILD,
)
@@ -2840,7 +2880,9 @@ def test_load_or_create_agent_app_debug_conversations_supports_runtime_backed_ag
assert result["agent-1"]
assert result["agent-3"]
assert fake_session.commits == 1
assert len([value for value in fake_session.added if isinstance(value, AgentDebugConversation)]) == 2
mappings = [value for value in fake_session.added if isinstance(value, AgentDebugConversation)]
assert len(mappings) == 2
assert all(mapping.draft_type == AgentConfigDraftType.DEBUG_BUILD for mapping in mappings)
def test_agent_app_visible_versions_exclude_draft_saves():
@@ -3276,6 +3318,7 @@ class TestAgentAppBackingAgent:
assert mappings[0].agent_id == "agent-1"
assert mappings[0].app_id == "app-1"
assert mappings[0].account_id == "account-1"
assert mappings[0].draft_type == AgentConfigDraftType.DEBUG_BUILD
assert mappings[0].conversation_id == conversation_id
assert session.deleted == []
assert session.commits == 1
@@ -3361,8 +3404,10 @@ class TestAgentAppBackingAgent:
payload = cleanup_delay.call_args.args[0]
assert payload["metadata"]["conversation_id"] == "old-conversation"
assert payload["metadata"]["agent_id"] == "agent-9"
assert payload["metadata"]["draft_type"] == "debug_build"
assert (
payload["idempotency_key"] == "tenant-1:agent-1:account-1:old-conversation:debug-session-cleanup:"
payload["idempotency_key"]
== "tenant-1:agent-1:account-1:debug_build:old-conversation:debug-session-cleanup:"
"agent-9:snap-9:run-old"
)
cleanup_store.mark_cleaned.assert_called_once_with(