using AIStudio.Provider; using AIStudio.Settings; namespace AIStudio.Chat; /// /// Text content in the chat. /// public sealed class ContentText : IContent { /// /// The minimum time between two streaming events, when the user /// enables the energy saving mode. /// private static readonly TimeSpan MIN_TIME = TimeSpan.FromSeconds(3); #region Implementation of IContent /// public bool InitialRemoteWait { get; set; } /// public bool IsStreaming { get; set; } /// public Func StreamingDone { get; set; } = () => Task.CompletedTask; public Func StreamingEvent { get; set; } = () => Task.CompletedTask; /// public async Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread? chatThread, CancellationToken token = default) { if(chatThread is null) return; // Store the last time we got a response. We use this later, // to determine whether we should notify the UI about the // new content or not. Depends on the energy saving mode // the user chose. var last = DateTimeOffset.Now; // Start another thread by using a task, to uncouple // the UI thread from the AI processing: await Task.Run(async () => { // We show the waiting animation until we get the first response: this.InitialRemoteWait = true; // Iterate over the responses from the AI: await foreach (var deltaText in provider.StreamChatCompletion(jsRuntime, settings, chatModel, chatThread, token)) { // When the user cancels the request, we stop the loop: if (token.IsCancellationRequested) break; // Stop the waiting animation: this.InitialRemoteWait = false; this.IsStreaming = true; // Add the response to the text: this.Text += deltaText; // Notify the UI that the content has changed, // depending on the energy saving mode: var now = DateTimeOffset.Now; switch (settings.ConfigurationData.IsSavingEnergy) { // Energy saving mode is off. We notify the UI // as fast as possible -- no matter the odds: case false: await this.StreamingEvent(); break; // Energy saving mode is on. We notify the UI // only when the time between two events is // greater than the minimum time: case true when now - last > MIN_TIME: last = now; await this.StreamingEvent(); break; } } // Stop the waiting animation (in case the loop // was stopped or no content was received): this.InitialRemoteWait = false; this.IsStreaming = false; }, token); // Inform the UI that the streaming is done: await this.StreamingDone(); } #endregion /// /// The text content. /// public string Text { get; set; } = string.Empty; }