Skip to content

Commit f509493

Browse files
authored
fix: Don't trim whitespace from end of each chat completion delta in aggregator (#4152)
1 parent aebf168 commit f509493

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

lib/llm/src/protocols/openai/chat_completions/aggregator.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl DeltaAggregator {
157157
});
158158
// Append content if available.
159159
if let Some(content) = &choice.delta.content {
160-
state_choice.text.push_str(content.trim_end());
160+
state_choice.text.push_str(content);
161161
}
162162

163163
if let Some(reasoning_content) = &choice.delta.reasoning_content {
@@ -545,6 +545,53 @@ mod tests {
545545
);
546546
}
547547

548+
#[tokio::test]
549+
async fn test_preserves_intermediate_whitespace_chunks() {
550+
// This validates behavior before/after removing trim_end():
551+
// If a whitespace-only chunk (" ") arrives between tokens, it must be preserved.
552+
// With trim_end(), that chunk was dropped, yielding "Helloworld" instead of "Hello world".
553+
554+
let annotated_delta1 = create_test_delta(
555+
0,
556+
"Hello",
557+
Some(dynamo_async_openai::types::Role::User),
558+
None,
559+
None,
560+
None,
561+
);
562+
// A whitespace-only chunk
563+
let annotated_delta2 = create_test_delta(0, " ", None, None, None, None);
564+
let annotated_delta3 = create_test_delta(
565+
0,
566+
"world",
567+
None,
568+
Some(dynamo_async_openai::types::FinishReason::Stop),
569+
None,
570+
None,
571+
);
572+
573+
let stream = Box::pin(stream::iter(vec![
574+
annotated_delta1,
575+
annotated_delta2,
576+
annotated_delta3,
577+
]));
578+
579+
let result = DeltaAggregator::apply(stream, ParsingOptions::default()).await;
580+
581+
assert!(result.is_ok());
582+
let response = result.unwrap();
583+
assert_eq!(response.choices.len(), 1);
584+
let choice = &response.choices[0];
585+
586+
assert_eq!(choice.index, 0);
587+
assert_eq!(choice.message.content.as_deref(), Some("Hello world"));
588+
assert_eq!(
589+
choice.finish_reason,
590+
Some(dynamo_async_openai::types::FinishReason::Stop)
591+
);
592+
assert_eq!(choice.message.role, dynamo_async_openai::types::Role::User);
593+
}
594+
548595
#[allow(deprecated)]
549596
#[tokio::test]
550597
async fn test_multiple_choices() {

0 commit comments

Comments
 (0)