From ffbd2e38ec1c6b35474cf5f4f766c14e996b9374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Mon, 17 Nov 2025 19:27:07 +0100 Subject: [PATCH] debug: Add extensive logging for finish_reason handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added debug logging to track: - Whether assistant_item and reasoning_item exist when finish_reason is received - Content being emitted - Completed event transmission This revealed the actual bug is in HTTP client layer - responses are being consumed immediately instead of streaming, causing "Request completed" to appear before any SSE chunks are processed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- llmx-rs/core/src/chat_completions.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/llmx-rs/core/src/chat_completions.rs b/llmx-rs/core/src/chat_completions.rs index 9d4f4cd4..08a54140 100644 --- a/llmx-rs/core/src/chat_completions.rs +++ b/llmx-rs/core/src/chat_completions.rs @@ -869,13 +869,21 @@ async fn process_chat_sse( } "stop" | "length" => { // Regular turn without tool-call, or hit max_tokens limit. + debug!("Processing finish_reason={}, assistant_item.is_some()={}, reasoning_item.is_some()={}", + finish_reason, assistant_item.is_some(), reasoning_item.is_some()); // Emit the final assistant message as a single OutputItemDone so non-delta consumers see the result. if let Some(item) = assistant_item.take() { + debug!("Emitting assistant_item: {:?}", item); let _ = tx_event.send(Ok(ResponseEvent::OutputItemDone(item))).await; + } else { + debug!("No assistant_item to emit"); } // Also emit a terminal Reasoning item so UIs can finalize raw reasoning. if let Some(item) = reasoning_item.take() { + debug!("Emitting reasoning_item"); let _ = tx_event.send(Ok(ResponseEvent::OutputItemDone(item))).await; + } else { + debug!("No reasoning_item to emit"); } } _ => { @@ -891,12 +899,14 @@ async fn process_chat_sse( } // Emit Completed regardless of reason so the agent can advance. + debug!("Sending Completed event after finish_reason={}", finish_reason); let _ = tx_event .send(Ok(ResponseEvent::Completed { response_id: String::new(), token_usage: token_usage.clone(), })) .await; + debug!("Completed event sent, returning from SSE processor"); // Prepare for potential next turn (should not happen in same stream). // fn_call_state = FunctionCallState::default();