-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added the writer mode experiment (#167)
- Loading branch information
1 parent
432ae30
commit 0a951ea
Showing
6 changed files
with
239 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
@attribute [Route(Routes.WRITER)] | ||
@inherits MSGComponentBase | ||
|
||
<MudText Typo="Typo.h3" Class="mb-2 mr-3"> | ||
Writer | ||
</MudText> | ||
|
||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings"/> | ||
<InnerScrolling HeaderHeight="12.3em"> | ||
<ChildContent> | ||
<MudTextField | ||
@ref="@this.textField" | ||
T="string" | ||
Label="Write your text" | ||
@bind-Text="@this.userInput" | ||
Immediate="@true" | ||
Lines="16" | ||
MaxLines="16" | ||
Typo="Typo.body1" | ||
Variant="Variant.Outlined" | ||
InputMode="InputMode.text" | ||
FullWidth="@true" | ||
OnKeyDown="@this.InputKeyEvent" | ||
UserAttributes="@USER_INPUT_ATTRIBUTES"/> | ||
|
||
<MudTextField | ||
T="string" | ||
Label="Your stage directions" | ||
@bind-Text="@this.userDirection" | ||
Immediate="@true" | ||
Lines="4" | ||
MaxLines="4" | ||
Typo="Typo.body1" | ||
Variant="Variant.Outlined" | ||
InputMode="InputMode.text" | ||
FullWidth="@true" | ||
UserAttributes="@USER_INPUT_ATTRIBUTES"/> | ||
</ChildContent> | ||
<FooterContent> | ||
@if (this.isStreaming) | ||
{ | ||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-6" /> | ||
} | ||
<MudTextField | ||
T="string" | ||
Label="Suggestion" | ||
@bind-Text="@this.suggestion" | ||
ReadOnly="@true" | ||
Lines="3" | ||
Typo="Typo.body1" | ||
Variant="Variant.Outlined" | ||
FullWidth="@true" | ||
UserAttributes="@USER_INPUT_ATTRIBUTES"/> | ||
</FooterContent> | ||
</InnerScrolling> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
using AIStudio.Chat; | ||
using AIStudio.Components; | ||
using AIStudio.Provider; | ||
|
||
using Microsoft.AspNetCore.Components; | ||
using Microsoft.AspNetCore.Components.Web; | ||
|
||
using Timer = System.Timers.Timer; | ||
|
||
namespace AIStudio.Pages; | ||
|
||
public partial class Writer : MSGComponentBase, IAsyncDisposable | ||
{ | ||
[Inject] | ||
private ILogger<Chat> Logger { get; init; } = null!; | ||
|
||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new(); | ||
private readonly Timer typeTimer = new(TimeSpan.FromMilliseconds(1_500)); | ||
|
||
private MudTextField<string> textField = null!; | ||
private AIStudio.Settings.Provider providerSettings; | ||
private ChatThread? chatThread; | ||
private bool isStreaming; | ||
private string userInput = string.Empty; | ||
private string userDirection = string.Empty; | ||
private string suggestion = string.Empty; | ||
|
||
#region Overrides of ComponentBase | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
this.ApplyFilters([], []); | ||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES); | ||
this.typeTimer.Elapsed += async (_, _) => await this.InvokeAsync(this.GetSuggestions); | ||
this.typeTimer.AutoReset = false; | ||
|
||
await base.OnInitializedAsync(); | ||
} | ||
|
||
#endregion | ||
|
||
#region Overrides of MSGComponentBase | ||
|
||
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default | ||
{ | ||
return Task.FromResult(default(TResult)); | ||
} | ||
|
||
#endregion | ||
|
||
private bool IsProviderSelected => this.providerSettings.UsedLLMProvider != LLMProviders.NONE; | ||
|
||
private async Task InputKeyEvent(KeyboardEventArgs keyEvent) | ||
{ | ||
var key = keyEvent.Code.ToLowerInvariant(); | ||
var isTab = key is "tab"; | ||
var isModifier = keyEvent.AltKey || keyEvent.CtrlKey || keyEvent.MetaKey || keyEvent.ShiftKey; | ||
|
||
if (isTab && !isModifier) | ||
{ | ||
await this.textField.FocusAsync(); | ||
this.AcceptNextWord(); | ||
return; | ||
} | ||
|
||
if (isTab && isModifier) | ||
{ | ||
await this.textField.FocusAsync(); | ||
this.AcceptEntireSuggestion(); | ||
return; | ||
} | ||
|
||
if(!isModifier) | ||
{ | ||
this.typeTimer.Stop(); | ||
this.typeTimer.Start(); | ||
} | ||
} | ||
|
||
private async Task GetSuggestions() | ||
{ | ||
if (!this.IsProviderSelected) | ||
return; | ||
|
||
this.chatThread ??= new() | ||
{ | ||
WorkspaceId = Guid.Empty, | ||
ChatId = Guid.NewGuid(), | ||
Name = string.Empty, | ||
Seed = 798798, | ||
SystemPrompt = """ | ||
You are an assistant who helps with writing documents. You receive a sample | ||
from a document as input. As output, you provide how the begun sentence could | ||
continue. You give exactly one variant, not multiple. If the current sentence | ||
is complete, you provide an empty response. You do not ask questions, and you | ||
do not repeat the task. | ||
""", | ||
Blocks = [], | ||
}; | ||
|
||
var time = DateTimeOffset.Now; | ||
this.chatThread.Blocks.Clear(); | ||
this.chatThread.Blocks.Add(new ContentBlock | ||
{ | ||
Time = time, | ||
ContentType = ContentType.TEXT, | ||
Role = ChatRole.USER, | ||
Content = new ContentText | ||
{ | ||
// We use the maximum 160 characters from the end of the text: | ||
Text = this.userInput.Length > 160 ? this.userInput[^160..] : this.userInput, | ||
}, | ||
}); | ||
|
||
var aiText = new ContentText | ||
{ | ||
// We have to wait for the remote | ||
// for the content stream: | ||
InitialRemoteWait = true, | ||
}; | ||
|
||
this.chatThread?.Blocks.Add(new ContentBlock | ||
{ | ||
Time = time, | ||
ContentType = ContentType.TEXT, | ||
Role = ChatRole.AI, | ||
Content = aiText, | ||
}); | ||
|
||
this.isStreaming = true; | ||
this.StateHasChanged(); | ||
|
||
await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.SettingsManager, this.providerSettings.Model, this.chatThread); | ||
this.suggestion = aiText.Text; | ||
|
||
this.isStreaming = false; | ||
this.StateHasChanged(); | ||
} | ||
|
||
private void AcceptEntireSuggestion() | ||
{ | ||
if(this.userInput.Last() != ' ') | ||
this.userInput += ' '; | ||
|
||
this.userInput += this.suggestion; | ||
this.suggestion = string.Empty; | ||
this.StateHasChanged(); | ||
} | ||
|
||
private void AcceptNextWord() | ||
{ | ||
var words = this.suggestion.Split(' ', StringSplitOptions.RemoveEmptyEntries); | ||
if(words.Length == 0) | ||
return; | ||
|
||
if(this.userInput.Last() != ' ') | ||
this.userInput += ' '; | ||
|
||
this.userInput += words[0] + ' '; | ||
this.suggestion = string.Join(' ', words.Skip(1)); | ||
this.StateHasChanged(); | ||
} | ||
|
||
#region Implementation of IAsyncDisposable | ||
|
||
public ValueTask DisposeAsync() | ||
{ | ||
return ValueTask.CompletedTask; | ||
} | ||
|
||
#endregion | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters