using Microsoft.Extensions.AI; using OpenAI; namespace AnchorCli; /// /// Handles streaming responses from the chat client, including token usage capture. /// internal sealed class ResponseStreamer { private readonly ChatSession _session; public ResponseStreamer(ChatSession session) { _session = session; } /// /// Streams a response from the session and captures token usage. /// Returns an async enumerable that yields text chunks as they arrive. /// public async IAsyncEnumerable StreamAsync( [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) { await using var stream = _session .GetStreamingResponseAsync(cancellationToken) .GetAsyncEnumerator(cancellationToken); int respIn = 0, respOut = 0; void CaptureUsage(ChatResponseUpdate update) { if (update.RawRepresentation is OpenAI.Chat.StreamingChatCompletionUpdate raw && raw.Usage != null) { respIn = raw.Usage.InputTokenCount; respOut += raw.Usage.OutputTokenCount; } } // Stream all chunks while (await stream.MoveNextAsync()) { cancellationToken.ThrowIfCancellationRequested(); CaptureUsage(stream.Current); var text = stream.Current.Text; if (!string.IsNullOrEmpty(text)) { yield return text; } } // Store final usage stats LastInputTokens = respIn; LastOutputTokens = respOut; } public int LastInputTokens { get; private set; } public int LastOutputTokens { get; private set; } }