-
Notifications
You must be signed in to change notification settings - Fork 0
Fix SSE stream corruption by buffering raw bytes #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -334,15 +334,26 @@ impl Provider for OpenAIProvider { | |
| } | ||
|
|
||
| let mut stream = response.bytes_stream(); | ||
| let mut raw_buf: Vec<u8> = Vec::new(); | ||
|
|
||
| while let Some(chunk) = stream.next().await { | ||
| 'outer: while let Some(chunk) = stream.next().await { | ||
| let chunk = chunk?; | ||
| let text = String::from_utf8_lossy(&chunk); | ||
| raw_buf.extend_from_slice(&chunk); | ||
|
|
||
| while let Some(newline_pos) = raw_buf.iter().position(|&b| b == b'\n') { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new parser in Useful? React with 👍 / 👎. |
||
| let line_bytes = raw_buf.drain(..=newline_pos).collect::<Vec<u8>>(); | ||
| let line = match std::str::from_utf8(&line_bytes) { | ||
| Ok(s) => s.trim().to_string(), | ||
| Err(_) => continue, | ||
| }; | ||
|
|
||
| if line.is_empty() { | ||
| continue; | ||
| } | ||
|
|
||
| for line in text.lines() { | ||
| if let Some(data) = line.strip_prefix("data: ") { | ||
| if data == "[DONE]" { | ||
| break; | ||
| break 'outer; | ||
| } | ||
|
|
||
| if let Ok(response) = serde_json::from_str::<OpenAIResponse>(data) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like the OpenAI path, this loop now parses only newline-terminated records and never drains
raw_bufafter the stream ends. If the last Anthropicdata:event arrives without a terminating newline, it remains buffered and is never deserialized, which drops the final text chunk. This behavior is newly introduced by the raw-byte buffering change and can truncate outputs in EOF-terminated streams.Useful? React with 👍 / 👎.