Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/cortex-tui/src/app/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use std::time::Instant;
#[derive(Debug, Clone, Default)]
pub struct StreamingState {
pub is_streaming: bool,
/// Whether we are actively receiving tokens from the LLM.
/// False when waiting for first token (processing), true when streaming.
pub is_actively_streaming: bool,
pub current_tool: Option<String>,
pub tool_status: Option<String>,
pub thinking: bool,
Expand All @@ -29,6 +32,7 @@ impl StreamingState {
/// If false, preserves existing timer (use for tool continuations).
pub fn start(&mut self, tool: Option<String>, reset_timer: bool) {
self.is_streaming = true;
self.is_actively_streaming = false; // Not yet receiving tokens
self.thinking = true;
self.current_tool = tool;
self.task_started_at = Some(Instant::now());
Expand All @@ -39,6 +43,12 @@ impl StreamingState {
}
}

/// Mark that we started actively receiving tokens from the LLM.
/// This transitions from "Execute" (waiting) to "Streaming.." state.
pub fn start_active_streaming(&mut self) {
self.is_actively_streaming = true;
}

/// Get the elapsed seconds since the task started
pub fn elapsed_seconds(&self) -> u64 {
self.task_started_at
Expand All @@ -57,6 +67,7 @@ impl StreamingState {
/// Reset streaming state when task completes
pub fn stop(&mut self) {
self.is_streaming = false;
self.is_actively_streaming = false;
self.thinking = false;
self.current_tool = None;
self.tool_status = None;
Expand Down
2 changes: 2 additions & 0 deletions src/cortex-tui/src/runner/event_loop/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ impl EventLoop {
match event {
StreamEvent::Delta(delta) => {
self.stream_controller.append_text(&delta);
// Mark that we are now actively receiving tokens (transition from Execute to Streaming..)
Comment on lines 305 to +308
Copy link

Choose a reason for hiding this comment

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

Active-streaming flips too early

start_active_streaming() is called on every StreamEvent::Delta, but Delta can be an empty/whitespace keepalive (or other non-user-visible chunk). In that case the UI will switch from Execute to Streaming.. even though the user still hasn’t received the first token. Consider guarding this with something like if !delta.is_empty() / if delta.chars().any(|c| !c.is_whitespace()) (or using a dedicated “first content token received” event/flag from the streaming layer).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cortex-tui/src/runner/event_loop/streaming.rs
Line: 305:308

Comment:
**Active-streaming flips too early**

`start_active_streaming()` is called on every `StreamEvent::Delta`, but `Delta` can be an empty/whitespace keepalive (or other non-user-visible chunk). In that case the UI will switch from `Execute` to `Streaming..` even though the user still hasn’t received the first token. Consider guarding this with something like `if !delta.is_empty()` / `if delta.chars().any(|c| !c.is_whitespace())` (or using a dedicated “first content token received” event/flag from the streaming layer).

How can I resolve this? If you propose a fix, please make it concise.

self.app_state.streaming.start_active_streaming();
// Track text for interleaved display
self.app_state.append_streaming_text(&delta);
// Keep scroll at bottom if pinned (user hasn't scrolled up)
Expand Down
7 changes: 6 additions & 1 deletion src/cortex-tui/src/views/minimal_session/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,12 @@ impl<'a> MinimalSessionView<'a> {
} else if self.app_state.streaming.thinking && self.app_state.thinking_budget.is_some() {
"Thinking".to_string()
} else if self.app_state.streaming.is_streaming {
"Working".to_string()
// Differentiate between waiting for first token and actively streaming
if self.app_state.streaming.is_actively_streaming {
"Streaming..".to_string()
} else {
"Execute".to_string()
}
} else {
"Idle".to_string()
}
Expand Down