(
line: &str,
on_token: &Channel<String>,
output: &mut String,
usage: &mut UsageAccumulator,
)
| 678 | } |
| 679 | |
| 680 | fn parse_openai_sse_line( |
| 681 | line: &str, |
| 682 | on_token: &Channel<String>, |
| 683 | output: &mut String, |
| 684 | usage: &mut UsageAccumulator, |
| 685 | ) -> AppResult<bool> { |
| 686 | let trimmed = line.trim(); |
| 687 | if trimmed.is_empty() { |
| 688 | return Ok(false); |
| 689 | } |
| 690 | |
| 691 | let Some(payload) = trimmed.strip_prefix("data:") else { |
| 692 | return Ok(false); |
| 693 | }; |
| 694 | let payload = payload.trim(); |
| 695 | if payload == "[DONE]" { |
| 696 | return Ok(true); |
| 697 | } |
| 698 | |
| 699 | let value: Value = serde_json::from_str(payload)?; |
| 700 | |
| 701 | if let Some(u) = value.get("usage") { |
| 702 | if let Some(pt) = u.get("prompt_tokens").and_then(Value::as_u64) { |
| 703 | usage.prompt_tokens = pt as u32; |
| 704 | } |
| 705 | if let Some(ct) = u.get("completion_tokens").and_then(Value::as_u64) { |
| 706 | usage.completion_tokens = ct as u32; |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | if let Some(token) = value |
| 711 | .get("choices") |
| 712 | .and_then(Value::as_array) |
| 713 | .and_then(|c| c.first()) |
| 714 | .and_then(|c| c.get("delta")) |
| 715 | .and_then(|d| d.get("content")) |
| 716 | .and_then(Value::as_str) |
| 717 | { |
| 718 | output.push_str(token); |
| 719 | let _ = on_token.send(token.to_string()); |
| 720 | } |
| 721 | |
| 722 | Ok(false) |
| 723 | } |
| 724 | |
| 725 | async fn stream_anthropic_sse( |
| 726 | response: reqwest::Response, |
no test coverage detected