diff --git a/api/services/message_service.py b/api/services/message_service.py index 8f5e028d4d..e8d1b6232b 100644 --- a/api/services/message_service.py +++ b/api/services/message_service.py @@ -2,7 +2,6 @@ import logging from collections.abc import Sequence from typing import cast -from pydantic import TypeAdapter from sqlalchemy import select from sqlalchemy.orm import sessionmaker @@ -23,7 +22,6 @@ from models.model import ( App, AppMode, AppModelConfig, - AppModelConfigDict, EndUser, Message, MessageFeedback, @@ -42,7 +40,6 @@ from services.errors.message import ( ) from services.workflow_service import WorkflowService -_app_model_config_adapter: TypeAdapter[AppModelConfigDict] = TypeAdapter(AppModelConfigDict) logger = logging.getLogger(__name__) @@ -297,14 +294,12 @@ class MessageService: .limit(1) ) else: - conversation_override_model_configs = _app_model_config_adapter.validate_json( - conversation.override_model_configs - ) app_model_config = AppModelConfig( app_id=app_model.id, ) - app_model_config.id = conversation.app_model_config_id - app_model_config = app_model_config.from_model_config_dict(conversation_override_model_configs) + # Reuse Conversation.model_config so suggested-questions reads the same + # compatibility-normalized config as the rest of the message flow. + app_model_config = app_model_config.from_model_config_dict(conversation.model_config) if not app_model_config: raise ValueError("did not find app model config") diff --git a/api/tests/unit_tests/services/test_message_service.py b/api/tests/unit_tests/services/test_message_service.py index 51f8b3ef5b..005dcec886 100644 --- a/api/tests/unit_tests/services/test_message_service.py +++ b/api/tests/unit_tests/services/test_message_service.py @@ -1,3 +1,4 @@ +import json from datetime import datetime from unittest.mock import MagicMock, patch @@ -1056,6 +1057,117 @@ class TestMessageServiceSuggestedQuestions: ) mock_model_manager.return_value.get_model_instance.assert_not_called() + @patch("services.message_service.db") + @patch("services.message_service.ModelManager.for_tenant") + @patch("services.message_service.TokenBufferMemory") + @patch("services.message_service.LLMGenerator") + @patch("services.message_service.TraceQueueManager") + @patch.object(MessageService, "get_message") + @patch("services.message_service.ConversationService") + def test_get_suggested_questions_chat_app_uses_compatible_override_model_config( + self, + mock_conversation_service, + mock_get_message, + mock_trace_manager, + mock_llm_gen, + mock_memory, + mock_model_manager, + mock_db, + factory, + ): + """Test legacy override configs are normalized before suggested questions reads them.""" + app = factory.create_app_mock(mode=AppMode.CHAT) + app.tenant_id = "tenant-123" + user = factory.create_end_user_mock() + message = factory.create_message_mock() + mock_get_message.return_value = message + + conversation = MagicMock() + conversation.override_model_configs = json.dumps( + { + "speech_to_text": {"enabled": False}, + "text_to_speech": {"enabled": False}, + "retriever_resource": {"enabled": False}, + "model": {"provider": "openai", "name": "gpt-4o-mini", "mode": "chat"}, + "user_input_form": [], + "dataset_query_variable": "", + "pre_prompt": "", + "agent_mode": { + "enabled": False, + "max_iteration": 5, + "strategy": "function_call", + "tools": [], + }, + "prompt_type": "simple", + "chat_prompt_config": {}, + "completion_prompt_config": {}, + "dataset_configs": {"retrieval_model": "single", "datasets": {"datasets": []}}, + "file_upload": { + "image": { + "detail": "high", + "enabled": False, + "number_limits": 3, + "transfer_methods": ["remote_url", "local_file"], + } + }, + "suggested_questions_after_answer": { + "enabled": True, + "prompt": "legacy prompt", + }, + } + ) + conversation.model_config = { + "opening_statement": None, + "suggested_questions": [], + "suggested_questions_after_answer": { + "enabled": True, + "prompt": "legacy prompt", + }, + "speech_to_text": {"enabled": False}, + "text_to_speech": {"enabled": False}, + "retriever_resource": {"enabled": False}, + "annotation_reply": {"enabled": False}, + "more_like_this": {"enabled": False}, + "sensitive_word_avoidance": {"enabled": False, "type": "", "config": {}}, + "external_data_tools": [], + "model": {"provider": "openai", "name": "gpt-4o-mini", "mode": "chat"}, + "user_input_form": [], + "dataset_query_variable": "", + "pre_prompt": "", + "agent_mode": {"enabled": False, "strategy": "function_call", "tools": [], "prompt": None}, + "prompt_type": "simple", + "chat_prompt_config": {}, + "completion_prompt_config": {}, + "dataset_configs": {"retrieval_model": "single", "datasets": {"datasets": []}}, + "file_upload": { + "image": { + "detail": "high", + "enabled": False, + "number_limits": 3, + "transfer_methods": ["remote_url", "local_file"], + } + }, + "model_id": None, + "provider": None, + } + mock_conversation_service.get_conversation.return_value = conversation + + mock_memory.return_value.get_history_prompt_text.return_value = "histories" + mock_llm_gen.generate_suggested_questions_after_answer.return_value = ["Q1?"] + + result = MessageService.get_suggested_questions_after_answer( + app_model=app, user=user, message_id="msg-123", invoke_from=MagicMock() + ) + + assert result == ["Q1?"] + mock_db.session.scalar.assert_not_called() + mock_llm_gen.generate_suggested_questions_after_answer.assert_called_once_with( + tenant_id="tenant-123", + histories="histories", + instruction_prompt="legacy prompt", + model_config=None, + ) + # Test 30: get_suggested_questions_after_answer - Disabled Error @patch("services.message_service.WorkflowService") @patch("services.message_service.AdvancedChatAppConfigManager")