mirror of
https://github.com/langgenius/dify.git
synced 2026-09-24 23:22:26 +08:00
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:
co-authored by
autofix-ci[bot]
parent
187501f53e
commit
0f9fffb7a2
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user