Skip to content

fix(gateway): suppress stream consumer on API failure and cap fallback chunks to prevent Telegram flood - #67887

Open
krzysu wants to merge 2 commits into
NousResearch:mainfrom
krzysu:fix/gateway-stream-fallback-flood-guard
Open

fix(gateway): suppress stream consumer on API failure and cap fallback chunks to prevent Telegram flood#67887
krzysu wants to merge 2 commits into
NousResearch:mainfrom
krzysu:fix/gateway-stream-fallback-flood-guard

Conversation

@krzysu

@krzysu krzysu commented Jul 20, 2026

Copy link
Copy Markdown

What does this PR do?

When the model API is down (rate-limit / 429 / connection drop), the GatewayStreamConsumer flushes its accumulated streaming buffer to Telegram as individual messages — which can be ~100 messages containing the full system prompt / skill context. This PR introduces an API-failure guard that suppresses that flood.

API-failure detection: agent.conversation_loop records agent.api_failed_summary on failure (reset to None at every turn start to prevent stale errors from suppressing a valid next turn). The gateway wires this into the stream consumer via api_error_fn. When the API failed at final flush, the consumer suppresses the accumulated buffer and delivers one clean error message instead.

The previous revision of this PR also added an unconditional _MAX_FALLBACK_CHUNKS cap that replaced any large legitimate reply with an error. Per review feedback (hermes-sweeper), that cap was dropped: a successful long reply must still be delivered in full. The API-failure-specific guard above is the correct, narrow fix for the Telegram flood symptom.

Related Issue

N/A — reported by multiple Telegram users in community channels.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • agent/agent_init.py: Initialize agent.api_failed_summary = None (line ~679).
  • agent/conversation_loop.py: Set agent.api_failed_summary at both terminal-error exit points; reset it to None at every turn start.
  • gateway/stream_consumer.py: Add api_error_fn param, _send_api_error_final helper, api-error guard at final flush and at mid-stream overflow split.
  • gateway/run.py: Wire api_error_fn lambda into GatewayStreamConsumer constructor, reading the live agent.api_failed_summary lazily so a summary recorded between the last on_delta and final flush is honored.
  • tests/gateway/test_stream_consumer_flood_guard.py: New test file with 6 tests across 2 classes:
    • TestApiErrorSuppression — direct consumer test: API failure suppresses buffer; no-error case still delivers.
    • TestApiErrorEndToEndWiring — exercises the real lambda shape from gateway/run.py (lazy getattr on the live agent_holder[0]) so a regression in either the agent-state lifecycle or the gateway wiring surfaces here. Covers: terminal API failure at final flush, late failure recorded after the last delta, no-failure case, and the agent_holder[0] is None defensive path.

How to Test

  1. Reproduction (before the fix): Trigger a Telegram bot session while the model API returns 429/rate-limit. Observe ~100 messages dumped containing the full system prompt / skill context.

  2. After the fix: Repeat step 1. Confirm:

    • A single clean error message appears: ⚠️ Model API error — no response was generated. <summary>
    • No raw buffer (system prompt / skill context) is sent
    • Normal (non-failure) long replies still deliver in full
  3. Regression: Run ./venv/bin/python -m pytest tests/gateway/test_stream_consumer*.py -q. All 214 tests pass (208 existing + 6 new).

Checklist

Code

  • I have read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(gateway): …)
  • I searched for existing PRs to make sure this is not a duplicate
  • My PR contains only changes related to this fix
  • I have run ./venv/bin/python -m pytest tests/gateway/test_stream_consumer*.py -q and all 214 tests pass
  • I have added tests for my changes — 6 new tests in TestApiErrorSuppression and TestApiErrorEndToEndWiring
  • I have tested on my platform: macOS 26.5.1 arm64

Documentation & Housekeeping

  • I have updated relevant documentation (README, docs/, docstrings) — or N/A (no public API change; behavior described in docstrings and inline comments)
  • I have updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I have updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I have considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A (the fix uses Python builtins only; fully cross-platform)

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 20, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to the stream-consumer flood-control family (#43761, #62253, #55869), but this patch specifically suppresses an accumulated buffer after model-API failure and caps oversized fallback chunking.

@x7peeps

x7peeps commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Closing as duplicate

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for isolating the accumulated-stream failure path and adding focused coverage.

Problems

  • The new fallback cap discards valid output whenever a normal fallback response needs more than ten messages. Current main deliberately computes and sends every fallback chunk in gateway/stream_consumer.py:1375-1458; the proposed if len(chunks) > _MAX_FALLBACK_CHUNKS branch would replace a legitimate long answer with an error. The API-failure guard can suppress incomplete failed output without imposing that loss on successful large replies.
  • The added tests inject api_error_fn directly into the consumer, so they do not validate the new agent-state lifecycle or the gateway/run.py wiring. A regression through the terminal API-error path is needed.

Suggested changes

  • Drop the unconditional fallback chunk cap and retain the API-failure-specific guard.
  • Add an end-to-end gateway/agent failure test asserting one concise error and no accumulated buffer.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/streaming Streaming responses: gateway delivery, provider wire labels Jul 30, 2026
@krzysu

krzysu commented Jul 30, 2026

Copy link
Copy Markdown
Author

Thanks for the review — both points addressed in 2a3015524.

Dropped the unconditional fallback chunk cap. _MAX_FALLBACK_CHUNKS and _send_fallback_too_large are gone. A successful long reply now flows through truncate_message / _send_fallback_final exactly as it did on main. The only flood guard remaining is the API-failure-specific one in _send_api_error_final, which is the narrow fix the symptom actually needs.

Added end-to-end coverage of the agent → gateway → consumer wiring. The old TestFallbackChunkCap is replaced by TestApiErrorEndToEndWiring, which builds the consumer with the real lambda shape from gateway/run.py (lazy getattr(agent_holder[0], "api_failed_summary", None)) and exercises:

  • Terminal API failure at final flush — buffer suppressed, one clean error.
  • Late api_failed_summary recorded between the last on_delta and run() — the consumer still sees it.
  • No-failure case — the guard does not fire, the real reply reaches the user.
  • agent_holder[0] is None — lambda returns None, no suppression.

A regression through the terminal API-error path (broken api_failed_summary lifecycle or broken gateway/run.py wiring) now fails one of these four tests rather than passing because a static api_error_fn was injected directly.

Test run: ./venv/bin/python -m pytest tests/gateway/test_stream_consumer*.py -q214 passed (208 existing + 6 new). PR description updated to reflect the narrowed scope.

@krzysu
krzysu force-pushed the fix/gateway-stream-fallback-flood-guard branch from 2a30155 to fe41fa5 Compare July 30, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/streaming Streaming responses: gateway delivery, provider wire comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants