fix(tui): accept compact SSE data fields
Accept SSE data frames with either `data: {...}` or `data:{...}` in Chat Completions, Responses, and Anthropic stream readers.
Harvested from PR #3152.
Co-authored-by: wgeeker <169752135+wgeeker@users.noreply.github.com>
This commit is contained in:
@@ -49,6 +49,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- **SSE data lines without spaces (#3152).** Chat Completions, Responses, and
|
||||
Anthropic stream readers now accept both `data: {...}` and `data:{...}` SSE
|
||||
frames, matching the spec and preventing providers that omit the optional
|
||||
space from streaming empty output. Thanks @wgeeker for the PR.
|
||||
- **SiliconFlow China provider config (#2893/#2895).** `siliconflow-CN`
|
||||
now reads its own `[providers.siliconflow_cn]` / `[providers.siliconflow-CN]`
|
||||
table and falls back to `[providers.siliconflow]` only for unset
|
||||
|
||||
@@ -49,6 +49,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- **SSE data lines without spaces (#3152).** Chat Completions, Responses, and
|
||||
Anthropic stream readers now accept both `data: {...}` and `data:{...}` SSE
|
||||
frames, matching the spec and preventing providers that omit the optional
|
||||
space from streaming empty output. Thanks @wgeeker for the PR.
|
||||
- **SiliconFlow China provider config (#2893/#2895).** `siliconflow-CN`
|
||||
now reads its own `[providers.siliconflow_cn]` / `[providers.siliconflow-CN]`
|
||||
table and falls back to `[providers.siliconflow]` only for unset
|
||||
|
||||
@@ -1543,6 +1543,11 @@ mod anthropic;
|
||||
mod chat;
|
||||
mod responses;
|
||||
|
||||
fn extract_sse_data_value(line: &str) -> Option<&str> {
|
||||
line.strip_prefix("data:")
|
||||
.map(|value| value.strip_prefix(' ').unwrap_or(value))
|
||||
}
|
||||
|
||||
pub(crate) use chat::{CacheWarmupKey, PromptInspection};
|
||||
|
||||
pub(crate) fn inspect_prompt_for_request(request: &MessageRequest) -> PromptInspection {
|
||||
@@ -3942,4 +3947,28 @@ mod tests {
|
||||
"https://api.deepseek.com/v1/chat/completions"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_sse_data_value_accepts_optional_space() {
|
||||
assert_eq!(
|
||||
extract_sse_data_value("data: {\"ok\":true}"),
|
||||
Some("{\"ok\":true}")
|
||||
);
|
||||
assert_eq!(
|
||||
extract_sse_data_value("data:{\"ok\":true}"),
|
||||
Some("{\"ok\":true}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_sse_data_value_handles_done_marker() {
|
||||
assert_eq!(extract_sse_data_value("data: [DONE]"), Some("[DONE]"));
|
||||
assert_eq!(extract_sse_data_value("data:[DONE]"), Some("[DONE]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_sse_data_value_rejects_non_data_lines() {
|
||||
assert_eq!(extract_sse_data_value("event: message"), None);
|
||||
assert_eq!(extract_sse_data_value(": heartbeat"), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl DeepSeekClient {
|
||||
|
||||
// `event:` lines are redundant (the data payload carries
|
||||
// `type`) and comment/heartbeat lines are ignorable.
|
||||
let Some(data) = line.strip_prefix("data: ") else {
|
||||
let Some(data) = super::extract_sse_data_value(&line) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
||||
@@ -437,7 +437,7 @@ impl DeepSeekClient {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(data) = line.strip_prefix("data: ") {
|
||||
if let Some(data) = super::extract_sse_data_value(&line) {
|
||||
line_buf.push_str(data);
|
||||
}
|
||||
// Ignore other SSE fields (event:, id:, retry:)
|
||||
|
||||
@@ -165,7 +165,7 @@ impl DeepSeekClient {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(data) = line.strip_prefix("data: ") {
|
||||
if let Some(data) = super::extract_sse_data_value(&line) {
|
||||
if data == "[DONE]" {
|
||||
done = true;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user