(
response: reqwest::Response,
on_token: &Channel<String>,
cancel_token: &CancellationToken,
)
| 723 | } |
| 724 | |
| 725 | async fn stream_anthropic_sse( |
| 726 | response: reqwest::Response, |
| 727 | on_token: &Channel<String>, |
| 728 | cancel_token: &CancellationToken, |
| 729 | ) -> AppResult<(String, Option<TokenUsage>)> { |
| 730 | let mut stream = response.bytes_stream(); |
| 731 | let mut line_buffer = String::new(); |
| 732 | let mut output = String::new(); |
| 733 | // Track the most recent `event:` line so we can use it when parsing the |
| 734 | // subsequent `data:` line. Some gateways omit the `"type"` field from |
| 735 | // the JSON payload, so we fall back to the SSE event name. |
| 736 | let mut current_event = String::new(); |
| 737 | let mut usage = UsageAccumulator::default(); |
| 738 | let mut message_stop_received = false; |
| 739 | |
| 740 | 'outer: loop { |
| 741 | tokio::select! { |
| 742 | _ = cancel_token.cancelled() => { |
| 743 | return Err(AppError::Cancelled); |
| 744 | } |
| 745 | chunk = stream.next() => { |
| 746 | match chunk { |
| 747 | Some(Ok(bytes)) => { |
| 748 | line_buffer.push_str(&String::from_utf8_lossy(&bytes)); |
| 749 | |
| 750 | while let Some(pos) = line_buffer.find('\n') { |
| 751 | let mut line = line_buffer[..pos].to_string(); |
| 752 | line_buffer.drain(..=pos); |
| 753 | if line.ends_with('\r') { |
| 754 | line.pop(); |
| 755 | } |
| 756 | |
| 757 | if parse_anthropic_sse_line( |
| 758 | &line, |
| 759 | &mut current_event, |
| 760 | on_token, |
| 761 | &mut output, |
| 762 | &mut usage, |
| 763 | )? { |
| 764 | message_stop_received = true; |
| 765 | break 'outer; |
| 766 | } |
| 767 | } |
| 768 | } |
| 769 | Some(Err(e)) => return Err(AppError::Http(e.to_string())), |
| 770 | None => break, |
| 771 | } |
| 772 | } |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | if !message_stop_received { |
| 777 | return Err(AppError::Http( |
| 778 | "Stream ended without completion signal — connection may have been interrupted. Please retry.".to_string(), |
| 779 | )); |
| 780 | } |
| 781 | |
| 782 | Ok((output, usage.finish())) |
no test coverage detected