Added assistant plugins (#659)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions

This commit is contained in:
nilskruthoff authored and GitHub committed 2026-04-09 10:01:24 +02:00
1 parent a573409a24
commit f6a128f2e4
100 files changed
+10146 -85

No files matched your search

@@ -0,0 +1,91 @@
using Lua;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public sealed class AssistantButton : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.BUTTON;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Text
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Text));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Text), value);
}
public bool IsIconButton
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsIconButton));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsIconButton), value);
}
public LuaFunction? Action
{
get => this.Props.TryGetValue(nameof(this.Action), out var value) && value is LuaFunction action ? action : null;
set => AssistantComponentPropHelper.WriteObject(this.Props, nameof(this.Action), value);
}
public string Variant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Variant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Variant), value);
}
public string Color
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value);
}
public bool IsFullWidth
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsFullWidth));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsFullWidth), value);
}
public string StartIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.StartIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.StartIcon), value);
}
public string EndIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.EndIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.EndIcon), value);
}
public string IconColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconColor), value);
}
public string IconSize
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconSize));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconSize), value);
}
public string Size
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Size));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Size), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
public Variant GetButtonVariant() => Enum.TryParse<Variant>(this.Variant, out var variant) ? variant : MudBlazor.Variant.Filled;
}
@@ -0,0 +1,58 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public sealed class AssistantButtonGroup : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.BUTTON_GROUP;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Variant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Variant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Variant), value);
}
public string Color
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value);
}
public string Size
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Size));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Size), value);
}
public bool OverrideStyles
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.OverrideStyles));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.OverrideStyles), value);
}
public bool Vertical
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.Vertical));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.Vertical), value);
}
public bool DropShadow
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.DropShadow), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.DropShadow), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
public Variant GetVariant() => Enum.TryParse<Variant>(this.Variant, out var variant) ? variant : MudBlazor.Variant.Filled;
}
@@ -0,0 +1,80 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantColorPicker : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.COLOR_PICKER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public string Placeholder
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Placeholder));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Placeholder), value);
}
public bool ShowAlpha
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.ShowAlpha), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.ShowAlpha), value);
}
public bool ShowToolbar
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.ShowToolbar), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.ShowToolbar), value);
}
public bool ShowModeSwitch
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.ShowModeSwitch), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.ShowModeSwitch), value);
}
public string PickerVariant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PickerVariant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefuleAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.Colors.ContainsKey(this.Name))
state.Colors[this.Name] = this.Placeholder;
}
public override string UserPromptFallback(AssistantState state)
{
state.Colors.TryGetValue(this.Name, out var userInput);
return this.BuildAuditPromptBlock(userInput);
}
#endregion
public PickerVariant GetPickerVariant() => Enum.TryParse<PickerVariant>(this.PickerVariant, out var variant) ? variant : MudBlazor.PickerVariant.Static;
}
@@ -0,0 +1,8 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public abstract class AssistantComponentBase : IAssistantComponent
{
public abstract AssistantComponentType Type { get; }
public abstract Dictionary<string, object> Props { get; set; }
public abstract List<IAssistantComponent> Children { get; set; }
}
@@ -0,0 +1,65 @@
using AIStudio.Tools.PluginSystem.Assistants.Icons;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal static class AssistantComponentPropHelper
{
public static string ReadString(Dictionary<string, object> props, string key)
{
if (props.TryGetValue(key, out var value))
return value.ToString() ?? string.Empty;
return string.Empty;
}
public static void WriteString(Dictionary<string, object> props, string key, string value) => props[key] = value;
public static int ReadInt(Dictionary<string, object> props, string key, int fallback = 0)
{
return props.TryGetValue(key, out var value) && int.TryParse(value.ToString(), out var i) ? i : fallback;
}
public static void WriteInt(Dictionary<string, object> props, string key, int value) => props[key] = value;
public static int? ReadNullableInt(Dictionary<string, object> props, string key)
{
return props.TryGetValue(key, out var value) && int.TryParse(value.ToString(), out var i) ? i : null;
}
public static void WriteNullableInt(Dictionary<string, object> props, string key, int? value)
{
if (value.HasValue)
props[key] = value.Value;
else
props.Remove(key);
}
public static bool ReadBool(Dictionary<string, object> props, string key, bool fallback = false)
{
return props.TryGetValue(key, out var value) && bool.TryParse(value.ToString(), out var b) ? b : fallback;
}
public static void WriteBool(Dictionary<string, object> props, string key, bool value) => props[key] = value;
public static void WriteObject(Dictionary<string, object> props, string key, object? value)
{
if (value is null)
props.Remove(key);
else
props[key] = value;
}
public static Color GetColor(string value, Color fallback) => Enum.TryParse<Color>(value, out var color) ? color : fallback;
public static Variant GetVariant(string value, Variant fallback) => Enum.TryParse<Variant>(value, out var variant) ? variant : fallback;
public static Adornment GetAdornment(string value, Adornment fallback) => Enum.TryParse<Adornment>(value, out var adornment) ? adornment : fallback;
public static string GetIconSvg(string value) => MudBlazorIconRegistry.TryGetSvg(value.TrimStart('@'), out var svg) ? svg : string.Empty;
public static Size GetComponentSize(string value, Size fallback) => Enum.TryParse<Size>(value, out var size) ? size : fallback;
public static Justify? GetJustify(string value) => Enum.TryParse<Justify>(value, out var justify) ? justify : null;
public static AlignItems? GetItemsAlignment(string value) => Enum.TryParse<AlignItems>(value, out var alignment) ? alignment : null;
public static Align GetAlignment(string value, Align fallback = Align.Inherit) => Enum.TryParse<Align>(value, out var alignment) ? alignment : fallback;
public static Typo GetTypography(string value, Typo fallback = Typo.body1) => Enum.TryParse<Typo>(value, out var typo) ? typo : fallback;
public static Wrap? GetWrap(string value) => Enum.TryParse<Wrap>(value, out var wrap) ? wrap : null;
public static StretchItems? GetStretching(string value) => Enum.TryParse<StretchItems>(value, out var stretch) ? stretch : null;
public static Breakpoint GetBreakpoint(string value, Breakpoint fallback) => Enum.TryParse<Breakpoint>(value, out var breakpoint) ? breakpoint : fallback;
public static PickerVariant GetPickerVariant(string pickerValue, PickerVariant fallback) => Enum.TryParse<PickerVariant>(pickerValue, out var variant) ? variant : fallback;
}
@@ -0,0 +1,29 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public enum AssistantComponentType
{
FORM,
TEXT_AREA,
BUTTON,
BUTTON_GROUP,
DROPDOWN,
PROVIDER_SELECTION,
PROFILE_SELECTION,
SWITCH,
HEADING,
TEXT,
LIST,
WEB_CONTENT_READER,
FILE_CONTENT_READER,
IMAGE,
COLOR_PICKER,
DATE_PICKER,
DATE_RANGE_PICKER,
TIME_PICKER,
LAYOUT_ITEM,
LAYOUT_GRID,
LAYOUT_PAPER,
LAYOUT_STACK,
LAYOUT_ACCORDION,
LAYOUT_ACCORDION_SECTION,
}
@@ -0,0 +1,64 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public static class AssistantComponentTypeExtensions
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AssistantComponentTypeExtensions).Namespace, nameof(AssistantComponentTypeExtensions));
public static string GetDisplayName(this AssistantComponentType type) => type switch
{
AssistantComponentType.FORM => TB("Root"),
AssistantComponentType.TEXT_AREA => TB("Text Area"),
AssistantComponentType.BUTTON => TB("Button"),
AssistantComponentType.BUTTON_GROUP => TB("Button group"),
AssistantComponentType.DROPDOWN => TB("Dropdown"),
AssistantComponentType.PROVIDER_SELECTION => TB("Provider Selection"),
AssistantComponentType.PROFILE_SELECTION => TB("Profile Selection"),
AssistantComponentType.SWITCH => TB("Switch"),
AssistantComponentType.HEADING => TB("Heading"),
AssistantComponentType.TEXT => TB("Text"),
AssistantComponentType.LIST => TB("List"),
AssistantComponentType.WEB_CONTENT_READER => TB("Web Content Reader"),
AssistantComponentType.FILE_CONTENT_READER => TB("File Content Reader"),
AssistantComponentType.IMAGE => TB("Image"),
AssistantComponentType.COLOR_PICKER => TB("Color Selection"),
AssistantComponentType.DATE_PICKER => TB("Date Selection"),
AssistantComponentType.DATE_RANGE_PICKER => TB("Date Range Selection"),
AssistantComponentType.TIME_PICKER => TB("Time Selection"),
AssistantComponentType.LAYOUT_ITEM => TB("Grid Item"),
AssistantComponentType.LAYOUT_GRID => TB("Grid"),
AssistantComponentType.LAYOUT_PAPER => TB("Container"),
AssistantComponentType.LAYOUT_STACK => TB("Stack"),
AssistantComponentType.LAYOUT_ACCORDION => TB("Accordion"),
AssistantComponentType.LAYOUT_ACCORDION_SECTION => TB("Accordion Section"),
_ => TB("Unknown Element")
};
public static string GetIcon(this AssistantComponentType type) => type switch
{
AssistantComponentType.BUTTON => MudBlazor.Icons.Material.Filled.AdsClick,
AssistantComponentType.BUTTON_GROUP => MudBlazor.Icons.Material.Filled.LinearScale,
AssistantComponentType.DROPDOWN => MudBlazor.Icons.Material.Filled.Rule,
AssistantComponentType.PROVIDER_SELECTION => MudBlazor.Icons.Material.Filled.Memory,
AssistantComponentType.PROFILE_SELECTION => MudBlazor.Icons.Material.Filled.Badge,
AssistantComponentType.SWITCH => MudBlazor.Icons.Material.Filled.ToggleOn,
AssistantComponentType.HEADING => MudBlazor.Icons.Material.Filled.Title,
AssistantComponentType.TEXT => MudBlazor.Icons.Material.Filled.TextFields,
AssistantComponentType.TEXT_AREA => MudBlazor.Icons.Material.Filled.Wysiwyg,
AssistantComponentType.LIST => MudBlazor.Icons.Material.Filled.List,
AssistantComponentType.WEB_CONTENT_READER => MudBlazor.Icons.Material.Filled.Public,
AssistantComponentType.FILE_CONTENT_READER => MudBlazor.Icons.Material.Filled.AttachFile,
AssistantComponentType.IMAGE => MudBlazor.Icons.Material.Filled.Image,
AssistantComponentType.COLOR_PICKER => MudBlazor.Icons.Material.Filled.Palette,
AssistantComponentType.DATE_PICKER => MudBlazor.Icons.Material.Filled.CalendarMonth,
AssistantComponentType.DATE_RANGE_PICKER => MudBlazor.Icons.Material.Filled.DateRange,
AssistantComponentType.TIME_PICKER => MudBlazor.Icons.Material.Filled.Schedule,
AssistantComponentType.LAYOUT_ITEM => MudBlazor.Icons.Material.Filled.DashboardCustomize,
AssistantComponentType.LAYOUT_GRID => MudBlazor.Icons.Material.Filled.GridView,
AssistantComponentType.LAYOUT_PAPER => MudBlazor.Icons.Material.Filled.Inbox,
AssistantComponentType.LAYOUT_STACK => MudBlazor.Icons.Material.Filled.Layers,
AssistantComponentType.LAYOUT_ACCORDION => MudBlazor.Icons.Material.Filled.CalendarViewDay,
AssistantComponentType.LAYOUT_ACCORDION_SECTION => MudBlazor.Icons.Material.Filled.HorizontalSplit,
AssistantComponentType.FORM => MudBlazor.Icons.Material.Filled.AccountTree,
_ => MudBlazor.Icons.Material.Filled.AccountTree,
};
}
@@ -0,0 +1,125 @@
using System.Globalization;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantDatePicker : StatefulAssistantComponentBase
{
private static readonly CultureInfo INVARIANT_CULTURE = CultureInfo.InvariantCulture;
private static readonly string[] FALLBACK_DATE_FORMATS = ["dd.MM.yyyy", "yyyy-MM-dd", "MM/dd/yyyy"];
public override AssistantComponentType Type => AssistantComponentType.DATE_PICKER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public string Value
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Value));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Value), value);
}
public string Color
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value);
}
public string Placeholder
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Placeholder));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Placeholder), value);
}
public string HelperText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HelperText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HelperText), value);
}
public string DateFormat
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.DateFormat));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.DateFormat), value);
}
public string PickerVariant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PickerVariant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.Dates.ContainsKey(this.Name))
state.Dates[this.Name] = this.Value;
}
public override string UserPromptFallback(AssistantState state)
{
state.Dates.TryGetValue(this.Name, out var userInput);
return this.BuildAuditPromptBlock(userInput);
}
#endregion
public string GetDateFormat() => string.IsNullOrWhiteSpace(this.DateFormat) ? "yyyy-MM-dd" : this.DateFormat;
public DateTime? ParseValue(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
return TryParseDate(value, this.GetDateFormat(), out var parsedDate) ? parsedDate : null;
}
public string FormatValue(DateTime? value) => value.HasValue ? FormatDate(value.Value, this.GetDateFormat()) : string.Empty;
private static bool TryParseDate(string value, string? format, out DateTime parsedDate)
{
if (!string.IsNullOrWhiteSpace(format) &&
DateTime.TryParseExact(value, format, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate))
{
return true;
}
return DateTime.TryParseExact(value, FALLBACK_DATE_FORMATS, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate) ||
DateTime.TryParse(value, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate);
}
private static string FormatDate(DateTime value, string? format)
{
try
{
return value.ToString(string.IsNullOrWhiteSpace(format) ? FALLBACK_DATE_FORMATS[0] : format, INVARIANT_CULTURE);
}
catch (FormatException)
{
return value.ToString(FALLBACK_DATE_FORMATS[0], INVARIANT_CULTURE);
}
}
}
@@ -0,0 +1,146 @@
using System.Globalization;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantDateRangePicker : StatefulAssistantComponentBase
{
private static readonly CultureInfo INVARIANT_CULTURE = CultureInfo.InvariantCulture;
private static readonly string[] FALLBACK_DATE_FORMATS = ["dd.MM.yyyy", "yyyy-MM-dd" , "MM/dd/yyyy"];
public override AssistantComponentType Type => AssistantComponentType.DATE_RANGE_PICKER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public string Value
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Value));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Value), value);
}
public string Color
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value);
}
public string PlaceholderStart
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PlaceholderStart));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PlaceholderStart), value);
}
public string PlaceholderEnd
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PlaceholderEnd));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PlaceholderEnd), value);
}
public string HelperText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HelperText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HelperText), value);
}
public string DateFormat
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.DateFormat));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.DateFormat), value);
}
public string PickerVariant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PickerVariant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.DateRanges.ContainsKey(this.Name))
state.DateRanges[this.Name] = this.Value;
}
public override string UserPromptFallback(AssistantState state)
{
state.DateRanges.TryGetValue(this.Name, out var userInput);
return this.BuildAuditPromptBlock(userInput);
}
#endregion
public string GetDateFormat() => string.IsNullOrWhiteSpace(this.DateFormat) ? "yyyy-MM-dd" : this.DateFormat;
public DateRange? ParseValue(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
var format = this.GetDateFormat();
var parts = value.Split(" - ", 2, StringSplitOptions.TrimEntries);
if (parts.Length != 2)
return null;
if (!TryParseDate(parts[0], format, out var start) || !TryParseDate(parts[1], format, out var end))
return null;
return new DateRange(start, end);
}
public string FormatValue(DateRange? value)
{
if (value?.Start is null || value.End is null)
return string.Empty;
var format = this.GetDateFormat();
return $"{FormatDate(value.Start.Value, format)} - {FormatDate(value.End.Value, format)}";
}
private static bool TryParseDate(string value, string? format, out DateTime parsedDate)
{
if (!string.IsNullOrWhiteSpace(format) &&
DateTime.TryParseExact(value, format, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate))
{
return true;
}
return DateTime.TryParseExact(value, FALLBACK_DATE_FORMATS, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate) ||
DateTime.TryParse(value, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate);
}
private static string FormatDate(DateTime value, string? format)
{
try
{
return value.ToString(string.IsNullOrWhiteSpace(format) ? FALLBACK_DATE_FORMATS[0] : format, INVARIANT_CULTURE);
}
catch (FormatException)
{
return value.ToString(FALLBACK_DATE_FORMATS[0], INVARIANT_CULTURE);
}
}
}
@@ -0,0 +1,157 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantDropdown : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.DROPDOWN;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public AssistantDropdownItem Default
{
get
{
if (this.Props.TryGetValue(nameof(this.Default), out var v) && v is AssistantDropdownItem adi)
return adi;
return this.Items.Count > 0 ? this.Items[0] : AssistantDropdownItem.Default();
}
set => this.Props[nameof(this.Default)] = value;
}
public List<AssistantDropdownItem> Items
{
get => this.Props.TryGetValue(nameof(this.Items), out var v) && v is List<AssistantDropdownItem> list
? list
: [];
set => this.Props[nameof(this.Items)] = value;
}
public string ValueType
{
get => this.Props.TryGetValue(nameof(this.ValueType), out var v)
? v.ToString() ?? "string"
: "string";
set => this.Props[nameof(this.ValueType)] = value;
}
public bool IsMultiselect
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsMultiselect));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsMultiselect), value);
}
public bool HasSelectAll
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HasSelectAll));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HasSelectAll), value);
}
public string SelectAllText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.SelectAllText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.SelectAllText), value);
}
public string HelperText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HelperText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HelperText), value);
}
public string OpenIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.OpenIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.OpenIcon), value);
}
public string CloseIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.CloseIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.CloseIcon), value);
}
public string IconColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconColor), value);
}
public string IconPositon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconPositon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconPositon), value);
}
public string Variant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Variant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Variant), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (this.IsMultiselect)
{
if (!state.MultiSelect.ContainsKey(this.Name))
state.MultiSelect[this.Name] = string.IsNullOrWhiteSpace(this.Default.Value) ? [] : [this.Default.Value];
return;
}
if (!state.SingleSelect.ContainsKey(this.Name))
state.SingleSelect[this.Name] = this.Default.Value;
}
public override string UserPromptFallback(AssistantState state)
{
if (this.IsMultiselect && state.MultiSelect.TryGetValue(this.Name, out var selections))
return this.BuildAuditPromptBlock(string.Join(Environment.NewLine, selections.OrderBy(static value => value, StringComparer.Ordinal)));
state.SingleSelect.TryGetValue(this.Name, out var userInput);
return this.BuildAuditPromptBlock(userInput);
}
#endregion
public IEnumerable<object> GetParsedDropdownValues()
{
foreach (var item in this.Items)
{
switch (this.ValueType.ToLowerInvariant())
{
case "int":
if (int.TryParse(item.Value, out var i)) yield return i;
break;
case "double":
if (double.TryParse(item.Value, out var d)) yield return d;
break;
case "bool":
if (bool.TryParse(item.Value, out var b)) yield return b;
break;
default:
yield return item.Value;
break;
}
}
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,9 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public sealed class AssistantDropdownItem
{
public string Value { get; set; } = string.Empty;
public string Display { get; set; } = string.Empty;
public static AssistantDropdownItem Default() => new() { Value = string.Empty, Display = string.Empty};
}
@@ -0,0 +1,38 @@
using AIStudio.Assistants.Dynamic;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantFileContentReader : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.FILE_CONTENT_READER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.FileContent.ContainsKey(this.Name))
state.FileContent[this.Name] = new FileContentState();
}
public override string UserPromptFallback(AssistantState state)
{
state.FileContent.TryGetValue(this.Name, out var fileState);
return this.BuildAuditPromptBlock(fileState?.Content);
}
#endregion
}
@@ -0,0 +1,8 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public class AssistantForm : AssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.FORM;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
}
@@ -0,0 +1,32 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantHeading : AssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.HEADING;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Text
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Text));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Text), value);
}
public int Level
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Level), 2);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Level), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,84 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantImage : AssistantComponentBase
{
private const string PLUGIN_SCHEME = "plugin://";
public override AssistantComponentType Type => AssistantComponentType.IMAGE;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Src
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Src));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Src), value);
}
public string Alt
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Alt));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Alt), value);
}
public string Caption
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Caption));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Caption), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
public string ResolveSource(string pluginPath)
{
if (string.IsNullOrWhiteSpace(this.Src))
return string.Empty;
var resolved = this.Src;
if (resolved.StartsWith(PLUGIN_SCHEME, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(pluginPath))
{
var relative = resolved[PLUGIN_SCHEME.Length..]
.TrimStart('/', '\\')
.Replace('/', Path.DirectorySeparatorChar)
.Replace('\\', Path.DirectorySeparatorChar);
var filePath = Path.Join(pluginPath, relative);
if (!File.Exists(filePath))
return string.Empty;
var mime = GetImageMimeType(filePath);
var data = Convert.ToBase64String(File.ReadAllBytes(filePath));
return $"data:{mime};base64,{data}";
}
if (!Uri.TryCreate(resolved, UriKind.Absolute, out var uri))
return string.Empty;
return uri.Scheme is "http" or "https" or "data" ? resolved : string.Empty;
}
private static string GetImageMimeType(string path)
{
var extension = Path.GetExtension(path).TrimStart('.').ToLowerInvariant();
return extension switch
{
"svg" => "image/svg+xml",
"png" => "image/png",
"jpg" => "image/jpeg",
"jpeg" => "image/jpeg",
"gif" => "image/gif",
"webp" => "image/webp",
"bmp" => "image/bmp",
_ => "image/png",
};
}
}
@@ -0,0 +1,28 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantList : AssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LIST;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public List<AssistantListItem> Items
{
get => this.Props.TryGetValue(nameof(this.Items), out var v) && v is List<AssistantListItem> list
? list
: [];
set => this.Props[nameof(this.Items)] = value;
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,10 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public class AssistantListItem
{
public string Type { get; set; } = "TEXT";
public string Text { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public string IconColor { get; set; } = string.Empty;
public string? Href { get; set; }
}
@@ -0,0 +1,271 @@
using System.Collections;
using Lua;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal static class AssistantLuaConversion
{
/// <summary>
/// Converts a sequence of scalar .NET values into the array-like Lua table shape used by assistant state.
/// </summary>
public static LuaTable CreateLuaArray(IEnumerable values) => CreateLuaArrayCore(values);
/// <summary>
/// Reads a Lua value into either a scalar .NET value or one of the structured assistant data model types.
/// Lua itself only exposes scalars and tables, so structured assistant types such as dropdown/list items
/// must be detected from well-known table shapes.
/// </summary>
public static bool TryReadScalarOrStructuredValue(LuaValue value, out object result)
{
if (value.TryRead<string>(out var stringValue))
{
result = stringValue;
return true;
}
if (value.TryRead<bool>(out var boolValue))
{
result = boolValue;
return true;
}
if (value.TryRead<double>(out var doubleValue))
{
result = doubleValue;
return true;
}
if (value.TryRead<LuaTable>(out var table) && TryParseDropdownItem(table, out var dropdownItem))
{
result = dropdownItem;
return true;
}
if (value.TryRead<LuaTable>(out var dropdownListTable) && TryParseDropdownItemList(dropdownListTable, out var dropdownItems))
{
result = dropdownItems;
return true;
}
if (value.TryRead<LuaTable>(out var listItemListTable) && TryParseListItemList(listItemListTable, out var listItems))
{
result = listItems;
return true;
}
result = null!;
return false;
}
/// <summary>
/// Writes an assistant value into a Lua table.
/// This supports a broader set of .NET types than <see cref="TryReadScalarOrStructuredValue"/>,
/// because assistant props and state already exist as rich C# objects before being serialized back to Lua.
/// </summary>
public static bool TryWriteAssistantValue(LuaTable table, string key, object? value)
{
if (value is null or LuaFunction)
return false;
switch (value)
{
case LuaValue { Type: not LuaValueType.Nil } luaValue:
table[key] = luaValue;
return true;
case LuaTable luaTable:
table[key] = luaTable;
return true;
case string stringValue:
table[key] = (LuaValue)stringValue;
return true;
case bool boolValue:
table[key] = boolValue;
return true;
case byte byteValue:
table[key] = byteValue;
return true;
case sbyte sbyteValue:
table[key] = sbyteValue;
return true;
case short shortValue:
table[key] = shortValue;
return true;
case ushort ushortValue:
table[key] = ushortValue;
return true;
case int intValue:
table[key] = intValue;
return true;
case uint uintValue:
table[key] = uintValue;
return true;
case long longValue:
table[key] = longValue;
return true;
case ulong ulongValue:
table[key] = ulongValue;
return true;
case float floatValue:
table[key] = floatValue;
return true;
case double doubleValue:
table[key] = doubleValue;
return true;
case decimal decimalValue:
table[key] = (double)decimalValue;
return true;
case Enum enumValue:
table[key] = enumValue.ToString();
return true;
case AssistantDropdownItem dropdownItem:
table[key] = CreateDropdownItemTable(dropdownItem);
return true;
case IEnumerable<AssistantDropdownItem> dropdownItems:
table[key] = CreateLuaArrayCore(dropdownItems.Select(CreateDropdownItemTable));
return true;
case IEnumerable<AssistantListItem> listItems:
table[key] = CreateLuaArrayCore(listItems.Select(CreateListItemTable));
return true;
case IEnumerable<string> strings:
table[key] = CreateLuaArrayCore(strings);
return true;
default:
return false;
}
}
private static bool TryParseDropdownItem(LuaTable table, out AssistantDropdownItem item)
{
item = new AssistantDropdownItem();
if (!table.TryGetValue("Value", out var valueValue) || !valueValue.TryRead<string>(out var value))
return false;
if (!table.TryGetValue("Display", out var displayValue) || !displayValue.TryRead<string>(out var display))
return false;
item.Value = value;
item.Display = display;
return true;
}
private static bool TryParseDropdownItemList(LuaTable table, out List<AssistantDropdownItem> items)
{
items = new List<AssistantDropdownItem>();
for (var index = 1; index <= table.ArrayLength; index++)
{
var value = table[index];
if (!value.TryRead<LuaTable>(out var itemTable) || !TryParseDropdownItem(itemTable, out var item))
{
items = null!;
return false;
}
items.Add(item);
}
return true;
}
private static bool TryParseListItem(LuaTable table, out AssistantListItem item)
{
item = new AssistantListItem();
if (!table.TryGetValue("Text", out var textValue) || !textValue.TryRead<string>(out var text))
return false;
if (!table.TryGetValue("Type", out var typeValue) || !typeValue.TryRead<string>(out var type))
return false;
table.TryGetValue("Icon", out var iconValue);
iconValue.TryRead<string>(out var icon);
table.TryGetValue("IconColor", out var iconColorValue);
iconColorValue.TryRead<string>(out var iconColor);
item.Text = text;
item.Type = type;
item.Icon = icon;
item.IconColor = iconColor;
if (table.TryGetValue("Href", out var hrefValue) && hrefValue.TryRead<string>(out var href))
item.Href = href;
return true;
}
private static bool TryParseListItemList(LuaTable table, out List<AssistantListItem> items)
{
items = new List<AssistantListItem>();
for (var index = 1; index <= table.ArrayLength; index++)
{
var value = table[index];
if (!value.TryRead<LuaTable>(out var itemTable) || !TryParseListItem(itemTable, out var item))
{
items = null!;
return false;
}
items.Add(item);
}
return true;
}
private static LuaTable CreateDropdownItemTable(AssistantDropdownItem item) =>
new()
{
["Value"] = item.Value,
["Display"] = item.Display,
};
private static LuaTable CreateListItemTable(AssistantListItem item)
{
var table = new LuaTable
{
["Type"] = item.Type,
["Text"] = item.Text,
["Icon"] = item.Icon,
["IconColor"] = item.IconColor,
};
if (!string.IsNullOrWhiteSpace(item.Href))
table["Href"] = item.Href;
return table;
}
private static LuaTable CreateLuaArrayCore(IEnumerable values)
{
var luaArray = new LuaTable();
var index = 1;
foreach (var value in values)
{
luaArray[index++] = value switch
{
null => LuaValue.Nil,
LuaValue luaValue => luaValue,
LuaTable luaTable => luaTable,
string stringValue => (LuaValue)stringValue,
bool boolValue => boolValue,
byte byteValue => byteValue,
sbyte sbyteValue => sbyteValue,
short shortValue => shortValue,
ushort ushortValue => ushortValue,
int intValue => intValue,
uint uintValue => uintValue,
long longValue => longValue,
ulong ulongValue => ulongValue,
float floatValue => floatValue,
double doubleValue => doubleValue,
decimal decimalValue => (double)decimalValue,
_ => LuaValue.Nil,
};
}
return luaArray;
}
}
@@ -0,0 +1,26 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantProfileSelection : AssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.PROFILE_SELECTION;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string ValidationMessage
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.ValidationMessage));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.ValidationMessage), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,26 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantProviderSelection : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.PROVIDER_SELECTION;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,233 @@
using AIStudio.Assistants.Dynamic;
using Lua;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public sealed class AssistantState
{
public readonly Dictionary<string, string> Text = new(StringComparer.Ordinal);
public readonly Dictionary<string, string> SingleSelect = new(StringComparer.Ordinal);
public readonly Dictionary<string, HashSet<string>> MultiSelect = new(StringComparer.Ordinal);
public readonly Dictionary<string, bool> Booleans = new(StringComparer.Ordinal);
public readonly Dictionary<string, WebContentState> WebContent = new(StringComparer.Ordinal);
public readonly Dictionary<string, FileContentState> FileContent = new(StringComparer.Ordinal);
public readonly Dictionary<string, string> Colors = new(StringComparer.Ordinal);
public readonly Dictionary<string, string> Dates = new(StringComparer.Ordinal);
public readonly Dictionary<string, string> DateRanges = new(StringComparer.Ordinal);
public readonly Dictionary<string, string> Times = new(StringComparer.Ordinal);
public void Clear()
{
this.Text.Clear();
this.SingleSelect.Clear();
this.MultiSelect.Clear();
this.Booleans.Clear();
this.WebContent.Clear();
this.FileContent.Clear();
this.Colors.Clear();
this.Dates.Clear();
this.DateRanges.Clear();
this.Times.Clear();
}
public bool TryApplyValue(string fieldName, LuaValue value, out string expectedType)
{
expectedType = string.Empty;
if (this.Text.ContainsKey(fieldName))
{
expectedType = "string";
if (!value.TryRead<string>(out var textValue))
return false;
this.Text[fieldName] = textValue;
return true;
}
if (this.SingleSelect.ContainsKey(fieldName))
{
expectedType = "string";
if (!value.TryRead<string>(out var singleSelectValue))
return false;
this.SingleSelect[fieldName] = singleSelectValue;
return true;
}
if (this.MultiSelect.ContainsKey(fieldName))
{
expectedType = "string[]";
if (value.TryRead<LuaTable>(out var multiselectTable))
{
this.MultiSelect[fieldName] = ReadStringValues(multiselectTable);
return true;
}
if (!value.TryRead<string>(out var singleValue))
return false;
this.MultiSelect[fieldName] = string.IsNullOrWhiteSpace(singleValue) ? [] : [singleValue];
return true;
}
if (this.Booleans.ContainsKey(fieldName))
{
expectedType = "boolean";
if (!value.TryRead<bool>(out var boolValue))
return false;
this.Booleans[fieldName] = boolValue;
return true;
}
if (this.WebContent.TryGetValue(fieldName, out var webContentState))
{
expectedType = "string";
if (!value.TryRead<string>(out var webContentValue))
return false;
webContentState.Content = webContentValue;
return true;
}
if (this.FileContent.TryGetValue(fieldName, out var fileContentState))
{
expectedType = "string";
if (!value.TryRead<string>(out var fileContentValue))
return false;
fileContentState.Content = fileContentValue;
return true;
}
if (this.Colors.ContainsKey(fieldName))
{
expectedType = "string";
if (!value.TryRead<string>(out var colorValue))
return false;
this.Colors[fieldName] = colorValue;
return true;
}
if (this.Dates.ContainsKey(fieldName))
{
expectedType = "string";
if (!value.TryRead<string>(out var dateValue))
return false;
this.Dates[fieldName] = dateValue;
return true;
}
if (this.DateRanges.ContainsKey(fieldName))
{
expectedType = "string";
if (!value.TryRead<string>(out var dateRangeValue))
return false;
this.DateRanges[fieldName] = dateRangeValue;
return true;
}
if (this.Times.ContainsKey(fieldName))
{
expectedType = "string";
if (!value.TryRead<string>(out var timeValue))
return false;
this.Times[fieldName] = timeValue;
return true;
}
return false;
}
public LuaTable ToLuaTable(IEnumerable<IAssistantComponent> components)
{
var table = new LuaTable();
this.AddEntries(table, components);
return table;
}
private void AddEntries(LuaTable target, IEnumerable<IAssistantComponent> components)
{
foreach (var component in components)
{
if (component is INamedAssistantComponent named)
{
target[named.Name] = new LuaTable
{
["Type"] = Enum.GetName(component.Type) ?? string.Empty,
["Value"] = component is IStatefulAssistantComponent ? this.ReadValueForLua(named.Name) : LuaValue.Nil,
["Props"] = this.CreatePropsTable(component),
};
}
if (component.Children.Count > 0)
this.AddEntries(target, component.Children);
}
}
private LuaValue ReadValueForLua(string name)
{
if (this.Text.TryGetValue(name, out var textValue))
return textValue;
if (this.SingleSelect.TryGetValue(name, out var singleSelectValue))
return singleSelectValue;
if (this.MultiSelect.TryGetValue(name, out var multiSelectValue))
return AssistantLuaConversion.CreateLuaArray(multiSelectValue.OrderBy(static value => value, StringComparer.Ordinal));
if (this.Booleans.TryGetValue(name, out var boolValue))
return boolValue;
if (this.WebContent.TryGetValue(name, out var webContentValue))
return webContentValue.Content;
if (this.FileContent.TryGetValue(name, out var fileContentValue))
return fileContentValue.Content;
if (this.Colors.TryGetValue(name, out var colorValue))
return colorValue;
if (this.Dates.TryGetValue(name, out var dateValue))
return dateValue;
if (this.DateRanges.TryGetValue(name, out var dateRangeValue))
return dateRangeValue;
if (this.Times.TryGetValue(name, out var timeValue))
return timeValue;
return LuaValue.Nil;
}
private LuaTable CreatePropsTable(IAssistantComponent component)
{
var table = new LuaTable();
var nonReadableProps = ComponentPropSpecs.SPECS.TryGetValue(component.Type, out var propSpec)
? propSpec.NonReadable
: [];
foreach (var key in component.Props.Keys)
{
if (nonReadableProps.Contains(key, StringComparer.Ordinal))
continue;
if (!component.Props.TryGetValue(key, out var value))
continue;
if (!AssistantLuaConversion.TryWriteAssistantValue(table, key, value))
// ReSharper disable once RedundantJumpStatement
continue;
}
return table;
}
private static HashSet<string> ReadStringValues(LuaTable values)
{
var parsedValues = new HashSet<string>(StringComparer.Ordinal);
foreach (var entry in values)
{
if (entry.Value.TryRead<string>(out var value) && !string.IsNullOrWhiteSpace(value))
parsedValues.Add(value);
}
return parsedValues;
}
}
@@ -0,0 +1,109 @@
using AIStudio.Tools.PluginSystem.Assistants.Icons;
using Lua;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public sealed class AssistantSwitch : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.SWITCH;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public bool Value
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.Value));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.Value), value);
}
public bool Disabled
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.Disabled));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.Disabled), value);
}
public LuaFunction? OnChanged
{
get => this.Props.TryGetValue(nameof(this.OnChanged), out var value) && value is LuaFunction onChanged ? onChanged : null;
set => AssistantComponentPropHelper.WriteObject(this.Props, nameof(this.OnChanged), value);
}
public string LabelOn
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.LabelOn));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.LabelOn), value);
}
public string LabelOff
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.LabelOff));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.LabelOff), value);
}
public string LabelPlacement
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.LabelPlacement));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.LabelPlacement), value);
}
public string CheckedColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.CheckedColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.CheckedColor), value);
}
public string UncheckedColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.UncheckedColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UncheckedColor), value);
}
public string Icon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Icon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Icon), value);
}
public string IconColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconColor), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.Booleans.ContainsKey(this.Name))
state.Booleans[this.Name] = this.Value;
}
public override string UserPromptFallback(AssistantState state)
{
state.Booleans.TryGetValue(this.Name, out var userDecision);
return this.BuildAuditPromptBlock(userDecision.ToString());
}
#endregion
public static Color GetColor(string colorString) => Enum.TryParse<Color>(colorString, out var color) ? color : Color.Inherit;
public Placement GetLabelPlacement() => Enum.TryParse<Placement>(this.LabelPlacement, out var placement) ? placement : Placement.Right;
public string GetIconSvg() => MudBlazorIconRegistry.TryGetSvg(this.Icon, out var svg) ? svg : string.Empty;
}
@@ -0,0 +1,28 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantText : AssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.TEXT;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Content
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Content));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Content), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,118 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantTextArea : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.TEXT_AREA;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public string HelperText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HelperText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HelperText), value);
}
public bool HelperTextOnFocus
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HelperTextOnFocus));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HelperTextOnFocus), value);
}
public string Adornment
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Adornment));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Adornment), value);
}
public string AdornmentIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.AdornmentIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.AdornmentIcon), value);
}
public string AdornmentText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.AdornmentText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.AdornmentText), value);
}
public string AdornmentColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.AdornmentColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.AdornmentColor), value);
}
public string PrefillText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PrefillText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PrefillText), value);
}
public int? Counter
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Counter));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Counter), value);
}
public int MaxLength
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.MaxLength), PluginAssistants.TEXT_AREA_MAX_VALUE);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.MaxLength), value);
}
public bool IsImmediate
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsImmediate));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsImmediate), value);
}
public bool IsSingleLine
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsSingleLine));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsSingleLine), value);
}
public bool ReadOnly
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.ReadOnly));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.ReadOnly), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.Text.ContainsKey(this.Name))
state.Text[this.Name] = this.PrefillText;
}
public override string UserPromptFallback(AssistantState state)
{
state.Text.TryGetValue(this.Name, out var userInput);
return this.BuildAuditPromptBlock(userInput);
}
#endregion
public Adornment GetAdornmentPos() => Enum.TryParse<Adornment>(this.Adornment, out var position) ? position : MudBlazor.Adornment.Start;
public Color GetAdornmentColor() => Enum.TryParse<Color>(this.AdornmentColor, out var color) ? color : Color.Default;
}
@@ -0,0 +1,144 @@
using System.Globalization;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantTimePicker : StatefulAssistantComponentBase
{
private static readonly CultureInfo INVARIANT_CULTURE = CultureInfo.InvariantCulture;
private static readonly string[] FALLBACK_TIME_FORMATS = ["HH:mm", "HH:mm:ss", "hh:mm tt", "h:mm tt"];
public override AssistantComponentType Type => AssistantComponentType.TIME_PICKER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
public string Value
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Value));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Value), value);
}
public string Placeholder
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Placeholder));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Placeholder), value);
}
public string HelperText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HelperText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HelperText), value);
}
public string Color
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value);
}
public string TimeFormat
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.TimeFormat));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.TimeFormat), value);
}
public bool AmPm
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.AmPm));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.AmPm), value);
}
public string PickerVariant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PickerVariant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implementation of IStatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.Times.ContainsKey(this.Name))
state.Times[this.Name] = this.Value;
}
public override string UserPromptFallback(AssistantState state)
{
state.Times.TryGetValue(this.Name, out var userInput);
return this.BuildAuditPromptBlock(userInput);
}
#endregion
public string GetTimeFormat()
{
if (!string.IsNullOrWhiteSpace(this.TimeFormat))
return this.TimeFormat;
return this.AmPm ? "hh:mm tt" : "HH:mm";
}
public TimeSpan? ParseValue(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return null;
return TryParseTime(value, this.GetTimeFormat(), out var parsedTime) ? parsedTime : null;
}
public string FormatValue(TimeSpan? value) => value.HasValue ? FormatTime(value.Value, this.GetTimeFormat()) : string.Empty;
private static bool TryParseTime(string value, string? format, out TimeSpan parsedTime)
{
if ((!string.IsNullOrWhiteSpace(format) &&
DateTime.TryParseExact(value, format, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out var dateTime)) ||
DateTime.TryParseExact(value, FALLBACK_TIME_FORMATS, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out dateTime))
{
parsedTime = dateTime.TimeOfDay;
return true;
}
if (TimeSpan.TryParse(value, INVARIANT_CULTURE, out parsedTime))
return true;
parsedTime = TimeSpan.Zero;
return false;
}
private static string FormatTime(TimeSpan value, string? format)
{
var dateTime = DateTime.Today.Add(value);
try
{
return dateTime.ToString(string.IsNullOrWhiteSpace(format) ? FALLBACK_TIME_FORMATS[0] : format, INVARIANT_CULTURE);
}
catch (FormatException)
{
return dateTime.ToString(FALLBACK_TIME_FORMATS[0], INVARIANT_CULTURE);
}
}
}
@@ -0,0 +1,56 @@
using AIStudio.Assistants.Dynamic;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantWebContentReader : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.WEB_CONTENT_READER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public bool Preselect
{
get => this.Props.TryGetValue(nameof(this.Preselect), out var v) && v is true;
set => this.Props[nameof(this.Preselect)] = value;
}
public bool PreselectContentCleanerAgent
{
get => this.Props.TryGetValue(nameof(this.PreselectContentCleanerAgent), out var v) && v is true;
set => this.Props[nameof(this.PreselectContentCleanerAgent)] = value;
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
#region Implemention of StatefulAssistantComponent
public override void InitializeState(AssistantState state)
{
if (!state.WebContent.ContainsKey(this.Name))
{
state.WebContent[this.Name] = new WebContentState
{
Preselect = this.Preselect,
PreselectContentCleanerAgent = this.PreselectContentCleanerAgent,
};
}
}
public override string UserPromptFallback(AssistantState state)
{
state.WebContent.TryGetValue(this.Name, out var webState);
return this.BuildAuditPromptBlock(webState?.Content);
}
#endregion
}
@@ -0,0 +1,167 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public static class ComponentPropSpecs
{
public static readonly IReadOnlyDictionary<AssistantComponentType, PropSpec> SPECS =
new Dictionary<AssistantComponentType, PropSpec>
{
[AssistantComponentType.FORM] = new(
required: ["Children"],
optional: ["Class", "Style"]
),
[AssistantComponentType.TEXT_AREA] = new(
required: ["Name", "Label"],
optional: [
"HelperText", "HelperTextOnFocus", "UserPrompt", "PrefillText",
"ReadOnly", "IsSingleLine", "Counter", "MaxLength", "IsImmediate",
"Adornment", "AdornmentIcon", "AdornmentText", "AdornmentColor", "Class", "Style",
],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ]
),
[AssistantComponentType.BUTTON] = new(
required: ["Name", "Action"],
optional: [
"Text", "IsIconButton", "Variant", "Color", "IsFullWidth", "Size",
"StartIcon", "EndIcon", "IconColor", "IconSize", "Class", "Style"
],
confidential: ["Action"],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.BUTTON_GROUP] = new(
required: ["Name"],
optional: ["Variant", "Color", "Size", "OverrideStyles", "Vertical", "DropShadow", "Class", "Style"],
nonWriteable: ["Class", "Style" ]
),
[AssistantComponentType.DROPDOWN] = new(
required: ["Name", "Label", "Default", "Items"],
optional: [
"UserPrompt", "IsMultiselect", "HasSelectAll", "SelectAllText", "HelperText", "ValueType",
"OpenIcon", "CloseIcon", "IconColor", "IconPositon", "Variant", "Class", "Style"
],
nonWriteable: ["Name", "UserPrompt", "ValueType", "Class", "Style" ]
),
[AssistantComponentType.PROVIDER_SELECTION] = new(
required: ["Name", "Label"],
optional: ["Class", "Style"],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.PROFILE_SELECTION] = new(
required: [],
optional: ["ValidationMessage", "Class", "Style"],
nonWriteable: ["Class", "Style" ]
),
[AssistantComponentType.SWITCH] = new(
required: ["Name", "Value"],
optional: [
"Label", "OnChanged", "LabelOn", "LabelOff", "LabelPlacement", "Icon", "IconColor",
"UserPrompt", "CheckedColor", "UncheckedColor", "Disabled", "Class", "Style",
],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ],
confidential: ["OnChanged"]
),
[AssistantComponentType.HEADING] = new(
required: ["Text", "Level"],
optional: ["Class", "Style"],
nonWriteable: ["Class", "Style" ]
),
[AssistantComponentType.TEXT] = new(
required: ["Content"],
optional: ["Class", "Style"],
nonWriteable: ["Class", "Style" ]
),
[AssistantComponentType.LIST] = new(
required: ["Items"],
optional: ["Class", "Style"],
nonWriteable: ["Class", "Style" ]
),
[AssistantComponentType.WEB_CONTENT_READER] = new(
required: ["Name"],
optional: ["UserPrompt", "Preselect", "PreselectContentCleanerAgent", "Class", "Style"],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ]
),
[AssistantComponentType.FILE_CONTENT_READER] = new(
required: ["Name"],
optional: ["UserPrompt", "Class", "Style"],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ]
),
[AssistantComponentType.IMAGE] = new(
required: ["Src"],
optional: ["Alt", "Caption", "Class", "Style"],
nonWriteable: ["Src", "Alt", "Class", "Style" ]
),
[AssistantComponentType.COLOR_PICKER] = new(
required: ["Name", "Label"],
optional: [
"Placeholder", "ShowAlpha", "ShowToolbar", "ShowModeSwitch",
"PickerVariant", "UserPrompt", "Class", "Style"
],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ]
),
[AssistantComponentType.DATE_PICKER] = new(
required: ["Name", "Label"],
optional: [
"Value", "Placeholder", "HelperText", "DateFormat", "Color", "Elevation",
"PickerVariant", "UserPrompt", "Class", "Style"
],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ]
),
[AssistantComponentType.DATE_RANGE_PICKER] = new(
required: ["Name", "Label"],
optional: [
"Value", "PlaceholderStart", "PlaceholderEnd", "HelperText", "DateFormat",
"Elevation", "Color", "PickerVariant", "UserPrompt", "Class", "Style"
],
nonWriteable: ["Name", "UserPrompt", "Class", "Style" ]
),
[AssistantComponentType.TIME_PICKER] = new(
required: ["Name", "Label"],
optional: [
"Value", "Placeholder", "HelperText", "TimeFormat", "AmPm", "Color",
"Elevation", "PickerVariant", "UserPrompt", "Class", "Style"
]
),
[AssistantComponentType.LAYOUT_ITEM] = new(
required: ["Name"],
optional: ["Xs", "Sm", "Md", "Lg", "Xl", "Xxl", "Class", "Style"],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.LAYOUT_GRID] = new(
required: ["Name"],
optional: ["Justify", "Spacing", "Class", "Style"],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.LAYOUT_PAPER] = new(
required: ["Name"],
optional: [
"Elevation", "Height", "MaxHeight", "MinHeight", "Width", "MaxWidth", "MinWidth",
"IsOutlined", "IsSquare", "Class", "Style"
],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.LAYOUT_STACK] = new(
required: ["Name"],
optional: [
"IsRow", "IsReverse", "Breakpoint", "Align", "Justify", "Stretch",
"Wrap", "Spacing", "Class", "Style",
],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.LAYOUT_ACCORDION] = new(
required: ["Name"],
optional: [
"AllowMultiSelection", "IsDense", "HasOutline", "IsSquare", "Elevation",
"HasSectionPaddings", "Class", "Style",
],
nonWriteable: ["Name", "Class", "Style" ]
),
[AssistantComponentType.LAYOUT_ACCORDION_SECTION] = new(
required: ["Name", "HeaderText"],
optional: [
"IsDisabled", "IsExpanded", "IsDense", "HasInnerPadding", "HideIcon", "HeaderIcon", "HeaderColor",
"HeaderTypo", "HeaderAlign", "MaxHeight","ExpandIcon", "Class", "Style",
],
nonWriteable: ["Name", "Class", "Style" ]
),
};
}
@@ -0,0 +1,8 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public interface IAssistantComponent
{
AssistantComponentType Type { get; }
Dictionary<string, object> Props { get; }
List<IAssistantComponent> Children { get; }
}
@@ -0,0 +1,6 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public interface INamedAssistantComponent : IAssistantComponent
{
string Name { get; }
}
@@ -0,0 +1,8 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public interface IStatefulAssistantComponent : INamedAssistantComponent
{
void InitializeState(AssistantState state);
string UserPromptFallback(AssistantState state);
string UserPrompt { get; set; }
}
@@ -0,0 +1,56 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
internal sealed class AssistantAccordion : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LAYOUT_ACCORDION;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public bool AllowMultiSelection
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.AllowMultiSelection));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.AllowMultiSelection), value);
}
public bool IsDense
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsDense));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsDense), value);
}
public bool HasOutline
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HasOutline), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HasOutline), value);
}
public bool IsSquare
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsSquare));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsSquare), value);
}
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation));
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public bool HasSectionPaddings
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HasSectionPaddings), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HasSectionPaddings), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,94 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
internal sealed class AssistantAccordionSection : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LAYOUT_ACCORDION_SECTION;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public bool KeepContentAlive = true;
public string HeaderText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HeaderText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HeaderText), value);
}
public string HeaderColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HeaderColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HeaderColor), value);
}
public string HeaderIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HeaderIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HeaderIcon), value);
}
public string HeaderTypo
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HeaderTypo));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HeaderTypo), value);
}
public string HeaderAlign
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HeaderAlign));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HeaderAlign), value);
}
public bool IsDisabled
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsDisabled));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsDisabled), value);
}
public bool IsExpanded
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsExpanded));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsExpanded), value);
}
public bool IsDense
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsDense));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsDense), value);
}
public bool HasInnerPadding
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HasInnerPadding), true);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HasInnerPadding), value);
}
public bool HideIcon
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HideIcon));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HideIcon), value);
}
public int? MaxHeight
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.MaxHeight));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.MaxHeight), value);
}
public string ExpandIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.ExpandIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.ExpandIcon), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,32 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
internal sealed class AssistantGrid : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LAYOUT_GRID;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public string Justify
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Justify));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Justify), value);
}
public int Spacing
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Spacing), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Spacing), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,56 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
internal sealed class AssistantItem : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LAYOUT_ITEM;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public int? Xs
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Xs));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Xs), value);
}
public int? Sm
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Sm));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Sm), value);
}
public int? Md
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Md));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Md), value);
}
public int? Lg
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Lg));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Lg), value);
}
public int? Xl
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Xl));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Xl), value);
}
public int? Xxl
{
get => AssistantComponentPropHelper.ReadNullableInt(this.Props, nameof(this.Xxl));
set => AssistantComponentPropHelper.WriteNullableInt(this.Props, nameof(this.Xxl), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,74 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
internal sealed class AssistantPaper : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LAYOUT_PAPER;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 1);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Height
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Height));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Height), value);
}
public string MaxHeight
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.MaxHeight));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.MaxHeight), value);
}
public string MinHeight
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.MinHeight));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.MinHeight), value);
}
public string Width
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Width));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Width), value);
}
public string MaxWidth
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.MaxWidth));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.MaxWidth), value);
}
public string MinWidth
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.MinWidth));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.MinWidth), value);
}
public bool IsOutlined
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsOutlined));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsOutlined), value);
}
public bool IsSquare
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsSquare));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsSquare), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,68 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
internal sealed class AssistantStack : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.LAYOUT_STACK;
public override Dictionary<string, object> Props { get; set; } = new();
public override List<IAssistantComponent> Children { get; set; } = new();
public bool IsRow
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsRow));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsRow), value);
}
public bool IsReverse
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsReverse));
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsReverse), value);
}
public string Breakpoint
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Breakpoint));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Breakpoint), value);
}
public string Align
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Align));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Align), value);
}
public string Justify
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Justify));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Justify), value);
}
public string Stretch
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Stretch));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Stretch), value);
}
public string Wrap
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Wrap));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Wrap), value);
}
public int Spacing
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Spacing), 3);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Spacing), value);
}
public string Class
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value);
}
public string Style
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value);
}
}
@@ -0,0 +1,10 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public abstract class NamedAssistantComponentBase : AssistantComponentBase, INamedAssistantComponent
{
public string Name
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Name));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Name), value);
}
}
@@ -0,0 +1,22 @@
using System.Collections.Immutable;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public class PropSpec(
IEnumerable<string> required,
IEnumerable<string> optional,
IEnumerable<string>? nonReadable = null,
IEnumerable<string>? nonWriteable = null,
IEnumerable<string>? confidential = null)
{
public ImmutableArray<string> Required { get; } = MaterializeDistinct(required);
public ImmutableArray<string> Optional { get; } = MaterializeDistinct(optional);
public ImmutableArray<string> Confidential { get; } = MaterializeDistinct(confidential ?? []);
public ImmutableArray<string> NonReadable { get; } = MaterializeDistinct((nonReadable ?? []).Concat(confidential ?? []));
public ImmutableArray<string> NonWriteable { get; } = MaterializeDistinct((nonWriteable ?? []).Concat(confidential ?? []));
private static ImmutableArray<string> MaterializeDistinct(IEnumerable<string> source)
{
return source.Distinct(StringComparer.Ordinal).ToImmutableArray();
}
}
@@ -0,0 +1,30 @@
using System.Text;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public abstract class StatefulAssistantComponentBase : NamedAssistantComponentBase, IStatefulAssistantComponent
{
public abstract void InitializeState(AssistantState state);
public abstract string UserPromptFallback(AssistantState state);
public string UserPrompt
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.UserPrompt));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
}
protected string BuildAuditPromptBlock(string? value)
{
var builder = new StringBuilder();
var fieldName = this.Type.ToString().ToLowerInvariant();
builder.AppendLine($"[{fieldName}]");
builder.Append("name: ").AppendLine(this.Name);
builder.AppendLine("context:");
builder.AppendLine(!string.IsNullOrEmpty(this.UserPrompt) ? this.UserPrompt : "<not provided>");
builder.AppendLine("value:");
builder.AppendLine(!string.IsNullOrEmpty(value) ? value : "<empty>");
builder.Append($"[/{fieldName}]").AppendLine().AppendLine();
return builder.ToString();
}
}