diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 03184ac1c20..753f8c231e0 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -2534,22 +2534,22 @@ class TelegramAdapter(BasePlatformAdapter): result_text = f"Error switching model: {exc}" # Edit message to show confirmation, remove buttons + try: + await query.edit_message_text( + text=self.format_message(result_text), + parse_mode=ParseMode.MARKDOWN_V2, + reply_markup=None, + ) + except Exception: + # Markdown parse failure — retry as plain text try: await query.edit_message_text( - text=self.format_message(result_text), - parse_mode=ParseMode.MARKDOWN_V2, + text=result_text, + parse_mode=None, reply_markup=None, ) except Exception: - # Markdown parse failure — retry as plain text - try: - await query.edit_message_text( - text=result_text, - parse_mode=None, - reply_markup=None, - ) - except Exception: - pass + pass await query.answer(text="Model switched!") # Clean up state diff --git a/scripts/release.py b/scripts/release.py index f9de395d195..60093b4821a 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -715,6 +715,7 @@ AUTHOR_MAP = { "tangyuanjc@JCdeAIfenshendeMac-mini.local": "tangyuanjc", "harryplusplus@gmail.com": "harryplusplus", "anthhub@163.com": "anthhub", + "vmphuongit@gmail.com": "phuongvm", "allard.quek@singtel.com": "AllardQuek", "shenuu@gmail.com": "shenuu", "xiayh17@gmail.com": "xiayh0107", diff --git a/tests/gateway/test_telegram_model_picker.py b/tests/gateway/test_telegram_model_picker.py index 19928ffa128..3e1d4cf71e8 100644 --- a/tests/gateway/test_telegram_model_picker.py +++ b/tests/gateway/test_telegram_model_picker.py @@ -102,6 +102,50 @@ class TestTelegramModelPicker: assert "provider\\_one" in edit_kwargs["text"] assert "`model_1`" in edit_kwargs["text"] + @pytest.mark.asyncio + async def test_model_selected_edits_message_on_success(self): + """Regression: the mm: (model selected → switch) success path must + edit the picker message to show the confirmation and remove the + buttons. An earlier revision of this PR over-indented the + edit_message_text block so it lived inside the except branch and + only fired when the callback raised.""" + adapter = _make_adapter() + callback = AsyncMock(return_value="Switched to `gpt-5`") + adapter._model_picker_state["12345"] = { + "providers": [ + {"slug": "openai", "name": "OpenAI", "total_models": 1, "is_current": True} + ], + "current_model": "model_1", + "current_provider": "openai", + "session_key": "s", + "on_model_selected": callback, + "selected_provider": "openai", + "model_list": ["gpt-5"], + "msg_id": 42, + } + + query = AsyncMock() + query.data = "mm:0" + query.message = MagicMock() + query.message.chat_id = 12345 + query.answer = AsyncMock() + query.edit_message_text = AsyncMock() + + await adapter._handle_model_picker_callback(query, "mm:0", "12345") + + # The callback was invoked with the selected model + callback.assert_awaited_once() + # edit_message_text MUST be called on the success path (this is the + # regression we're guarding). + query.edit_message_text.assert_awaited() + edit_kwargs = query.edit_message_text.call_args[1] + assert "MARKDOWN_V2" in repr(edit_kwargs["parse_mode"]) + # The dynamic result text was routed through format_message + # (backtick code blocks survive escaping). + assert "`gpt-5`" in edit_kwargs["text"] + # State is cleaned up after a successful switch. + assert "12345" not in adapter._model_picker_state + @pytest.mark.asyncio async def test_retries_without_thread_when_thread_not_found(self): adapter = _make_adapter()