fix(providers): harden reasoning_content fallback behavior

This commit is contained in:
Chummy 2026-02-18 17:02:28 +08:00
parent dd4f5271d1
commit bc5b1a7841
2 changed files with 29 additions and 16 deletions

View file

@ -241,9 +241,9 @@ struct ResponsesContent {
text: Option<String>,
}
// ═══════════════════════════════════════════════════════════════
// ---------------------------------------------------------------
// Streaming support (SSE parser)
// ═══════════════════════════════════════════════════════════════
// ---------------------------------------------------------------
/// Server-Sent Event stream chunk for OpenAI-compatible streaming.
#[derive(Debug, Deserialize)]
@ -291,7 +291,9 @@ fn parse_sse_line(line: &str) -> StreamResult<Option<String>> {
// Extract content from delta
if let Some(choice) = chunk.choices.first() {
if let Some(content) = &choice.delta.content {
return Ok(Some(content.clone()));
if !content.is_empty() {
return Ok(Some(content.clone()));
}
}
// Fallback to reasoning_content for thinking models
if let Some(reasoning) = &choice.delta.reasoning_content {
@ -976,9 +978,9 @@ mod tests {
);
}
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
// Custom endpoint path tests (Issue #114)
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
#[test]
fn chat_completions_url_standard_openai() {
@ -1123,9 +1125,9 @@ mod tests {
);
}
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
// Provider-specific endpoint tests (Issue #167)
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
#[test]
fn chat_completions_url_zai() {
@ -1174,9 +1176,9 @@ mod tests {
assert!(result.is_ok());
}
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
// Reasoning model fallback tests (reasoning_content)
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
#[test]
fn reasoning_content_fallback_when_content_empty() {
@ -1217,7 +1219,7 @@ mod tests {
#[test]
fn reasoning_content_both_absent_returns_empty() {
// Neither content nor reasoning_content returns empty string
// Neither content nor reasoning_content - returns empty string
let json = r#"{"choices":[{"message":{}}]}"#;
let resp: ApiChatResponse = serde_json::from_str(json).unwrap();
let msg = &resp.choices[0].message;
@ -1234,9 +1236,9 @@ mod tests {
assert_eq!(msg.effective_content(), "Hello from Venice!");
}
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
// SSE streaming reasoning_content fallback tests
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
#[test]
fn parse_sse_line_with_content() {
@ -1259,6 +1261,14 @@ mod tests {
assert_eq!(result, Some("real answer".to_string()));
}
#[test]
fn parse_sse_line_with_empty_content_falls_back_to_reasoning_content() {
let line =
r#"data: {"choices":[{"delta":{"content":"","reasoning_content":"thinking..."}}]}"#;
let result = parse_sse_line(line).unwrap();
assert_eq!(result, Some("thinking...".to_string()));
}
#[test]
fn parse_sse_line_done_sentinel() {
let line = "data: [DONE]";

View file

@ -460,9 +460,12 @@ mod tests {
#[test]
fn response_with_unicode() {
let json = r#"{"choices":[{"message":{"content":"こんにちは 🦀"}}]}"#;
let json = r#"{"choices":[{"message":{"content":"Hello \u03A9"}}]}"#;
let resp: ChatResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.choices[0].message.effective_content(), "こんにちは 🦀");
assert_eq!(
resp.choices[0].message.effective_content(),
"Hello \u{03A9}"
);
}
#[test]
@ -483,9 +486,9 @@ mod tests {
assert!(result.is_ok());
}
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
// Reasoning model fallback tests (reasoning_content)
// ══════════════════════════════════════════════════════════
// ----------------------------------------------------------
#[test]
fn reasoning_content_fallback_empty_content() {