Added preview feature configuration (#227)

This commit is contained in:
Thorsten Sommer 2024-12-03 21:02:37 +01:00 committed by GitHub
parent e2859b3d76
commit 55d7895f58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 270 additions and 41 deletions

View File

@ -13,6 +13,7 @@
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png"/>
<link href="system/MudBlazor/MudBlazor.min.css" rel="stylesheet" /> <link href="system/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link href="system/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" /> <link href="system/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" />
<link href="system/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet" />
<link href="app.css" rel="stylesheet" /> <link href="app.css" rel="stylesheet" />
<HeadOutlet/> <HeadOutlet/>
<script src="diff.js"></script> <script src="diff.js"></script>
@ -24,6 +25,7 @@
<script src="boot.js"></script> <script src="boot.js"></script>
<script src="system/MudBlazor/MudBlazor.min.js"></script> <script src="system/MudBlazor/MudBlazor.min.js"></script>
<script src="system/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script> <script src="system/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script>
<script src="system/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
<script src="app.js"></script> <script src="app.js"></script>
</body> </body>

View File

@ -0,0 +1,23 @@
@inherits ConfigurationBase
@typeparam T
<MudSelectExtended
T="T"
MultiSelection="@true"
MultiSelectionTextFunc="@this.GetMultiSelectionText"
SelectedValues="@this.SelectedValues()"
Strict="@true"
Disabled="@this.Disabled()"
Margin="Margin.Dense"
Label="@this.OptionDescription"
Class="@GetClass"
Variant="Variant.Outlined"
HelperText="@this.OptionHelp"
SelectedValuesChanged="@this.OptionChanged">
@foreach (var data in this.Data)
{
<MudSelectItemExtended Value="@data.Value">
@data.Name
</MudSelectItemExtended>
}
</MudSelectExtended>

View File

@ -0,0 +1,54 @@
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components;
/// <summary>
/// Configuration component for selecting many values from a list.
/// </summary>
/// <typeparam name="T">The type of the value to select.</typeparam>
public partial class ConfigurationMultiSelect<T> : ConfigurationBase
{
/// <summary>
/// The data to select from.
/// </summary>
[Parameter]
public IEnumerable<ConfigurationSelectData<T>> Data { get; set; } = [];
/// <summary>
/// The selected values.
/// </summary>
[Parameter]
public Func<HashSet<T>> SelectedValues { get; set; } = () => [];
/// <summary>
/// An action that is called when the selection changes.
/// </summary>
[Parameter]
public Action<HashSet<T>> SelectionUpdate { get; set; } = _ => { };
private async Task OptionChanged(IEnumerable<T?>? updatedValues)
{
if(updatedValues is null)
this.SelectionUpdate([]);
else
this.SelectionUpdate(updatedValues.Where(n => n is not null).ToHashSet()!);
await this.SettingsManager.StoreSettings();
await this.InformAboutChange();
}
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";
private string GetMultiSelectionText(List<T?>? selectedValues)
{
if(selectedValues is null || selectedValues.Count == 0)
return "No preview features selected.";
if(selectedValues.Count == 1)
return $"You have selected 1 preview feature.";
return $"You have selected {selectedValues.Count} preview features.";
}
}

View File

@ -1,7 +1,7 @@
@inherits ConfigurationBase @inherits ConfigurationBase
@typeparam T @typeparam T
<MudSelect T="T" Value="@this.SelectedValue()" Strict="@true" Disabled="@this.Disabled()" Margin="Margin.Dense" Label="@this.OptionDescription" Class="@GetClass" Variant="Variant.Outlined" HelperText="@this.OptionHelp" ValueChanged="@this.OptionChanged"> <MudSelect T="T" Value="@this.SelectedValue()" Strict="@true" ShrinkLabel="@true" Disabled="@this.Disabled()" Margin="Margin.Dense" Label="@this.OptionDescription" Class="@GetClass" Variant="Variant.Outlined" HelperText="@this.OptionHelp" ValueChanged="@this.OptionChanged">
@foreach (var data in this.Data) @foreach (var data in this.Data)
{ {
<MudSelectItem Value="@data.Value"> <MudSelectItem Value="@data.Value">

View File

@ -1,3 +1,4 @@
@using AIStudio @using AIStudio
@using AIStudio.Tools @using AIStudio.Tools
@using MudBlazor @using MudBlazor
@using MudExtensions

View File

@ -5,3 +5,4 @@ global using AIStudio.Tools;
global using Microsoft.JSInterop; global using Microsoft.JSInterop;
global using MudBlazor; global using MudBlazor;
global using MudExtensions;

View File

@ -104,7 +104,8 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
private void LoadNavItems() private void LoadNavItems()
{ {
var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager); var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager);
if (this.SettingsManager.ConfigurationData.App.PreviewVisibility < PreviewVisibility.EXPERIMENTAL) var isWriterModePreviewEnabled = PreviewFeatures.PRE_WRITER_MODE_2024.IsEnabled(this.SettingsManager);
if (!isWriterModePreviewEnabled)
{ {
this.navItems = new List<NavBarItem> this.navItems = new List<NavBarItem>
{ {
@ -116,7 +117,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false), new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false),
}; };
} }
else if (this.SettingsManager.ConfigurationData.App.PreviewVisibility >= PreviewVisibility.EXPERIMENTAL) else
{ {
this.navItems = new List<NavBarItem> this.navItems = new List<NavBarItem>
{ {

View File

@ -45,6 +45,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="7.1.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.71" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.71" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.0" />
<PackageReference Include="MudBlazor" Version="7.15.0" /> <PackageReference Include="MudBlazor" Version="7.15.0" />

View File

@ -39,6 +39,7 @@
<ThirdPartyComponent Name=".NET" Developer="Microsoft & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/dotnet/runtime/blob/main/LICENSE.TXT" RepositoryUrl="https://github.com/dotnet" UseCase="The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK."/> <ThirdPartyComponent Name=".NET" Developer="Microsoft & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/dotnet/runtime/blob/main/LICENSE.TXT" RepositoryUrl="https://github.com/dotnet" UseCase="The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK."/>
<ThirdPartyComponent Name="MudBlazor" Developer="Jonny Larsson, Meinrad Recheis & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MudBlazor/MudBlazor/blob/dev/LICENSE" RepositoryUrl="https://github.com/MudBlazor/MudBlazor/" UseCase="Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor."/> <ThirdPartyComponent Name="MudBlazor" Developer="Jonny Larsson, Meinrad Recheis & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MudBlazor/MudBlazor/blob/dev/LICENSE" RepositoryUrl="https://github.com/MudBlazor/MudBlazor/" UseCase="Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor."/>
<ThirdPartyComponent Name="MudBlazor.Markdown" Developer="My Nihongo & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MyNihongo/MudBlazor.Markdown/blob/main/LICENSE" RepositoryUrl="https://github.com/MyNihongo/MudBlazor.Markdown" UseCase="This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read."/> <ThirdPartyComponent Name="MudBlazor.Markdown" Developer="My Nihongo & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MyNihongo/MudBlazor.Markdown/blob/main/LICENSE" RepositoryUrl="https://github.com/MyNihongo/MudBlazor.Markdown" UseCase="This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read."/>
<ThirdPartyComponent Name="CodeBeam.MudBlazor.Extensions" Developer="Mehmet Can Karagöz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions/blob/dev/LICENSE" RepositoryUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions" UseCase="This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library."/>
<ThirdPartyComponent Name="Rust" Developer="Graydon Hoare, Rust Foundation, Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/rust/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/rust" UseCase="The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software."/> <ThirdPartyComponent Name="Rust" Developer="Graydon Hoare, Rust Foundation, Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/rust/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/rust" UseCase="The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software."/>
<ThirdPartyComponent Name="Tauri" Developer="Daniel Thompson-Yvetot, Lucas Nogueira, Tensor, Boscop, Serge Zaitsev, George Burton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT" RepositoryUrl="https://github.com/tauri-apps/tauri" UseCase="Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!"/> <ThirdPartyComponent Name="Tauri" Developer="Daniel Thompson-Yvetot, Lucas Nogueira, Tensor, Boscop, Serge Zaitsev, George Burton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT" RepositoryUrl="https://github.com/tauri-apps/tauri" UseCase="Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!"/>
<ThirdPartyComponent Name="Rocket" Developer="Sergio Benitez & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rwf2/Rocket/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rwf2/Rocket" UseCase="We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust."/> <ThirdPartyComponent Name="Rocket" Developer="Sergio Benitez & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rwf2/Rocket/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rwf2/Rocket" UseCase="We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust."/>

View File

@ -131,7 +131,7 @@
</ExpansionPanel> </ExpansionPanel>
@if (this.SettingsManager.ConfigurationData.App.PreviewVisibility >= PreviewVisibility.PROTOTYPE) @if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{ {
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.IntegrationInstructions" HeaderText="Configure Embeddings"> <ExpansionPanel HeaderIcon="@Icons.Material.Filled.IntegrationInstructions" HeaderText="Configure Embeddings">
<PreviewPrototype/> <PreviewPrototype/>
@ -253,7 +253,17 @@
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." /> <ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." />
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/> <ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/> <ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
<ConfigurationSelect OptionDescription="Preview feature visibility" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreviewVisibility = selectedValue)" OptionHelp="Do you want to show preview features in the app?"/> <ConfigurationSelect OptionDescription="Preview feature visibility" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="Do you want to show preview features in the app?"/>
@if(this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
{
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
if (availablePreviewFeatures.Count > 0)
{
<ConfigurationMultiSelect OptionDescription="Select preview features" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures)" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="Which preview features would you like to enable?"/>
}
}
<ConfigurationProviderSelection Data="@this.availableLLMProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/> <ConfigurationProviderSelection Data="@this.availableLLMProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/> <ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/>
</ExpansionPanel> </ExpansionPanel>

View File

@ -1,6 +1,7 @@
using AIStudio.Dialogs; using AIStudio.Dialogs;
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
@ -42,6 +43,16 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
#endregion #endregion
#region Preview-feature related
private void UpdatePreviewFeatures(PreviewVisibility previewVisibility)
{
this.SettingsManager.ConfigurationData.App.PreviewVisibility = previewVisibility;
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = previewVisibility.FilterPreviewFeatures(this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures);
}
#endregion
#region Provider related #region Provider related
private async Task AddLLMProvider() private async Task AddLLMProvider()

View File

@ -3,3 +3,4 @@
@using AIStudio.Tools @using AIStudio.Tools
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Routing
@using MudBlazor @using MudBlazor
@using MudExtensions

View File

@ -7,6 +7,8 @@ using Microsoft.Extensions.Logging.Console;
using MudBlazor.Services; using MudBlazor.Services;
using MudExtensions.Services;
#if !DEBUG #if !DEBUG
using System.Reflection; using System.Reflection;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
@ -93,6 +95,7 @@ internal sealed class Program
options.FormatterName = TerminalLogger.FORMATTER_NAME; options.FormatterName = TerminalLogger.FORMATTER_NAME;
}).AddConsoleFormatter<TerminalLogger, ConsoleFormatterOptions>(); }).AddConsoleFormatter<TerminalLogger, ConsoleFormatterOptions>();
builder.Services.AddMudExtensions();
builder.Services.AddMudServices(config => builder.Services.AddMudServices(config =>
{ {
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft; config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft;

View File

@ -88,6 +88,12 @@ public static class ConfigurationSelectDataFactory
yield return new("Show also experimental features: these are experimental; expect bugs, missing features, many changes", PreviewVisibility.EXPERIMENTAL); yield return new("Show also experimental features: these are experimental; expect bugs, missing features, many changes", PreviewVisibility.EXPERIMENTAL);
} }
public static IEnumerable<ConfigurationSelectData<PreviewFeatures>> GetPreviewFeaturesData(SettingsManager settingsManager)
{
foreach (var source in settingsManager.ConfigurationData.App.PreviewVisibility.GetPreviewFeatures())
yield return new(source.GetPreviewDescription(), source);
}
public static IEnumerable<ConfigurationSelectData<NavBehavior>> GetNavBehaviorData() public static IEnumerable<ConfigurationSelectData<NavBehavior>> GetNavBehaviorData()
{ {
yield return new("Navigation expands on mouse hover", NavBehavior.EXPAND_ON_HOVER); yield return new("Navigation expands on mouse hover", NavBehavior.EXPAND_ON_HOVER);

View File

@ -33,6 +33,11 @@ public sealed class DataApp
/// </summary> /// </summary>
public PreviewVisibility PreviewVisibility { get; set; } = PreviewVisibility.NONE; public PreviewVisibility PreviewVisibility { get; set; } = PreviewVisibility.NONE;
/// <summary>
/// The enabled preview features.
/// </summary>
public HashSet<PreviewFeatures> EnabledPreviewFeatures { get; set; } = new();
/// <summary> /// <summary>
/// Should we preselect a provider for the entire app? /// Should we preselect a provider for the entire app?
/// </summary> /// </summary>

View File

@ -0,0 +1,14 @@
namespace AIStudio.Settings.DataModel;
public static class PreviewFeatureExtensions
{
public static string GetPreviewDescription(this PreviewFeatures feature) => feature switch
{
PreviewFeatures.PRE_WRITER_MODE_2024 => "Writer Mode: Experiments about how to write long texts using AI",
PreviewFeatures.PRE_RAG_2024 => "RAG: Preview of our RAG implementation where you can refer your files or integrate enterprise data within your company",
_ => "Unknown preview feature"
};
public static bool IsEnabled(this PreviewFeatures feature, SettingsManager settingsManager) => settingsManager.ConfigurationData.App.EnabledPreviewFeatures.Contains(feature);
}

View File

@ -0,0 +1,11 @@
namespace AIStudio.Settings.DataModel;
public enum PreviewFeatures
{
//
// Important: Never delete any enum value from this list.
// We must be able to deserialize old settings files that may contain these values.
//
PRE_WRITER_MODE_2024,
PRE_RAG_2024,
}

View File

@ -0,0 +1,45 @@
namespace AIStudio.Settings.DataModel;
public static class PreviewVisibilityExtensions
{
public static IList<PreviewFeatures> GetPreviewFeatures(this PreviewVisibility visibility)
{
var features = new List<PreviewFeatures>();
if (visibility >= PreviewVisibility.RELEASE_CANDIDATE)
{
}
if (visibility >= PreviewVisibility.BETA)
{
}
if (visibility >= PreviewVisibility.ALPHA)
{
}
if (visibility >= PreviewVisibility.PROTOTYPE)
{
features.Add(PreviewFeatures.PRE_RAG_2024);
}
if (visibility >= PreviewVisibility.EXPERIMENTAL)
{
features.Add(PreviewFeatures.PRE_WRITER_MODE_2024);
}
return features;
}
public static HashSet<PreviewFeatures> FilterPreviewFeatures(this PreviewVisibility visibility, HashSet<PreviewFeatures> enabledFeatures)
{
var filteredFeatures = new HashSet<PreviewFeatures>();
var previewFeatures = visibility.GetPreviewFeatures();
foreach (var feature in enabledFeatures)
{
if (previewFeatures.Contains(feature))
filteredFeatures.Add(feature);
}
return filteredFeatures;
}
}

View File

@ -84,6 +84,14 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
} }
this.ConfigurationData = SettingsMigrations.Migrate(this.logger, settingsVersion, await File.ReadAllTextAsync(settingsPath), JSON_OPTIONS); this.ConfigurationData = SettingsMigrations.Migrate(this.logger, settingsVersion, await File.ReadAllTextAsync(settingsPath), JSON_OPTIONS);
//
// We filter the enabled preview features based on the preview visibility.
// This is necessary when the app starts up: some preview features may have
// been disabled or released from the last time the app was started.
//
this.ConfigurationData.App.EnabledPreviewFeatures = this.ConfigurationData.App.PreviewVisibility.FilterPreviewFeatures(this.ConfigurationData.App.EnabledPreviewFeatures);
return; return;
} }

View File

@ -2,6 +2,20 @@
"version": 1, "version": 1,
"dependencies": { "dependencies": {
"net8.0": { "net8.0": {
"CodeBeam.MudBlazor.Extensions": {
"type": "Direct",
"requested": "[7.1.0, )",
"resolved": "7.1.0",
"contentHash": "qbyCT4XMc/lbi2XdkUh9aSPu97RUPDisU7fpTCU0Q4nGywqJsAxrwcpaxJqoycq+uj3smwX5zOn6yzfsHUObeQ==",
"dependencies": {
"BuildBundlerMinifier": "3.2.449",
"CsvHelper": "31.0.3",
"Microsoft.AspNetCore.Components": "8.0.11",
"Microsoft.AspNetCore.Components.Web": "8.0.11",
"MudBlazor": "7.15.0",
"ZXing.Net": "0.16.9"
}
},
"HtmlAgilityPack": { "HtmlAgilityPack": {
"type": "Direct", "type": "Direct",
"requested": "[1.11.71, )", "requested": "[1.11.71, )",
@ -53,6 +67,16 @@
"HtmlAgilityPack": "1.11.61" "HtmlAgilityPack": "1.11.61"
} }
}, },
"BuildBundlerMinifier": {
"type": "Transitive",
"resolved": "3.2.449",
"contentHash": "uA9sYDy4VepL3xwzBTLcP2LyuVYMt0ZIT3gaSiXvGoX15Ob+rOP+hGydhevlSVd+rFo+Y+VQFEHDuWU8HBW+XA=="
},
"CsvHelper": {
"type": "Transitive",
"resolved": "31.0.3",
"contentHash": "ygck8DR4mG/VDA/LgIVVGpEtXXPDVaaNZNJGrOAJ4pckVw4MbAQ3n/u6YFDv3bwlQhlxTmPhCyk5E4hxe96Crg=="
},
"Markdig": { "Markdig": {
"type": "Transitive", "type": "Transitive",
"resolved": "0.37.0", "resolved": "0.37.0",
@ -60,66 +84,66 @@
}, },
"Microsoft.AspNetCore.Authorization": { "Microsoft.AspNetCore.Authorization": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "e+ehkJTx0bqUb9zzM9ohV830LhoK3fy5lfVGPWAozbq6I2rJpyq7L0OUgEGNEQLMTzawkDjpNlJ4WU1ck/TDjw==", "contentHash": "ACaLyjBSz9WUzbaJe0Sv09/FihRNHYlRUIj3uQ8CZFFByf6Qwv3+PXnbltidFKz2iOyqdvppQias3emdQUY2nA==",
"dependencies": { "dependencies": {
"Microsoft.AspNetCore.Metadata": "8.0.8", "Microsoft.AspNetCore.Metadata": "8.0.11",
"Microsoft.Extensions.Logging.Abstractions": "8.0.1", "Microsoft.Extensions.Logging.Abstractions": "8.0.2",
"Microsoft.Extensions.Options": "8.0.2" "Microsoft.Extensions.Options": "8.0.2"
} }
}, },
"Microsoft.AspNetCore.Components": { "Microsoft.AspNetCore.Components": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "VszyvGli+bgbt6gANOPSx8dzHWIH8qrDmWaNzb0GcU8MDKi2eULNW4ga+xyj7ApOYDLZA2V1FL4710Edw0Om4g==", "contentHash": "kyhSQcVEQvMnv2BNRn7JRgYCr+PIO5Uh1mhIFdCNycxE/k8NsI72sV693s1KVmVebMA8g3hTBmfBEheWb3hhww==",
"dependencies": { "dependencies": {
"Microsoft.AspNetCore.Authorization": "8.0.8", "Microsoft.AspNetCore.Authorization": "8.0.11",
"Microsoft.AspNetCore.Components.Analyzers": "8.0.8" "Microsoft.AspNetCore.Components.Analyzers": "8.0.11"
} }
}, },
"Microsoft.AspNetCore.Components.Analyzers": { "Microsoft.AspNetCore.Components.Analyzers": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "PKujmeqEO8fY/U5bWoww63yMF4XqjSnrq0cP4fXUyz4JVYTkh+MZEFktfLaoVaGU4JbtUNjJFvEYA96H8lbb8Q==" "contentHash": "4JtFt5IR0ixuFpwY6D2Xi5R+vZQ6iykd2EuG3puHETCOZOgYG8M538LCY1lbgQTkHOL04YKDjQTQu8PU/BaXRQ=="
}, },
"Microsoft.AspNetCore.Components.Forms": { "Microsoft.AspNetCore.Components.Forms": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "81P0TqjN+Q52QZbMje1Cirb7DOPKBW8CiemLo9x0KITWEGofdNJJECK1FPoMfUkgyaOknuCRQy0L+6G3K6+rPg==", "contentHash": "60g+idqaiVhPVNOqauy/vH5lREpjcuKl3/w6zJhdU1PFWg4jtdoyIPQH+qxBKsUohkELhH3cRfzGRKElVuZuwg==",
"dependencies": { "dependencies": {
"Microsoft.AspNetCore.Components": "8.0.8" "Microsoft.AspNetCore.Components": "8.0.11"
} }
}, },
"Microsoft.AspNetCore.Components.Web": { "Microsoft.AspNetCore.Components.Web": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "PX6JYvx2A5R1cQNLiTMMcm+7zvloVCGxvuRf24Ezaj1aUvZDYnSfyNkD2gyVun7AizGAn+x3QYr4mBfbZ9L5TA==", "contentHash": "IDmjQ/K7hv6zUEz2LsCkQBngZx6PMnty8OdSPf0hYGMpC+4Yi37pgCc/25fFu3CSBe8nDirqTrqKtfToHWCpbw==",
"dependencies": { "dependencies": {
"Microsoft.AspNetCore.Components": "8.0.8", "Microsoft.AspNetCore.Components": "8.0.11",
"Microsoft.AspNetCore.Components.Forms": "8.0.8", "Microsoft.AspNetCore.Components.Forms": "8.0.11",
"Microsoft.Extensions.DependencyInjection": "8.0.0", "Microsoft.Extensions.DependencyInjection": "8.0.1",
"Microsoft.Extensions.Primitives": "8.0.0", "Microsoft.Extensions.Primitives": "8.0.0",
"Microsoft.JSInterop": "8.0.8", "Microsoft.JSInterop": "8.0.11",
"System.IO.Pipelines": "8.0.0" "System.IO.Pipelines": "8.0.0"
} }
}, },
"Microsoft.AspNetCore.Metadata": { "Microsoft.AspNetCore.Metadata": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "4WSCv7ddawjabLZ7hj0Ai7hTzFYLw5PsvHVKrJkuBgrv+blELbIDMVj7b5q7PZs7Xvz0W0ViKY/rcVkqD0D5yQ==" "contentHash": "cy04xnMSTXTkRPjEwseRz57R5zjR/CWsdEOHH6NhWbNl97k+U1w6dSjqIOC7kv08tyzmM30FzIilSDtE5HdL/A=="
}, },
"Microsoft.Extensions.DependencyInjection": { "Microsoft.Extensions.DependencyInjection": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.0", "resolved": "8.0.1",
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==",
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
} }
}, },
"Microsoft.Extensions.DependencyInjection.Abstractions": { "Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.1", "resolved": "8.0.2",
"contentHash": "fGLiCRLMYd00JYpClraLjJTNKLmMJPnqxMaiRzEBIIvevlzxz33mXy39Lkd48hu1G+N21S7QpaO5ZzKsI6FRuA==" "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
}, },
"Microsoft.Extensions.FileProviders.Abstractions": { "Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive", "type": "Transitive",
@ -147,10 +171,10 @@
}, },
"Microsoft.Extensions.Logging.Abstractions": { "Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.1", "resolved": "8.0.2",
"contentHash": "RIFgaqoaINxkM2KTOw72dmilDmTrYA0ns2KW4lDz4gZ2+o6IQ894CzmdL3StM2oh7QQq44nCWiqKqc4qUI9Jmg==", "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
"dependencies": { "dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1" "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
} }
}, },
"Microsoft.Extensions.Options": { "Microsoft.Extensions.Options": {
@ -169,15 +193,20 @@
}, },
"Microsoft.JSInterop": { "Microsoft.JSInterop": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.8", "resolved": "8.0.11",
"contentHash": "vJhGdkYKvytLIMi5J2vpsyfjPKzSm7dnCfdGwjt4skvJcomlJyKDTA3wAwxIMuMbiWcOi1HuiQ6R+85C+qFJrw==" "contentHash": "UYSbAkNGTWVUne3I04/9IRQel3Bt1Ww6Y5cjvZEZ89rWhBD1yWu7YDotvQS62V6mgSfFaXXPGrCUm1VG824QXw=="
}, },
"System.IO.Pipelines": { "System.IO.Pipelines": {
"type": "Transitive", "type": "Transitive",
"resolved": "8.0.0", "resolved": "8.0.0",
"contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA=="
},
"ZXing.Net": {
"type": "Transitive",
"resolved": "0.16.9",
"contentHash": "7WaVMHklpT3Ye2ragqRIwlFRsb6kOk63BOGADV0fan3ulVfGLUYkDi5yNUsZS/7FVNkWbtHAlDLmu4WnHGfqvQ=="
} }
}, },
"net8.0/osx-arm64": {} "net8.0/osx-x64": {}
} }
} }

View File

@ -1,5 +1,5 @@
# v0.9.22, build 197 (2024-1x-xx xx:xx UTC) # v0.9.22, build 197 (2024-1x-xx xx:xx UTC)
- Added the possibility to configure preview feature visibility in the app settings. This is useful for users who want to test new features before they are officially released. - Added the possibility to configure preview feature visibility in the app settings. This is useful for users who want to test new features before they are officially released.
- Added the possibility to configure embedding providers in the app settings. Embeddings are necessary in order to integrate local data and files. - Added the possibility to configure embedding providers in the app settings as a prototype preview feature. Embeddings are necessary in order to integrate local data and files.
- Added the writer mode as an experimental preview feature. This feature is just an experiment as we explore how to implement long text support in AI Studio. - Added the writer mode as an experimental preview feature. This feature is just an experiment as we explore how to implement long text support in AI Studio.
- Improved self-hosted LLM provider configuration by filtering embedding models. - Improved self-hosted LLM provider configuration by filtering embedding models.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
function auto_size(n){n.style.height="5px";n.style.height=n.scrollHeight+4+"px"}function getcss(n,t){const i=document.querySelector(n);return i.style.getPropertyValue(t)}function setcss(n,t,i){const r=document.querySelectorAll(n);for(let n=0;n<r.length;n++)r[n].style.setProperty(t,i)}class MudScrollManagerExtended{scrollToMiddle(n,t){let i=document.getElementById(n),r=document.getElementById(t);i.scrollTop=r.offsetTop-i.offsetHeight+i.offsetHeight/2+r.offsetHeight/2}}window.mudScrollManagerExtended=new MudScrollManagerExtended;window.mudTeleport={teleport:(n,t)=>{const i=document.querySelector(t);return i?(i.appendChild(n),"ok"):"not found"},removeFromDOM:n=>{n&&n.__internalId!==null&&n.remove()}};class MudSignaturePadManager{constructor(){this.pads=[]}addPad(n,t,i){const r=new MudSignaturePad(n,t,i);r.init();this.pads.push(r)}togglePadEraser(n){const t=this.getPad(n);t&&t.toggleEraser()}disposePad(n){const t=this.getPad(n);t&&t.dispose()}clearPad(n){const t=this.getPad(n);t&&t.clear(!0)}downloadPadImage(n){const t=this.getPad(n);t&&t.download()}getBase64(n){const t=this.getPad(n);if(t)return t.getBase64()}updatePadOptions(n,t){const i=this.getPad(n);i&&i.setOptions(t)}updatePadImage(n,t){const i=this.getPad(n);if(i){if(t.startsWith("data:image/png;base64,")){i.updateImage(t);return}i.updateImage(`data:image/png;base64,${t}`)}}getPad(n){const t=this.pads.findIndex(t=>t.canvas.id===n.id);return t>=0?this.pads[t]:null}}class MudSignaturePad{constructor(n,t,i){this.canvas=t;this.options=i;this.isMouseDown=!1;this.isErasing=!1;this.memCanvas=document.createElement("canvas");this.points=[];this.dotnetRef=n}get ctx(){return this.canvas.getContext("2d")}get memCtx(){return this.memCanvas.getContext("2d")}getBase64(){return this.canvas.toDataURL()}init(){this.setCanvasSize();this.setOptions(this.options);this.canvas.addEventListener("mousedown",n=>this.startDrawing(n));this.canvas.addEventListener("mousemove",n=>this.drawLine(n));this.canvas.addEventListener("mouseup",()=>this.stopDrawing());this.canvas.addEventListener("mouseout",()=>this.stopDrawing());this.canvas.addEventListener("touchstart",n=>this.startDrawing(n));this.canvas.addEventListener("touchend",()=>this.stopDrawing());this.canvas.addEventListener("touchmove",n=>this.drawLine(n));this.setPencilCursor()}download(){const n=document.createElement("a");n.download="signature.png";n.href=this.canvas.toDataURL();n.click();n.remove()}updateImage(n){this.clear(!0);const t=new Image,i=this.ctx,r=this.memCtx;t.onload=function(){i.drawImage(t,0,0);r.drawImage(t,0,0);t.remove()};t.src=n}setCanvasSize(){const t=this.canvas.parentElement,n=t.getBoundingClientRect();this.canvas.width=n.width;this.canvas.height=n.height;this.memCanvas.height=n.height;this.memCanvas.width=n.width}dispose(){this.canvas.removeEventListener("mousedown");this.canvas.removeEventListener("mousemove");this.canvas.removeEventListener("mouseup");this.canvas.removeEventListener("mouseout");this.canvas.removeEventListener("touchstart");this.canvas.removeEventListener("touchend");this.canvas.removeEventListener("touchmove")}clear(n){n===!0&&this.memCtx.clearRect(0,0,this.canvas.width,this.canvas.height);this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}stopDrawing(){this.isMouseDown=!1;this.memCtx.clearRect(0,0,this.memCanvas.width,this.memCanvas.height);this.memCtx.drawImage(this.canvas,0,0);this.points=[]}startDrawing(n){this.isMouseDown=!0;this.points.push({x:n.offsetX,y:n.offsetY})}setOptions(n){this.ctx.lineWidth=n.lineWidth;this.ctx.lineJoin=n.lineJoin;this.ctx.lineCap=n.lineCap;this.ctx.strokeStyle=n.strokeStyle;this.options=n}toggleEraser(){if(this.isErasing=!this.isErasing,this.isErasing){this.setEraserCursor();return}this.setPencilCursor()}setPencilCursor(){this.canvas.setAttribute("style","cursor:url('_content/CodeBeam.MudBlazor.Extensions/pencil.cur'), auto;")}setEraserCursor(){this.canvas.setAttribute("style","cursor:url('_content/CodeBeam.MudBlazor.Extensions/eraser.cur'), auto;")}drawLine(n){this.isMouseDown&&(this.isErasing===!1?(this.clear(),this.ctx.drawImage(this.memCanvas,0,0),this.points.push({x:n.offsetX,y:n.offsetY}),this.drawPoints(this.ctx,this.points)):this.ctx.clearRect(n.offsetX,n.offsetY,23,23))}drawPoints(n,t){if(!(t.length<6)){if(t.length<6){const i=t[0];n.beginPath();n.arc(i.x,i.y,n.lineWidth/2,0,Math.PI*2,!0);n.closePath();n.fill();this.pushUpdateToBlazorComponent();return}n.beginPath();n.moveTo(t[0].x,t[0].y);let i;for(let r=1;r<t.length-2;r++){const u=(t[r].x+t[r+1].x)/2,f=(t[r].y+t[r+1].y)/2;n.quadraticCurveTo(t[r].x,t[r].y,u,f);i=r}n.quadraticCurveTo(t[i].x,t[i].y,t[i+1].x,t[i+1].y);n.stroke();this.pushUpdateToBlazorComponent()}}pushUpdateToBlazorComponent(){this.dotnetRef.invokeMethodAsync("SignatureDataChangedAsync")}}window.mudSignaturePad=new MudSignaturePadManager;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B