From c9eabe16120553305d3aa2d468012edd82720459 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 27 Sep 2025 02:54:57 +0800 Subject: [PATCH] Use POST for suggested questions --- api/controllers/console/app/message.py | 2 +- api/controllers/console/explore/message.py | 2 +- api/controllers/web/message.py | 2 +- ...console_app_message_suggested_questions.py | 60 +++++++++++++ ...ole_explore_message_suggested_questions.py | 84 +++++++++++++++++++ .../test_web_message_suggested_questions.py | 67 +++++++++++++++ web/service/debug.ts | 6 +- web/service/share.ts | 4 +- 8 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 api/tests/unit_tests/controllers/console/app/test_console_app_message_suggested_questions.py create mode 100644 api/tests/unit_tests/controllers/console/explore/test_console_explore_message_suggested_questions.py create mode 100644 api/tests/unit_tests/controllers/web/test_web_message_suggested_questions.py diff --git a/api/controllers/console/app/message.py b/api/controllers/console/app/message.py index 46523feccc..b25549d21e 100644 --- a/api/controllers/console/app/message.py +++ b/api/controllers/console/app/message.py @@ -269,7 +269,7 @@ class MessageSuggestedQuestionApi(Resource): @login_required @account_initialization_required @get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT]) - def get(self, app_model, message_id): + def post(self, app_model, message_id): message_id = str(message_id) try: diff --git a/api/controllers/console/explore/message.py b/api/controllers/console/explore/message.py index 217267031d..057de8f77f 100644 --- a/api/controllers/console/explore/message.py +++ b/api/controllers/console/explore/message.py @@ -163,7 +163,7 @@ class MessageMoreLikeThisApi(InstalledAppResource): endpoint="installed_app_suggested_question", ) class MessageSuggestedQuestionApi(InstalledAppResource): - def get(self, installed_app, message_id): + def post(self, installed_app, message_id): app_model = installed_app.app app_mode = AppMode.value_of(app_model.mode) if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}: diff --git a/api/controllers/web/message.py b/api/controllers/web/message.py index c74414c52b..a61ba91d17 100644 --- a/api/controllers/web/message.py +++ b/api/controllers/web/message.py @@ -249,7 +249,7 @@ class MessageSuggestedQuestionApi(WebApiResource): } ) @marshal_with(suggested_questions_response_fields) - def get(self, app_model, end_user, message_id): + def post(self, app_model, end_user, message_id): app_mode = AppMode.value_of(app_model.mode) if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}: raise NotCompletionAppError() diff --git a/api/tests/unit_tests/controllers/console/app/test_console_app_message_suggested_questions.py b/api/tests/unit_tests/controllers/console/app/test_console_app_message_suggested_questions.py new file mode 100644 index 0000000000..96756cb6da --- /dev/null +++ b/api/tests/unit_tests/controllers/console/app/test_console_app_message_suggested_questions.py @@ -0,0 +1,60 @@ +import inspect +import uuid +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest +from flask import Flask + +from controllers.console.app import message as console_message_module +from controllers.console.app.message import MessageSuggestedQuestionApi +from core.app.entities.app_invoke_entities import InvokeFrom +from models.account import Account + + +@pytest.fixture +def flask_app(): + app = Flask(__name__) + app.config["TESTING"] = True + return app + + +@pytest.fixture +def account_user(): + user = Account(name="Tester", email="tester@example.com") + user.id = "user-id" + return user + + +class TestConsoleAppMessageSuggestedQuestionApi: + def test_post_forwards_to_service(self, flask_app, account_user, monkeypatch): + app_model = SimpleNamespace(id="app-id", mode="chat") + questions = ["a", "b"] + service_mock = MagicMock(return_value=questions) + + monkeypatch.setattr(console_message_module, "current_user", account_user, raising=False) + monkeypatch.setattr( + console_message_module.MessageService, + "get_suggested_questions_after_answer", + service_mock, + raising=False, + ) + + handler = inspect.unwrap(MessageSuggestedQuestionApi.post) + controller = MessageSuggestedQuestionApi() + message_id = uuid.uuid4() + + with flask_app.test_request_context( + f"/apps/{app_model.id}/chat-messages/{message_id}/suggested-questions", + method="POST", + json={}, + ): + result = handler(controller, app_model, message_id) + + assert result == {"data": questions} + service_mock.assert_called_once_with( + app_model=app_model, + message_id=str(message_id), + user=account_user, + invoke_from=InvokeFrom.DEBUGGER, + ) diff --git a/api/tests/unit_tests/controllers/console/explore/test_console_explore_message_suggested_questions.py b/api/tests/unit_tests/controllers/console/explore/test_console_explore_message_suggested_questions.py new file mode 100644 index 0000000000..b690933121 --- /dev/null +++ b/api/tests/unit_tests/controllers/console/explore/test_console_explore_message_suggested_questions.py @@ -0,0 +1,84 @@ +import inspect +import uuid +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest +from flask import Flask + +from controllers.console.explore.error import NotChatAppError +from controllers.console.explore.message import MessageSuggestedQuestionApi +from core.app.entities.app_invoke_entities import InvokeFrom +from models.account import Account +from models.model import AppMode + + +@pytest.fixture +def flask_app(): + app = Flask(__name__) + app.config["TESTING"] = True + return app + + +@pytest.fixture +def account_user(): + user = Account(name="Tester", email="tester@example.com") + user.id = "user-id" + return user + + +class TestConsoleExploreMessageSuggestedQuestionApi: + def test_post_returns_questions(self, flask_app, account_user, monkeypatch): + installed_app = SimpleNamespace(app=SimpleNamespace(mode=AppMode.CHAT.value)) + questions = ["q1"] + service_mock = MagicMock(return_value=questions) + + monkeypatch.setattr( + "controllers.console.explore.message.current_user", + account_user, + raising=False, + ) + monkeypatch.setattr( + "controllers.console.explore.message.MessageService.get_suggested_questions_after_answer", + service_mock, + raising=False, + ) + + handler = inspect.unwrap(MessageSuggestedQuestionApi.post) + controller = MessageSuggestedQuestionApi() + message_id = uuid.uuid4() + + with flask_app.test_request_context( + f"/messages/{message_id}/suggested-questions", + method="POST", + json={}, + ): + result = handler(controller, installed_app, message_id) + + assert result == {"data": questions} + service_mock.assert_called_once_with( + app_model=installed_app.app, + user=account_user, + message_id=str(message_id), + invoke_from=InvokeFrom.EXPLORE, + ) + + def test_non_chat_app_raises(self, flask_app, account_user, monkeypatch): + installed_app = SimpleNamespace(app=SimpleNamespace(mode=AppMode.COMPLETION.value)) + monkeypatch.setattr( + "controllers.console.explore.message.current_user", + account_user, + raising=False, + ) + + handler = inspect.unwrap(MessageSuggestedQuestionApi.post) + controller = MessageSuggestedQuestionApi() + message_id = uuid.uuid4() + + with flask_app.test_request_context( + f"/messages/{message_id}/suggested-questions", + method="POST", + json={}, + ): + with pytest.raises(NotChatAppError): + handler(controller, installed_app, message_id) diff --git a/api/tests/unit_tests/controllers/web/test_web_message_suggested_questions.py b/api/tests/unit_tests/controllers/web/test_web_message_suggested_questions.py new file mode 100644 index 0000000000..3f981733c5 --- /dev/null +++ b/api/tests/unit_tests/controllers/web/test_web_message_suggested_questions.py @@ -0,0 +1,67 @@ +import inspect +import uuid +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest +from flask import Flask + +from controllers.web.error import NotCompletionAppError +from controllers.web.message import MessageSuggestedQuestionApi +from core.app.entities.app_invoke_entities import InvokeFrom +from models.model import AppMode + + +@pytest.fixture +def flask_app(): + app = Flask(__name__) + app.config["TESTING"] = True + return app + + +class TestWebMessageSuggestedQuestionApi: + def test_post_returns_questions(self, flask_app, monkeypatch): + app_model = SimpleNamespace(mode=AppMode.CHAT.value) + end_user = SimpleNamespace() + questions = ["Q1", "Q2"] + + service_mock = MagicMock(return_value=questions) + monkeypatch.setattr( + "controllers.web.message.MessageService.get_suggested_questions_after_answer", + service_mock, + raising=False, + ) + + handler = inspect.unwrap(MessageSuggestedQuestionApi.post) + controller = MessageSuggestedQuestionApi() + message_id = uuid.uuid4() + + with flask_app.test_request_context( + f"/messages/{message_id}/suggested-questions", + method="POST", + json={}, + ): + result = handler(controller, app_model, end_user, message_id) + + assert result == {"data": questions} + service_mock.assert_called_once_with( + app_model=app_model, + user=end_user, + message_id=str(message_id), + invoke_from=InvokeFrom.WEB_APP, + ) + + def test_non_chat_app_raises(self, flask_app): + app_model = SimpleNamespace(mode=AppMode.COMPLETION.value) + end_user = SimpleNamespace() + handler = inspect.unwrap(MessageSuggestedQuestionApi.post) + controller = MessageSuggestedQuestionApi() + message_id = uuid.uuid4() + + with flask_app.test_request_context( + f"/messages/{message_id}/suggested-questions", + method="POST", + json={}, + ): + with pytest.raises(NotCompletionAppError): + handler(controller, app_model, end_user, message_id) diff --git a/web/service/debug.ts b/web/service/debug.ts index fab2910c5e..09a9ea0282 100644 --- a/web/service/debug.ts +++ b/web/service/debug.ts @@ -61,9 +61,11 @@ export const sendCompletionMessage = async (appId: string, body: Record { - return get( + return post( `apps/${appId}/chat-messages/${messageId}/suggested-questions`, - {}, + { + body: {}, + }, { getAbortController, }, diff --git a/web/service/share.ts b/web/service/share.ts index 559bb2a158..40828bac73 100644 --- a/web/service/share.ts +++ b/web/service/share.ts @@ -271,7 +271,9 @@ export const removeMessage = (messageId: string, isInstalledApp: boolean, instal } export const fetchSuggestedQuestions = (messageId: string, isInstalledApp: boolean, installedAppId = '') => { - return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/suggested-questions`, isInstalledApp, installedAppId)) + return (getAction('post', isInstalledApp))(getUrl(`/messages/${messageId}/suggested-questions`, isInstalledApp, installedAppId), { + body: {}, + }) } export const audioToText = (url: string, isPublicAPI: boolean, body: FormData) => {