| 316 | } |
| 317 | |
| 318 | fn parse_legacy_sse_data( |
| 319 | data: &str, |
| 320 | state: &mut LegacySseParserState, |
| 321 | ) -> Vec<StreamEvent> { |
| 322 | let mut events = Vec::new(); |
| 323 | |
| 324 | if data == "[DONE]" { |
| 325 | if state.reasoning_open { |
| 326 | events.push(StreamEvent::ReasoningEnd { |
| 327 | id: "reasoning-0".to_string(), |
| 328 | }); |
| 329 | state.reasoning_open = false; |
| 330 | } |
| 331 | events.push(StreamEvent::Done); |
| 332 | return events; |
| 333 | } |
| 334 | |
| 335 | let chunk: Value = match serde_json::from_str(data) { |
| 336 | Ok(v) => v, |
| 337 | Err(_) => return events, |
| 338 | }; |
| 339 | |
| 340 | let usage = chunk.get("usage"); |
| 341 | let prompt_tokens = usage |
| 342 | .and_then(|u| u.get("prompt_tokens")) |
| 343 | .and_then(Value::as_u64) |
| 344 | .unwrap_or(0); |
| 345 | let completion_tokens = usage |
| 346 | .and_then(|u| u.get("completion_tokens")) |
| 347 | .and_then(Value::as_u64) |
| 348 | .unwrap_or(0); |
| 349 | |
| 350 | if usage.is_some() { |
| 351 | events.push(StreamEvent::Usage { |
| 352 | prompt_tokens, |
| 353 | completion_tokens, |
| 354 | }); |
| 355 | } |
| 356 | |
| 357 | if let Some(choices) = chunk.get("choices").and_then(Value::as_array) { |
| 358 | for choice in choices { |
| 359 | if let Some(delta) = choice.get("delta") { |
| 360 | let reasoning = delta |
| 361 | .get("reasoning_content") |
| 362 | .or_else(|| delta.get("reasoning_text")) |
| 363 | .and_then(Value::as_str) |
| 364 | .unwrap_or_default(); |
| 365 | |
| 366 | if !reasoning.is_empty() { |
| 367 | if !state.reasoning_open { |
| 368 | state.reasoning_open = true; |
| 369 | events.push(StreamEvent::ReasoningStart { |
| 370 | id: "reasoning-0".to_string(), |
| 371 | }); |
| 372 | } |
| 373 | events.push(StreamEvent::ReasoningDelta { |
| 374 | id: "reasoning-0".to_string(), |
| 375 | text: reasoning.to_string(), |