vibego 1.0.10__py3-none-any.whl → 1.0.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bot.py CHANGED
@@ -20,6 +20,7 @@ from aiogram.filters import Command, CommandStart
20
20
  from aiogram.filters.command import CommandObject
21
21
  from aiogram.fsm.context import FSMContext
22
22
  from aiogram.fsm.storage.memory import MemoryStorage
23
+ from aiogram.dispatcher.event.bases import SkipHandler
23
24
  from aiogram.types import (
24
25
  Message,
25
26
  BufferedInputFile,
@@ -990,6 +991,22 @@ async def _dispatch_prompt_to_model(
990
991
  ) -> tuple[bool, Optional[Path]]:
991
992
  """Handle session binding, acknowledgement, and watcher setup after pushing a prompt."""
992
993
 
994
+ if _is_menu_control_message(prompt):
995
+ worker_log.warning(
996
+ "Rejected menu control prompt before tmux dispatch",
997
+ extra={
998
+ "chat": chat_id,
999
+ **_session_extra(),
1000
+ "token": _normalize_choice_token(prompt),
1001
+ },
1002
+ )
1003
+ await _reply_to_chat(
1004
+ chat_id,
1005
+ "Skip/Cancel inputs are ignored outside interactive menus. Please send an actual prompt.",
1006
+ reply_to=reply_to,
1007
+ )
1008
+ return False, None
1009
+
993
1010
  prev_watcher = CHAT_WATCHERS.pop(chat_id, None)
994
1011
  if prev_watcher is not None:
995
1012
  if not prev_watcher.done():
@@ -1816,6 +1833,17 @@ async def _enqueue_media_group_message(message: Message, text_part: Optional[str
1816
1833
  async def _handle_prompt_dispatch(message: Message, prompt: str) -> None:
1817
1834
  """Wrapper routine that pushes prompts to the model."""
1818
1835
 
1836
+ if _is_menu_control_message(prompt):
1837
+ worker_log.info(
1838
+ "Dropped menu control prompt during dispatch",
1839
+ extra={
1840
+ **_session_extra(),
1841
+ "chat": getattr(message.chat, "id", None),
1842
+ "token": _normalize_choice_token(prompt),
1843
+ },
1844
+ )
1845
+ return
1846
+
1819
1847
  if ENV_ISSUES:
1820
1848
  message_text = _format_env_issue_message()
1821
1849
  worker_log.warning(
@@ -2299,6 +2327,12 @@ def _is_cancel_message(value: Optional[str]) -> bool:
2299
2327
  return lowered in cancel_tokens
2300
2328
 
2301
2329
 
2330
+ def _is_menu_control_message(value: Optional[str]) -> bool:
2331
+ """Return True when the payload represents a generic Skip/Cancel menu action."""
2332
+
2333
+ return _is_skip_message(value) or _is_cancel_message(value)
2334
+
2335
+
2302
2336
  _MARKDOWN_ESCAPE_RE = re.compile(r"([_*\[\]()~`>#+=|{}.!])")
2303
2337
  TASK_REFERENCE_PATTERN = re.compile(r"/?TASK[_]?\d{4,}")
2304
2338
 
@@ -2394,6 +2428,9 @@ MODEL_PUSH_SUPPLEMENT_STATUSES: set[str] = {
2394
2428
  "research",
2395
2429
  "test",
2396
2430
  }
2431
+ PUSH_MODEL_SUPPLEMENT_IN_PROGRESS_TEXT = (
2432
+ "A supplementary description prompt is already active. Please respond or tap Skip/Cancel."
2433
+ )
2397
2434
 
2398
2435
  SUMMARY_COMMAND_PREFIX = "/task_summary_request_"
2399
2436
  SUMMARY_COMMAND_ALIASES: tuple[str, ...] = (
@@ -6849,6 +6886,10 @@ async def on_task_push_model(callback: CallbackQuery, state: FSMContext) -> None
6849
6886
  await callback.answer("Callback parameter error.", show_alert=True)
6850
6887
  return
6851
6888
  _, _, task_id = parts
6889
+ current_state = await state.get_state()
6890
+ existing_context: Dict[str, Any] = {}
6891
+ if current_state == TaskPushStates.waiting_supplement.state:
6892
+ existing_context = await state.get_data()
6852
6893
  task = await TASK_SERVICE.get_task(task_id)
6853
6894
  if task is None:
6854
6895
  await callback.answer("Task does not exist", show_alert=True)
@@ -6859,10 +6900,20 @@ async def on_task_push_model(callback: CallbackQuery, state: FSMContext) -> None
6859
6900
  actor = _actor_from_callback(callback)
6860
6901
  chat_id = callback.message.chat.id if callback.message else callback.from_user.id
6861
6902
  if task.status in MODEL_PUSH_SUPPLEMENT_STATUSES:
6903
+ origin_message = callback.message
6904
+ origin_message_id = origin_message.message_id if origin_message else None
6905
+ if (
6906
+ current_state == TaskPushStates.waiting_supplement.state
6907
+ and existing_context.get("task_id") == task_id
6908
+ and existing_context.get("origin_message_id") == origin_message_id
6909
+ ):
6910
+ await callback.answer(PUSH_MODEL_SUPPLEMENT_IN_PROGRESS_TEXT)
6911
+ return
6862
6912
  await state.clear()
6863
6913
  await state.update_data(
6864
6914
  task_id=task_id,
6865
- origin_message=callback.message,
6915
+ origin_message=origin_message,
6916
+ origin_message_id=origin_message_id,
6866
6917
  chat_id=chat_id,
6867
6918
  actor=actor,
6868
6919
  )
@@ -6962,16 +7013,31 @@ async def on_task_push_model_fill(callback: CallbackQuery, state: FSMContext) ->
6962
7013
  await callback.answer("Callback parameter error.", show_alert=True)
6963
7014
  return
6964
7015
  _, _, task_id = parts
7016
+ current_state = await state.get_state()
7017
+ existing_context: Dict[str, Any] = {}
7018
+ if current_state == TaskPushStates.waiting_supplement.state:
7019
+ existing_context = await state.get_data()
6965
7020
  task = await TASK_SERVICE.get_task(task_id)
6966
7021
  if task is None:
6967
7022
  await state.clear()
6968
7023
  await callback.answer("Task does not exist", show_alert=True)
6969
7024
  return
6970
7025
  actor = _actor_from_callback(callback)
7026
+ origin_message = callback.message
7027
+ origin_message_id = origin_message.message_id if origin_message else None
7028
+ if (
7029
+ current_state == TaskPushStates.waiting_supplement.state
7030
+ and existing_context.get("task_id") == task_id
7031
+ and existing_context.get("origin_message_id") == origin_message_id
7032
+ ):
7033
+ await callback.answer(PUSH_MODEL_SUPPLEMENT_IN_PROGRESS_TEXT)
7034
+ return
7035
+ await state.clear()
6971
7036
  await state.update_data(
6972
7037
  task_id=task_id,
6973
- origin_message=callback.message,
6974
- chat_id=callback.message.chat.id if callback.message else callback.from_user.id,
7038
+ origin_message=origin_message,
7039
+ origin_message_id=origin_message_id,
7040
+ chat_id=origin_message.chat.id if origin_message else callback.from_user.id,
6975
7041
  actor=actor,
6976
7042
  )
6977
7043
  await state.set_state(TaskPushStates.waiting_supplement)
@@ -6989,18 +7055,18 @@ async def on_task_push_model_supplement(message: Message, state: FSMContext) ->
6989
7055
  if resolved == "Cancel" or trimmed == "Cancel":
6990
7056
  await state.clear()
6991
7057
  await message.answer("Push to the model cancelled.", reply_markup=_build_worker_main_keyboard())
6992
- return
7058
+ raise SkipHandler()
6993
7059
  data = await state.get_data()
6994
7060
  task_id = data.get("task_id")
6995
7061
  if not task_id:
6996
7062
  await state.clear()
6997
7063
  await message.answer("The push session has expired, please click the button again.", reply_markup=_build_worker_main_keyboard())
6998
- return
7064
+ raise SkipHandler()
6999
7065
  task = await TASK_SERVICE.get_task(task_id)
7000
7066
  if task is None:
7001
7067
  await state.clear()
7002
7068
  await message.answer("Task not found. Push cancelled.", reply_markup=_build_worker_main_keyboard())
7003
- return
7069
+ raise SkipHandler()
7004
7070
  supplement: Optional[str] = None
7005
7071
  if trimmed and resolved != SKIP_TEXT:
7006
7072
  if len(trimmed) > DESCRIPTION_MAX_LENGTH:
@@ -7008,7 +7074,7 @@ async def on_task_push_model_supplement(message: Message, state: FSMContext) ->
7008
7074
  f"Supplementary task description cannot exceed {DESCRIPTION_MAX_LENGTH} characters. Please re-enter:",
7009
7075
  reply_markup=_build_description_keyboard(),
7010
7076
  )
7011
- return
7077
+ raise SkipHandler()
7012
7078
  supplement = raw_text.strip()
7013
7079
  chat_id = data.get("chat_id") or message.chat.id
7014
7080
  origin_message = data.get("origin_message")
@@ -7029,11 +7095,11 @@ async def on_task_push_model_supplement(message: Message, state: FSMContext) ->
7029
7095
  extra={"task_id": task_id, "status": task.status if task else None},
7030
7096
  )
7031
7097
  await message.answer("Push failed: missing template configuration.", reply_markup=_build_worker_main_keyboard())
7032
- return
7098
+ raise SkipHandler()
7033
7099
  await state.clear()
7034
7100
  if not success:
7035
7101
  await message.answer("Push failed: model is not ready. Please retry shortly.", reply_markup=_build_worker_main_keyboard())
7036
- return
7102
+ raise SkipHandler()
7037
7103
  preview_block, preview_parse_mode = _wrap_text_in_code_block(prompt)
7038
7104
  await _reply_to_chat(
7039
7105
  chat_id,
@@ -7044,6 +7110,7 @@ async def on_task_push_model_supplement(message: Message, state: FSMContext) ->
7044
7110
  )
7045
7111
  if session_path is not None:
7046
7112
  await _send_session_ack(chat_id, session_path, reply_to=origin_message)
7113
+ raise SkipHandler()
7047
7114
 
7048
7115
 
7049
7116
  @router.callback_query(F.data.startswith("task:history:"))
@@ -8141,10 +8208,14 @@ async def on_start(m: Message):
8141
8208
  await m.answer(_format_env_issue_message())
8142
8209
 
8143
8210
  @router.message(F.text)
8144
- async def on_text(m: Message):
8211
+ async def on_text(m: Message, state: FSMContext | None = None):
8145
8212
  # Automatically record chat when first received message_id to state document
8146
8213
  _auto_record_chat_id(m.chat.id)
8147
8214
 
8215
+ current_state: Optional[str] = None
8216
+ if state is not None:
8217
+ current_state = await state.get_state()
8218
+
8148
8219
  prompt = (m.text or "").strip()
8149
8220
  if not prompt:
8150
8221
  return await m.answer("Please enter a non-empty prompt word")
@@ -8154,6 +8225,22 @@ async def on_text(m: Message):
8154
8225
  return
8155
8226
  if prompt.startswith("/"):
8156
8227
  return
8228
+ if current_state:
8229
+ worker_log.debug(
8230
+ "Suppressed model dispatch due to active wizard state",
8231
+ extra={**_session_extra(), "chat": m.chat.id, "state": current_state},
8232
+ )
8233
+ return
8234
+ if _is_menu_control_message(prompt):
8235
+ worker_log.info(
8236
+ "Suppressed stray menu control input without active wizard",
8237
+ extra={
8238
+ **_session_extra(),
8239
+ "chat": getattr(m.chat, "id", None),
8240
+ "token": _normalize_choice_token(prompt),
8241
+ },
8242
+ )
8243
+ return
8157
8244
  await _handle_prompt_dispatch(m, prompt)
8158
8245
 
8159
8246
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibego
3
- Version: 1.0.10
3
+ Version: 1.0.13
4
4
  Summary: vibego CLI: tools for bootstrapping and managing the Telegram Master Bot
5
5
  Author: DavidChan
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,4 +1,4 @@
1
- bot.py,sha256=bQ-tadaS-CX9699JhO6wtgBeAiXZsbb21GDRAusXJiQ,300928
1
+ bot.py,sha256=CYafcUAWUSm2jiYLQR0B41jeacMcIuIhKyBqU33o7OE,304395
2
2
  logging_setup.py,sha256=ViXK11TNVakIbA8QMLKXa6ywpAV_j1ydcws0Hx2QqNo,5084
3
3
  master.py,sha256=bpcqGsUpk8kHW8sqD6QG6ss-5LGO1OsQ7zsdvkgDJ3E,132763
4
4
  project_repository.py,sha256=fdO3xzvt7eBLvek_3mXjXkFpALwllrNWDWeSjtscW2I,17675
@@ -427,15 +427,15 @@ tasks/constants.py,sha256=BNMxSnswF-PTij-p7paMK0G5Tj6wKN-6qpUlMwo1JQA,318
427
427
  tasks/fsm.py,sha256=6n0hdFNF3j6ZUcpdJ_TwZBgkrE8aMYHNLHppdGbXGO4,1494
428
428
  tasks/models.py,sha256=3OJL3F3nVZIQIejL6LplZkPQxYJXgOhCNA9Rikg8ihk,2461
429
429
  tasks/service.py,sha256=rzd2v3kZaRpTi54cEAg2GJbeg45VTufC8bGSL1nGJWc,40115
430
- vibego-1.0.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
431
- vibego_cli/__init__.py,sha256=FPYqYQIBrm5aP_ujpHbtBhSFUsvBDlKfE0fD-RdZFAo,350
430
+ vibego-1.0.13.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
431
+ vibego_cli/__init__.py,sha256=kDTL5x8W9WpwZMorsUDzSw8PmcBEXF5_gcQsTfAPkJM,350
432
432
  vibego_cli/__main__.py,sha256=lM_m-i1x3yVNQhzRt8wqvuIYeeo1OlLNAEVoJVD7tmw,155
433
433
  vibego_cli/config.py,sha256=W-n5y4CTrRpencadWlmhpdrhUHX6tdXQEGXJG1pRM0U,3149
434
434
  vibego_cli/deps.py,sha256=nFT-DMsfAiD1lLFnDotxe6aYfeeD9V5TrIPbIbAMTF4,1478
435
435
  vibego_cli/main.py,sha256=CZAUHWr45GzzEfALoLuSrnvG2VUuCexiFuCpIvyH8Jc,12506
436
436
  vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
437
- vibego-1.0.10.dist-info/METADATA,sha256=gnkCSgLPXn8SwqS3DpNp4G8rIdrXxXZPvy3kRkVrHLI,11901
438
- vibego-1.0.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
439
- vibego-1.0.10.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
440
- vibego-1.0.10.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
441
- vibego-1.0.10.dist-info/RECORD,,
437
+ vibego-1.0.13.dist-info/METADATA,sha256=LxftGfu-gPYEuPCl4Yt5XgvSBM1USH7xDQsWSoab8-s,11901
438
+ vibego-1.0.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
439
+ vibego-1.0.13.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
440
+ vibego-1.0.13.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
441
+ vibego-1.0.13.dist-info/RECORD,,
vibego_cli/__init__.py CHANGED
@@ -8,6 +8,6 @@ from __future__ import annotations
8
8
 
9
9
  __all__ = ["main", "__version__"]
10
10
 
11
- __version__ = "1.0.10"
11
+ __version__ = "1.0.13"
12
12
 
13
13
  from .main import main # noqa: E402