diff --git a/llmx-rs/core/src/chat_completions.rs b/llmx-rs/core/src/chat_completions.rs index 4f24e3e3..14d81294 100644 --- a/llmx-rs/core/src/chat_completions.rs +++ b/llmx-rs/core/src/chat_completions.rs @@ -841,6 +841,7 @@ async fn process_chat_sse( // Emit end-of-turn when finish_reason signals completion. if let Some(finish_reason) = choice.get("finish_reason").and_then(|v| v.as_str()) { + debug!("Received finish_reason: {}", finish_reason); match finish_reason { "tool_calls" if fn_call_state.active => { // First, flush the terminal raw reasoning so UIs can finalize @@ -888,6 +889,22 @@ async fn process_chat_sse( } } } + + // Stream ended without finish_reason - this can happen when the stream closes abruptly + debug!("Stream ended without finish_reason, emitting final items and Completed event"); + if let Some(item) = assistant_item.take() { + let _ = tx_event.send(Ok(ResponseEvent::OutputItemDone(item))).await; + } + if let Some(item) = reasoning_item.take() { + let _ = tx_event.send(Ok(ResponseEvent::OutputItemDone(item))).await; + } + // Send Completed event so llmx knows the turn is done + let _ = tx_event + .send(Ok(ResponseEvent::Completed { + response_id: String::new(), + token_usage: token_usage.clone(), + })) + .await; } /// Optional client-side aggregation helper diff --git a/llmx-rs/core/src/model_provider_info.rs b/llmx-rs/core/src/model_provider_info.rs index 58da2580..4eaa3794 100644 --- a/llmx-rs/core/src/model_provider_info.rs +++ b/llmx-rs/core/src/model_provider_info.rs @@ -295,7 +295,6 @@ pub fn built_in_model_providers() -> HashMap { stream_max_retries: None, stream_idle_timeout_ms: None, max_tokens: None, - max_tokens: None, requires_openai_auth: false, }, ), @@ -337,7 +336,6 @@ pub fn built_in_model_providers() -> HashMap { stream_max_retries: None, stream_idle_timeout_ms: None, max_tokens: None, - max_tokens: None, requires_openai_auth: true, }, ), @@ -384,7 +382,6 @@ pub fn create_oss_provider_with_base_url(base_url: &str) -> ModelProviderInfo { stream_max_retries: None, stream_idle_timeout_ms: None, max_tokens: None, - max_tokens: None, requires_openai_auth: false, } }