Skip to content

fix: enrich gateway WS stream relay + accepted/final ack - #3471

Merged
MervinPraison merged 2 commits into
mainfrom
claude/issue-3469-20260728-0914
Jul 29, 2026
Merged

fix: enrich gateway WS stream relay + accepted/final ack#3471
MervinPraison merged 2 commits into
mainfrom
claude/issue-3469-20260728-0914

Conversation

@praisonai-triage-agent

@praisonai-triage-agent praisonai-triage-agent Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #3469

Summary

Makes the gateway's live WS output richer and unambiguous, so a real-time UI can render live progress and reliably tell an accepted ack from the final result.

1. Structured event relay (was lossy)

_make_stream_relay previously forwarded only DELTA_TEXT/DELTA_TOOL_CALL/STREAM_END and dropped everything else. It now forwards a closed set of structured progress events under stable EventTypes:

  • reasoning/thinking deltas → REASONING_STREAM (a DELTA_TEXT with is_reasoning=True)
  • tool progress → TOOL_PROGRESS_STREAM
  • stream errors → STREAM_ERROR
  • (unchanged) text → TOKEN_STREAM, tool-call → TOOL_CALL_STREAM, end → STREAM_END

These are advertised in the hello_ok features list when the client negotiates streaming.

2. Accepted-vs-final acknowledgement (was overloaded)

The provisional ack ("Started processing."/"Message queued.") and the real answer both used type: "response". Now:

  • the provisional frame carries status: "accepted"
  • the final frame carries status: "final" plus a structured outcome ({"status": "ok"|"error"|"rejected"}) alongside the text.

Layer placement

  • Core (praisonaiagents/gateway/protocols.py): +3 EventType values (canonical vocabulary shared by all channel adapters).
  • Wrapper (praisonai_bot/gateway/server.py): relay mapping + frame emission (heavy behaviour).

Minimal and backward-compatible: existing type: "response"/token_stream consumers keep working; new fields are additive.

Testing

  • 388 gateway unit tests pass (-k gateway).
  • One pre-existing, unrelated collection error in tests/unit/config/test_precedence_ladder.py (missing resolve_output import) — not touched by this change.

Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added richer streaming updates for reasoning, tool progress, and stream errors.
    • WebSocket clients now receive an immediate confirmation when a message is accepted.
    • Final responses now clearly indicate completion status and include outcome details.
    • Streaming capabilities are advertised during connection setup.

@MervinPraison

Copy link
Copy Markdown
Owner

@coderabbitai review

@MervinPraison

Copy link
Copy Markdown
Owner

/review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@MervinPraison MervinPraison added pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:manual-review Blocked: requires manual review pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet pipeline/final-claude-pending Reviews done; waiting for FINAL @claude labels Jul 28, 2026
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fb4c748-9b77-4a9c-be6f-6ce4af82bbfa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The gateway adds reasoning, tool-progress, and stream-error event types, advertises them during streaming handshakes, distinguishes accepted and final responses, reports terminal outcomes, and expands agent stream event forwarding.

Changes

Gateway streaming contract

Layer / File(s) Summary
Streaming event contract and advertisement
src/praisonai-agents/.../gateway/protocols.py, src/praisonai-bot/.../gateway/server.py
Adds REASONING_STREAM, TOOL_PROGRESS_STREAM, and STREAM_ERROR, and advertises them for streaming sessions.
Accepted and final response lifecycle
src/praisonai-bot/.../gateway/server.py
Marks provisional responses as accepted and terminal responses as final with structured outcome status, including error outcomes.
Agent stream event relay
src/praisonai-bot/.../gateway/server.py
Relays reasoning text, token text, tool progress, and stream errors through corresponding gateway event types while dropping unmapped events.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebSocketClient
  participant GatewayServer
  participant AgentStream
  WebSocketClient->>GatewayServer: send message
  GatewayServer-->>WebSocketClient: response status accepted
  GatewayServer->>AgentStream: process agent message
  AgentStream-->>GatewayServer: streaming event
  GatewayServer-->>WebSocketClient: mapped stream event
  GatewayServer-->>WebSocketClient: response status final with outcome
Loading

Suggested reviewers: mervinpraison

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning It fixes accepted-vs-final responses and forwards reasoning/tool-progress/error streams, but the issue also required usage/cost and lifecycle events that aren't shown. Add the missing usage/cost and lifecycle stream mappings and confirm the gateway forwards the full required event set.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the gateway stream relay and accepted/final acknowledgement changes.
Out of Scope Changes check ✅ Passed The changes stay within the gateway streaming contract and core event vocabulary, with no unrelated features evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/issue-3469-20260728-0914

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/praisonai-bot/praisonai_bot/gateway/server.py`:
- Around line 3145-3177: The stream relay must persist every newly supported
frame type for replay, not only legacy token/tool/end events. Update
_send_to_client to include EventType.REASONING_STREAM,
EventType.TOOL_PROGRESS_STREAM, and EventType.STREAM_ERROR in its
persisted-frame tracking alongside the existing stream types, preserving current
replay behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b21ac72c-0852-4544-be8f-8e68b44c697f

📥 Commits

Reviewing files that changed from the base of the PR and between b43688a and 046871d.

📒 Files selected for processing (2)
  • src/praisonai-agents/praisonaiagents/gateway/protocols.py
  • src/praisonai-bot/praisonai_bot/gateway/server.py

Comment on lines 3145 to +3177
if event_type == StreamEventType.DELTA_TEXT:
gw_type = EventType.TOKEN_STREAM
# A reasoning/thinking delta is surfaced under its own event
# so clients can show "thinking…" separately from the answer.
if getattr(event, 'is_reasoning', False):
gw_type = EventType.REASONING_STREAM
else:
gw_type = EventType.TOKEN_STREAM
data = {
"content": getattr(event, 'content', ''),
"session_id": session.session_id,
"session_id": sid,
}
elif event_type == StreamEventType.DELTA_TOOL_CALL:
gw_type = EventType.TOOL_CALL_STREAM
data = {
"tool_call": getattr(event, 'tool_call', {}),
"session_id": session.session_id,
"session_id": sid,
}
elif event_type == StreamEventType.TOOL_PROGRESS:
gw_type = EventType.TOOL_PROGRESS_STREAM
data = {
"content": getattr(event, 'content', ''),
"metadata": getattr(event, 'metadata', None),
"session_id": sid,
}
elif event_type == StreamEventType.ERROR:
gw_type = EventType.STREAM_ERROR
data = {
"error": getattr(event, 'error', None),
"session_id": sid,
}
elif event_type == StreamEventType.STREAM_END:
gw_type = EventType.STREAM_END
data = {"session_id": session.session_id}
data = {"session_id": sid}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Persist the newly relayed stream frames for replay.

_send_to_client tracks only legacy stream types at Lines 3329-3336. Reconnecting clients therefore replay token/tool/end frames but lose reasoning, tool-progress, and stream-error frames.

Proposed fix
                 "token_stream",
                 "tool_call_stream",
+                "reasoning_stream",
+                "tool_progress_stream",
+                "stream_error",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if event_type == StreamEventType.DELTA_TEXT:
gw_type = EventType.TOKEN_STREAM
# A reasoning/thinking delta is surfaced under its own event
# so clients can show "thinking…" separately from the answer.
if getattr(event, 'is_reasoning', False):
gw_type = EventType.REASONING_STREAM
else:
gw_type = EventType.TOKEN_STREAM
data = {
"content": getattr(event, 'content', ''),
"session_id": session.session_id,
"session_id": sid,
}
elif event_type == StreamEventType.DELTA_TOOL_CALL:
gw_type = EventType.TOOL_CALL_STREAM
data = {
"tool_call": getattr(event, 'tool_call', {}),
"session_id": session.session_id,
"session_id": sid,
}
elif event_type == StreamEventType.TOOL_PROGRESS:
gw_type = EventType.TOOL_PROGRESS_STREAM
data = {
"content": getattr(event, 'content', ''),
"metadata": getattr(event, 'metadata', None),
"session_id": sid,
}
elif event_type == StreamEventType.ERROR:
gw_type = EventType.STREAM_ERROR
data = {
"error": getattr(event, 'error', None),
"session_id": sid,
}
elif event_type == StreamEventType.STREAM_END:
gw_type = EventType.STREAM_END
data = {"session_id": session.session_id}
data = {"session_id": sid}
"token_stream",
"tool_call_stream",
"reasoning_stream",
"tool_progress_stream",
"stream_error",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/praisonai-bot/praisonai_bot/gateway/server.py` around lines 3145 - 3177,
The stream relay must persist every newly supported frame type for replay, not
only legacy token/tool/end events. Update _send_to_client to include
EventType.REASONING_STREAM, EventType.TOOL_PROGRESS_STREAM, and
EventType.STREAM_ERROR in its persisted-frame tracking alongside the existing
stream types, preserving current replay behavior.

Comment thread src/praisonai-bot/praisonai_bot/gateway/server.py
Comment thread src/praisonai-bot/praisonai_bot/gateway/server.py
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR enriches WebSocket streaming and makes acknowledgement state explicit.

  • Adds canonical event types for reasoning, tool progress, and stream errors.
  • Advertises the additional stream events during capability negotiation.
  • Distinguishes accepted acknowledgements from final responses and adds structured outcomes.
  • Resolves missing agents terminally and drains scheduled stream sends before emitting the final frame.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/praisonai-agents/praisonaiagents/gateway/protocols.py Adds the canonical reasoning, tool-progress, and stream-error event vocabulary used by gateway adapters.
src/praisonai-bot/praisonai_bot/gateway/server.py Relays richer streaming events, emits explicit accepted/final response states, handles unavailable agents terminally, and orders final delivery after scheduled stream sends.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Gateway
  participant Agent
  Client->>Gateway: message
  Gateway-->>Client: response (status: accepted)
  Gateway->>Agent: execute turn
  loop Live progress
    Agent-->>Gateway: stream event
    Gateway-->>Client: token/reasoning/tool/error event
  end
  Gateway->>Gateway: drain scheduled stream sends
  Gateway-->>Client: response (status: final, outcome)
Loading

Reviews (3): Last reviewed commit: "fix: persist new stream frames, terminat..." | Re-trigger Greptile

@MervinPraison

Copy link
Copy Markdown
Owner

@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions
  4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep
  5. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params.
  6. Repo routing: agent-callable tools → PraisonAI-Tools; lifecycle plugins → PraisonAI-Plugins; optional sandbox backends → PraisonAI-Plugins (praisonai.sandbox entry point) — request changes if wrongly added to praisonaiagents/

Phase 2: FIX Valid Issues
7. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
8. Also independently identify and fix any gaps or issues you find in the changed code — do not rely only on prior reviewer feedback
9. Push all code fixes directly to THIS branch (do NOT create a new PR)
10. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
11. If all issues are resolved, approve the PR / close the Issue
12. If blocking issues remain, request changes / leave clear action items

@MervinPraison MervinPraison added pipeline/awaiting-merge-gate FINAL done; waiting for merge gate / CI pipeline/blocked:cooldown Blocked: post-push or @claude cooldown and removed pipeline/final-claude-pending Reviews done; waiting for FINAL @claude pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet labels Jul 28, 2026
@praisonai-triage-agent

praisonai-triage-agent Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Claude finished @MervinPraison's task —— View job


Final Architecture Review — PR #3471

  • Read AGENTS.md + guidelines
  • Reviewed changed files (protocols.py, server.py)
  • Evaluated reviewer feedback (CodeRabbit, Greptile)
  • Fixed valid issues
  • Tested (625 gateway tests pass)
  • Final verdict

Phase 1 — Architecture (per AGENTS.md)

Check Verdict
Protocol-driven layering ✅ Correct. The 3 new EventTypes (REASONING_STREAM, TOOL_PROGRESS_STREAM, STREAM_ERROR) live in core (praisonaiagents/gateway/protocols.py) as shared vocabulary; the heavy relay/frame behaviour lives in the wrapper (praisonai_bot/gateway/server.py).
Backward compatible ✅ Additive only. Existing type:"response" / token_stream consumers keep working; new status/outcome fields and event types are extra.
Performance ✅ No hot-path regression. Relay maps a closed set and drops the rest; future-draining is bounded to the small per-turn list.
SDK value ✅ Genuine value — unambiguous accepted-vs-final acks + structured progress for real-time UIs, with no new Agent params or module bloat.
Agent class bloat ✅ None — no changes to agent.py.
Repo routing ✅ Correct — gateway streaming contract, no tools/plugins misplacement.

Phase 2 — Fixes applied (pushed to this branch)

src/praisonai-bot/praisonai_bot/gateway/server.py

  1. Persist new stream frames for replay (CodeRabbit — valid). _send_to_client only tracked token_stream/tool_call_stream, so reconnecting clients lost reasoning/tool-progress/error frames. Added reasoning_stream, tool_progress_stream, stream_error to the persisted set.
  2. Missing agent falsely accepted (Greptile P1 — valid). An absent agent never enqueues a turn, yet the handler still sent status:"accepted", leaving the client waiting forever. It now resolves terminally with status:"final" + outcome:{status:"error"} and returns early.
  3. final overtaking trailing stream events (Greptile P1 — valid). The relay schedules cross-thread sends via run_coroutine_threadsafe while the final frame is directly awaited — a tail race. _make_stream_relay now records its send futures; the queue processor drains them (asyncio.gather) before emitting final, guaranteeing ordering.

Skipped (with reason)

  • CodeRabbit pre-merge "usage/cost + lifecycle events" gap → out of scope for this PR; adding unrequested event mappings would be scope creep against AGENTS.md's lightweight mandate. The accepted/final + reasoning/tool/error set is a complete, coherent slice.
  • tests/unit/config/test_precedence_ladder.py collection error and the itsdangerous/starlette integration import errors → pre-existing / env-only, unrelated to this change.

Testing

  • -k gateway: 625 passed, 3 skipped (5 errors are pre-existing missing-dep integration tests — itsdangerous, unrelated).
  • -k "relay or stream or accepted or final or response": 26 passed.
  • Both files syntax-verified.

Phase 3 — Verdict

Approve. All valid reviewer findings are fixed; change is minimal, protocol-driven, backward-compatible, and adds clear SDK value. No blocking issues remain.

Commit: a69c5db55
· Branch: claude/issue-3469-20260728-0914

@MervinPraison MervinPraison added pipeline/blocked:stale-final Blocked: FINAL stale after new commits and removed pipeline/blocked:stale-final Blocked: FINAL stale after new commits labels Jul 28, 2026
@praisonai-triage-agent praisonai-triage-agent Bot removed pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:cooldown Blocked: post-push or @claude cooldown labels Jul 28, 2026
@MervinPraison

Copy link
Copy Markdown
Owner

@claude this PR has merge conflicts with main. Please rebase onto latest main, resolve conflicts (keep this PR's intent, merge in newer main logic), run targeted tests, and force-push with --force-with-lease. Comment which files you resolved. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params. Keep the resolution minimal — the aim is a lightweight and powerful package; do not introduce scope creep while resolving conflicts.

@praisonai-triage-agent

praisonai-triage-agent Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Claude finished @MervinPraison's task —— View job


Rebase PR #3471 onto latest main — Done

  • Setup git identity & auth
  • Fetch main & start rebase
  • Resolve conflicts (keep PR intent + newer main logic)
  • Run targeted tests
  • Force-push with --force-with-lease
  • Comment files resolved

Files resolved

src/praisonai-bot/praisonai_bot/gateway/server.py — one conflict in _run_session_queue.

Resolution: kept both, since both are consumed downstream — controller/timeout by _drive_turn(...), and outcome_status by the terminal {"status": "final", "outcome": {...}} frame (also set to "rejected"/"error" on the admission/exception paths). No feature dropped from either side.

protocols.py applied cleanly (no conflict).

Verification

  • git rebase origin/main: both PR commits reapplied on top of ec1dfb36a.
  • py_compile clean on server.py.
  • Targeted tests: 509 passed, 1 skipped (tests/unit/gateway/), plus 16 passed on test_critical_session_fixes.py + test_gateway_session.py (directly exercise the changed relay/response/queue paths).

Skipped (env-only, pre-existing — unrelated to this change)

  • test_pairing_routes.py requires httpx2 (starlette TestClient) — not installed in this runner.

Force-pushed to claude/issue-3469-20260728-0914 with --force-with-lease. No Agent-class params added; resolution kept minimal.
· Branch: claude/issue-3469-20260728-0914

praisonai-triage-agent Bot and others added 2 commits July 29, 2026 10:41
Forward a closed set of structured progress events (reasoning, tool
progress, stream errors) to WS clients instead of dropping everything
but text/tool-call/end, and disambiguate the overloaded response frame
with status: "accepted" (provisional) vs status: "final" (with a
structured outcome).

Co-authored-by: MervinPraison <454862+MervinPraison@users.noreply.github.com>
…final after stream

- Persist reasoning/tool-progress/stream-error frames for reconnect replay (CodeRabbit)
- Missing agent now resolves terminally (final+error) instead of a dangling accepted ack (Greptile P1)
- Drain relay futures before final frame so final never overtakes trailing stream events (Greptile P1)

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
@MervinPraison MervinPraison added pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:cooldown Blocked: post-push or @claude cooldown labels Jul 29, 2026
@praisonai-triage-agent praisonai-triage-agent Bot removed pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:cooldown Blocked: post-push or @claude cooldown labels Jul 29, 2026
@MervinPraison
MervinPraison merged commit 77185b2 into main Jul 29, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pipeline/awaiting-merge-gate FINAL done; waiting for merge gate / CI pipeline/blocked:manual-review Blocked: requires manual review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gateway streaming to WS clients is lossy and the response frame is overloaded (no accepted-vs-final acknowledgement)

1 participant