Files
obsidian-vault/personal/projects/hermes/no-edit-segment-send-bug.md
T

2.8 KiB
Raw Blame History

tags
tags
hermes
bug
gateway
stream_consumer
fixed

No-edit (webhook) segment send bug

Дата: 20.06.2026 Файл: gateway/stream_consumer.py (lines 574, 581, 591) Статус: Исправлено, закоммичено

Симптом

При webhook-доставке (Zulip, Telegram через no-edit) текст, который модель генерирует между tool calls (перед tool call, после возврата результата), не отправлялся промежуточно. Весь текст накапливался в _accumulated и улетал единым куском только на got_done. Пользователь видел только tool calls и финальный ответ.

Root Cause

В stream_consumer.py в adapter_supports_edit=False ветке:

# Строка 574 — ДО (баг):
_has_nl = "\\\\n" in self._accumulated   # ищет буквальный \n (2 символа: \ + n)

# Строка 581 — ДО (баг):
_should_send = _has_nl and _can_send     # segment_break не отправляет без \n

# Строка 591 — ДО (баг):
_nl_pos = self._accumulated.rfind("\\\\n")  # ищет 2 символа вместо 1

"\\\\n" в Python-файле — это 4 слеша: Python видит \\n (строка из 2 символов: \ + n). А _accumulated содержит реальные символы newline (ASCII 10, один символ). Проверка всегда возвращала False, поэтому:

  • _has_nl = False_should_send = False (даже с got_segment_break=True)
  • Текст копился до got_done

Фикс

# Строка 574 — ПОСЛЕ:
_has_nl = "\\n" in self._accumulated   # Python escape для newline

# Строка 581 — ПОСЛЕ:
_should_send = _can_send               # segment break/got_done всегда отправляют

# Строка 591 — ПОСЛЕ:
_nl_pos = self._accumulated.rfind("\\n")  # Python escape для newline

Тесты

Новый файл: tests/gateway/test_stream_consumer_no_edit.py

Три теста:

  1. test_no_edit_sends_text_across_segment_breaks — текст с \n, 3 сегмента → 3 sends
  2. test_no_edit_sends_text_without_newline_on_segment_break — текст без \n, segment_break → отправляет
  3. test_no_edit_multiple_segments_are_separate_messages — 4 сегмента → 4 sends

Все 92 существующих теста stream_consumer проходят зелеными.

Верификация

После фикса тест с 3x вызова терминала + промежуточные фразы — все сообщения дошли до пользователя.