Skip to content

Commit

Permalink
Push changes from last evening...
Browse files Browse the repository at this point in the history
  • Loading branch information
IEvangelist committed Jun 2, 2023
1 parent ff54e74 commit c49113d
Show file tree
Hide file tree
Showing 23 changed files with 256 additions and 120 deletions.
7 changes: 3 additions & 4 deletions Azure.OpenAI.Client.EndToEndTests/TestPageLoad.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) David Pine. All rights reserved.
// Licensed under the MIT License.

using Azure.OpenAI.Shared;

namespace Azure.OpenAI.Client.EndToEndTests;

[Collection(PlaywrightCollectionDefinition.EndToEndTests)]
Expand Down Expand Up @@ -49,10 +51,7 @@ public sealed partial class TestPageLoad
static async Task ExpectedThemeStateAsync(IBrowserContext cxt, string key, bool expected)
{
var json = await cxt.StorageStateAsync();
var settings = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
PropertyNameCaseInsensitive = true
};
var settings = JsonSerializationDefaults.Options;
var state = JsonSerializer.Deserialize<StorageState>(json, settings);
Assert.NotNull(state);
Expand Down
1 change: 1 addition & 0 deletions Azure.OpenAI/Client/Azure.OpenAI.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PackageReference Include="Blazor.SessionStorage.WebAssembly" Version="7.0.3" />
<PackageReference Include="Blazor.SpeechRecognition.WebAssembly" Version="7.0.3" />
<PackageReference Include="Blazor.SpeechSynthesis.WebAssembly" Version="7.0.3" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="7.0.5" />
<PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.5" />
Expand Down
16 changes: 16 additions & 0 deletions Azure.OpenAI/Client/Components/AnswerError.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<MudPaper Class="pa-6" Elevation="3">
<MudStack Spacing="4" Class="full-width">
<MudIcon Icon="@Icons.Material.Filled.Error" Color="Color.Error" Size="Size.Large" />
<MudText Typo="Typo.h6">
Error
</MudText>
<MudText Typo="Typo.subtitle2">
@((MarkupString)Error)
</MudText>
<div class="d-flex justify-end pt-4">
<MudFab Size="Size.Medium" Color="Color.Info"
EndIcon="@Icons.Material.Filled.Replay"
OnClick="@OnRetryClickedAsync" Label="Retry" />
</div>
</MudStack>
</MudPaper>
19 changes: 19 additions & 0 deletions Azure.OpenAI/Client/Components/AnswerError.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) David Pine. All rights reserved.
// Licensed under the MIT License.

namespace Azure.OpenAI.Client.Components;

public sealed partial class AnswerError
{
[Parameter, EditorRequired] public required UserQuestion Question { get; set; }
[Parameter, EditorRequired] public required string Error { get; set; }
[Parameter, EditorRequired] public required EventCallback<UserQuestion> OnRetryClicked { get; set; }

private async Task OnRetryClickedAsync()
{
if (OnRetryClicked.HasDelegate)
{
await OnRetryClicked.InvokeAsync(Question);
}
}
}
1 change: 1 addition & 0 deletions Azure.OpenAI/Client/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
global using Azure.OpenAI.Client.Extensions;
global using Azure.OpenAI.Client.Interop;
global using Azure.OpenAI.Client.Models;
global using Microsoft.Extensions.ObjectPool;
global using Azure.OpenAI.Client.Services;
global using Azure.OpenAI.Shared;
global using Blazor.Serialization.Extensions;
Expand Down
12 changes: 12 additions & 0 deletions Azure.OpenAI/Client/Models/GeneratedAnswer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) David Pine. All rights reserved.
// Licensed under the MIT License.

namespace Azure.OpenAI.Client.Models;

public readonly record struct GeneratedAnswer(
string? Answer = null,
string? Error = null)
{
[JsonIgnore]
public bool ContainsError => string.IsNullOrWhiteSpace(Error) is false;
}
3 changes: 2 additions & 1 deletion Azure.OpenAI/Client/Models/PromptResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ namespace Azure.OpenAI.Client.Models;
public record class PromptResponse(
string Prompt,
string Response,
bool IsComplete = false);
bool IsComplete = false,
bool IsError = false);
2 changes: 1 addition & 1 deletion Azure.OpenAI/Client/Models/QuestionAndAnswer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ namespace Azure.OpenAI.Client.Models;

public record class QuestionAndAnswer(
UserQuestion Question,
string? Answer = null);
GeneratedAnswer? Answer = null);
16 changes: 12 additions & 4 deletions Azure.OpenAI/Client/Pages/VoiceChat.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@(Localizer["Interact_with"])
</MudText>
<MudText Typo="Typo.h1" Class="align-self-center"
Style="font-family: monospace;">
Style="font-family: monospace;">
Blazor 📎 Clippy
</MudText>
<MudText Typo="Typo.h2" Class="align-self-center">
Expand All @@ -36,7 +36,7 @@
Icon="@Icons.Material.Filled.QuestionMark" Class="d-flex align-self-end">
<MudPaper Class="pa-6 d-flex flex-column" Elevation="3">
<MudText Typo="Typo.body1" Align="Align.End">
@((MarkupString)question.Question)
@((MarkupString)question.Question.ToHtml())
</MudText>
<MudText Typo="Typo.caption" Align="Align.End" Color="Color.Secondary">
@Localizer["AskedOnFormat", askedOn]
Expand All @@ -57,13 +57,21 @@
</MudPaper>
</MudBadge>
}
else if (answer.Value.ContainsError)
{
<MudBadge Origin="Origin.TopLeft" Overlap="true" Color="Color.Error"
Icon="@Icons.Material.Filled.Error">
<AnswerError Error="@answer.Value.Error" Question="@question"
OnRetryClicked="@OnAskQuestionAsync" />
</MudBadge>
}
else
{
<MudBadge Origin="Origin.TopLeft" Overlap="true" Color="Color.Secondary"
Icon="@Icons.Material.Filled.AutoAwesome">
<MudPaper Class="pa-6" Elevation="3">
<MudText Typo="Typo.body1">
@((MarkupString)answer)
@((MarkupString)(answer.Value.Answer ?? "Unable to generate answer."))
</MudText>
</MudPaper>
</MudBadge>
Expand Down Expand Up @@ -108,7 +116,7 @@
<MudItem xs="9" Class="pa-2">
<MudFab Color="Color.Secondary" Label=@Localizer["Ask"] Size="Size.Large"
StartIcon=@Icons.Material.Filled.Send Class="full-width"
OnClick="@OnSendPrompt"
OnClick="@(_ => OnSendPrompt())"
Disabled=@(_isRecognizingSpeech || _isReceivingResponse || string.IsNullOrWhiteSpace(_userQuestion)) />
</MudItem>
<MudItem xs="3" Class="pa-2">
Expand Down
40 changes: 26 additions & 14 deletions Azure.OpenAI/Client/Pages/VoiceChat.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ namespace Azure.OpenAI.Client.Pages;
public sealed partial class VoiceChat : IDisposable
{
private const string AnswerElementId = "replies";
private const string SessionChatHistoryKey = "session-chat-history";

private DateTime _askedOn;
private string _userQuestion = "";
Expand All @@ -31,7 +30,7 @@ public sealed partial class VoiceChat : IDisposable

protected override void OnInitialized()
{
if (SessionStorage.GetItem<Dictionary<DateTime, QuestionAndAnswer>>(SessionChatHistoryKey)
if (SessionStorage.GetItem<Dictionary<DateTime, QuestionAndAnswer>>(StorageKeys.SessionChatHistoryKey)
is { } map)
{
if (map is null or { Count: 0 })
Expand All @@ -41,12 +40,12 @@ protected override void OnInitialized()

_questionAndAnswerMap = map;

_ = Task.Delay(TimeSpan.FromSeconds(1))
_ = Task.Delay(TimeSpan.FromSeconds(.2))
.ContinueWith(
_ => JavaScriptModule.ScrollIntoView(AnswerElementId));
}

State.OnDeleteClicked += OnDeleteHistoryClick;
State.OnDeleteHistoryClicked += OnDeleteHistoryClick;
}

protected override async Task OnAfterRenderAsync(bool firstRender)
Expand All @@ -57,32 +56,47 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
}
}

private void OnSendPrompt()
private Task OnAskQuestionAsync(UserQuestion userQuestion)
{
_currentQuestion = userQuestion;

OnSendPrompt(true);

return Task.CompletedTask;
}

private void OnSendPrompt(bool repeatQuestion = false)
{
if (_isReceivingResponse || string.IsNullOrWhiteSpace(_userQuestion))
{
return;
}

_isReceivingResponse = true;
_askedOn = DateTime.Now;
_currentQuestion = new(_userQuestion.ToHtml(), _askedOn);
if (repeatQuestion is false)
{
_askedOn = DateTime.Now;
_currentQuestion = new(_userQuestion, _askedOn);
}

_questionAndAnswerMap[_askedOn] = new QuestionAndAnswer(_currentQuestion);

OpenAIPrompts.Enqueue(
_userQuestion,
async (PromptResponse response) => await InvokeAsync(() =>
{
var (_, responseText, isComplete) = response;
var (_, responseText, isComplete, isError) = response;
var html = responseText.ToHtml();
_questionAndAnswerMap[_askedOn] = _questionAndAnswerMap[_askedOn] with
{
Answer = html
Answer = isError ? new(Answer: null, Error: html) : new(Answer: html)
};
SessionStorage.SetItem(StorageKeys.SessionChatHistoryKey, _questionAndAnswerMap);
_isReceivingResponse = isComplete is false;
if (isComplete)
if (isComplete && isError is false)
{
TrySpeakResponse(responseText);
ResetState();
Expand Down Expand Up @@ -125,8 +139,6 @@ private void TrySpeakResponse(string responseText)

private void ResetState()
{
SessionStorage.SetItem(SessionChatHistoryKey, _questionAndAnswerMap);

_userQuestion = "";
_currentQuestion = default;
}
Expand All @@ -137,7 +149,7 @@ private void OnDeleteHistoryClick()
_currentQuestion = default;
_questionAndAnswerMap.Clear();

SessionStorage.RemoveItem(SessionChatHistoryKey);
SessionStorage.RemoveItem(StorageKeys.SessionChatHistoryKey);
StateHasChanged();
}

Expand Down Expand Up @@ -224,6 +236,6 @@ public void Dispose()
{
_recognitionSubscription?.Dispose();

State.OnDeleteClicked -= OnDeleteHistoryClick;
State.OnDeleteHistoryClicked -= OnDeleteHistoryClick;
}
}
3 changes: 3 additions & 0 deletions Azure.OpenAI/Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
builder.Services.AddMudServices();
builder.Services.AddLocalization();
builder.Services.AddScoped<CultureService>();
builder.Services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
builder.Services.AddSingleton(
sp => sp.GetRequiredService<ObjectPoolProvider>().CreateStringBuilderPool());

var host = builder.Build()
.DetectClientCulture();
Expand Down
4 changes: 2 additions & 2 deletions Azure.OpenAI/Client/Services/AppState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Azure.OpenAI.Client.Services;

public sealed class AppState
{
public event Action? OnDeleteClicked;
public event Action? OnDeleteHistoryClicked;

public void DeleteClicked() => OnDeleteClicked?.Invoke();
public void DeleteHistoryClicked() => OnDeleteHistoryClicked?.Invoke();
}
8 changes: 1 addition & 7 deletions Azure.OpenAI/Client/Services/CultureService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@ public sealed class CultureService

var cultures = await client.GetFromJsonAsync<SharedCultures>(
"languages?api-version=3.0&scope=translation",
new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
Converters =
{
new JsonStringEnumConverter()
}
});
JsonSerializationDefaults.Options);

if (cultures is null or { AvailableCultures.Count: 0 })
{
Expand Down
Loading

0 comments on commit c49113d

Please sign in to comment.