feat: add reactive streaming and remove dead panel stream code

- Subscribe to StreamCascadeReactiveUpdates for real-time cascade state diffs
- Fall back to timer-based polling if streaming RPC unavailable
- Remove StreamCascadePanelReactiveUpdates code (dead end, only has plan_status/user_settings)
- Remove debug diff file-saving code
- Add stream_reactive_rpc() helper to backend
This commit is contained in:
Nikketryhard
2026-02-14 21:39:04 -06:00
parent 3d7a7f492b
commit b965be3f60
3 changed files with 169 additions and 5 deletions

View File

@@ -443,7 +443,7 @@ async fn handle_responses_stream(
}),
));
// ── Phase 1: Poll for thinking content (arrives before response text) ──
// ── Stream cascade updates: event-driven instead of timer-based polling ──
let start = std::time::Instant::now();
let mut last_text = String::new();
let mut thinking_emitted = false;
@@ -451,6 +451,20 @@ async fn handle_responses_stream(
let mut message_started = false;
let reasoning_id = format!("rs_{}", uuid::Uuid::new_v4().to_string().replace('-', ""));
// Try to open a reactive streaming connection for real-time notifications.
// Falls back to timer-based polling if the streaming RPC is unavailable.
let mut reactive_rx = match state.backend.stream_cascade_updates(&cascade_id).await {
Ok(rx) => {
debug!("Using reactive streaming for cascade updates");
Some(rx)
}
Err(e) => {
debug!("Reactive streaming unavailable, falling back to polling: {e}");
None
}
};
while start.elapsed().as_secs() < timeout {
if let Ok((status, data)) = state.backend.get_steps(&cascade_id).await {
if status == 200 {
@@ -648,8 +662,33 @@ async fn handle_responses_stream(
}
}
let poll_ms: u64 = rand::thread_rng().gen_range(150..250);
tokio::time::sleep(tokio::time::Duration::from_millis(poll_ms)).await;
// Wait for next update: either reactive notification or fallback timer
match reactive_rx {
Some(ref mut rx) => {
// Wait for reactive notification with a safety timeout
let timeout = tokio::time::timeout(
tokio::time::Duration::from_millis(500),
rx.recv(),
).await;
match timeout {
Ok(Some(_diff)) => {
// Drain any additional queued notifications (coalesce)
while rx.try_recv().is_ok() {}
}
Ok(None) => {
// Stream closed — fall back to polling
debug!("Reactive stream closed, falling back to polling");
reactive_rx = None;
}
Err(_) => {} // timeout — fetch anyway as safety net
}
}
None => {
// Fallback: timer-based polling
let poll_ms: u64 = rand::thread_rng().gen_range(150..250);
tokio::time::sleep(tokio::time::Duration::from_millis(poll_ms)).await;
}
}
}
// Timeout — emit incomplete response