t-261010092931: typing «печатает…» весь тик LLM — без привязки к pending-инбоксу; чаты из всего TG-инбокса, фолбэк на чат харнеса; тест: всё seen/пустой инбокс/только портал/чат неизвестен (25 PASS)

This commit is contained in:
Simbiot Harness 2026-10-10 10:05:14 +00:00
parent 58cc3785c6
commit 57a0f97318
2 changed files with 77 additions and 20 deletions

View file

@ -481,19 +481,30 @@ def run_level_tick(level_cfg, seq: int, model: dict):
claimed = None claimed = None
prompt = build_context(level_cfg, model, batch) prompt = build_context(level_cfg, model, batch)
# Индикация «взял и работаю» (e-261002202857/73bbaf): пока LLM-тик гоняется, # Индикация «взял и работаю» (e-261002202857/73bbaf): пока LLM-тик гоняется,
# фоновый поток держит sendChatAction typing в TG-чатах инбокса (обновление # фоновый поток держит sendChatAction typing (обновление каждые ~2с) —
# каждые ~2с) — «печатает…» виден весь тик. Реакция 👀 на конкретные # «печатает…» виден весь тик. Реакция 👀 на конкретные входящие — поллером
# входящие — поллером при приёме (MessageSeen) и по требованию cli.py tg-seen. # при приёме (MessageSeen) и по требованию cli.py tg-seen.
# Останов потока — в finally в конце функции. # t-261010092931: раньше поток поднимался ТОЛЬКО при непросмотренном
# pending-инбоксе — с пустым/просмотренным инбоксом индикатора не было весь
# тик. Теперь «печатает…» горит весь тик LLM независимо от pending и от
# claimed-задач: чаты берём из всего инбокса модели (seen/replied не важны),
# а при пустом инбоксе — последний живой TG-чат харнеса (_default_chat_id,
# личка Alex). typing — только TG-чаты: портал-каналу и TUI typing API
# недоступен. Останов потока — в finally в конце функции.
from . import tg_inbox from . import tg_inbox
keepalive = None keepalive = None
keep_stop = threading.Event() keep_stop = threading.Event()
pending = [m for m in _pending_inbox(model) if not m.get("seen")] tg_chat_ids = list({m.get("chat_id") for m in (model.get("inbox") or {}).values()
if pending:
# typing — только TG-чаты (портал-каналу typing API недоступен)
tg_chat_ids = list({m.get("chat_id") for m in pending
if m.get("chat_id") if m.get("chat_id")
and not str(m.get("chat_id")).startswith("portal")}) and not str(m.get("chat_id")).startswith(("portal", "tui"))})
if not tg_chat_ids:
# пустой инбокс — показываем «печатает…» в чате харнеса, если он известен
try:
_dc = tg_inbox._default_chat_id()
except Exception:
_dc = None
if _dc is not None:
tg_chat_ids = [_dc]
if tg_chat_ids: if tg_chat_ids:
keepalive = threading.Thread( keepalive = threading.Thread(
target=tg_inbox.typing_keepalive, target=tg_inbox.typing_keepalive,

View file

@ -9,8 +9,9 @@
(нет ни реакции, ни второго события), TG-сбой — False без события; (нет ни реакции, ни второго события), TG-сбой — False без события;
- receive_message сохраняет message_id (нативный, не хэш-фолбэк); - receive_message сохраняет message_id (нативный, не хэш-фолбэк);
- typing_keepalive: держит sendChatAction typing и останавливается по Event; - typing_keepalive: держит sendChatAction typing и останавливается по Event;
- run_level_tick: поднимает keepalive по TG-чатам неотвеченного инбокса, - run_level_tick: поднимает keepalive по TG-чатам инбокса ВЕСЬ тик, без
исключает портал-чаты, гасит поток в finally (на любом исходе возврата). привязки к pending/seen (t-261010092931), исключает портал-чаты, при
пустом инбоксе — фолбэк на чат харнеса; гасит поток в finally (любой исход).
""" """
import json import json
import os import os
@ -173,7 +174,9 @@ try:
check("tick: worker упал → finally всё равно гасит keepalive", check("tick: worker упал → finally всё равно гасит keepalive",
ka_seen.get("stop") is not None and ka_seen.get("stop").is_set()) ka_seen.get("stop") is not None and ka_seen.get("stop").is_set())
# все сообщения уже seen → keepalive не поднимается вовсе # t-261010092931: всё seen → «печатает…» всё равно горит весь тик
# (индикатор больше не зависит от pending: Alex видит «печатает…»
# всё время, пока LLM-тик работает)
ka_seen.clear() ka_seen.clear()
model2 = dict(model) model2 = dict(model)
model2["inbox"] = { model2["inbox"] = {
@ -182,7 +185,50 @@ try:
} }
_ir_module.run_llm_tick = staticmethod(_FakeRunner.run_llm_tick) _ir_module.run_llm_tick = staticmethod(_FakeRunner.run_llm_tick)
run_level_tick(LEVELS["hour"], 4244, model2) run_level_tick(LEVELS["hour"], 4244, model2)
check("tick: всё seen → keepalive не поднимается", ka_seen == {}, str(ka_seen)) check("tick: всё seen → keepalive всё равно поднят по TG-чату",
ka_seen.get("ids") == [42], str(ka_seen))
check("tick: всё seen → finally погасил keepalive",
ka_seen.get("stop") is not None and ka_seen.get("stop").is_set())
# t-261010092931: пустой инбокс → фолбэк на последний живой TG-чат
# (_default_chat_id, личка Alex): индикатор горит и без входящих
ka_seen.clear()
model3 = dict(model)
model3["inbox"] = {}
_orig_default_chat = tg_inbox._default_chat_id
tg_inbox._default_chat_id = lambda: 42
try:
_ir_module.run_llm_tick = staticmethod(_FakeRunner.run_llm_tick)
run_level_tick(LEVELS["hour"], 4245, model3)
finally:
tg_inbox._default_chat_id = _orig_default_chat
check("tick: пустой инбокс → keepalive по чату харнеса (фолбэк)",
ka_seen.get("ids") == [42], str(ka_seen))
check("tick: пустой инбокс → finally погасил keepalive",
ka_seen.get("stop") is not None and ka_seen.get("stop").is_set())
# инбокс без TG-чатов (только портал/tui) → тоже фолбэк на чат харнеса
ka_seen.clear()
model4 = dict(model)
model4["inbox"] = {"m-2": model["inbox"]["m-2"]}
tg_inbox._default_chat_id = lambda: 42
try:
_ir_module.run_llm_tick = staticmethod(_FakeRunner.run_llm_tick)
run_level_tick(LEVELS["hour"], 4246, model4)
finally:
tg_inbox._default_chat_id = _orig_default_chat
check("tick: только портал-чаты → keepalive по фолбэку, портал исключён",
ka_seen.get("ids") == [42], str(ka_seen))
# фолбэк недоступен (нет известного чата) → keepalive не поднимается
ka_seen.clear()
tg_inbox._default_chat_id = lambda: None
try:
_ir_module.run_llm_tick = staticmethod(_FakeRunner.run_llm_tick)
run_level_tick(LEVELS["hour"], 4247, model3)
finally:
tg_inbox._default_chat_id = _orig_default_chat
check("tick: чат неизвестен → keepalive не поднимается", ka_seen == {}, str(ka_seen))
finally: finally:
scheduler.es = _orig_es scheduler.es = _orig_es
tg_inbox.typing_keepalive = _orig_tg_typing_backup tg_inbox.typing_keepalive = _orig_tg_typing_backup