150 lines
9.2 KiB
Markdown
150 lines
9.2 KiB
Markdown
# Phase 2 — Approval Reactions
|
||
|
||
## Баг 1: Сообщения дропаются при аппрувл-прерывании (подтверждён)
|
||
|
||
**Дата:** 2026-06-18
|
||
**Статус:** Исправлен — добавлен streaming через `display.platforms.webhook.streaming: true`
|
||
|
||
## Баг 2: `▉` курсор в стриминге для webhook/Zulip (активный)
|
||
|
||
**Дата:** 2026-06-18
|
||
**Статус:** Диагностируется
|
||
|
||
### Симптом
|
||
При стриминге через webhook (Zulip) — каждое partial сообщение содержит `▉` курсор в конце. Zulip не поддерживает редактирование сообщений, курсор добавляется и никогда не убирается.
|
||
|
||
### Почему
|
||
`GatewayStreamConsumer` добавляет курсор `▉` к каждому partial сообщению (`on_delta` → `_send_or_edit`). Когда `adapter_supports_edit == False` — курсор остаётся навсегда, потому что:
|
||
1. Partial шлётся с курсором (первый send)
|
||
2. Edit не поддерживается → нельзя перезаписать/убрать курсор
|
||
3. `_send_fallback_final` при `got_done` — должен убрать курсор, но если edit не поддерживается — не может
|
||
|
||
### Что сделано
|
||
|
||
**Конфиг (`~/.hermes/hermes-whale/config.yaml`):**
|
||
- Добавлено `display.platforms.webhook.streaming: true` — стриминг для webhook
|
||
- Добавлено `platforms.webhook.extra.supports_message_editing: false` — указание что webhook не поддерживает edit
|
||
|
||
**Код (`gateway/run.py`):**
|
||
- Добавлено чтение `platforms.{platform}.extra.supports_message_editing` из конфига (строка 16712-16718)
|
||
- Если в конфиге `supports_message_editing: false` — `_adapter_supports_edit = False` → `_effective_cursor = ""` → курсор не добавляется
|
||
|
||
**Текущий статус:** `supports_message_editing: false` в конфиге есть, но логи всё ещё показывают `adapter_edit=True`. Причина выясняется.
|
||
|
||
### Логи (актуальные)
|
||
```
|
||
[whale-cursor] add cursor: ... adapter_edit=True
|
||
[whale-send] send_or_edit allowed: ... adapter_edit=True
|
||
```
|
||
Все логи показывают `adapter_edit=True` — настройка не применяется.
|
||
|
||
### Версии
|
||
- PID 17593 (Кит, порт 8645)
|
||
- Gateway запущен `python -m hermes_cli.main gateway run --replace`
|
||
- Логи: `~/.hermes/hermes-whale/logs/gateway.log`
|
||
- Конфиг: `~/.hermes/hermes-whale/config.yaml`
|
||
|
||
### Симптом
|
||
Когда LLM пишет текст (через стриминг), потом делает tool call с опасной командой — аппрув-промпт прерывает стрим. Весь текст, который LLM уже сказала ДО опасной команды, **не доходит до юзера**. Юзер видит только аппрув-промпт и финальный ответ (если он есть).
|
||
|
||
### Почему
|
||
Причина в **GatewayStreamConsumer** (`gateway/stream_consumer.py`).
|
||
|
||
При `streaming.enabled: true` для webhook/Zulip:
|
||
1. LLM стримит текст → `on_delta()` кладёт в очередь
|
||
2. Стрим прерывается аппрувом (interrupt) → `got_done = False`, цикл `run()` выходит раньше времени
|
||
3. `_send_fallback_final()` не вызывается — потому что он только при `got_done` (строка 580)
|
||
4. Накопленный `_accumulated` текст **теряется**
|
||
|
||
Дополнительно: `edit_supported=False` для Zulip/Webhook — стример не может редактировать, fallback mode активируется, но не успевает отработать.
|
||
|
||
### Логи из теста
|
||
```
|
||
[KIT-stream2] path=webhook streaming_enabled=True want_deltas=N/A _scfg.enabled=True transport=auto
|
||
[consumer] on_delta: 1 chars, queue=1
|
||
[send_or_edit] enter: ... edit_supported=False fallback_final=True ... (10+ раз, зациклен)
|
||
— got_done NOT FOUND в логах —
|
||
```
|
||
|
||
### Что меняли для диагностики
|
||
|
||
**`gateway/stream_consumer.py`:**
|
||
- `logger.debug` → `logger.warning` в `_send_or_edit` (enter, cursor-only)
|
||
- `logger.debug` → `logger.warning` в `_send_fallback_final` (enter)
|
||
- Добавлен `warnings.warn` на уровне импорта (для проверки загрузки модуля)
|
||
|
||
**`gateway/run.py`:**
|
||
- `[KIT] GatewayRunner.__init__ called` — проверка что run.py подхватывается
|
||
- `[KIT-stream] entering streaming setup` — первый path (не для webhook)
|
||
- `[KIT-stream2] path=webhook streaming_enabled=...` — второй path (для webhook)
|
||
|
||
**`/Users/admin/.hermes/hermes-whale/config.yaml`:**
|
||
- Добавлено `display.platforms.webhook.streaming: true` — чтобы стриминг работал для вебхука (был `_TIER_MINIMAL` default)
|
||
|
||
**`gateway/__pycache__/stream_consumer.cpython-311.pyc`:**
|
||
- Был stale от June 4 — не влияет на проблему (редактируемый install работает)
|
||
|
||
### Как чинить
|
||
Нужно чтобы при interrupt (аппрув-прерывание) стример **отправлял накопленный текст** перед выходом. Варианты:
|
||
|
||
1. **В `run.py` после прерывания стрима** — вызвать `_send_fallback_final(_accumulated)` если есть что отправлять
|
||
2. **В `stream_consumer.py` на interrupt path** — форсировать `got_done` или прямой вызов `_send_fallback_final`
|
||
3. **Не прерывать стрим при аппруве** — пусть стриминг продолжается до естественного завершения (`got_done`)
|
||
|
||
### История расследования
|
||
1. Сначала думали что проблема в `stream_consumer` с streaming enabled — но баг был ДО включения streaming
|
||
2. Выяснили что streaming был выключен для webhook из-за `_TIER_MINIMAL` в `display_config.py` → `streaming: False`
|
||
3. После включения `display.platforms.webhook.streaming: true` — стриминг пошёл
|
||
4. Тест с `rm -rf` подтвердил: текст до аппрува **не появляется** у юзера
|
||
5. Логи подтвердили: `got_done` не вызывается → `_send_fallback_final` не срабатывает
|
||
|
||
---
|
||
|
||
## Tool progress для webhook
|
||
|
||
**Статус:** Исправлено ✅
|
||
|
||
### Проблема
|
||
Tool progress (уведомления о tool calls) не работали для вебхука — сообщения о ходе выполнения тулов не доходили до юзера.
|
||
|
||
### Почему
|
||
Два гарда в `gateway/run.py`:
|
||
|
||
1. **Первый гард** (`run.py:16992`) — условие включения прогресса:
|
||
```python
|
||
tool_progress_enabled = progress_mode != "off" and (
|
||
source.platform != Platform.WEBHOOK
|
||
or cfg.display.platforms.get("webhook", {}).get("tool_progress", "off") != "off"
|
||
)
|
||
```
|
||
По умолчанию `display.platforms.webhook.tool_progress` не было в конфиге → `"off"` → прогресс выключен.
|
||
|
||
2. **Второй гард** (`run.py:17184-17191`) — проверка что у адаптера есть свой `edit_message`:
|
||
```python
|
||
if type(adapter).edit_message is BasePlatformAdapter.edit_message
|
||
and source.platform != Platform.WEBHOOK:
|
||
# очистить очередь и выйти
|
||
```
|
||
У `WebhookAdapter` нет своего `edit_message` (наследует базовый) → прогресс скипается даже если включён.
|
||
|
||
### Что сделано
|
||
|
||
1. **Конфиг** — добавлено `display.platforms.webhook.tool_progress: all`
|
||
2. **`run.py:16992`** — изменён первый гард: разрешает tool progress для вебхука если в конфиге не `"off"`
|
||
3. **`run.py:17215-17217`** — добавлен форс `can_edit=False` для `Platform.WEBHOOK`:
|
||
```python
|
||
if source.platform == Platform.WEBHOOK:
|
||
can_edit = False
|
||
```
|
||
Прогресс-сообщения для вебхука шлются новыми сообщениями через `adapter.send()`, не `edit_message()`.
|
||
|
||
### Коммиты
|
||
| Дата | Коммит | Описание |
|
||
|------|--------|----------|
|
||
| 2026-06-18 | `714026a6e` | Исправлен первый гард — разрешён tool progress для webhook |
|
||
| 2026-06-18 | (config) | Добавлен `display.platforms.webhook.tool_progress: all` |
|
||
| 2026-06-18 | `a92840dcd` | force `can_edit=False` для webhook — прогресс через `send()`, не `edit_message()` |
|
||
|
||
### Проверка
|
||
Tool progress ходит через вебхук — юзер видит уведомления о tool calls (подтверждено).
|