Skip to content

Commit

Permalink
Added Blazor sample. (#12)
Browse files Browse the repository at this point in the history
* Added Blazor sample.

* Added missed files.

* Collapsed if statements.
  • Loading branch information
johniwasz authored Jan 16, 2023
1 parent f8424a0 commit 1ae19e9
Show file tree
Hide file tree
Showing 57 changed files with 21,438 additions and 14 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Supported features include:

- Command line bot
- Azure Function Twitter Webhook that responds to DMs
- Blazor UI

## Dependency Injection Quickstart

Expand Down
3 changes: 2 additions & 1 deletion src/Whetstone.ChatGPT.Test/ChatClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ public async Task GPTModel()
[Fact]
public void NullSessionConstruction()
{
Assert.Throws<ArgumentException>(() => new ChatGPTClient((string?) null));
IChatGPTClient constructedClient = new ChatGPTClient((string?) null);
Assert.NotNull(constructedClient);
}

/*
Expand Down
12 changes: 12 additions & 0 deletions src/Whetstone.ChatGPT.Test/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"Whetstone.ChatGPT.Test": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:52444;http://localhost:52449"
}
}
}
10 changes: 10 additions & 0 deletions src/Whetstone.ChatGPT.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whetstone.TweetGPT.DirectMe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whetstone.WebhookCommander", "examples\twitter-webhook\Whetstone.WebhookCommander\Whetstone.WebhookCommander.csproj", "{C9A56EE8-1A8C-4C84-AAC1-EA1863D9D890}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "blazor", "blazor", "{FC6A47D8-BB5F-41C7-B618-8DFF73A0F692}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whetstone.ChatGPT.Blazor.App", "examples\blazor\Whetstone.ChatGPT.Blazor.App\Whetstone.ChatGPT.Blazor.App\Whetstone.ChatGPT.Blazor.App.csproj", "{D6126997-07B8-40AF-AC0D-44AD5DCE99D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -63,6 +67,10 @@ Global
{C9A56EE8-1A8C-4C84-AAC1-EA1863D9D890}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9A56EE8-1A8C-4C84-AAC1-EA1863D9D890}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9A56EE8-1A8C-4C84-AAC1-EA1863D9D890}.Release|Any CPU.Build.0 = Release|Any CPU
{D6126997-07B8-40AF-AC0D-44AD5DCE99D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6126997-07B8-40AF-AC0D-44AD5DCE99D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6126997-07B8-40AF-AC0D-44AD5DCE99D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6126997-07B8-40AF-AC0D-44AD5DCE99D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -75,6 +83,8 @@ Global
{62C1E24D-745D-4FD9-93EF-CE5608C6149C} = {4C926884-5370-4997-BA8D-9A52C7FEE1C7}
{833D6DC5-60F7-411F-813A-EC99825DD712} = {4C926884-5370-4997-BA8D-9A52C7FEE1C7}
{C9A56EE8-1A8C-4C84-AAC1-EA1863D9D890} = {4C926884-5370-4997-BA8D-9A52C7FEE1C7}
{FC6A47D8-BB5F-41C7-B618-8DFF73A0F692} = {F931A1C0-3807-4C06-8957-DB604C6544CD}
{D6126997-07B8-40AF-AC0D-44AD5DCE99D9} = {FC6A47D8-BB5F-41C7-B618-8DFF73A0F692}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0770FC85-633A-4988-B2E1-EC344D55FD44}
Expand Down
27 changes: 16 additions & 11 deletions src/Whetstone.ChatGPT/ChatGPTClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
#if NET6_0_OR_GREATER
Expand All @@ -32,10 +33,12 @@ public class ChatGPTClient : IChatGPTClient

private readonly bool _isHttpClientProvided = true;

private readonly ChatGPTCredentials _chatCredentials;
private ChatGPTCredentials? _chatCredentials;

private bool _isDisposed;

public ChatGPTCredentials? Credentials { set => _chatCredentials = value; }

#region Constructors

/// <summary>
Expand Down Expand Up @@ -87,6 +90,8 @@ public ChatGPTClient(IOptions<ChatGPTCredentials> credentialsOptions, HttpClient
{
}



/// <summary>
/// Creates a new instance of the <see cref="ChatGPTClient"/> class.
/// </summary>
Expand All @@ -95,16 +100,6 @@ public ChatGPTClient(IOptions<ChatGPTCredentials> credentialsOptions, HttpClient
/// <exception cref="ArgumentException">API Key is required.</exception>
private ChatGPTClient(ChatGPTCredentials credentials, HttpClient httpClient)
{
if (credentials is null)
{
throw new ArgumentNullException(nameof(credentials));
}

if (string.IsNullOrWhiteSpace(credentials.ApiKey))
{
throw new ArgumentException("ApiKey preoperty cannot be null or whitespace.", nameof(credentials));
}

_chatCredentials = credentials;

if (httpClient is null)
Expand Down Expand Up @@ -909,6 +904,16 @@ private HttpRequestMessage CreateRequestMessage(HttpMethod method, string url)
{
HttpRequestMessage request = new(method, url);

if (_chatCredentials is null)
{
throw new ChatGPTException("ChatGPTCredentials are null.");
}

if (string.IsNullOrWhiteSpace(_chatCredentials.ApiKey))
{
throw new ChatGPTException("ApiKey property cannot be null or whitespace.");
}

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _chatCredentials.ApiKey);

if (!string.IsNullOrWhiteSpace(_chatCredentials.Organization))
Expand Down
5 changes: 5 additions & 0 deletions src/Whetstone.ChatGPT/IChatGPTClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,5 +298,10 @@ public interface IChatGPTClient : IDisposable
/// <exception cref="ArgumentException">Requires generatedImage</exception>
/// <exception cref="ChatGPTException">Exception generated while processing request.</exception>
Task<byte[]?> DownloadImageAsync(GeneratedImage generatedImage, CancellationToken? cancellationToken = null);

/// <summary>
/// Apply new credentails.
/// </summary>
public ChatGPTCredentials? Credentials { set; }
}
}
8 changes: 8 additions & 0 deletions src/Whetstone.ChatGPT/Models/ChatGPTCredentials.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

#if NET6_0_OR_GREATER
using System.ComponentModel.DataAnnotations;
#endif

namespace Whetstone.ChatGPT.Models
{

Expand Down Expand Up @@ -45,6 +49,10 @@ public ChatGPTCredentials(string apiKey, string? organization)
/// The OpenAI API uses API keys for authentication. Visit your <see href="https://beta.openai.com/account/api-keys">API Keys</see> page to retrieve the API key you'll use in your requests.
/// </summary>
/// <remarks>Required for authentication.</remarks>
#if NET6_0_OR_GREATER
[Required]
[DataType(DataType.Password)]
#endif
public string? ApiKey
{
get;
Expand Down
8 changes: 7 additions & 1 deletion src/Whetstone.ChatGPT/Whetstone.ChatGPT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
<Title>Whetstone ChatGPT</Title>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>
# 1.3.0

- Added set-only Credentials property to IChatGPTClient so that credentials can be changed after instantiation.
- Moved Credentials validation from constructor to API invocation pre-flight checks.
- Started Blazor sample project.

# 1.2.0

- Added support for dependency injection
Expand All @@ -24,7 +30,7 @@
<PackageTags>chatgpt; api; openapi; gpt; gpt-3</PackageTags>
<RepositoryUrl>https://github.com/johniwasz/whetstone.chatgpt</RepositoryUrl>
<Copyright>2023</Copyright>
<Version>1.2.0</Version>
<Version>1.3.0</Version>
<PackageProjectUrl>https://github.com/johniwasz/whetstone.chatgpt</PackageProjectUrl>
<Description>A simple light-weight library that wraps the ChatGPT API. Includes support for dependency injection.</Description>
<PackageIcon>packlogo.png</PackageIcon>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"Whetstone.ChatGPT.CommandLineBot": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:52443;http://localhost:52448"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"Whetstone.ChatGPT.SimpleCommandLineBot": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:52450;http://localhost:52452"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<Blazorise.ThemeProvider Theme="@theme">
<Router AppAssembly="typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Whetstone.ChatGPT.Blazor.App.Layouts.MainLayout)" />
</Found>
<NotFound>
<p>Sorry, there's nothing at this address.</p>
</NotFound>
</Router>
<MessageProvider />
< NotificationProvider />
<PageProgressProvider />
</Blazorise.ThemeProvider>
@code {
private Theme theme = new()
{
BarOptions = new()
{
HorizontalHeight = "72px"
},
ColorOptions = new()
{
Primary = "#0288D1",
Secondary = "#A65529",
Success = "#23C02E",
Info = "#9BD8FE",
Warning = "#F8B86C",
Danger = "#F95741",
Light = "#F0F0F0",
Dark = "#535353",
},
BackgroundOptions = new()
{
Primary = "#0288D1",
Secondary = "#A65529",
Success = "#23C02E",
Info = "#9BD8FE",
Warning = "#F8B86C",
Danger = "#F95741",
Light = "#F0F0F0",
Dark = "#535353",
},
InputOptions = new()
{
CheckColor = "#0288D1",
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

@inject NavigationManager Navigation

@inject ApplicationState CurrentState
@inject IChatGPTClient ChatClient



<h4>
@if (CurrentState.IsOpenAIAuthenticated)
{
<Button Clicked="@PurgeCredentials"><Blazorise.Icons.FontAwesome.Icon Name="IconName.LockOpen" IconSize="IconSize.Large" />Clear API Key</Button>
}
else
{
<Button Clicked="@ShowLogin">
<Blazorise.Icons.FontAwesome.Icon Name="IconName.LockOpen" IconSize="IconSize.Large"/>Validate API Key</Button>
}
</h4>

<LogIn @ref="loginModal"></LogIn>

<!--
<Button Color="" @onclick="() => smModal?.ShowAsync()">Small modal</Button>
<Modal @ref="smModal" Title="Small modal" Size="ModalSize.Small">
<BodyTemplate>Small modal</BodyTemplate>
</Modal>
-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

using Blazorise.Bootstrap5;
using Whetstone.ChatGPT.Blazor.App.State;

namespace Whetstone.ChatGPT.Blazor.App.Components
{
public partial class AuthenticationStatus
{
private LogIn? loginModal;

private void ShowLogin()
{
if (loginModal is not null)
{
loginModal?.Show();
}
}

public void PurgeCredentials()
{
ChatClient.Credentials = null;
CurrentState.IsOpenAIAuthenticated = false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

<Alert Color="Color.Danger" @ref="errAlert">
<CloseButton Float="Float.End" Clicked="@ErrorClosedAsync"/>
<AlertDescription>
@if(StatusCode is not null)
{
<div class="row">
Status: @StatusCode
</div>
}

@if(chatError is null)
{
<div class="row">
Message: @exception?.Message
</div>
}
else
{
@if (chatError?.Message is not null)
{
<div class="row">
Message: @chatError.Message
</div>
}

@if (chatError?.Type is not null)
{
<div class="row">
Type: @chatError.Type
</div>
}

@if (chatError?.Code is not null)
{
<div class="row">
Code: @chatError.Code
</div>
}
}
</AlertDescription>

</Alert>
Loading

0 comments on commit 1ae19e9

Please sign in to comment.