From 26deeea830eb4a4aa39651fd7b2fbb523eb2a78d Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Thu, 14 May 2026 07:42:24 -0700 Subject: [PATCH] fix(telegram): restore model-switch success path + author map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cherry-picked PR over-indented the edit_message_text block for the mm: (model selected → switch) success path so the confirmation edit lived inside the preceding 'except Exception as exc' branch and only fired when the callback raised. Dedent the try/except back to 12-space indent so it runs after the callback succeeds, restoring the original flow that removes the inline buttons and shows the 'Switched to ...' confirmation. Add a regression test (test_model_selected_edits_message_on_success) that asserts edit_message_text is awaited and the result text is routed through format_message (MARKDOWN_V2 + backtick survival). Add phuongvm to scripts/release.py AUTHOR_MAP. --- gateway/platforms/telegram.py | 22 +++++------ scripts/release.py | 1 + tests/gateway/test_telegram_model_picker.py | 44 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 11 deletions(-) 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()