fix(providers): harden reasoning_content fallback behavior
This commit is contained in:
parent
dd4f5271d1
commit
bc5b1a7841
2 changed files with 29 additions and 16 deletions
|
|
@ -241,9 +241,9 @@ struct ResponsesContent {
|
||||||
text: Option<String>,
|
text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ---------------------------------------------------------------
|
||||||
// Streaming support (SSE parser)
|
// Streaming support (SSE parser)
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
/// Server-Sent Event stream chunk for OpenAI-compatible streaming.
|
/// Server-Sent Event stream chunk for OpenAI-compatible streaming.
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
@ -291,8 +291,10 @@ fn parse_sse_line(line: &str) -> StreamResult<Option<String>> {
|
||||||
// Extract content from delta
|
// Extract content from delta
|
||||||
if let Some(choice) = chunk.choices.first() {
|
if let Some(choice) = chunk.choices.first() {
|
||||||
if let Some(content) = &choice.delta.content {
|
if let Some(content) = &choice.delta.content {
|
||||||
|
if !content.is_empty() {
|
||||||
return Ok(Some(content.clone()));
|
return Ok(Some(content.clone()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Fallback to reasoning_content for thinking models
|
// Fallback to reasoning_content for thinking models
|
||||||
if let Some(reasoning) = &choice.delta.reasoning_content {
|
if let Some(reasoning) = &choice.delta.reasoning_content {
|
||||||
return Ok(Some(reasoning.clone()));
|
return Ok(Some(reasoning.clone()));
|
||||||
|
|
@ -976,9 +978,9 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
// Custom endpoint path tests (Issue #114)
|
// Custom endpoint path tests (Issue #114)
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chat_completions_url_standard_openai() {
|
fn chat_completions_url_standard_openai() {
|
||||||
|
|
@ -1123,9 +1125,9 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
// Provider-specific endpoint tests (Issue #167)
|
// Provider-specific endpoint tests (Issue #167)
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chat_completions_url_zai() {
|
fn chat_completions_url_zai() {
|
||||||
|
|
@ -1174,9 +1176,9 @@ mod tests {
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
// Reasoning model fallback tests (reasoning_content)
|
// Reasoning model fallback tests (reasoning_content)
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reasoning_content_fallback_when_content_empty() {
|
fn reasoning_content_fallback_when_content_empty() {
|
||||||
|
|
@ -1217,7 +1219,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reasoning_content_both_absent_returns_empty() {
|
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 json = r#"{"choices":[{"message":{}}]}"#;
|
||||||
let resp: ApiChatResponse = serde_json::from_str(json).unwrap();
|
let resp: ApiChatResponse = serde_json::from_str(json).unwrap();
|
||||||
let msg = &resp.choices[0].message;
|
let msg = &resp.choices[0].message;
|
||||||
|
|
@ -1234,9 +1236,9 @@ mod tests {
|
||||||
assert_eq!(msg.effective_content(), "Hello from Venice!");
|
assert_eq!(msg.effective_content(), "Hello from Venice!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
// SSE streaming reasoning_content fallback tests
|
// SSE streaming reasoning_content fallback tests
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_sse_line_with_content() {
|
fn parse_sse_line_with_content() {
|
||||||
|
|
@ -1259,6 +1261,14 @@ mod tests {
|
||||||
assert_eq!(result, Some("real answer".to_string()));
|
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]
|
#[test]
|
||||||
fn parse_sse_line_done_sentinel() {
|
fn parse_sse_line_done_sentinel() {
|
||||||
let line = "data: [DONE]";
|
let line = "data: [DONE]";
|
||||||
|
|
|
||||||
|
|
@ -460,9 +460,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn response_with_unicode() {
|
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();
|
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]
|
#[test]
|
||||||
|
|
@ -483,9 +486,9 @@ mod tests {
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
// Reasoning model fallback tests (reasoning_content)
|
// Reasoning model fallback tests (reasoning_content)
|
||||||
// ══════════════════════════════════════════════════════════
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reasoning_content_fallback_empty_content() {
|
fn reasoning_content_fallback_empty_content() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue