Skip to content

feat: real-time inference progress events for web channel#514

Open
senamakel wants to merge 2 commits intotinyhumansai:mainfrom
senamakel:feat/streaming
Open

feat: real-time inference progress events for web channel#514
senamakel wants to merge 2 commits intotinyhumansai:mainfrom
senamakel:feat/streaming

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented Apr 12, 2026

Summary

  • Adds real-time progress events during the agent turn loop so the web channel (and app UI) can show live status updates instead of retroactive tool-call batches
  • Introduces AgentProgress enum with events: TurnStarted, IterationStarted, ToolCallStarted, ToolCallCompleted, SubagentSpawned, SubagentCompleted, SubagentFailed, TurnCompleted
  • Replaces the retroactive publish_tool_events_from_history() with a real-time spawn_progress_bridge() that maps progress events to WebChannelEvents over socket.io
  • Frontend now shows a live inference status indicator (thinking/tool use/sub-agent phases) and receives tool timeline updates in real-time

Changes

Rust core

  • New: src/openhuman/agent/progress.rsAgentProgress event enum
  • Modified: Agent struct gains on_progress: Option<mpsc::Sender<AgentProgress>> field + set_on_progress() setter
  • Modified: turn.rs — emits progress events at turn start, each iteration, tool call start/complete, and turn end
  • Modified: web.rs — creates per-request progress channel, spawns bridge task that maps AgentProgressWebChannelEvent, removed retroactive publish_tool_events_from_history()

New socket events

Event When
inference_start Agent turn begins
iteration_start Each LLM call iteration starts
tool_call Tool about to execute (now real-time)
tool_result Tool completed (now real-time)
subagent_spawned Sub-agent dispatched
subagent_completed / subagent_failed Sub-agent finished

Frontend

  • Modified: chatService.ts — new event types and subscribeChatEvents listeners for all new events
  • Modified: Conversations.tsxInferenceStatus state tracking, live pulsing indicator showing current phase, sub-agents in tool timeline

Test plan

  • Send a message via web channel and verify inference_start fires immediately
  • Verify tool_call events appear in real-time as tools execute (not batched after completion)
  • Verify tool_result events update the timeline status as each tool finishes
  • Verify the inference status indicator shows "Thinking..." during LLM calls
  • Verify the indicator switches to "Running <tool_name>..." during tool execution
  • Verify inference status clears on chat_done and chat_error
  • Verify cancellation still works correctly
  • cargo check passes
  • tsc --noEmit passes

Summary by CodeRabbit

Release Notes

New Features

  • Added real-time inference status indicator displaying agent reasoning progress, including iteration tracking and tool execution details
  • Implemented live visibility into sub-agent spawning and completion events during agent execution
  • Enhanced progress feedback with detailed execution metrics (tool names, iteration counts, elapsed time)

Improvements

  • Replaced delayed tool reporting with real-time event streaming for instant feedback during agent operations

- Added new event listeners for inference start, iteration start, subagent spawning, and completion to track the live state of chat interactions.
- Introduced an `InferenceStatus` interface to manage the current phase and active tools/subagents for each thread.
- Updated the UI to display inference status indicators, enhancing user experience during chat interactions.
- Created a new `progress` module in the Rust backend to emit real-time progress events, allowing for better integration with the web channel.
- Refactored the `subscribeChatEvents` function to include new event handlers for managing inference and subagent events, improving clarity and maintainability of the event handling logic.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

The PR introduces a real-time progress event streaming system for agent execution. It adds new event types to the chat service, instruments the Rust agent turn loop to emit lifecycle events (inference start, iteration, tool execution, subagent lifecycle) via an mpsc channel, bridges these events to web socket emissions, and updates the frontend to handle and display these new events through a new inference status UI component.

Changes

Cohort / File(s) Summary
Frontend Chat Event Infrastructure
app/src/services/chatService.ts
Added new event payload types (ChatInferenceStartEvent, ChatIterationStartEvent, ChatSubagentSpawnedEvent, ChatSubagentDoneEvent) and extended ChatEventListeners interface with corresponding optional callbacks. Updated subscribeChatEvents to register Socket.IO listeners for snake_case event names and include cleanup handlers.
Frontend Inference Status UI
app/src/pages/Conversations.tsx
Added inferenceStatusByThread state map and new inference status indicator UI block. Implemented typed inference lifecycle callbacks (onInferenceStart, onIterationStart, onSubagentSpawned, onSubagentDone) and updated existing tool callbacks to drive inference UI state with phase transitions (thinking, tool_use, subagent).
Rust Agent Progress Module
src/openhuman/agent/progress.rs, src/openhuman/agent/mod.rs
Created new progress.rs module defining AgentProgress enum with variants for turn lifecycle, iterations, tool execution, and subagent events. Added public module export in mod.rs.
Rust Agent Type System
src/openhuman/agent/harness/session/types.rs
Added on_progress field to Agent struct as pub(super) Option<tokio::sync::mpsc::Sender<AgentProgress>> to enable optional progress event emission.
Rust Agent Runtime
src/openhuman/agent/harness/session/runtime.rs, src/openhuman/agent/harness/session/builder.rs
Added public set_on_progress() method to Agent for configuring the progress channel. Updated AgentBuilder::build to explicitly set on_progress: None during construction.
Rust Agent Execution Instrumentation
src/openhuman/agent/harness/session/turn.rs
Instrumented turn lifecycle to emit progress events (TurnStarted, IterationStarted, TurnCompleted). Updated execute_tool_call and execute_tools signatures to accept iteration: usize parameter. Emit ToolCallStarted/ToolCallCompleted with tool metadata, success flag, output length, and elapsed time for each tool execution.
Real-Time Progress Bridging
src/openhuman/channels/providers/web.rs
Replaced retroactive history-based tool event publishing with real-time progress pipeline. Now creates mpsc channel in run_chat_task, registers via agent.set_on_progress(), and spawns spawn_progress_bridge to map AgentProgress events to WebChannelEvents (inference_start, iteration_start, tool_call, tool_result, subagent_spawned, subagent_completed, subagent_failed, turn_completed) with updated payload shapes.

Sequence Diagram

sequenceDiagram
    participant Agent as Agent Turn Loop
    participant ProgressTx as Progress<br/>Channel (Tx)
    participant Bridge as Progress Bridge
    participant WebEvent as Web Event Publish
    participant Socket as WebSocket
    participant Service as Chat Service
    participant UI as Conversations UI

    rect rgba(100, 150, 200, 0.5)
        Note over Agent,UI: Real-Time Progress Streaming Flow
    end

    Agent->>ProgressTx: emit_progress(TurnStarted)
    Agent->>ProgressTx: emit_progress(IterationStarted)
    Agent->>ProgressTx: emit_progress(ToolCallStarted)
    ProgressTx->>Bridge: receive AgentProgress
    Bridge->>WebEvent: map to inference_start / iteration_start
    WebEvent->>Socket: emit WebChannelEvent
    Socket->>Service: subscribeChatEvents listener receives
    Service->>UI: onInferenceStart / onIterationStart callback
    UI->>UI: update inferenceStatusByThread<br/>set phase = "thinking"

    Agent->>ProgressTx: emit_progress(ToolCallCompleted)
    ProgressTx->>Bridge: receive AgentProgress
    Bridge->>WebEvent: map to tool_call event
    WebEvent->>Socket: emit WebChannelEvent
    Socket->>Service: subscribeChatEvents listener receives
    Service->>UI: onToolCall callback
    UI->>UI: update inferenceStatusByThread<br/>set phase = "tool_use"<br/>record activeTool

    Agent->>ProgressTx: emit_progress(SubagentSpawned)
    ProgressTx->>Bridge: receive AgentProgress
    Bridge->>WebEvent: map to subagent_spawned event
    WebEvent->>Socket: emit WebChannelEvent
    Socket->>Service: subscribeChatEvents listener receives
    Service->>UI: onSubagentSpawned callback
    UI->>UI: update phase = "subagent"<br/>add to tool timeline

    Agent->>ProgressTx: emit_progress(TurnCompleted)
    ProgressTx->>Bridge: receive AgentProgress
    Bridge->>WebEvent: map to turn_completed event
    WebEvent->>Socket: emit WebChannelEvent
    Socket->>Service: subscribeChatEvents listener receives
    Service->>UI: onDone callback
    UI->>UI: clear inferenceStatusByThread
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

Poem

🐰 Progress flows like clover streams, real-time dreams,
From agent turns to UI beams, iteration by iteration gleams,
Tools and subagents dance with glee, events all free,
A rabbit's joy—at last we see! 🌱✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding real-time inference progress events for the web channel, which is the central focus of this PR across both backend and frontend.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/streaming

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/openhuman/channels/providers/web.rs (1)

489-500: ⚠️ Potential issue | 🟡 Minor

Remove the unused parse_tool_args function and its test.

The function is only called in tests and has no production usage. With the removal of history-based tool event publishing, this function is now dead code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/channels/providers/web.rs` around lines 489 - 500, Remove the
dead helper by deleting the parse_tool_args function (fn
parse_tool_args(arguments: &str) -> Value) from
src/openhuman/channels/providers/web.rs and also remove the associated unit
test(s) that only reference this helper; search for references to
parse_tool_args in the repo and ensure any test file (or #[cfg(test)] block)
that solely exists to exercise this function is removed or refactored to no
longer call it, then run cargo test to confirm no remaining references.
app/src/services/chatService.ts (1)

143-186: ⚠️ Potential issue | 🟠 Major

Add trace logs for the new progress-event handlers.

Line 143–Line 186 introduces the new real-time inference/subagent flow, but there are no namespaced debug checkpoints when payloads are received and forwarded. Please add debug logs (with stable prefix + correlation fields like thread_id, request_id, round, event, success) in these callbacks so sequencing/cancellation issues can be traced end-to-end.

As per coding guidelines, "Add substantial, development-oriented logs on new/changed flows in TypeScript/React app code; use namespaced debug logs and dev-only detail as needed" and "Use grep-friendly log prefixes ([feature], domain name, or JSON-RPC method) in app code for correlation with sidecar and Tauri output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/services/chatService.ts` around lines 143 - 186, Add namespaced debug
logs inside each progress-event callback (the callbacks registered for
listeners.onInferenceStart, onIterationStart, onToolCall, onToolResult,
onSubagentSpawned, and the onSubagentDone handlers) so every received payload is
logged before forwarding; include grep-friendly prefix (e.g.
"[realtime][chat]"), and correlation fields thread_id, request_id, round, event
(use EVENTS.* symbol name) and success (boolean) in the log entry. Ensure logs
are emitted only in dev/debug builds if needed (wrap with the app's dev-check)
and keep the logging placement in the existing cb/onCompleted/onFailed functions
so sequencing/cancellation can be traced end-to-end.
🧹 Nitpick comments (1)
src/openhuman/channels/providers/web.rs (1)

396-406: Consider using serde_json::json! for the output payload.

The manual JSON string formatting is fragile and could break with special characters in future fields. Using the macro would be safer and more consistent with the rest of the codebase.

♻️ Suggested improvement
-                        output: Some(format!(
-                            "{{\"output_chars\":{output_chars},\"elapsed_ms\":{elapsed_ms}}}"
-                        )),
+                        output: Some(
+                            serde_json::json!({
+                                "output_chars": output_chars,
+                                "elapsed_ms": elapsed_ms
+                            })
+                            .to_string(),
+                        ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/channels/providers/web.rs` around lines 396 - 406, The output
field currently builds JSON via string formatting (output: Some(format!(...)))
which is fragile; replace that with serde_json::json! to construct a proper JSON
value (e.g., json!({"output_chars": output_chars, "elapsed_ms": elapsed_ms}))
and then serialize it to a String (to_string() or serde_json::to_string())
before assigning to the output field; update the import to use serde_json::json
if not already present and ensure this change is applied where the struct is
constructed (the block setting skill_id, args, output, success, round, etc.).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@app/src/services/chatService.ts`:
- Around line 143-186: Add namespaced debug logs inside each progress-event
callback (the callbacks registered for listeners.onInferenceStart,
onIterationStart, onToolCall, onToolResult, onSubagentSpawned, and the
onSubagentDone handlers) so every received payload is logged before forwarding;
include grep-friendly prefix (e.g. "[realtime][chat]"), and correlation fields
thread_id, request_id, round, event (use EVENTS.* symbol name) and success
(boolean) in the log entry. Ensure logs are emitted only in dev/debug builds if
needed (wrap with the app's dev-check) and keep the logging placement in the
existing cb/onCompleted/onFailed functions so sequencing/cancellation can be
traced end-to-end.

In `@src/openhuman/channels/providers/web.rs`:
- Around line 489-500: Remove the dead helper by deleting the parse_tool_args
function (fn parse_tool_args(arguments: &str) -> Value) from
src/openhuman/channels/providers/web.rs and also remove the associated unit
test(s) that only reference this helper; search for references to
parse_tool_args in the repo and ensure any test file (or #[cfg(test)] block)
that solely exists to exercise this function is removed or refactored to no
longer call it, then run cargo test to confirm no remaining references.

---

Nitpick comments:
In `@src/openhuman/channels/providers/web.rs`:
- Around line 396-406: The output field currently builds JSON via string
formatting (output: Some(format!(...))) which is fragile; replace that with
serde_json::json! to construct a proper JSON value (e.g., json!({"output_chars":
output_chars, "elapsed_ms": elapsed_ms})) and then serialize it to a String
(to_string() or serde_json::to_string()) before assigning to the output field;
update the import to use serde_json::json if not already present and ensure this
change is applied where the struct is constructed (the block setting skill_id,
args, output, success, round, etc.).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 139802f4-e56c-4359-ba25-6afadb8f12d9

📥 Commits

Reviewing files that changed from the base of the PR and between ace0006 and 4061bca.

📒 Files selected for processing (9)
  • app/src/pages/Conversations.tsx
  • app/src/services/chatService.ts
  • src/openhuman/agent/harness/session/builder.rs
  • src/openhuman/agent/harness/session/runtime.rs
  • src/openhuman/agent/harness/session/turn.rs
  • src/openhuman/agent/harness/session/types.rs
  • src/openhuman/agent/mod.rs
  • src/openhuman/agent/progress.rs
  • src/openhuman/channels/providers/web.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant