diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor b/app/MindWork AI Studio/Assistants/AssistantBase.razor
index 9555278..ab8a01f 100644
--- a/app/MindWork AI Studio/Assistants/AssistantBase.razor
+++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor
@@ -48,17 +48,20 @@
@if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO))
{
-
- @foreach (var assistant in Enum.GetValues().OrderBy(n => n.Name().Length))
- {
- if (assistant is Components.NONE || this.Component == assistant)
- continue;
+ @if (this.ShowSendTo)
+ {
+
+ @foreach (var assistant in Enum.GetValues().OrderBy(n => n.Name().Length))
+ {
+ if (assistant is Components.NONE || this.Component == assistant)
+ continue;
-
- @assistant.Name()
-
- }
-
+
+ @assistant.Name()
+
+ }
+
+ }
}
@foreach (var button in this.FooterButtons)
@@ -95,13 +98,19 @@
}
}
-
- Copy result
-
-
-
- Reset
-
+ @if (this.ShowCopyResult)
+ {
+
+ Copy result
+
+ }
+
+ @if (this.ShowReset)
+ {
+
+ Reset
+
+ }
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
{
diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
index 8135850..1a7b404 100644
--- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
+++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
@@ -74,6 +74,12 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
protected virtual bool ShowProfileSelection => true;
protected virtual bool ShowDedicatedProgress => false;
+
+ protected virtual bool ShowSendTo => true;
+
+ protected virtual bool ShowCopyResult => true;
+
+ protected virtual bool ShowReset => true;
protected virtual ChatThread ConvertToChatThread => this.chatThread ?? new();
@@ -173,14 +179,36 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
Blocks = [],
};
}
+
+ protected Guid CreateChatThread(Guid workspaceId, string name)
+ {
+ var chatId = Guid.NewGuid();
+ this.chatThread = new()
+ {
+ WorkspaceId = workspaceId,
+ ChatId = chatId,
+ Name = name,
+ Seed = this.RNG.Next(),
+ SystemPrompt = !this.AllowProfiles ? this.SystemPrompt :
+ $"""
+ {this.SystemPrompt}
+
+ {this.currentProfile.ToSystemPrompt()}
+ """,
+ Blocks = [],
+ };
+
+ return chatId;
+ }
- protected DateTimeOffset AddUserRequest(string request)
+ protected DateTimeOffset AddUserRequest(string request, bool hideContentFromUser = false)
{
var time = DateTimeOffset.Now;
this.chatThread!.Blocks.Add(new ContentBlock
{
Time = time,
ContentType = ContentType.TEXT,
+ HideFromUser = hideContentFromUser,
Role = ChatRole.USER,
Content = new ContentText
{
@@ -237,9 +265,9 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
return icon;
}
- private Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
+ protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
{
- var contentToSend = sendToButton.UseResultingContentBlockData switch
+ var contentToSend = sendToButton == default ? string.Empty : sendToButton.UseResultingContentBlockData switch
{
false => sendToButton.GetText(),
true => this.resultingContentBlock?.Content switch
diff --git a/app/MindWork AI Studio/Assistants/BiasDay/BiasOfTheDayAssistant.razor b/app/MindWork AI Studio/Assistants/BiasDay/BiasOfTheDayAssistant.razor
new file mode 100644
index 0000000..f5acf48
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/BiasDay/BiasOfTheDayAssistant.razor
@@ -0,0 +1,14 @@
+@attribute [Route(Routes.ASSISTANT_BIAS)]
+@inherits AssistantBaseCore
+
+
+ Links:
+
+
+ Wikipedia list of cognitive biases
+ Extended bias poster
+ Blog post of Buster Benson: "Cognitive bias cheat sheet"
+
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Assistants/BiasDay/BiasOfTheDayAssistant.razor.cs b/app/MindWork AI Studio/Assistants/BiasDay/BiasOfTheDayAssistant.razor.cs
new file mode 100644
index 0000000..a7a750c
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/BiasDay/BiasOfTheDayAssistant.razor.cs
@@ -0,0 +1,164 @@
+using System.Text;
+
+using AIStudio.Components;
+using AIStudio.Settings.DataModel;
+
+namespace AIStudio.Assistants.BiasDay;
+
+public partial class BiasOfTheDayAssistant : AssistantBaseCore
+{
+ public override Tools.Components Component => Tools.Components.BIAS_DAY_ASSISTANT;
+
+ protected override string Title => "Bias of the Day";
+
+ protected override string Description =>
+ """
+ Learn about a different cognitive bias every day. You can also ask the LLM your questions. The idea behind
+ "Bias of the Day" is based on work by Buster Benson, John Manoogian III, and Brian Rene Morrissette. Buster
+ Benson grouped the biases, and the original texts come from Wikipedia. Brian Rene Morrissette condensed them
+ into a shorter version. Finally, John Manoogian III created the original poster based on Benson's work and
+ Morrissette's texts. Thorsten Sommer compared all texts for integration into AI Studio with the current Wikipedia
+ versions, updated them, and added source references. The idea of learning about one bias each day based on John's
+ poster comes from Drew Nelson.
+ """;
+
+ protected override string SystemPrompt => $"""
+ You are a friendly, helpful expert on cognitive bias. You studied psychology and
+ have a lot of experience. You explain a bias every day. Today's bias belongs to
+ the category: "{this.biasOfTheDay.Category.ToName()}". We have the following
+ thoughts on this category:
+
+ {this.biasOfTheDay.Category.GetThoughts()}
+
+ Today's bias is:
+ {this.biasOfTheDay.Description}
+ {this.SystemPromptSources()}
+ Important: you use the following language: {this.SystemPromptLanguage()}. Please
+ ask the user a personal question at the end to encourage them to think about
+ this bias.
+ """;
+
+ protected override IReadOnlyList FooterButtons => [];
+
+ protected override string SubmitText => "Show me the bias of the day";
+
+ protected override Func SubmitAction => this.TellBias;
+
+ protected override bool ShowSendTo => false;
+
+ protected override bool ShowCopyResult => false;
+
+ protected override bool ShowReset => false;
+
+ protected override void ResetFrom()
+ {
+ if (!this.MightPreselectValues())
+ {
+ this.selectedTargetLanguage = CommonLanguages.AS_IS;
+ this.customTargetLanguage = string.Empty;
+ }
+ }
+
+ protected override bool MightPreselectValues()
+ {
+ if (this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)
+ {
+ this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage;
+ this.customTargetLanguage = this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage;
+ return true;
+ }
+
+ return false;
+ }
+
+ private Bias biasOfTheDay = BiasCatalog.NONE;
+ private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
+ private string customTargetLanguage = string.Empty;
+
+ private string? ValidateTargetLanguage(CommonLanguages language)
+ {
+ if(language is CommonLanguages.AS_IS)
+ return "Please select a target language for the bias.";
+
+ return null;
+ }
+
+ private string? ValidateCustomLanguage(string language)
+ {
+ if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
+ return "Please provide a custom language.";
+
+ return null;
+ }
+
+ private string SystemPromptSources()
+ {
+ var sb = new StringBuilder();
+ if (this.biasOfTheDay.Links.Count > 0)
+ {
+ sb.AppendLine();
+ sb.AppendLine("Please share the following sources with the user as a Markdown list:");
+ foreach (var link in this.biasOfTheDay.Links)
+ sb.AppendLine($"- {link}");
+
+ sb.AppendLine();
+ }
+
+ return sb.ToString();
+ }
+
+ private string SystemPromptLanguage()
+ {
+ if(this.selectedTargetLanguage is CommonLanguages.OTHER)
+ return this.customTargetLanguage;
+
+ return this.selectedTargetLanguage.Name();
+ }
+
+ private async Task TellBias()
+ {
+ bool useDrawnBias = false;
+ if(this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay)
+ {
+ if(this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn == DateOnly.FromDateTime(DateTime.Now))
+ {
+ var biasChat = new LoadChat
+ {
+ WorkspaceId = Workspaces.WORKSPACE_ID_BIAS,
+ ChatId = this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayChatId,
+ };
+
+ if (Workspaces.IsChatExisting(biasChat))
+ {
+ MessageBus.INSTANCE.DeferMessage(this, Event.LOAD_CHAT, biasChat);
+ this.NavigationManager.NavigateTo(Routes.CHAT);
+ return;
+ }
+ else
+ useDrawnBias = true;
+ }
+ }
+
+ await this.form!.Validate();
+ if (!this.inputIsValid)
+ return;
+
+ this.biasOfTheDay = useDrawnBias ?
+ BiasCatalog.ALL_BIAS[this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayId] :
+ BiasCatalog.GetRandomBias(this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias);
+
+ var chatId = this.CreateChatThread(Workspaces.WORKSPACE_ID_BIAS, this.biasOfTheDay.Name);
+ this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayId = this.biasOfTheDay.Id;
+ this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayChatId = chatId;
+ this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn = DateOnly.FromDateTime(DateTime.Now);
+ await this.SettingsManager.StoreSettings();
+ var time = this.AddUserRequest(
+ """
+ Please tell me about the bias of the day.
+ """, true);
+
+ // Start the AI response without waiting for it to finish:
+ _ = this.AddAIResponseAsync(time);
+ await this.SendToAssistant(Tools.Components.CHAT, default);
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Chat/ContentBlock.cs b/app/MindWork AI Studio/Chat/ContentBlock.cs
index 05a6bd1..632d98d 100644
--- a/app/MindWork AI Studio/Chat/ContentBlock.cs
+++ b/app/MindWork AI Studio/Chat/ContentBlock.cs
@@ -18,10 +18,15 @@ public class ContentBlock
///
/// The content of the block.
///
- public IContent? Content { get; init; } = null;
+ public IContent? Content { get; init; }
///
/// The role of the content block in the chat thread, e.g., user, AI, etc.
///
public ChatRole Role { get; init; } = ChatRole.NONE;
+
+ ///
+ /// Should the content block be hidden from the user?
+ ///
+ public bool HideFromUser { get; set; }
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
index 756d3a6..2bef5f9 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
@@ -64,7 +64,7 @@ public partial class ContentBlockComponent : ComponentBase
private async Task AfterStreaming()
{
// Might be called from a different thread, so we need to invoke the UI thread:
- await this.InvokeAsync(() =>
+ await this.InvokeAsync(async () =>
{
//
// Issue we try to solve: When the content changes during streaming,
@@ -83,6 +83,9 @@ public partial class ContentBlockComponent : ComponentBase
// Let Blazor update the UI, i.e., to see the render tree diff:
this.StateHasChanged();
+
+ // Inform the chat that the streaming is done:
+ await MessageBus.INSTANCE.SendMessage(this, Event.CHAT_STREAMING_DONE);
});
}
diff --git a/app/MindWork AI Studio/Components/Workspaces.razor.cs b/app/MindWork AI Studio/Components/Workspaces.razor.cs
index edf0d28..fcb845e 100644
--- a/app/MindWork AI Studio/Components/Workspaces.razor.cs
+++ b/app/MindWork AI Studio/Components/Workspaces.razor.cs
@@ -37,6 +37,7 @@ public partial class Workspaces : ComponentBase
private const Placement WORKSPACE_ITEM_TOOLTIP_PLACEMENT = Placement.Bottom;
+ public static readonly Guid WORKSPACE_ID_BIAS = Guid.Parse("82050a4e-ee92-43d7-8ee5-ab512f847e02");
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
{
WriteIndented = true,
@@ -63,6 +64,7 @@ public partial class Workspaces : ComponentBase
// - When assigning the tree items to the MudTreeViewItem component, we must set the Value property to the value of the item
//
+ await this.EnsureBiasWorkspace();
await this.LoadTreeItems();
await base.OnInitializedAsync();
}
@@ -250,7 +252,7 @@ public partial class Workspaces : ComponentBase
return result;
}
- public async Task StoreChat(ChatThread chat)
+ public async Task StoreChat(ChatThread chat, bool reloadTreeItems = true)
{
string chatDirectory;
if (chat.WorkspaceId == Guid.Empty)
@@ -270,9 +272,29 @@ public partial class Workspaces : ComponentBase
await File.WriteAllTextAsync(chatPath, JsonSerializer.Serialize(chat, JSON_OPTIONS), Encoding.UTF8);
// Reload the tree items:
- await this.LoadTreeItems();
+ if(reloadTreeItems)
+ await this.LoadTreeItems();
+
this.StateHasChanged();
}
+
+ public async Task LoadChat(LoadChat loadChat)
+ {
+ var chatPath = loadChat.WorkspaceId == Guid.Empty
+ ? Path.Join(SettingsManager.DataDirectory, "tempChats", loadChat.ChatId.ToString())
+ : Path.Join(SettingsManager.DataDirectory, "workspaces", loadChat.WorkspaceId.ToString(), loadChat.ChatId.ToString());
+
+ await this.LoadChat(chatPath, switchToChat: true);
+ }
+
+ public static bool IsChatExisting(LoadChat loadChat)
+ {
+ var chatPath = loadChat.WorkspaceId == Guid.Empty
+ ? Path.Join(SettingsManager.DataDirectory, "tempChats", loadChat.ChatId.ToString())
+ : Path.Join(SettingsManager.DataDirectory, "workspaces", loadChat.WorkspaceId.ToString(), loadChat.ChatId.ToString());
+
+ return Directory.Exists(chatPath);
+ }
private async Task LoadChat(string? chatPath, bool switchToChat)
{
@@ -434,6 +456,18 @@ public partial class Workspaces : ComponentBase
await this.LoadTreeItems();
}
+
+ private async Task EnsureBiasWorkspace()
+ {
+ var workspacePath = Path.Join(SettingsManager.DataDirectory, "workspaces", WORKSPACE_ID_BIAS.ToString());
+
+ if(Path.Exists(workspacePath))
+ return;
+
+ Directory.CreateDirectory(workspacePath);
+ var workspaceNamePath = Path.Join(workspacePath, "name");
+ await File.WriteAllTextAsync(workspaceNamePath, "Bias of the Day", Encoding.UTF8);
+ }
private async Task DeleteWorkspace(string? workspacePath)
{
diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor
index 2368878..565469a 100644
--- a/app/MindWork AI Studio/Pages/Assistants.razor
+++ b/app/MindWork AI Studio/Pages/Assistants.razor
@@ -29,6 +29,13 @@
+
+ Learning
+
+
+
+
+
Software Engineering
diff --git a/app/MindWork AI Studio/Pages/Chat.razor b/app/MindWork AI Studio/Pages/Chat.razor
index 5e993db..8162231 100644
--- a/app/MindWork AI Studio/Pages/Chat.razor
+++ b/app/MindWork AI Studio/Pages/Chat.razor
@@ -22,7 +22,10 @@
{
foreach (var block in this.chatThread.Blocks.OrderBy(n => n.Time))
{
-
+ @if (!block.HideFromUser)
+ {
+
+ }
}
}
diff --git a/app/MindWork AI Studio/Pages/Chat.razor.cs b/app/MindWork AI Studio/Pages/Chat.razor.cs
index c5d3180..909b3cb 100644
--- a/app/MindWork AI Studio/Pages/Chat.razor.cs
+++ b/app/MindWork AI Studio/Pages/Chat.razor.cs
@@ -45,7 +45,11 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
private bool workspacesVisible;
private Workspaces? workspaces;
private bool mustScrollToBottomAfterRender;
+ private bool mustStoreChat;
+ private bool mustLoadChat;
+ private LoadChat loadChat;
private byte scrollRenderCountdown;
+ private bool autoSaveEnabled;
// Unfortunately, we need the input field reference to blur the focus away. Without
// this, we cannot clear the input field.
@@ -55,7 +59,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
protected override async Task OnInitializedAsync()
{
- this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE ]);
+ this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE ]);
// Configure the spellchecking for the user input:
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
@@ -68,14 +72,23 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
this.chatThread = deferredContent;
if (this.chatThread is not null)
{
- var firstUserBlock = this.chatThread.Blocks.FirstOrDefault(x => x.Role == ChatRole.USER);
- if (firstUserBlock is not null)
+ if (string.IsNullOrWhiteSpace(this.chatThread.Name))
{
- this.chatThread.Name = firstUserBlock.Content switch
+ var firstUserBlock = this.chatThread.Blocks.FirstOrDefault(x => x.Role == ChatRole.USER);
+ if (firstUserBlock is not null)
{
- ContentText textBlock => this.ExtractThreadName(textBlock.Text),
- _ => "Thread"
- };
+ this.chatThread.Name = firstUserBlock.Content switch
+ {
+ ContentText textBlock => this.ExtractThreadName(textBlock.Text),
+ _ => "Thread"
+ };
+ }
+ }
+
+ if(this.chatThread.WorkspaceId != Guid.Empty)
+ {
+ this.autoSaveEnabled = true;
+ this.mustStoreChat = true;
}
}
@@ -86,12 +99,33 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
this.StateHasChanged();
}
}
+
+ var deferredLoading = MessageBus.INSTANCE.CheckDeferredMessages(Event.LOAD_CHAT).FirstOrDefault();
+ if (deferredLoading != default)
+ {
+ this.loadChat = deferredLoading;
+ this.mustLoadChat = true;
+ }
await base.OnInitializedAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
+ if (firstRender && this.workspaces is not null && this.chatThread is not null && this.mustStoreChat)
+ {
+ this.mustStoreChat = false;
+ await this.workspaces.StoreChat(this.chatThread, false);
+ this.currentWorkspaceId = this.chatThread.WorkspaceId;
+ this.currentWorkspaceName = await this.workspaces.LoadWorkspaceName(this.chatThread.WorkspaceId);
+ }
+
+ if (firstRender && this.workspaces is not null && this.mustLoadChat)
+ {
+ this.mustLoadChat = false;
+ await this.workspaces.LoadChat(this.loadChat);
+ }
+
if(this.mustScrollToBottomAfterRender)
{
if (--this.scrollRenderCountdown == 0)
@@ -455,16 +489,19 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
#region Overrides of MSGComponentBase
- public override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
+ public override async Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
switch (triggeredEvent)
{
case Event.RESET_CHAT_STATE:
this.ResetState();
break;
+
+ case Event.CHAT_STREAMING_DONE:
+ if(this.autoSaveEnabled)
+ await this.SaveThread();
+ break;
}
-
- return Task.CompletedTask;
}
public override Task ProcessMessageWithResult(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor
index df5ed94..f37b277 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Pages/Settings.razor
@@ -3,6 +3,7 @@
@using AIStudio.Assistants.TextSummarizer
@using AIStudio.Provider
@using AIStudio.Settings
+@using AIStudio.Settings.DataModel
@using Host = AIStudio.Provider.SelfHosted.Host
Settings
@@ -400,6 +401,32 @@
+
+
+
+
+
+
+
+ You have learned about @this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias.Count out of @BiasCatalog.ALL_BIAS.Count biases.
+
+
+ Reset
+
+
+
+
+
+
+ @if (this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage is CommonLanguages.OTHER)
+ {
+
+ }
+
+
+
+
+
diff --git a/app/MindWork AI Studio/Pages/Settings.razor.cs b/app/MindWork AI Studio/Pages/Settings.razor.cs
index 9ff7beb..75197a4 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor.cs
+++ b/app/MindWork AI Studio/Pages/Settings.razor.cs
@@ -249,6 +249,29 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED);
}
+ #endregion
+
+ #region Bias-of-the-day related
+
+ private async Task ResetBiasOfTheDayHistory()
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { "Message", "Are you sure you want to reset your bias-of-the-day statistics? The system will no longer remember which biases you already know. As a result, biases you are already familiar with may be addressed again." },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Reset your bias-of-the-day statistics", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias.Clear();
+ this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn = DateOnly.MinValue;
+ await this.SettingsManager.StoreSettings();
+
+ await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED);
+ }
+
#endregion
#region Implementation of IMessageBusReceiver
diff --git a/app/MindWork AI Studio/Routes.razor.cs b/app/MindWork AI Studio/Routes.razor.cs
index 7b3e24e..03ef143 100644
--- a/app/MindWork AI Studio/Routes.razor.cs
+++ b/app/MindWork AI Studio/Routes.razor.cs
@@ -22,5 +22,6 @@ public sealed partial class Routes
public const string ASSISTANT_SYNONYMS = "/assistant/synonyms";
public const string ASSISTANT_MY_TASKS = "/assistant/my-tasks";
public const string ASSISTANT_JOB_POSTING = "/assistant/job-posting";
+ public const string ASSISTANT_BIAS = "/assistant/bias-of-the-day";
// ReSharper restore InconsistentNaming
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/DataModel/BiasCatalog.cs b/app/MindWork AI Studio/Settings/DataModel/BiasCatalog.cs
index 16f46cb..53e36c9 100644
--- a/app/MindWork AI Studio/Settings/DataModel/BiasCatalog.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/BiasCatalog.cs
@@ -22,6 +22,16 @@ namespace AIStudio.Settings.DataModel;
///
public static class BiasCatalog
{
+ public static readonly Bias NONE = new()
+ {
+ Id = Guid.Empty,
+ Category = BiasCategory.NONE,
+ Name = "None",
+ Description = "No bias selected.",
+ Related = [],
+ Links = [],
+ };
+
#region WHAT_SHOULD_WE_REMEMBER
private static readonly Bias MISATTRIBUTION_OF_MEMORY = new()
@@ -5909,4 +5919,23 @@ public static class BiasCatalog
{ IMPLICIT_STEREOTYPES.Id, IMPLICIT_STEREOTYPES },
{ IMPLICIT_ASSOCIATIONS.Id, IMPLICIT_ASSOCIATIONS },
};
+
+ public static Bias GetRandomBias(IList usedBias)
+ {
+ if(usedBias.Count >= ALL_BIAS.Count)
+ usedBias.Clear();
+
+ int randomBiasIndex;
+ lock (RANDOM)
+ {
+ randomBiasIndex = RANDOM.Next(0, ALL_BIAS.Count);
+ while(usedBias.Contains(randomBiasIndex))
+ randomBiasIndex = RANDOM.Next(0, ALL_BIAS.Count);
+ }
+
+ usedBias.Add(randomBiasIndex);
+ return ALL_BIAS.Values.ElementAt(randomBiasIndex);
+ }
+
+ private static readonly Random RANDOM = new();
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/DataModel/Data.cs b/app/MindWork AI Studio/Settings/DataModel/Data.cs
index 720d0e0..59ceaae 100644
--- a/app/MindWork AI Studio/Settings/DataModel/Data.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/Data.cs
@@ -67,4 +67,6 @@ public sealed class Data
public DataMyTasks MyTasks { get; init; } = new();
public DataJobPostings JobPostings { get; init; } = new();
+
+ public DataBiasOfTheDay BiasOfTheDay { get; init; } = new();
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/DataModel/DataBiasOfTheDay.cs b/app/MindWork AI Studio/Settings/DataModel/DataBiasOfTheDay.cs
new file mode 100644
index 0000000..47e03bc
--- /dev/null
+++ b/app/MindWork AI Studio/Settings/DataModel/DataBiasOfTheDay.cs
@@ -0,0 +1,61 @@
+using AIStudio.Provider;
+
+namespace AIStudio.Settings.DataModel;
+
+public sealed class DataBiasOfTheDay
+{
+ ///
+ /// A list of bias IDs that have been used.
+ ///
+ public List UsedBias { get; set; } = new();
+
+ ///
+ /// When was the last bias drawn?
+ ///
+ public DateOnly DateLastBiasDrawn { get; set; } = DateOnly.MinValue;
+
+ ///
+ /// Which bias is the bias of the day? This isn't the bias id, but rather the chat id in the bias workspace.
+ ///
+ public Guid BiasOfTheDayChatId { get; set; } = Guid.Empty;
+
+ ///
+ /// Which bias is the bias of the day?
+ ///
+ public Guid BiasOfTheDayId { get; set; } = Guid.Empty;
+
+ ///
+ /// Restrict to one bias per day?
+ ///
+ public bool RestrictOneBiasPerDay { get; set; } = true;
+
+ ///
+ /// Preselect any rewrite options?
+ ///
+ public bool PreselectOptions { get; set; }
+
+ ///
+ /// Preselect the language?
+ ///
+ public CommonLanguages PreselectedTargetLanguage { get; set; }
+
+ ///
+ /// Preselect any other language?
+ ///
+ public string PreselectedOtherLanguage { get; set; } = string.Empty;
+
+ ///
+ /// The minimum confidence level required for a provider to be considered.
+ ///
+ public ConfidenceLevel MinimumProviderConfidence { get; set; } = ConfidenceLevel.NONE;
+
+ ///
+ /// Preselect a profile?
+ ///
+ public string PreselectedProfile { get; set; } = string.Empty;
+
+ ///
+ /// Preselect a provider?
+ ///
+ public string PreselectedProvider { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/Components.cs b/app/MindWork AI Studio/Tools/Components.cs
index ccebcd0..30dee58 100644
--- a/app/MindWork AI Studio/Tools/Components.cs
+++ b/app/MindWork AI Studio/Tools/Components.cs
@@ -16,6 +16,7 @@ public enum Components
SYNONYMS_ASSISTANT,
MY_TASKS_ASSISTANT,
JOB_POSTING_ASSISTANT,
+ BIAS_DAY_ASSISTANT,
CHAT,
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
index f9f9e8e..1aa92a1 100644
--- a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
+++ b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs
@@ -59,6 +59,7 @@ public static class ComponentsExtensions
Components.SYNONYMS_ASSISTANT => settingsManager.ConfigurationData.Synonyms.PreselectOptions ? settingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence : default,
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence : default,
Components.JOB_POSTING_ASSISTANT => settingsManager.ConfigurationData.JobPostings.PreselectOptions ? settingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence : default,
+ Components.BIAS_DAY_ASSISTANT => settingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions ? settingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence : default,
_ => default,
};
@@ -77,6 +78,7 @@ public static class ComponentsExtensions
Components.SYNONYMS_ASSISTANT => settingsManager.ConfigurationData.Synonyms.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Synonyms.PreselectedProvider) : default,
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.MyTasks.PreselectedProvider) : default,
Components.JOB_POSTING_ASSISTANT => settingsManager.ConfigurationData.JobPostings.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.JobPostings.PreselectedProvider) : default,
+ Components.BIAS_DAY_ASSISTANT => settingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider) : default,
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : default,
@@ -90,6 +92,7 @@ public static class ComponentsExtensions
Components.EMAIL_ASSISTANT => settingsManager.ConfigurationData.EMail.PreselectOptions ? settingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.EMail.PreselectedProfile) : default,
Components.LEGAL_CHECK_ASSISTANT => settingsManager.ConfigurationData.LegalCheck.PreselectOptions ? settingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.LegalCheck.PreselectedProfile) : default,
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.MyTasks.PreselectedProfile) : default,
+ Components.BIAS_DAY_ASSISTANT => settingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions ? settingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile) : default,
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProfile) : default,
diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs
index 691f8b3..2dde28a 100644
--- a/app/MindWork AI Studio/Tools/Event.cs
+++ b/app/MindWork AI Studio/Tools/Event.cs
@@ -16,6 +16,8 @@ public enum Event
// Chat events:
HAS_CHAT_UNSAVED_CHANGES,
RESET_CHAT_STATE,
+ LOAD_CHAT,
+ CHAT_STREAMING_DONE,
// Send events:
SEND_TO_GRAMMAR_SPELLING_ASSISTANT,
diff --git a/app/MindWork AI Studio/Tools/LoadChat.cs b/app/MindWork AI Studio/Tools/LoadChat.cs
new file mode 100644
index 0000000..6b9072e
--- /dev/null
+++ b/app/MindWork AI Studio/Tools/LoadChat.cs
@@ -0,0 +1,3 @@
+namespace AIStudio.Tools;
+
+public readonly record struct LoadChat(Guid WorkspaceId, Guid ChatId);
\ No newline at end of file