diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
index 7bf99ef9..59845188 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
+++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
@@ -1,5 +1,4 @@
@attribute [Route(Routes.ASSISTANT_DYNAMIC)]
-@using AIStudio.Components
@using AIStudio.Settings
@using AIStudio.Tools.PluginSystem.Assistants.DataModel
@using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout
@@ -21,7 +20,7 @@ else
@code {
private RenderFragment RenderSwitch(AssistantSwitch assistantSwitch) => @
- @(switchFields[assistantSwitch.Name] ? assistantSwitch.LabelOn : assistantSwitch.LabelOff)
+ @(this.assistantState.Bools[assistantSwitch.Name] ? assistantSwitch.LabelOn : assistantSwitch.LabelOff)
;
}
@@ -51,7 +50,8 @@ else
var lines = textArea.IsSingleLine ? 1 : 6;
@@ -114,7 +116,7 @@ else
if (assistantDropdown.IsMultiselect)
{
-
}
@@ -419,7 +423,7 @@ else
var format = datePicker.GetDateFormat();
-
-
-
private bool showFooterProfileSelection = true;
private PluginAssistants? assistantPlugin;
- private readonly Dictionary inputFields = new();
- private readonly Dictionary dropdownFields = new();
- private readonly Dictionary> multiselectDropdownFields = new();
- private readonly Dictionary switchFields = new();
- private readonly Dictionary webContentFields = new();
- private readonly Dictionary fileContentFields = new();
- private readonly Dictionary colorPickerFields = new();
- private readonly Dictionary datePickerFields = new();
- private readonly Dictionary dateRangePickerFields = new();
- private readonly Dictionary timePickerFields = new();
+ private readonly AssistantState assistantState = new();
private readonly Dictionary imageCache = new();
private readonly HashSet executingButtonActions = [];
private readonly HashSet executingSwitchActions = [];
@@ -126,35 +113,11 @@ public partial class AssistantDynamic : AssistantBaseCore
protected override void ResetForm()
{
- foreach (var entry in this.inputFields)
- {
- this.inputFields[entry.Key] = string.Empty;
- }
- foreach (var entry in this.webContentFields)
- {
- entry.Value.Content = string.Empty;
- entry.Value.AgentIsRunning = false;
- }
- foreach (var entry in this.fileContentFields)
- {
- entry.Value.Content = string.Empty;
- }
- foreach (var entry in this.colorPickerFields)
- {
- this.colorPickerFields[entry.Key] = string.Empty;
- }
- foreach (var entry in this.datePickerFields)
- {
- this.datePickerFields[entry.Key] = string.Empty;
- }
- foreach (var entry in this.dateRangePickerFields)
- {
- this.dateRangePickerFields[entry.Key] = string.Empty;
- }
- foreach (var entry in this.timePickerFields)
- {
- this.timePickerFields[entry.Key] = string.Empty;
- }
+ this.assistantState.Clear();
+
+ var rootComponent = this.RootComponent;
+ if (rootComponent is not null)
+ this.InitializeComponentState(rootComponent.Children);
}
protected override bool MightPreselectValues()
@@ -232,37 +195,10 @@ public partial class AssistantDynamic : AssistantBaseCore
private LuaTable BuildPromptInput()
{
var input = new LuaTable();
-
- var fields = new LuaTable();
- foreach (var entry in this.inputFields)
- fields[entry.Key] = entry.Value ?? string.Empty;
- foreach (var entry in this.dropdownFields)
- fields[entry.Key] = entry.Value ?? string.Empty;
- foreach (var entry in this.multiselectDropdownFields)
- fields[entry.Key] = CreateLuaArray(entry.Value);
- foreach (var entry in this.switchFields)
- fields[entry.Key] = entry.Value;
- foreach (var entry in this.webContentFields)
- fields[entry.Key] = entry.Value.Content ?? string.Empty;
- foreach (var entry in this.fileContentFields)
- fields[entry.Key] = entry.Value.Content ?? string.Empty;
- foreach (var entry in this.colorPickerFields)
- fields[entry.Key] = entry.Value ?? string.Empty;
- foreach (var entry in this.datePickerFields)
- fields[entry.Key] = entry.Value ?? string.Empty;
- foreach (var entry in this.dateRangePickerFields)
- fields[entry.Key] = entry.Value ?? string.Empty;
- foreach (var entry in this.timePickerFields)
- fields[entry.Key] = entry.Value ?? string.Empty;
-
- input["fields"] = fields;
-
- var meta = new LuaTable();
var rootComponent = this.RootComponent;
- if (rootComponent is not null)
- this.AddMetaEntries(meta, rootComponent.Children);
-
- input["meta"] = meta;
+ input["state"] = rootComponent is not null
+ ? this.assistantState.ToLuaTable(rootComponent.Children)
+ : new LuaTable();
var profile = new LuaTable
{
@@ -276,24 +212,6 @@ public partial class AssistantDynamic : AssistantBaseCore
return input;
}
- private void AddMetaEntry(LuaTable meta, string name, AssistantComponentType type, string? label, string? userPrompt)
- {
- if (string.IsNullOrWhiteSpace(name))
- return;
-
- var entry = new LuaTable
- {
- ["Type"] = type.ToString(),
- };
-
- if (!string.IsNullOrWhiteSpace(label))
- entry["Label"] = label!;
- if (!string.IsNullOrWhiteSpace(userPrompt))
- entry["UserPrompt"] = userPrompt!;
-
- meta[name] = entry;
- }
-
private string CollectUserPromptFallback()
{
var prompt = string.Empty;
@@ -308,61 +226,8 @@ public partial class AssistantDynamic : AssistantBaseCore
{
foreach (var component in components)
{
- switch (component.Type)
- {
- case AssistantComponentType.TEXT_AREA:
- if (component is AssistantTextArea textArea && !this.inputFields.ContainsKey(textArea.Name))
- this.inputFields.Add(textArea.Name, textArea.PrefillText);
- break;
- case AssistantComponentType.DROPDOWN:
- if (component is AssistantDropdown dropdown)
- {
- if (dropdown.IsMultiselect)
- {
- if (!this.multiselectDropdownFields.ContainsKey(dropdown.Name))
- this.multiselectDropdownFields.Add(dropdown.Name, CreateInitialMultiselectValues(dropdown));
- }
- else if (!this.dropdownFields.ContainsKey(dropdown.Name))
- {
- this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value);
- }
- }
- break;
- case AssistantComponentType.SWITCH:
- if (component is AssistantSwitch switchComponent && !this.switchFields.ContainsKey(switchComponent.Name))
- this.switchFields.Add(switchComponent.Name, switchComponent.Value);
- break;
- case AssistantComponentType.WEB_CONTENT_READER:
- if (component is AssistantWebContentReader webContent && !this.webContentFields.ContainsKey(webContent.Name))
- {
- this.webContentFields.Add(webContent.Name, new WebContentState
- {
- Preselect = webContent.Preselect,
- PreselectContentCleanerAgent = webContent.PreselectContentCleanerAgent,
- });
- }
- break;
- case AssistantComponentType.FILE_CONTENT_READER:
- if (component is AssistantFileContentReader fileContent && !this.fileContentFields.ContainsKey(fileContent.Name))
- this.fileContentFields.Add(fileContent.Name, new FileContentState());
- break;
- case AssistantComponentType.COLOR_PICKER:
- if (component is AssistantColorPicker assistantColorPicker && !this.colorPickerFields.ContainsKey(assistantColorPicker.Name))
- this.colorPickerFields.Add(assistantColorPicker.Name, assistantColorPicker.Placeholder);
- break;
- case AssistantComponentType.DATE_PICKER:
- if (component is AssistantDatePicker datePicker && !this.datePickerFields.ContainsKey(datePicker.Name))
- this.datePickerFields.Add(datePicker.Name, datePicker.Value);
- break;
- case AssistantComponentType.DATE_RANGE_PICKER:
- if (component is AssistantDateRangePicker dateRangePicker && !this.dateRangePickerFields.ContainsKey(dateRangePicker.Name))
- this.dateRangePickerFields.Add(dateRangePicker.Name, dateRangePicker.Value);
- break;
- case AssistantComponentType.TIME_PICKER:
- if (component is AssistantTimePicker timePicker && !this.timePickerFields.ContainsKey(timePicker.Name))
- this.timePickerFields.Add(timePicker.Name, timePicker.Value);
- break;
- }
+ if (component is IStatefulAssistantComponent statefulComponent)
+ statefulComponent.InitializeState(this.assistantState);
if (component.Children.Count > 0)
this.InitializeComponentState(component.Children);
@@ -415,7 +280,7 @@ public partial class AssistantDynamic : AssistantBaseCore
if (string.IsNullOrWhiteSpace(switchComponent.Name))
return;
- this.switchFields[switchComponent.Name] = value;
+ this.assistantState.Bools[switchComponent.Name] = value;
if (this.assistantPlugin is null || switchComponent.OnChanged is null)
{
@@ -463,123 +328,28 @@ public partial class AssistantDynamic : AssistantBaseCore
private void TryApplyFieldUpdate(string fieldName, LuaValue value, AssistantComponentType sourceType)
{
- if (this.inputFields.ContainsKey(fieldName))
+ if (this.assistantState.TryApplyValue(fieldName, value, out var expectedType))
+ return;
+
+ if (!string.IsNullOrWhiteSpace(expectedType))
{
- if (value.TryRead(out var textValue))
- this.inputFields[fieldName] = textValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
+ this.Logger.LogWarning($"Assistant {sourceType} callback tried to write an invalid value to '{fieldName}'. Expected {expectedType}.");
return;
}
- if (this.dropdownFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var dropdownValue))
- this.dropdownFields[fieldName] = dropdownValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- if (this.multiselectDropdownFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var multiselectDropdownValue))
- this.multiselectDropdownFields[fieldName] = ReadStringValues(multiselectDropdownValue);
- else if (value.TryRead(out var singleDropdownValue))
- this.multiselectDropdownFields[fieldName] = string.IsNullOrWhiteSpace(singleDropdownValue) ? [] : [singleDropdownValue];
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string[]", sourceType);
- return;
- }
-
- if (this.switchFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var boolValue))
- this.switchFields[fieldName] = boolValue;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "boolean", sourceType);
- return;
- }
-
- if (this.colorPickerFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var colorValue))
- this.colorPickerFields[fieldName] = colorValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- if (this.datePickerFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var dateValue))
- this.datePickerFields[fieldName] = dateValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- if (this.dateRangePickerFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var dateRangeValue))
- this.dateRangePickerFields[fieldName] = dateRangeValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- if (this.timePickerFields.ContainsKey(fieldName))
- {
- if (value.TryRead(out var timeValue))
- this.timePickerFields[fieldName] = timeValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- if (this.webContentFields.TryGetValue(fieldName, out var webContentState))
- {
- if (value.TryRead(out var webContentValue))
- webContentState.Content = webContentValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- if (this.fileContentFields.TryGetValue(fieldName, out var fileContentState))
- {
- if (value.TryRead(out var fileContentValue))
- fileContentState.Content = fileContentValue ?? string.Empty;
- else
- this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
- return;
- }
-
- this.Logger.LogWarning("Assistant {ComponentType} callback tried to update unknown field '{FieldName}'. The value is ignored.", sourceType, fieldName);
- }
-
- private void LogFieldUpdateTypeMismatch(string fieldName, string expectedType, AssistantComponentType sourceType)
- {
- this.Logger.LogWarning("Assistant {ComponentType} callback tried to write an invalid value to '{FieldName}'. Expected {ExpectedType}.", sourceType, fieldName, expectedType);
+ this.Logger.LogWarning($"Assistant {sourceType} callback tried to update unknown field '{fieldName}'. The value is ignored.");
}
private EventCallback> CreateMultiselectDropdownChangedCallback(string fieldName) =>
EventCallback.Factory.Create>(this, values =>
{
- this.multiselectDropdownFields[fieldName] = values;
+ this.assistantState.MultiSelect[fieldName] = values;
});
private string? ValidateProfileSelection(AssistantProfileSelection profileSelection, Profile? profile)
{
- if (profile == default || profile == Profile.NO_PROFILE)
- {
- if (!string.IsNullOrWhiteSpace(profileSelection.ValidationMessage))
- return profileSelection.ValidationMessage;
-
- return this.T("Please select one of your profiles.");
- }
-
- return null;
+ if (profile != default && profile != Profile.NO_PROFILE) return null;
+ return !string.IsNullOrWhiteSpace(profileSelection.ValidationMessage) ? profileSelection.ValidationMessage : this.T("Please select one of your profiles.");
}
private async Task Submit()
@@ -589,176 +359,22 @@ public partial class AssistantDynamic : AssistantBaseCore
await this.AddAIResponseAsync(time);
}
- private void AddMetaEntries(LuaTable meta, IEnumerable components)
- {
- foreach (var component in components)
- {
- switch (component)
- {
- case AssistantTextArea textArea:
- this.AddMetaEntry(meta, textArea.Name, component.Type, textArea.Label, textArea.UserPrompt);
- break;
- case AssistantDropdown dropdown:
- this.AddMetaEntry(meta, dropdown.Name, component.Type, dropdown.Label, dropdown.UserPrompt);
- break;
- case AssistantSwitch switchComponent:
- this.AddMetaEntry(meta, switchComponent.Name, component.Type, switchComponent.Label, switchComponent.UserPrompt);
- break;
- case AssistantWebContentReader webContent:
- this.AddMetaEntry(meta, webContent.Name, component.Type, null, webContent.UserPrompt);
- break;
- case AssistantFileContentReader fileContent:
- this.AddMetaEntry(meta, fileContent.Name, component.Type, null, fileContent.UserPrompt);
- break;
- case AssistantColorPicker colorPicker:
- this.AddMetaEntry(meta, colorPicker.Name, component.Type, colorPicker.Label, colorPicker.UserPrompt);
- break;
- case AssistantDatePicker datePicker:
- this.AddMetaEntry(meta, datePicker.Name, component.Type, datePicker.Label, datePicker.UserPrompt);
- break;
- case AssistantDateRangePicker dateRangePicker:
- this.AddMetaEntry(meta, dateRangePicker.Name, component.Type, dateRangePicker.Label, dateRangePicker.UserPrompt);
- break;
- case AssistantTimePicker timePicker:
- this.AddMetaEntry(meta, timePicker.Name, component.Type, timePicker.Label, timePicker.UserPrompt);
- break;
- }
-
- if (component.Children.Count > 0)
- this.AddMetaEntries(meta, component.Children);
- }
- }
-
private string CollectUserPromptFallback(IEnumerable components)
{
- var prompt = string.Empty;
+ var prompt = new StringBuilder();
foreach (var component in components)
{
- var userInput = string.Empty;
- var userDecision = false;
-
- switch (component.Type)
- {
- case AssistantComponentType.TEXT_AREA:
- if (component is AssistantTextArea textArea)
- {
- prompt += $"context:{Environment.NewLine}{textArea.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (this.inputFields.TryGetValue(textArea.Name, out userInput))
- prompt += $"user prompt:{Environment.NewLine}{userInput}";
- }
- break;
- case AssistantComponentType.DROPDOWN:
- if (component is AssistantDropdown dropdown)
- {
- prompt += $"{Environment.NewLine}context:{Environment.NewLine}{dropdown.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (dropdown.IsMultiselect && this.multiselectDropdownFields.TryGetValue(dropdown.Name, out var selections))
- prompt += $"user prompt:{Environment.NewLine}{string.Join(Environment.NewLine, selections.OrderBy(static value => value, StringComparer.Ordinal))}";
- else if (this.dropdownFields.TryGetValue(dropdown.Name, out userInput))
- prompt += $"user prompt:{Environment.NewLine}{userInput}";
- }
- break;
- case AssistantComponentType.SWITCH:
- if (component is AssistantSwitch switchComponent)
- {
- prompt += $"{Environment.NewLine}context:{Environment.NewLine}{switchComponent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (this.switchFields.TryGetValue(switchComponent.Name, out userDecision))
- prompt += $"user decision:{Environment.NewLine}{userDecision}";
- }
- break;
- case AssistantComponentType.WEB_CONTENT_READER:
- if (component is AssistantWebContentReader webContent &&
- this.webContentFields.TryGetValue(webContent.Name, out var webState))
- {
- if (!string.IsNullOrWhiteSpace(webContent.UserPrompt))
- prompt += $"{Environment.NewLine}context:{Environment.NewLine}{webContent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
-
- if (!string.IsNullOrWhiteSpace(webState.Content))
- prompt += $"user prompt:{Environment.NewLine}{webState.Content}";
- }
- break;
- case AssistantComponentType.FILE_CONTENT_READER:
- if (component is AssistantFileContentReader fileContent &&
- this.fileContentFields.TryGetValue(fileContent.Name, out var fileState))
- {
- if (!string.IsNullOrWhiteSpace(fileContent.UserPrompt))
- prompt += $"{Environment.NewLine}context:{Environment.NewLine}{fileContent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
-
- if (!string.IsNullOrWhiteSpace(fileState.Content))
- prompt += $"user prompt:{Environment.NewLine}{fileState.Content}";
- }
- break;
- case AssistantComponentType.COLOR_PICKER:
- if (component is AssistantColorPicker colorPicker)
- {
- prompt += $"context:{Environment.NewLine}{colorPicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (this.colorPickerFields.TryGetValue(colorPicker.Name, out userInput))
- prompt += $"user prompt:{Environment.NewLine}{userInput}";
- }
- break;
- case AssistantComponentType.DATE_PICKER:
- if (component is AssistantDatePicker datePicker)
- {
- prompt += $"context:{Environment.NewLine}{datePicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (this.datePickerFields.TryGetValue(datePicker.Name, out userInput))
- prompt += $"user prompt:{Environment.NewLine}{userInput}";
- }
- break;
- case AssistantComponentType.DATE_RANGE_PICKER:
- if (component is AssistantDateRangePicker dateRangePicker)
- {
- prompt += $"context:{Environment.NewLine}{dateRangePicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (this.dateRangePickerFields.TryGetValue(dateRangePicker.Name, out userInput))
- prompt += $"user prompt:{Environment.NewLine}{userInput}";
- }
- break;
- case AssistantComponentType.TIME_PICKER:
- if (component is AssistantTimePicker timePicker)
- {
- prompt += $"context:{Environment.NewLine}{timePicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
- if (this.timePickerFields.TryGetValue(timePicker.Name, out userInput))
- prompt += $"user prompt:{Environment.NewLine}{userInput}";
- }
- break;
- }
+ if (component is IStatefulAssistantComponent statefulComponent)
+ prompt.Append(statefulComponent.UserPromptFallback(this.assistantState));
if (component.Children.Count > 0)
- prompt += this.CollectUserPromptFallback(component.Children);
+ {
+ prompt.Append(this.CollectUserPromptFallback(component.Children));
+ }
}
- return prompt;
- }
-
- private static HashSet CreateInitialMultiselectValues(AssistantDropdown dropdown)
- {
- if (string.IsNullOrWhiteSpace(dropdown.Default.Value))
- return [];
-
- return [dropdown.Default.Value];
- }
-
- private static LuaTable CreateLuaArray(IEnumerable values)
- {
- var luaArray = new LuaTable();
- var index = 1;
-
- foreach (var value in values.OrderBy(static value => value, StringComparer.Ordinal))
- luaArray[index++] = value;
-
- return luaArray;
- }
-
- private static HashSet ReadStringValues(LuaTable values)
- {
- var parsedValues = new HashSet(StringComparer.Ordinal);
-
- foreach (var entry in values)
- {
- if (entry.Value.TryRead(out var value) && !string.IsNullOrWhiteSpace(value))
- parsedValues.Add(value);
- }
-
- return parsedValues;
+ return prompt.ToString();
}
private DateTime? ParseDatePickerValue(string? value, string? format)
@@ -774,7 +390,7 @@ public partial class AssistantDynamic : AssistantBaseCore
private void SetDatePickerValue(string fieldName, DateTime? value, string? format)
{
- this.datePickerFields[fieldName] = value.HasValue ? FormatDate(value.Value, format) : string.Empty;
+ this.assistantState.Dates[fieldName] = value.HasValue ? FormatDate(value.Value, format) : string.Empty;
}
private DateRange? ParseDateRangePickerValue(string? value, string? format)
@@ -796,11 +412,11 @@ public partial class AssistantDynamic : AssistantBaseCore
{
if (value?.Start is null || value.End is null)
{
- this.dateRangePickerFields[fieldName] = string.Empty;
+ this.assistantState.DateRanges[fieldName] = string.Empty;
return;
}
- this.dateRangePickerFields[fieldName] = $"{FormatDate(value.Start.Value, format)} - {FormatDate(value.End.Value, format)}";
+ this.assistantState.DateRanges[fieldName] = $"{FormatDate(value.Start.Value, format)} - {FormatDate(value.End.Value, format)}";
}
private TimeSpan? ParseTimePickerValue(string? value, string? format)
@@ -816,7 +432,7 @@ public partial class AssistantDynamic : AssistantBaseCore
private void SetTimePickerValue(string fieldName, TimeSpan? value, string? format)
{
- this.timePickerFields[fieldName] = value.HasValue ? FormatTime(value.Value, format) : string.Empty;
+ this.assistantState.Times[fieldName] = value.HasValue ? FormatTime(value.Value, format) : string.Empty;
}
private static bool TryParseDate(string value, string? format, out DateTime parsedDate)
diff --git a/app/MindWork AI Studio/Assistants/Dynamic/FileContentState.cs b/app/MindWork AI Studio/Assistants/Dynamic/FileContentState.cs
index f7e0da60..7ea92bd2 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/FileContentState.cs
+++ b/app/MindWork AI Studio/Assistants/Dynamic/FileContentState.cs
@@ -1,6 +1,6 @@
namespace AIStudio.Assistants.Dynamic;
-internal sealed class FileContentState
+public sealed class FileContentState
{
public string Content { get; set; } = string.Empty;
}
diff --git a/app/MindWork AI Studio/Assistants/Dynamic/WebContentState.cs b/app/MindWork AI Studio/Assistants/Dynamic/WebContentState.cs
index d9398f05..71735e67 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/WebContentState.cs
+++ b/app/MindWork AI Studio/Assistants/Dynamic/WebContentState.cs
@@ -1,6 +1,6 @@
namespace AIStudio.Assistants.Dynamic;
-internal sealed class WebContentState
+public sealed class WebContentState
{
public string Content { get; set; } = string.Empty;
public bool Preselect { get; set; }
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs
index 58864a6d..9853797b 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs
@@ -2,18 +2,12 @@ using Lua;
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
-public sealed class AssistantButton : AssistantComponentBase
+public sealed class AssistantButton : NamedAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.BUTTON;
public override Dictionary Props { get; set; } = new();
public override List Children { get; set; } = new();
- public string Name
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Name));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Name), value);
- }
-
public string Text
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Text));
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantColorPicker.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantColorPicker.cs
index e77a45eb..910ac218 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantColorPicker.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantColorPicker.cs
@@ -1,16 +1,11 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
-internal sealed class AssistantColorPicker : AssistantComponentBase
+internal sealed class AssistantColorPicker : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.COLOR_PICKER;
public override Dictionary Props { get; set; } = new();
public override List Children { get; set; } = new();
- public string Name
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Name));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Name), value);
- }
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
@@ -47,12 +42,6 @@ internal sealed class AssistantColorPicker : AssistantComponentBase
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
- public string UserPrompt
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.UserPrompt));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
- }
-
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
@@ -71,5 +60,26 @@ internal sealed class AssistantColorPicker : AssistantComponentBase
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)
+ {
+ var userInput = string.Empty;
+
+ var promptFragment = $"context:{Environment.NewLine}{this.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (state.Colors.TryGetValue(this.Name, out userInput) && !string.IsNullOrWhiteSpace(userInput))
+ promptFragment += $"user prompt:{Environment.NewLine}{userInput}";
+
+ return promptFragment;
+ }
+
+ #endregion
+
public PickerVariant GetPickerVariant() => Enum.TryParse(this.PickerVariant, out var variant) ? variant : MudBlazor.PickerVariant.Static;
}
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDatePicker.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDatePicker.cs
index 4be09a79..003392c6 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDatePicker.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDatePicker.cs
@@ -1,17 +1,11 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
-internal sealed class AssistantDatePicker : AssistantComponentBase
+internal sealed class AssistantDatePicker : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.DATE_PICKER;
public override Dictionary Props { get; set; } = new();
public override List Children { get; set; } = new();
- public string Name
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Name));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Name), value);
- }
-
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
@@ -53,12 +47,6 @@ internal sealed class AssistantDatePicker : AssistantComponentBase
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PickerVariant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
-
- public string UserPrompt
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.UserPrompt));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
- }
public int Elevation
{
@@ -78,5 +66,26 @@ internal sealed class AssistantDatePicker : AssistantComponentBase
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)
+ {
+ var userInput = string.Empty;
+
+ var promptFragment = $"context:{Environment.NewLine}{this.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (state.Dates.TryGetValue(this.Name, out userInput) && !string.IsNullOrWhiteSpace(userInput))
+ promptFragment += $"user prompt:{Environment.NewLine}{userInput}";
+
+ return promptFragment;
+ }
+
+ #endregion
+
public string GetDateFormat() => string.IsNullOrWhiteSpace(this.DateFormat) ? "yyyy-MM-dd" : this.DateFormat;
}
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDateRangePicker.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDateRangePicker.cs
index f80db2dc..d646584f 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDateRangePicker.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDateRangePicker.cs
@@ -1,17 +1,11 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
-internal sealed class AssistantDateRangePicker : AssistantComponentBase
+internal sealed class AssistantDateRangePicker : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.DATE_RANGE_PICKER;
public override Dictionary Props { get; set; } = new();
public override List Children { get; set; } = new();
- public string Name
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Name));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Name), value);
- }
-
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
@@ -59,12 +53,6 @@ internal sealed class AssistantDateRangePicker : AssistantComponentBase
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.PickerVariant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.PickerVariant), value);
}
-
- public string UserPrompt
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.UserPrompt));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
- }
public int Elevation
{
@@ -84,5 +72,26 @@ internal sealed class AssistantDateRangePicker : AssistantComponentBase
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)
+ {
+ var userInput = string.Empty;
+
+ var promptFragment = $"context:{Environment.NewLine}{this.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (state.DateRanges.TryGetValue(this.Name, out userInput) && !string.IsNullOrWhiteSpace(userInput))
+ promptFragment += $"user prompt:{Environment.NewLine}{userInput}";
+
+ return promptFragment;
+ }
+
+ #endregion
+
public string GetDateFormat() => string.IsNullOrWhiteSpace(this.DateFormat) ? "yyyy-MM-dd" : this.DateFormat;
}
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs
index 926cb145..4c9e35b2 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs
@@ -1,29 +1,17 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
-internal sealed class AssistantDropdown : AssistantComponentBase
+internal sealed class AssistantDropdown : StatefulAssistantComponentBase
{
public override AssistantComponentType Type => AssistantComponentType.DROPDOWN;
public override Dictionary Props { get; set; } = new();
public override List Children { get; set; } = new();
- public string Name
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Name));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Name), value);
- }
-
public string Label
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Label));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Label), value);
}
- public string UserPrompt
- {
- get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.UserPrompt));
- set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
- }
-
public AssistantDropdownItem Default
{
get
@@ -106,6 +94,41 @@ internal sealed class AssistantDropdown : AssistantComponentBase
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)
+ {
+ var userInput = string.Empty;
+
+ var promptFragment = $"{Environment.NewLine}context:{Environment.NewLine}{this.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (this.IsMultiselect && state.MultiSelect.TryGetValue(this.Name, out var selections))
+ {
+ promptFragment += $"user prompt:{Environment.NewLine}{string.Join(Environment.NewLine, selections.OrderBy(static value => value, StringComparer.Ordinal))}";
+ }
+ else if (state.SingleSelect.TryGetValue(this.Name, out userInput))
+ {
+ promptFragment += $"user prompt:{Environment.NewLine}{userInput}";
+ }
+
+ return promptFragment;
+ }
+
+ #endregion
+
public IEnumerable