diff --git a/lib/async-openai/src/types/chat.rs b/lib/async-openai/src/types/chat.rs index bb2314dd0ac..4411167bd49 100644 --- a/lib/async-openai/src/types/chat.rs +++ b/lib/async-openai/src/types/chat.rs @@ -1157,6 +1157,10 @@ pub struct ChatChoice { /// Log probability information for the choice. #[serde(skip_serializing_if = "Option::is_none")] pub logprobs: Option, + /// Flag indicating whether this choice includes reasoning content. + /// Set to `true` when `message.reasoning_content` is present. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_reasoning: Option, } /// Represents a chat completion response returned by model, based on the provided input. @@ -1256,6 +1260,10 @@ pub struct ChatChoiceStream { /// Log probability information for the choice. #[serde(skip_serializing_if = "Option::is_none")] pub logprobs: Option, + /// Flag indicating whether this choice includes reasoning content. + /// Set to `true` when `delta.reasoning_content` is present. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_reasoning: Option, } #[derive(ToSchema, Debug, Deserialize, Clone, PartialEq, Serialize)] diff --git a/lib/llm/src/audit/stream.rs b/lib/llm/src/audit/stream.rs index 6851be7f53e..310aca21c2e 100644 --- a/lib/llm/src/audit/stream.rs +++ b/lib/llm/src/audit/stream.rs @@ -217,6 +217,7 @@ pub fn final_response_to_one_chunk_stream( finish_reason: ch.finish_reason, stop_reason: ch.stop_reason.clone(), logprobs: ch.logprobs.clone(), + has_reasoning: ch.has_reasoning, }; choices.push(choice); } @@ -272,6 +273,7 @@ mod tests { finish_reason: None, stop_reason: None, logprobs: None, + has_reasoning: None, }; let response = NvCreateChatCompletionStreamResponse { @@ -311,6 +313,7 @@ mod tests { finish_reason: Some(FinishReason::Stop), stop_reason: None, logprobs: None, + has_reasoning: None, }; let response = NvCreateChatCompletionStreamResponse { @@ -441,6 +444,7 @@ mod tests { finish_reason: None, stop_reason: None, logprobs: None, + has_reasoning: None, } }], created: 1234567890, diff --git a/lib/llm/src/protocols/openai/chat_completions/aggregator.rs b/lib/llm/src/protocols/openai/chat_completions/aggregator.rs index 026f49cac36..ce56153c349 100644 --- a/lib/llm/src/protocols/openai/chat_completions/aggregator.rs +++ b/lib/llm/src/protocols/openai/chat_completions/aggregator.rs @@ -311,6 +311,11 @@ impl From for dynamo_async_openai::types::ChatChoice { None }; + let has_reasoning = delta + .reasoning_content + .as_ref() + .is_some_and(|r| !r.is_empty()); + dynamo_async_openai::types::ChatChoice { message: dynamo_async_openai::types::ChatCompletionResponseMessage { role: delta.role.expect("delta should have a Role"), @@ -325,6 +330,7 @@ impl From for dynamo_async_openai::types::ChatChoice { finish_reason, stop_reason: delta.stop_reason, logprobs: delta.logprobs, + has_reasoning: if has_reasoning { Some(true) } else { None }, } } } diff --git a/lib/llm/src/protocols/openai/chat_completions/delta.rs b/lib/llm/src/protocols/openai/chat_completions/delta.rs index cb829c0485d..d12f3884688 100644 --- a/lib/llm/src/protocols/openai/chat_completions/delta.rs +++ b/lib/llm/src/protocols/openai/chat_completions/delta.rs @@ -271,6 +271,9 @@ impl DeltaGenerator { finish_reason, stop_reason, logprobs, + // Note: reasoning_content is not available in the streaming delta path. + // has_reasoning is only set in the aggregated response path (aggregator.rs). + has_reasoning: None, }; let choices = vec![choice]; diff --git a/lib/llm/src/protocols/openai/chat_completions/jail.rs b/lib/llm/src/protocols/openai/chat_completions/jail.rs index 135210004c9..8c62ad3b7c6 100644 --- a/lib/llm/src/protocols/openai/chat_completions/jail.rs +++ b/lib/llm/src/protocols/openai/chat_completions/jail.rs @@ -135,6 +135,7 @@ fn create_choice_stream( finish_reason, stop_reason, logprobs, + has_reasoning: None, } } @@ -598,6 +599,7 @@ impl JailedStream { finish_reason: choice.finish_reason, stop_reason: choice.stop_reason.clone(), logprobs: choice.logprobs.clone(), + has_reasoning: choice.has_reasoning, }; all_emissions.push(ChoiceEmission::PassThrough(pass_through_choice)); }