From b9a9f2d823db98fd87cc4821028468ac5aa1b38e Mon Sep 17 00:00:00 2001 From: nilsk Date: Mon, 21 Jul 2025 14:38:08 +0200 Subject: [PATCH 01/20] WIP: Working out a data model and an example lua file to turn it into assistants --- .../Plugins/assistants/plugin.lua | 85 +++++++++++++++++++ .../Assistants/DataModel/AssistantButton.cs | 24 ++++++ .../DataModel/AssistantComponentBase.cs | 8 ++ .../Assistants/DataModel/AssistantDropdown.cs | 29 +++++++ .../DataModel/AssistantDropdownItem.cs | 7 ++ .../Assistants/DataModel/AssistantForm.cs | 8 ++ .../DataModel/AssistantProviderSelection.cs | 19 +++++ .../Assistants/DataModel/AssistantTextArea.cs | 26 ++++++ .../DataModel/AssistantUiCompontentType.cs | 11 +++ .../DataModel/IAssistantComponent.cs | 8 ++ .../Assistants/PluginAssistants.cs | 19 +++++ .../Tools/PluginSystem/PluginCategory.cs | 1 + 12 files changed, 245 insertions(+) create mode 100644 app/MindWork AI Studio/Plugins/assistants/plugin.lua create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua new file mode 100644 index 000000000..58176671a --- /dev/null +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -0,0 +1,85 @@ +require("icon") + +-- The ID for this plugin: +ID = "43065dbc-78d0-45b7-92be-f14c2926e2dc" + +-- The icon for the plugin: +ICON_SVG = SVG + +-- The name of the plugin: +NAME = "MindWork AI Studio - German / Deutsch" + +-- The description of the plugin: +DESCRIPTION = "Dieses Plugin bietet deutsche Sprachunterstützung für MindWork AI Studio." + +-- The version of the plugin: +VERSION = "1.0.0" + +-- The type of the plugin: +TYPE = "LANGUAGE" + +-- The authors of the plugin: +AUTHORS = {"MindWork AI Community"} + +-- The support contact for the plugin: +SUPPORT_CONTACT = "MindWork AI Community" + +-- The source URL for the plugin: +SOURCE_URL = "https://github.com/MindWorkAI/AI-Studio" + +-- The categories for the plugin: +CATEGORIES = { "ASSISTANT" } + +-- The target groups for the plugin: +TARGET_GROUPS = { "EVERYONE" } + +-- The flag for whether the plugin is maintained: +IS_MAINTAINED = true + +-- When the plugin is deprecated, this message will be shown to users: +DEPRECATION_MESSAGE = "" + +ASSISTANT = { + Name = "Grammatik- und Rechtschreibprüfung", + Description = "Grammatik und Rechtschreibung eines Textes überprüfen.", + UI = { + Type = "Form", + Children = { + { + Type = "TextArea", + Props = { + Name = "input", + Label = "Ihre Eingabe zur Überprüfung" + } + }, + { + Type = "Dropdown", + Props = { + Name = "language", + Label = "Sprache", + Default = "Sprache nicht angeben", + Items = { + { Value = "de-DE", Display = "Deutsch" }, + { Value = "en-UK", Display = "Englisch (UK)" }, + { Value = "en-US", Display = "Englisch (US)" }, + } + } + }, + { + Type = "ProviderSelection", + Props = { + Name = "Anbieter", + Label = "LLM auswählen" + } + }, + { + Type = "Button", + Props = { + Name = "submit", + Text = "Korrekturlesen", + Action = "OnSubmit" + } + }, + } + }, +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs new file mode 100644 index 000000000..76cd22507 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs @@ -0,0 +1,24 @@ +namespace AIStudio.Tools.PluginSystem; + +public class AssistantButton : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.BUTTON; + public Dictionary Props { get; set; } = new(); + public List Children { get; set; } = new(); + + public string Name + { + get => this.Props.TryGetValue(nameof(this.Name), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Name)] = value; + } + public string Text + { + get => this.Props.TryGetValue(nameof(this.Text), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Text)] = value; + } + public string Action + { + get => this.Props.TryGetValue(nameof(this.Action), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Action)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs new file mode 100644 index 000000000..260b4ef38 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.PluginSystem; + +public abstract class AssistantComponentBase : IAssistantComponent +{ + public abstract AssistantUiCompontentType Type { get; } + public Dictionary Props { get; } + public List Children { get; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs new file mode 100644 index 000000000..0f2049792 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs @@ -0,0 +1,29 @@ +namespace AIStudio.Tools.PluginSystem; + +public class AssistantDropdown : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.DROPDOWN; + public Dictionary Props { get; set; } = new(); + public List Children { get; set; } = new(); + + public string Name + { + get => this.Props.TryGetValue(nameof(this.Name), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Name)] = value; + } + public string Label + { + get => this.Props.TryGetValue(nameof(this.Label), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Label)] = value; + } + public string Default + { + get => this.Props.TryGetValue(nameof(this.Default), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Default)] = value; + } + public List Items + { + get => this.Props.TryGetValue(nameof(this.Items), out var v) && v is List list ? list : []; + set => this.Props[nameof(this.Items)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs new file mode 100644 index 000000000..d2968301a --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs @@ -0,0 +1,7 @@ +namespace AIStudio.Tools.PluginSystem; + +public class AssistantDropdownItem +{ + public string Value { get; set; } = string.Empty; + public string Display { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs new file mode 100644 index 000000000..1bc490f31 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.PluginSystem; + +public class AssistantForm : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.FORM; + public Dictionary Props { get; set; } = new(); + public List Children { get; set; } = new(); +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs new file mode 100644 index 000000000..af886db1f --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs @@ -0,0 +1,19 @@ +namespace AIStudio.Tools.PluginSystem; + +public class AssistantProviderSelection : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.PROVIDER_SELECTION; + public Dictionary Props { get; set; } = new(); + public List Children { get; set; } = new(); + + public string Name + { + get => this.Props.TryGetValue(nameof(this.Name), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Name)] = value; + } + public string Label + { + get => this.Props.TryGetValue(nameof(this.Label), out var v) ? v.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Label)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs new file mode 100644 index 000000000..943fd3ed1 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs @@ -0,0 +1,26 @@ +namespace AIStudio.Tools.PluginSystem; + +public class AssistantTextArea : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.TEXT_AREA; + public Dictionary Props { get; set; } = new(); + public List Children { get; set; } = new(); + + public string Name + { + get => this.Props.TryGetValue(nameof(this.Name), out var val) ? val.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Name)] = value; + } + + public string Label + { + get => this.Props.TryGetValue(nameof(this.Label), out var val) ? val.ToString() ?? string.Empty : string.Empty; + set => this.Props[nameof(this.Label)] = value; + } + + public bool ReadOnly + { + get => this.Props.TryGetValue(nameof(this.ReadOnly), out var val) && val is true; + set => this.Props[nameof(this.ReadOnly)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs new file mode 100644 index 000000000..6d4bc4fb9 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs @@ -0,0 +1,11 @@ +namespace AIStudio.Tools.PluginSystem; + +public enum AssistantUiCompontentType +{ + FORM, + TEXT_AREA, + BUTTON, + CHECKBOX, + DROPDOWN, + PROVIDER_SELECTION, +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs new file mode 100644 index 000000000..5c74a41f6 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.PluginSystem; + +public interface IAssistantComponent +{ + AssistantUiCompontentType Type { get; } + Dictionary Props { get; } + List Children { get; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs new file mode 100644 index 000000000..c34e27859 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -0,0 +1,19 @@ +using Lua; + +namespace AIStudio.Tools.PluginSystem; + +public sealed class PluginAssistants : PluginBase +{ + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginAssistants).Namespace, nameof(PluginAssistants)); + private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); + + public string AssistantTitle { get; set;} = string.Empty; + private string AssistantDescription {get; set;} = string.Empty; + + public PluginAssistants(bool isInternal, LuaState state, PluginType type) : base(isInternal, state, type) + { + + } + + +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs index 00afcd0ee..b3c39e0c3 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs @@ -30,4 +30,5 @@ public enum PluginCategory FICTION, WRITING, CONTENT_CREATION, + ASSISTANT, } \ No newline at end of file From 04c30602e42c9e505e76f9e9d1fd1ef40f06ed99 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 21 Jul 2025 15:10:40 +0200 Subject: [PATCH 02/20] WIP: changed to correct namespaces --- .../Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs | 2 +- .../PluginSystem/Assistants/DataModel/AssistantComponentBase.cs | 2 +- .../PluginSystem/Assistants/DataModel/AssistantDropdown.cs | 2 +- .../PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs | 2 +- .../Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs | 2 +- .../Assistants/DataModel/AssistantProviderSelection.cs | 2 +- .../PluginSystem/Assistants/DataModel/AssistantTextArea.cs | 2 +- .../Assistants/DataModel/AssistantUiCompontentType.cs | 2 +- .../PluginSystem/Assistants/DataModel/IAssistantComponent.cs | 2 +- .../Tools/PluginSystem/Assistants/PluginAssistants.cs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) 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 76cd22507..4d06e6f17 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public class AssistantButton : AssistantComponentBase { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs index 260b4ef38..8e41b4f5f 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentBase.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public abstract class AssistantComponentBase : IAssistantComponent { 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 0f2049792..59ea60f33 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public class AssistantDropdown : AssistantComponentBase { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs index d2968301a..0f07cc979 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public class AssistantDropdownItem { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs index 1bc490f31..a805fc8ae 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantForm.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public class AssistantForm : AssistantComponentBase { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs index af886db1f..e53095a9a 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public class AssistantProviderSelection : AssistantComponentBase { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs index 943fd3ed1..9fb7e31a6 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public class AssistantTextArea : AssistantComponentBase { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs index 6d4bc4fb9..37206a139 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public enum AssistantUiCompontentType { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs index 5c74a41f6..0c00b9667 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/IAssistantComponent.cs @@ -1,4 +1,4 @@ -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public interface IAssistantComponent { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index c34e27859..c85de500c 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -1,6 +1,6 @@ using Lua; -namespace AIStudio.Tools.PluginSystem; +namespace AIStudio.Tools.PluginSystem.Assistants; public sealed class PluginAssistants : PluginBase { From f5c475f354bd909cbd84f1cfcee47881b6f32d42 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 21 Jul 2025 16:14:05 +0200 Subject: [PATCH 03/20] Added a component factory and refactored Dropdowns; reformat getters --- .../Plugins/assistants/plugin.lua | 13 +++-- .../Assistants/AssistantComponentFactory.cs | 31 +++++++++++ .../Assistants/DataModel/AssistantButton.cs | 12 +++- .../Assistants/DataModel/AssistantDropdown.cs | 55 +++++++++++++++++-- .../DataModel/AssistantDropdownItem.cs | 2 + .../DataModel/AssistantProviderSelection.cs | 8 ++- .../Assistants/DataModel/AssistantTextArea.cs | 8 ++- .../DataModel/AssistantUiCompontentType.cs | 1 - 8 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index 58176671a..3146f75d3 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -54,15 +54,16 @@ ASSISTANT = { }, { Type = "Dropdown", + ValueType = "string", + Default = { Value = "", Display = "Sprache nicht angeben."} + Items = { + { Value = "de-DE", Display = "Deutsch" }, + { Value = "en-UK", Display = "Englisch (UK)" }, + { Value = "en-US", Display = "Englisch (US)" }, + }, Props = { Name = "language", Label = "Sprache", - Default = "Sprache nicht angeben", - Items = { - { Value = "de-DE", Display = "Deutsch" }, - { Value = "en-UK", Display = "Englisch (UK)" }, - { Value = "en-US", Display = "Englisch (US)" }, - } } }, { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs new file mode 100644 index 000000000..c558d6b9f --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs @@ -0,0 +1,31 @@ +using AIStudio.Tools.PluginSystem.Assistants.DataModel; + +namespace AIStudio.Tools.PluginSystem.Assistants; + +public class AssistantComponentFactory +{ + private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); + + public static IAssistantComponent CreateComponent( + AssistantUiCompontentType type, + Dictionary props, + List children) + { + switch (type) + { + case AssistantUiCompontentType.FORM: + return new AssistantForm { Props = props, Children = children }; + case AssistantUiCompontentType.TEXT_AREA: + return new AssistantTextArea { Props = props, Children = children }; + case AssistantUiCompontentType.BUTTON: + return new AssistantButton { Props = props, Children = children}; + case AssistantUiCompontentType.DROPDOWN: + return new AssistantDropdown { Props = props, Children = children }; + case AssistantUiCompontentType.PROVIDER_SELECTION: + return new AssistantProviderSelection { Props = props, Children = children }; + default: + LOGGER.LogError($"Unknown assistant component type!\n{type} is not a supported assistant component type"); + throw new Exception($"Unknown assistant component type: {type}"); + } + } +} \ No newline at end of file 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 4d06e6f17..b66fac794 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButton.cs @@ -8,17 +8,23 @@ public class AssistantButton : AssistantComponentBase public string Name { - get => this.Props.TryGetValue(nameof(this.Name), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Name), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Name)] = value; } public string Text { - get => this.Props.TryGetValue(nameof(this.Text), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Text), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Text)] = value; } public string Action { - get => this.Props.TryGetValue(nameof(this.Action), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Action), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Action)] = value; } } \ No newline at end of file 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 59ea60f33..fa28202c1 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs @@ -5,25 +5,68 @@ public class AssistantDropdown : AssistantComponentBase public override AssistantUiCompontentType Type => AssistantUiCompontentType.DROPDOWN; public Dictionary Props { get; set; } = new(); public List Children { get; set; } = new(); - + public string Name { - get => this.Props.TryGetValue(nameof(this.Name), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Name), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Name)] = value; } public string Label { - get => this.Props.TryGetValue(nameof(this.Label), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Label), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Label)] = value; } - public string Default + public AssistantDropdownItem Default { - get => this.Props.TryGetValue(nameof(this.Default), out var v) ? v.ToString() ?? string.Empty : string.Empty; + 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 Items { - get => this.Props.TryGetValue(nameof(this.Items), out var v) && v is List list ? list : []; + get => this.Props.TryGetValue(nameof(this.Items), out var v) && v is List 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 IEnumerable 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; + } + } + } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs index 0f07cc979..91e2831f9 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdownItem.cs @@ -4,4 +4,6 @@ public 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 }; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs index e53095a9a..9767cd1b0 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantProviderSelection.cs @@ -8,12 +8,16 @@ public class AssistantProviderSelection : AssistantComponentBase public string Name { - get => this.Props.TryGetValue(nameof(this.Name), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Name), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Name)] = value; } public string Label { - get => this.Props.TryGetValue(nameof(this.Label), out var v) ? v.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Label), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Label)] = value; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs index 9fb7e31a6..d51c131fc 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs @@ -8,13 +8,17 @@ public class AssistantTextArea : AssistantComponentBase public string Name { - get => this.Props.TryGetValue(nameof(this.Name), out var val) ? val.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Name), out var val) + ? val.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Name)] = value; } public string Label { - get => this.Props.TryGetValue(nameof(this.Label), out var val) ? val.ToString() ?? string.Empty : string.Empty; + get => this.Props.TryGetValue(nameof(this.Label), out var val) + ? val.ToString() ?? string.Empty + : string.Empty; set => this.Props[nameof(this.Label)] = value; } diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs index 37206a139..73973145a 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs @@ -5,7 +5,6 @@ public enum AssistantUiCompontentType FORM, TEXT_AREA, BUTTON, - CHECKBOX, DROPDOWN, PROVIDER_SELECTION, } \ No newline at end of file From cef643cd7f4ba98f5ec5da58a64726eb29b2b0c0 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 22 Jul 2025 20:15:36 +0200 Subject: [PATCH 04/20] WIP: Implementing a parser for the lua data structure --- .../Plugins/assistants/plugin.lua | 56 ++--- .../DataModel/ComponentPropSpecs.cs | 29 +++ .../Assistants/DataModel/PropSpec.cs | 7 + .../Assistants/PluginAssistants.cs | 234 +++++++++++++++++- 4 files changed, 293 insertions(+), 33 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/PropSpec.cs diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index 3146f75d3..c1af0050e 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -40,45 +40,45 @@ IS_MAINTAINED = true DEPRECATION_MESSAGE = "" ASSISTANT = { - Name = "Grammatik- und Rechtschreibprüfung", - Description = "Grammatik und Rechtschreibung eines Textes überprüfen.", - UI = { - Type = "Form", - Children = { + ["Title"] = "Grammatik- und Rechtschreibprüfung", + ["Description"] = "Grammatik und Rechtschreibung eines Textes überprüfen.", + ["UI"] = { + ["Type"] = "FORM", + ["Children"] = { { - Type = "TextArea", - Props = { - Name = "input", - Label = "Ihre Eingabe zur Überprüfung" + ["Type"] = "TEXT_AREA", + ["Props"] = { + ["Name"] = "input", + ["Label"] = "Ihre Eingabe zur Überprüfung" } }, { - Type = "Dropdown", - ValueType = "string", - Default = { Value = "", Display = "Sprache nicht angeben."} - Items = { - { Value = "de-DE", Display = "Deutsch" }, - { Value = "en-UK", Display = "Englisch (UK)" }, - { Value = "en-US", Display = "Englisch (US)" }, + ["Type"] = "DROPDOWN", + ["ValueType"] = "string", + ["Default"] = { ["Value"] = "", ["Display"] = "Sprache nicht angeben." }, + ["Items"] = { + { ["Value"] = "de-DE", ["Display"] = "Deutsch" }, + { ["Value"] = "en-UK", ["Display"] = "Englisch (UK)" }, + { ["Value"] = "en-US", ["Display"] = "Englisch (US)" }, }, - Props = { - Name = "language", - Label = "Sprache", + ["Props"] = { + ["Name"] = "language", + ["Label"] = "Sprache", } }, { - Type = "ProviderSelection", - Props = { - Name = "Anbieter", - Label = "LLM auswählen" + ["Type"] = "PROVIDER_SELECTION", + ["Props"] = { + ["Name"] = "Anbieter", + ["Label"] = "LLM auswählen" } }, { - Type = "Button", - Props = { - Name = "submit", - Text = "Korrekturlesen", - Action = "OnSubmit" + ["Type"] = "BUTTON", + ["Props"] = { + ["Name"] = "submit", + ["Text"] = "Korrekturlesen", + ["Action"] = "OnSubmit" } }, } diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs new file mode 100644 index 000000000..b9104c059 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -0,0 +1,29 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public static class ComponentPropSpecs +{ + public static readonly IReadOnlyDictionary SPECS = + new Dictionary + { + [AssistantUiCompontentType.FORM] = new( + required: ["Children"], + optional: [] + ), + [AssistantUiCompontentType.TEXT_AREA] = new( + required: ["Name", "Label"], + optional: [] + ), + [AssistantUiCompontentType.BUTTON] = new( + required: ["Name", "Text", "Action"], + optional: [] + ), + [AssistantUiCompontentType.DROPDOWN] = new( + required: ["Name", "Label", "Default", "Items"], + optional: [] + ), + [AssistantUiCompontentType.PROVIDER_SELECTION] = new( + required: ["Name", "Label"], + optional: [] + ), + }; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/PropSpec.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/PropSpec.cs new file mode 100644 index 000000000..7aa5e7b1c --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/PropSpec.cs @@ -0,0 +1,7 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public class PropSpec(IEnumerable required, IEnumerable optional) +{ + public IReadOnlyList Required { get; } = required.ToArray(); + public IReadOnlyList Optional { get; } = optional.ToArray(); +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index c85de500c..25a434910 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -1,19 +1,243 @@ -using Lua; +using System.Xml.XPath; +using AIStudio.Tools.PluginSystem.Assistants.DataModel; +using Lua; namespace AIStudio.Tools.PluginSystem.Assistants; public sealed class PluginAssistants : PluginBase { - private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginAssistants).Namespace, nameof(PluginAssistants)); + private static string TB(string fallbackEN) => + I18N.I.T(fallbackEN, typeof(PluginAssistants).Namespace, nameof(PluginAssistants)); + private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); - public string AssistantTitle { get; set;} = string.Empty; - private string AssistantDescription {get; set;} = string.Empty; + public AssistantForm RootComponent { get; set; } + public string AssistantTitle { get; set; } = string.Empty; + private string AssistantDescription { get; set; } = string.Empty; public PluginAssistants(bool isInternal, LuaState state, PluginType type) : base(isInternal, state, type) { + } + + /// + /// Tries to parse the assistant table into our internal assistant render tree data model. It follows this process: + /// + /// ASSISTANT → Title/Description → UI + /// UI: Root element → required Children → Components + /// Components: Type → Props → Children (recursively) + /// + /// + /// The error message, when parameters from the table could not be read. + /// True, when the assistant could be read successfully indicating the data model is populated. + private bool TryProcessAssistant(out string message) + { + message = string.Empty; + + // Ensure that the main ASSISTANT table exists and is a valid Lua table: + if (!this.state.Environment["ASSISTANT"].TryRead(out var assistantTable)) + { + message = TB("The ASSISTANT table does not exist or is not a valid table."); + return false; + } + if (!assistantTable.TryGetValue("Title", out var assistantTitleValue) || + !assistantTitleValue.TryRead(out var assistantTitle)) + { + message = TB("The ASSISTANT table does not contain a valid title."); + return false; + } + + if (!assistantTable.TryGetValue("Description", out var assistantDescriptionValue) || + !assistantDescriptionValue.TryRead(out var assistantDescription)) + { + message = TB("The ASSISTANT table does not contain a valid description."); + return false; + } + + this.AssistantTitle = assistantTitle; + this.AssistantDescription = assistantDescription; + + // Ensure that the UI table exists nested in the ASSISTANT table and is a valid Lua table: + if (!assistantTable.TryGetValue("UI", out var uiVal) || !uiVal.TryRead(out var uiTable)) + { + message = TB("The ASSISTANT table does not contain a valid UI section."); + return false; + } + + if (!this.TryReadRenderTree(uiTable, out var rootComponent)) + { + message = TB("Failed to parse the UI render tree."); + return false; + } + + this.RootComponent = (AssistantForm)rootComponent; + return true; + } + + /// + /// Parses the root FORM component and start to parse its required children (main ui components) + /// + /// The LuaTable containing all UI components + /// Outputs the root FORM component, if the parsing is successful. + /// True, when the UI table could be read successfully. + private bool TryReadRenderTree(LuaTable uiTable, out IAssistantComponent root) + { + root = null!; + + if (!uiTable.TryGetValue("Type", out var typeVal) + || !typeVal.TryRead(out var typeText) + || !Enum.TryParse(typeText, true, out var type) + || type != AssistantUiCompontentType.FORM) + { + LOGGER.LogWarning("UI table of the ASSISTANT table has no valid Form type."); + return false; + } + + if (!uiTable.TryGetValue("Children", out var childrenVal) || + !childrenVal.TryRead(out var childrenTable)) + { + LOGGER.LogWarning("Form has no valid Children table."); + return false; + } + + var children = new List(); + var count = childrenTable.ArrayLength; + for (var idx = 1; idx <= count; idx++) + { + var childVal = childrenTable[idx]; + if (!childVal.TryRead(out var childTable)) + { + LOGGER.LogWarning($"Child #{idx} is not a table."); + continue; + } + + if (!this.TryReadComponentTable(idx, childTable, out var comp)) + { + LOGGER.LogWarning($"Child #{idx} could not be parsed."); + continue; + } + + children.Add(comp); + } + + root = AssistantComponentFactory.CreateComponent( + AssistantUiCompontentType.FORM, + new Dictionary(), + children); + return true; + } + + /// + /// Parses the components' table containing all members and properties. + /// Recursively calls itself, if the component has a children table + /// + /// Current index inside the FORM children + /// The LuaTable containing all component properties + /// Outputs the component if the parsing is successful + /// True, when the component table could be read successfully. + private bool TryReadComponentTable(int idx, LuaTable componentTable, out IAssistantComponent component) + { + component = null!; + + if (!componentTable.TryGetValue("Type", out var typeVal) + || !typeVal.TryRead(out var typeText) + || !Enum.TryParse(typeText, true, out var type)) + { + LOGGER.LogWarning($"Component #{idx} missing valid Type."); + return false; + } + + Dictionary props = new(); + if (componentTable.TryGetValue("Props", out var propsVal) + && propsVal.TryRead(out var propsTable)) + { + if (!this.TryReadComponentProps(type, propsTable, out props)) + LOGGER.LogWarning($"Component #{idx} Props could not be fully read."); + } + + var children = new List(); + if (componentTable.TryGetValue("Children", out var childVal) + && childVal.TryRead(out var childTable)) + { + var cnt = childTable.ArrayLength; + for (var i = 1; i <= cnt; i++) + { + var cv = childTable[i]; + if (cv.TryRead(out var ct) + && this.TryReadComponentTable(i, ct, out var childComp)) + { + children.Add(childComp); + } + } + } + + component = AssistantComponentFactory.CreateComponent(type, props, children); + return true; + } + + private bool TryReadComponentProps( + AssistantUiCompontentType type, + LuaTable propsTable, + out Dictionary props) + { + props = new Dictionary(); + + if (!ComponentPropSpecs.SPECS.TryGetValue(type, out var spec)) + { + LOGGER.LogWarning($"No PropSpec defined for component type {type}"); + return false; + } + + foreach (var key in spec.Required) + { + if (!propsTable.TryGetValue(key, out var luaVal)) + { + LOGGER.LogWarning($"Component {type} missing required prop '{key}'."); + return false; + } + if (!this.TryConvertLuaValue(luaVal, out var dotNetVal)) + { + LOGGER.LogWarning($"Component {type}: prop '{key}' has wrong type."); + return false; + } + props[key] = dotNetVal; + } + + foreach (var key in spec.Optional) + { + if (!propsTable.TryGetValue(key, out var luaVal)) + continue; + + if (!this.TryConvertLuaValue(luaVal, out var dotNetVal)) + { + LOGGER.LogWarning($"Component {type}: optional prop '{key}' has wrong type, skipping."); + continue; + } + props[key] = dotNetVal; + } + + return true; } - + private bool TryConvertLuaValue(LuaValue val, out object result) + { + if (val.TryRead(out var s)) + { + result = s; + return true; + } + if (val.TryRead(out var b)) + { + result = b; + return true; + } + if (val.TryRead(out var d)) + { + result = d; + return true; + } + + result = null!; + return false; + } } \ No newline at end of file From 7b60c5f6e6301364e956befe7e03a772a73221ae Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 29 Sep 2025 20:58:01 +0200 Subject: [PATCH 05/20] changing the lua file to become a more generic usage example --- .../Plugins/assistants/icon.lua | 1 + .../Plugins/assistants/plugin.lua | 31 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 app/MindWork AI Studio/Plugins/assistants/icon.lua diff --git a/app/MindWork AI Studio/Plugins/assistants/icon.lua b/app/MindWork AI Studio/Plugins/assistants/icon.lua new file mode 100644 index 000000000..045bd983f --- /dev/null +++ b/app/MindWork AI Studio/Plugins/assistants/icon.lua @@ -0,0 +1 @@ +SVG = [[]] \ No newline at end of file diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index c1af0050e..bb0e48e99 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -1,34 +1,39 @@ require("icon") +-- ------ +-- This is an example of an assistant plugin that will build an assistant for you. +-- Please replace the placeholders and assign a valid ID. +-- ------ + -- The ID for this plugin: -ID = "43065dbc-78d0-45b7-92be-f14c2926e2dc" +ID = "00000000-0000-0000-0000-000000000000" -- The icon for the plugin: ICON_SVG = SVG -- The name of the plugin: -NAME = "MindWork AI Studio - German / Deutsch" +NAME = " - Configuration for " -- The description of the plugin: -DESCRIPTION = "Dieses Plugin bietet deutsche Sprachunterstützung für MindWork AI Studio." +DESCRIPTION = "This is a pre-defined configuration of " -- The version of the plugin: VERSION = "1.0.0" -- The type of the plugin: -TYPE = "LANGUAGE" +TYPE = "ASSISTANT" -- The authors of the plugin: -AUTHORS = {"MindWork AI Community"} +AUTHORS = {""} -- The support contact for the plugin: -SUPPORT_CONTACT = "MindWork AI Community" +SUPPORT_CONTACT = "" -- The source URL for the plugin: -SOURCE_URL = "https://github.com/MindWorkAI/AI-Studio" +SOURCE_URL = "" -- The categories for the plugin: -CATEGORIES = { "ASSISTANT" } +CATEGORIES = { "CORE" } -- The target groups for the plugin: TARGET_GROUPS = { "EVERYONE" } @@ -39,6 +44,16 @@ IS_MAINTAINED = true -- When the plugin is deprecated, this message will be shown to users: DEPRECATION_MESSAGE = "" +ASSISTANT = { + ["Title"] = "", + ["Description"] = "<Description presented to the users, explaining your assistant>", + ["UI"] = { + ["Type"] = "FORM", + ["Children"] = {} + }, +} + +-- An example of a assistant that resembles AI Studios translation assistant: ASSISTANT = { ["Title"] = "Grammatik- und Rechtschreibprüfung", ["Description"] = "Grammatik und Rechtschreibung eines Textes überprüfen.", From c12fa64d9b851d904e8337612b565991d04e89b6 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Mon, 29 Sep 2025 20:58:43 +0200 Subject: [PATCH 06/20] removing assistant from the PluginCategory enum --- app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs index b3c39e0c3..00afcd0ee 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginCategory.cs @@ -30,5 +30,4 @@ public enum PluginCategory FICTION, WRITING, CONTENT_CREATION, - ASSISTANT, } \ No newline at end of file From b42ee50b1d884513ca874cbf9da59fafdef1a585 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Mon, 29 Sep 2025 21:04:16 +0200 Subject: [PATCH 07/20] included the assistant plugin to load during plugin initialization --- .../Assistants/I18N/allTexts.lua | 15 +++++++++++++++ .../PluginSystem/Assistants/PluginAssistants.cs | 16 +++++++--------- .../Tools/PluginSystem/PluginFactory.Loading.cs | 8 +++++++- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index c1f40eb5f..c1bcded76 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -5353,6 +5353,21 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T567205144"] = "It seems that Pandoc i -- The latest Pandoc version was not found, installing version {0} instead. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T726914939"] = "The latest Pandoc version was not found, installing version {0} instead." +-- The ASSISTANT table does not contain a valid description. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2080819991"] = "The ASSISTANT table does not contain a valid description." + +-- Failed to parse the UI render tree. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2583341941"] = "Failed to parse the UI render tree." + +-- The ASSISTANT table does not contain a valid UI section. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3126717084"] = "The ASSISTANT table does not contain a valid UI section." + +-- The ASSISTANT table does not exist or is not a valid table. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T6004146"] = "The ASSISTANT table does not exist or is not a valid table." + +-- The ASSISTANT table does not contain a valid title. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T998753547"] = "The ASSISTANT table does not contain a valid title." + -- The table AUTHORS does not exist or is using an invalid syntax. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "The table AUTHORS does not exist or is using an invalid syntax." diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index 25a434910..67dd3b678 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -4,19 +4,20 @@ namespace AIStudio.Tools.PluginSystem.Assistants; -public sealed class PluginAssistants : PluginBase +public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType type) : PluginBase(isInternal, state, type) { - private static string TB(string fallbackEN) => - I18N.I.T(fallbackEN, typeof(PluginAssistants).Namespace, nameof(PluginAssistants)); + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginAssistants).Namespace, nameof(PluginAssistants)); private static readonly ILogger<PluginAssistants> LOGGER = Program.LOGGER_FACTORY.CreateLogger<PluginAssistants>(); - public AssistantForm RootComponent { get; set; } + public AssistantForm? RootComponent { get; set; } public string AssistantTitle { get; set; } = string.Empty; private string AssistantDescription { get; set; } = string.Empty; - public PluginAssistants(bool isInternal, LuaState state, PluginType type) : base(isInternal, state, type) + public void TryLoad() { + if(!this.TryProcessAssistant(out var issue)) + this.pluginIssues.Add(issue); } /// <summary> @@ -120,10 +121,7 @@ private bool TryReadRenderTree(LuaTable uiTable, out IAssistantComponent root) children.Add(comp); } - root = AssistantComponentFactory.CreateComponent( - AssistantUiCompontentType.FORM, - new Dictionary<string, object>(), - children); + root = AssistantComponentFactory.CreateComponent(AssistantUiCompontentType.FORM, new Dictionary<string, object>(), children); return true; } diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs index 92f77344b..d937df071 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs @@ -1,8 +1,9 @@ +using System.Runtime.CompilerServices; using System.Text; using AIStudio.Settings; using AIStudio.Settings.DataModel; - +using AIStudio.Tools.PluginSystem.Assistants; using Lua; using Lua.Standard; @@ -210,6 +211,11 @@ public static async Task<PluginBase> Load(string? pluginPath, string code, Cance await configPlug.InitializeAsync(true); return configPlug; + case PluginType.ASSISTANT: + var assistantPlugin = new PluginAssistants(isInternal, state, type); + assistantPlugin.TryLoad(); + return assistantPlugin; + default: return new NoPlugin("This plugin type is not supported yet. Please try again with a future version of AI Studio."); } From 5a6c91fae00533d09885003016d95b24d5df8e87 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 30 Sep 2025 14:45:35 +0200 Subject: [PATCH 08/20] included valid assistant plugins into the running plugins --- .../Tools/PluginSystem/PluginFactory.Starting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs index 5d734b06f..b2228bbf1 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Starting.cs @@ -64,7 +64,7 @@ private static async Task<List<PluginConfigurationObject>> RestartAllPlugins(Can try { - if (availablePlugin.IsInternal || SETTINGS_MANAGER.IsPluginEnabled(availablePlugin) || availablePlugin.Type == PluginType.CONFIGURATION) + if (availablePlugin.IsInternal || SETTINGS_MANAGER.IsPluginEnabled(availablePlugin) || availablePlugin.Type == PluginType.CONFIGURATION || availablePlugin.Type == PluginType.ASSISTANT) if(await Start(availablePlugin, cancellationToken) is { IsValid: true } plugin) { if (plugin is PluginConfiguration configPlugin) From 3d5831f52828b07ad40673ea7292d70fb2b49edd Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 30 Sep 2025 21:52:20 +0200 Subject: [PATCH 09/20] added a settings dialog for dynamic assistants --- .../Dialogs/Settings/SettingsDialogDynamic.razor | 5 +++++ .../Dialogs/Settings/SettingsDialogDynamic.razor.cs | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor create mode 100644 app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor.cs diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor new file mode 100644 index 000000000..3dcb1e784 --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor @@ -0,0 +1,5 @@ +@using AIStudio.Settings +@inherits SettingsDialogBase + +<MudDialog> +</MudDialog> \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor.cs new file mode 100644 index 000000000..5ee86a7f3 --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogDynamic.razor.cs @@ -0,0 +1,5 @@ +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Dialogs.Settings; + +public partial class SettingsDialogDynamic : SettingsDialogBase; \ No newline at end of file From aa31fa7182a519513bea210e3f8b78b396c92942 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 30 Sep 2025 21:53:23 +0200 Subject: [PATCH 10/20] included the installed plugin assistants to the assistants page --- app/MindWork AI Studio/Pages/Assistants.razor | 11 +++++++++++ app/MindWork AI Studio/Routes.razor.cs | 1 + 2 files changed, 12 insertions(+) diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor index d7fe0bed6..8ebce7f6d 100644 --- a/app/MindWork AI Studio/Pages/Assistants.razor +++ b/app/MindWork AI Studio/Pages/Assistants.razor @@ -1,5 +1,8 @@ @using AIStudio.Dialogs.Settings @using AIStudio.Settings.DataModel +@using AIStudio.Tools.PluginSystem +@using AIStudio.Tools.PluginSystem.Assistants +@using ReverseMarkdown.Converters @attribute [Route(Routes.ASSISTANTS)] @inherits MSGComponentBase @@ -31,6 +34,14 @@ <AssistantBlock TSettings="SettingsDialogJobPostings" Name="@T("Job Posting")" Description="@T("Generate a job posting for a given job description.")" Icon="@Icons.Material.Filled.Work" Link="@Routes.ASSISTANT_JOB_POSTING"/> <AssistantBlock TSettings="SettingsDialogLegalCheck" Name="@T("Legal Check")" Description="@T("Ask a question about a legal document.")" Icon="@Icons.Material.Filled.Gavel" Link="@Routes.ASSISTANT_LEGAL_CHECK"/> <AssistantBlock TSettings="SettingsDialogIconFinder" Name="@T("Icon Finder")" Description="@T("Use an LLM to find an icon for a given context.")" Icon="@Icons.Material.Filled.FindInPage" Link="@Routes.ASSISTANT_ICON_FINDER"/> + + @foreach (var assistant in PluginFactory.RunningPlugins.Where(e => e.Type == PluginType.ASSISTANT)) + { + if (assistant is PluginAssistants assistantPlugin) + { + <AssistantBlock TSettings="SettingsDialogTranslation" Name="@T(assistantPlugin.AssistantTitle)" Description="@T(assistantPlugin.Description)" Icon="@Icons.Material.Filled.FindInPage" Link="@Routes.ASSISTANT_DYNAMIC"/> + } + } </MudStack> <MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6"> diff --git a/app/MindWork AI Studio/Routes.razor.cs b/app/MindWork AI Studio/Routes.razor.cs index d59bffac9..e58a77194 100644 --- a/app/MindWork AI Studio/Routes.razor.cs +++ b/app/MindWork AI Studio/Routes.razor.cs @@ -27,5 +27,6 @@ public sealed partial class Routes public const string ASSISTANT_BIAS = "/assistant/bias-of-the-day"; public const string ASSISTANT_ERI = "/assistant/eri"; public const string ASSISTANT_AI_STUDIO_I18N = "/assistant/ai-studio/i18n"; + public const string ASSISTANT_DYNAMIC = "/assistant/dynamic"; // ReSharper restore InconsistentNaming } \ No newline at end of file From 09b187d3f7bd162488c37d62b925ef12216a35af Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 30 Sep 2025 21:53:56 +0200 Subject: [PATCH 11/20] started with the rendering of dynamic assistants --- .../Assistants/Dynamic/AssistantDynamic.razor | 27 ++++++++++ .../Dynamic/AssistantDynamic.razor.cs | 52 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor create mode 100644 app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor new file mode 100644 index 000000000..b1947cc34 --- /dev/null +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -0,0 +1,27 @@ +@attribute [Route(Routes.ASSISTANT_DYNAMIC)] +@using AIStudio.Tools.PluginSystem.Assistants.DataModel +@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogDynamic> + +@foreach (var component in this.RootComponent!.Children) +{ + @switch (component.Type) + { + case AssistantUiCompontentType.TEXT_AREA: + if (component is AssistantTextArea textArea) + { + <MudTextField T="string" @bind-Text="@this.inputText" Label="@textArea.Label" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3"/> + } + break; + + case AssistantUiCompontentType.PROVIDER_SELECTION: + if (component is AssistantProviderSelection providerSelection) + { + <ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/> + } + break; + + default: + break; + } +} + diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs new file mode 100644 index 000000000..418ede95e --- /dev/null +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs @@ -0,0 +1,52 @@ +using AIStudio.Dialogs.Settings; +using AIStudio.Tools.PluginSystem; +using AIStudio.Tools.PluginSystem.Assistants; +using AIStudio.Tools.PluginSystem.Assistants.DataModel; +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Assistants.Dynamic; + +public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic> +{ + [Parameter] + public AssistantForm? RootComponent { get; set; } = null!; + + private string? inputText; + private string title = string.Empty; + private string description = string.Empty; + private string systemPrompt = string.Empty; + private bool allowProfiles = true; + + protected override string Title => this.title; + protected override string Description => this.description; + protected override string SystemPrompt => this.systemPrompt; + protected override bool AllowProfiles => this.allowProfiles; + public override Tools.Components Component { get; } + protected override void OnInitialized() + { + var guid = Guid.Parse("958312de-a9e7-4666-901f-4d5b61647efb"); + var plugin = PluginFactory.RunningPlugins.FirstOrDefault(e => e.Id == guid); + if (plugin is PluginAssistants assistantPlugin) + { + this.RootComponent = assistantPlugin.RootComponent; + this.title = assistantPlugin.AssistantTitle; + this.description = assistantPlugin.AssistantDescription; + this.systemPrompt = assistantPlugin.SystemPrompt; + this.allowProfiles = assistantPlugin.AllowProfiles; + } + base.OnInitialized(); + } + + protected override void ResetForm() + { + throw new NotImplementedException(); + } + + protected override bool MightPreselectValues() + { + throw new NotImplementedException(); + } + + protected override string SubmitText { get; } + protected override Func<Task> SubmitAction { get; } +} \ No newline at end of file From 488747b7623b281c2ee7d980444fb4821717f83e Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 30 Sep 2025 21:54:24 +0200 Subject: [PATCH 12/20] included allowProfiles and system prompt properties to the lua parser --- .../Assistants/I18N/allTexts.lua | 6 ++++++ .../Assistants/PluginAssistants.cs | 20 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index c1bcded76..544e20cad 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -5356,12 +5356,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T726914939"] = "The latest Pandoc vers -- The ASSISTANT table does not contain a valid description. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2080819991"] = "The ASSISTANT table does not contain a valid description." +-- The ASSISTANT table does not contain a the boolean flag to control the allowance of profiles. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T221472268"] = "The ASSISTANT table does not contain a the boolean flag to control the allowance of profiles." + -- Failed to parse the UI render tree. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2583341941"] = "Failed to parse the UI render tree." -- The ASSISTANT table does not contain a valid UI section. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3126717084"] = "The ASSISTANT table does not contain a valid UI section." +-- The ASSISTANT table does not contain a valid system prompt. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3723171842"] = "The ASSISTANT table does not contain a valid system prompt." + -- The ASSISTANT table does not exist or is not a valid table. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T6004146"] = "The ASSISTANT table does not exist or is not a valid table." diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index 67dd3b678..cf92f55a7 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -12,7 +12,9 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType public AssistantForm? RootComponent { get; set; } public string AssistantTitle { get; set; } = string.Empty; - private string AssistantDescription { get; set; } = string.Empty; + public string AssistantDescription { get; set; } = string.Empty; + public string SystemPrompt { get; set; } = string.Empty; + public bool AllowProfiles { get; set; } = true; public void TryLoad() { @@ -54,9 +56,25 @@ private bool TryProcessAssistant(out string message) message = TB("The ASSISTANT table does not contain a valid description."); return false; } + + if (!assistantTable.TryGetValue("SystemPrompt", out var assistantSystemPromptValue) || + !assistantSystemPromptValue.TryRead<string>(out var assistantSystemPrompt)) + { + message = TB("The ASSISTANT table does not contain a valid system prompt."); + return false; + } + + if (!assistantTable.TryGetValue("AllowProfiles", out var assistantAllowProfilesValue) || + !assistantAllowProfilesValue.TryRead<bool>(out var assistantAllowProfiles)) + { + message = TB("The ASSISTANT table does not contain a the boolean flag to control the allowance of profiles."); + return false; + } this.AssistantTitle = assistantTitle; this.AssistantDescription = assistantDescription; + this.SystemPrompt = assistantSystemPrompt; + this.AllowProfiles = assistantAllowProfiles; // Ensure that the UI table exists nested in the ASSISTANT table and is a valid Lua table: if (!assistantTable.TryGetValue("UI", out var uiVal) || !uiVal.TryRead<LuaTable>(out var uiTable)) From d37c3f26fb54c546b206514ee0f9acfffacd7923 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 30 Sep 2025 23:12:16 +0200 Subject: [PATCH 13/20] fixed dropdown parsing --- .../Assistants/PluginAssistants.cs | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index cf92f55a7..d0993fe14 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -170,7 +170,7 @@ private bool TryReadComponentTable(int idx, LuaTable componentTable, out IAssist if (!this.TryReadComponentProps(type, propsTable, out props)) LOGGER.LogWarning($"Component #{idx} Props could not be fully read."); } - + var children = new List<IAssistantComponent>(); if (componentTable.TryGetValue("Children", out var childVal) && childVal.TryRead<LuaTable>(out var childTable)) @@ -253,7 +253,60 @@ private bool TryConvertLuaValue(LuaValue val, out object result) return true; } + // AssistantDropdownItem + if (val.TryRead<LuaTable>(out var table) && this.TryParseDropdownItem(table, out var item)) + { + result = item; + return true; + } + + // List<AssistantDropdownItem> + if (val.TryRead<LuaTable>(out var listTable) && this.TryParseDropdownItemList(listTable, out var itemList)) + { + result = itemList; + return true; + } + result = null!; return false; } + + private bool TryParseDropdownItem(LuaTable table, out AssistantDropdownItem item) + { + item = new AssistantDropdownItem(); + + if (!table.TryGetValue("Value", out var valueVal) || !valueVal.TryRead<string>(out var value)) + return false; + + if (!table.TryGetValue("Display", out var displayVal) || !displayVal.TryRead<string>(out var display)) + return false; + + item.Value = value; + item.Display = display; + return true; + } + + + private bool TryParseDropdownItemList(LuaTable table, out List<AssistantDropdownItem> items) + { + items = new List<AssistantDropdownItem>(); + + var length = table.ArrayLength; + for (var i = 1; i <= length; i++) + { + var value = table[i]; + + if (value.TryRead<LuaTable>(out var subTable) && this.TryParseDropdownItem(subTable, out var item)) + { + items.Add(item); + } + else + { + items = null!; + return false; + } + } + + return true; + } } \ No newline at end of file From 2afc8c63915a6fb4d7b2d08da8c124005c827817 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Mon, 10 Nov 2025 17:01:49 +0100 Subject: [PATCH 14/20] added DynamicAssistantDropdown to accomodate lua structure; included overrides from base Assistant for chat functionality --- .../Assistants/Dynamic/AssistantDynamic.razor | 21 ++++-- .../Dynamic/AssistantDynamic.razor.cs | 70 ++++++++++++++++--- .../Components/DynamicAssistantDropdown.razor | 17 +++++ .../DynamicAssistantDropdown.razor.cs | 33 +++++++++ .../Assistants/PluginAssistants.cs | 9 +++ 5 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor create mode 100644 app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index b1947cc34..1859192b3 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -9,19 +9,26 @@ case AssistantUiCompontentType.TEXT_AREA: if (component is AssistantTextArea textArea) { - <MudTextField T="string" @bind-Text="@this.inputText" Label="@textArea.Label" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3"/> + <MudTextField T="string" @bind-Text="@this.inputFields[textArea.Name]" Label="@textArea.Label" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3"/> } break; - + + case AssistantUiCompontentType.DROPDOWN: + if (component is AssistantDropdown assistantDropdown) + { + <DynamicAssistantDropdown Items="@assistantDropdown.Items" + @bind-Value="@this.selectedTargetLanguage" + Default="@assistantDropdown.Default" + Label="@assistantDropdown.Label" + Icon="@Icons.Material.Filled.Translate"/> + } + break; + case AssistantUiCompontentType.PROVIDER_SELECTION: if (component is AssistantProviderSelection providerSelection) { <ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/> } break; - - default: - break; } -} - +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs index 418ede95e..cb6926ce9 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs @@ -10,18 +10,26 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic> { [Parameter] public AssistantForm? RootComponent { get; set; } = null!; + + protected override string Title => this.title; + protected override string Description => this.description; + protected override string SystemPrompt => this.systemPrompt; + protected override bool AllowProfiles => this.allowProfiles; + protected override string SubmitText => this.submitText; + protected override Func<Task> SubmitAction => this.Submit; + public override Tools.Components Component { get; } private string? inputText; private string title = string.Empty; private string description = string.Empty; private string systemPrompt = string.Empty; private bool allowProfiles = true; - - protected override string Title => this.title; - protected override string Description => this.description; - protected override string SystemPrompt => this.systemPrompt; - protected override bool AllowProfiles => this.allowProfiles; - public override Tools.Components Component { get; } + private string submitText = string.Empty; + private string selectedTargetLanguage = string.Empty; + private string customTargetLanguage = string.Empty; + + private Dictionary<string, string> inputFields = new(); + protected override void OnInitialized() { var guid = Guid.Parse("958312de-a9e7-4666-901f-4d5b61647efb"); @@ -32,21 +40,63 @@ protected override void OnInitialized() this.title = assistantPlugin.AssistantTitle; this.description = assistantPlugin.AssistantDescription; this.systemPrompt = assistantPlugin.SystemPrompt; + this.submitText = assistantPlugin.SubmitText; this.allowProfiles = assistantPlugin.AllowProfiles; } + + foreach (var component in this.RootComponent!.Children) + { + switch (component.Type) + { + case AssistantUiCompontentType.TEXT_AREA: + if (component is AssistantTextArea textArea) + { + this.inputFields.Add(textArea.Name, string.Empty); + } + break; + } + } base.OnInitialized(); } protected override void ResetForm() { - throw new NotImplementedException(); + foreach (var entry in this.inputFields) + { + this.inputFields[entry.Key] = string.Empty; + } } protected override bool MightPreselectValues() { - throw new NotImplementedException(); + Console.WriteLine("throw new NotImplementedException();"); + return false; } - protected override string SubmitText { get; } - protected override Func<Task> SubmitAction { get; } + private string? ValidateCustomLanguage(string value) => string.Empty; + + private string CollectUserPrompt() + { + var prompt = string.Empty; + foreach (var entry in this.inputFields) + { + prompt += $"{entry.Value}{Environment.NewLine}"; + } + return prompt; + } + + private async Task Submit() + { + this.CreateChatThread(); + var time = this.AddUserRequest( + $""" + + The given text is: + + --- + {this.CollectUserPrompt()} + """); + + await this.AddAIResponseAsync(time); + } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor new file mode 100644 index 000000000..b826881ad --- /dev/null +++ b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor @@ -0,0 +1,17 @@ +<MudStack Row="true" Class="mb-3"> + <MudSelect + @bind-Value="@this.Value" + Label="@this.Label" + Placeholder="@this.Default.Value" + AdornmentIcon="@this.Icon" + Adornment="Adornment.Start" + Variant="Variant.Outlined" + Margin="Margin.Dense"> + @foreach (var item in Items) + { + <MudSelectItem Value="@item.Value"> + @item.Display + </MudSelectItem> + } + </MudSelect> +</MudStack> \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs new file mode 100644 index 000000000..7d4a72ce0 --- /dev/null +++ b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AIStudio.Tools.PluginSystem.Assistants.DataModel; +using Microsoft.AspNetCore.Components; +using MudBlazor; + +namespace AIStudio.Components +{ + public partial class DynamicAssistantDropdown : ComponentBase + { + [Parameter] + public List<AssistantDropdownItem> Items { get; set; } = new(); + + [Parameter] + public AssistantDropdownItem Default { get; set; } = new(); + + [Parameter] + public string Value { get; set; } = string.Empty; + + [Parameter] + public EventCallback<string> ValueChanged { get; set; } + + [Parameter] + public string Label { get; set; } = string.Empty; + + [Parameter] + public Func<string, string?> ValidateSelection { get; set; } = _ => null; + + [Parameter] + public string Icon { get; set; } = Icons.Material.Filled.ArrowDropDown; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index d0993fe14..3797001c9 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -14,6 +14,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType public string AssistantTitle { get; set; } = string.Empty; public string AssistantDescription { get; set; } = string.Empty; public string SystemPrompt { get; set; } = string.Empty; + public string SubmitText { get; set; } = string.Empty; public bool AllowProfiles { get; set; } = true; public void TryLoad() @@ -64,6 +65,13 @@ private bool TryProcessAssistant(out string message) return false; } + if (!assistantTable.TryGetValue("SubmitText", out var assistantSubmitTextValue) || + !assistantSubmitTextValue.TryRead<string>(out var assistantSubmitText)) + { + message = TB("The ASSISTANT table does not contain a valid system prompt."); + return false; + } + if (!assistantTable.TryGetValue("AllowProfiles", out var assistantAllowProfilesValue) || !assistantAllowProfilesValue.TryRead<bool>(out var assistantAllowProfiles)) { @@ -74,6 +82,7 @@ private bool TryProcessAssistant(out string message) this.AssistantTitle = assistantTitle; this.AssistantDescription = assistantDescription; this.SystemPrompt = assistantSystemPrompt; + this.SubmitText = assistantSubmitText; this.AllowProfiles = assistantAllowProfiles; // Ensure that the UI table exists nested in the ASSISTANT table and is a valid Lua table: From 5a3e49d839c817b799462295f425fcf2d386218d Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 11 Nov 2025 14:51:34 +0100 Subject: [PATCH 15/20] increased functionality for text area by adding optional properties --- .../Assistants/Dynamic/AssistantDynamic.razor | 3 ++- .../Dynamic/AssistantDynamic.razor.cs | 24 ++++++++++++++++--- .../Assistants/DataModel/AssistantTextArea.cs | 22 +++++++++++++++++ .../DataModel/ComponentPropSpecs.cs | 2 +- .../Assistants/PluginAssistants.cs | 2 -- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index 1859192b3..b5774e029 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -9,7 +9,8 @@ case AssistantUiCompontentType.TEXT_AREA: if (component is AssistantTextArea textArea) { - <MudTextField T="string" @bind-Text="@this.inputFields[textArea.Name]" Label="@textArea.Label" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3"/> + var lines = textArea.IsSingleLine ? 1 : 6; + <MudTextField T="string" @bind-Text="@this.inputFields[textArea.Name]" Label="@textArea.Label" ReadOnly="@textArea.ReadOnly" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Variant="Variant.Outlined" Lines="@lines" AutoGrow="@true" MaxLines="12" Class="mb-3"/> } break; diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs index cb6926ce9..375f235b9 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs @@ -51,7 +51,7 @@ protected override void OnInitialized() case AssistantUiCompontentType.TEXT_AREA: if (component is AssistantTextArea textArea) { - this.inputFields.Add(textArea.Name, string.Empty); + this.inputFields.Add(textArea.Name, textArea.PrefillText); } break; } @@ -78,10 +78,28 @@ protected override bool MightPreselectValues() private string CollectUserPrompt() { var prompt = string.Empty; - foreach (var entry in this.inputFields) + + foreach (var component in this.RootComponent!.Children) { - prompt += $"{entry.Value}{Environment.NewLine}"; + var userInput = string.Empty; + switch (component.Type) + { + case AssistantUiCompontentType.TEXT_AREA: + if (component is AssistantTextArea textArea) + { + prompt += $"context:{Environment.NewLine}{textArea.UserPrompt}{Environment.NewLine}{Environment.NewLine}---{Environment.NewLine}"; + if (this.inputFields.TryGetValue(textArea.Name, out userInput)) + { + prompt += $"user prompt:{Environment.NewLine}{userInput}"; + } + } + break; + default: + prompt += $"{userInput}{Environment.NewLine}"; + break; + } } + return prompt; } diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs index d51c131fc..94194301a 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs @@ -22,6 +22,28 @@ public string Label set => this.Props[nameof(this.Label)] = value; } + public string UserPrompt + { + get => this.Props.TryGetValue(nameof(this.UserPrompt), out var val) + ? val.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.UserPrompt)] = value; + } + + public string PrefillText + { + get => this.Props.TryGetValue(nameof(this.PrefillText), out var val) + ? val.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.PrefillText)] = value; + } + + public bool IsSingleLine + { + get => this.Props.TryGetValue(nameof(this.IsSingleLine), out var val) && val is true; + set => this.Props[nameof(this.IsSingleLine)] = value; + } + public bool ReadOnly { get => this.Props.TryGetValue(nameof(this.ReadOnly), out var val) && val is true; diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs index b9104c059..34b98341f 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -11,7 +11,7 @@ public static class ComponentPropSpecs ), [AssistantUiCompontentType.TEXT_AREA] = new( required: ["Name", "Label"], - optional: [] + optional: ["UserPrompt", "PrefillText", "ReadOnly", "IsSingleLine"] ), [AssistantUiCompontentType.BUTTON] = new( required: ["Name", "Text", "Action"], diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index 3797001c9..ac8167cde 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -262,14 +262,12 @@ private bool TryConvertLuaValue(LuaValue val, out object result) return true; } - // AssistantDropdownItem if (val.TryRead<LuaTable>(out var table) && this.TryParseDropdownItem(table, out var item)) { result = item; return true; } - // List<AssistantDropdownItem> if (val.TryRead<LuaTable>(out var listTable) && this.TryParseDropdownItemList(listTable, out var itemList)) { result = itemList; From 4fd21ad45bbccebf4a49289dd0557f6247808ca3 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 11 Nov 2025 15:57:15 +0100 Subject: [PATCH 16/20] fixed bug in dropdown that prevented values from changing; finished its functionality --- .../Assistants/Dynamic/AssistantDynamic.razor | 2 +- .../Dynamic/AssistantDynamic.razor.cs | 30 ++++++++++++------- .../Components/DynamicAssistantDropdown.razor | 20 ++++++++----- .../DynamicAssistantDropdown.razor.cs | 9 ++++++ .../Assistants/DataModel/AssistantDropdown.cs | 9 ++++++ .../DataModel/ComponentPropSpecs.cs | 2 +- 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index b5774e029..7dbef4bc6 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -18,7 +18,7 @@ if (component is AssistantDropdown assistantDropdown) { <DynamicAssistantDropdown Items="@assistantDropdown.Items" - @bind-Value="@this.selectedTargetLanguage" + @bind-Value="@this.dropdownFields[assistantDropdown.Name]" Default="@assistantDropdown.Default" Label="@assistantDropdown.Label" Icon="@Icons.Material.Filled.Translate"/> diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs index 375f235b9..f506cdfd0 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs @@ -29,6 +29,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic> private string customTargetLanguage = string.Empty; private Dictionary<string, string> inputFields = new(); + private Dictionary<string, string> dropdownFields = new(); protected override void OnInitialized() { @@ -54,6 +55,12 @@ protected override void OnInitialized() this.inputFields.Add(textArea.Name, textArea.PrefillText); } break; + case AssistantUiCompontentType.DROPDOWN: + if (component is AssistantDropdown dropdown) + { + this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value); + } + break; } } base.OnInitialized(); @@ -94,27 +101,30 @@ private string CollectUserPrompt() } } break; + case AssistantUiCompontentType.DROPDOWN: + if (component is AssistantDropdown dropdown) + { + prompt += $"{Environment.NewLine}context:{Environment.NewLine}{dropdown.UserPrompt}{Environment.NewLine}{Environment.NewLine}---{Environment.NewLine}"; + if (this.dropdownFields.TryGetValue(dropdown.Name, out userInput)) + { + prompt += $"user prompt:{Environment.NewLine}{userInput}"; + } + } + break; default: prompt += $"{userInput}{Environment.NewLine}"; break; } } - + + Console.WriteLine(prompt); return prompt; } private async Task Submit() { this.CreateChatThread(); - var time = this.AddUserRequest( - $""" - - The given text is: - - --- - {this.CollectUserPrompt()} - """); - + var time = this.AddUserRequest(this.CollectUserPrompt()); await this.AddAIResponseAsync(time); } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor index b826881ad..647bc2caf 100644 --- a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor +++ b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor @@ -1,17 +1,21 @@ <MudStack Row="true" Class="mb-3"> <MudSelect - @bind-Value="@this.Value" + T="string" + Value="@this.Value" + ValueChanged="@(val => this.OnValueChanged(val))" Label="@this.Label" Placeholder="@this.Default.Value" AdornmentIcon="@this.Icon" Adornment="Adornment.Start" Variant="Variant.Outlined" - Margin="Margin.Dense"> - @foreach (var item in Items) - { - <MudSelectItem Value="@item.Value"> - @item.Display - </MudSelectItem> - } + Margin="Margin.Dense" + MultiSelection="false" + > + @foreach (var item in Items) + { + <MudSelectItem Value="@item.Value"> + @item.Display + </MudSelectItem> + } </MudSelect> </MudStack> \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs index 7d4a72ce0..5ecc5a1ab 100644 --- a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs +++ b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs @@ -29,5 +29,14 @@ public partial class DynamicAssistantDropdown : ComponentBase [Parameter] public string Icon { get; set; } = Icons.Material.Filled.ArrowDropDown; + + private async Task OnValueChanged(string newValue) + { + if (this.Value != newValue) + { + this.Value = newValue; + await this.ValueChanged.InvokeAsync(newValue); + } + } } } \ No newline at end of file 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 fa28202c1..080be355e 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantDropdown.cs @@ -20,6 +20,15 @@ public string Label : string.Empty; set => this.Props[nameof(this.Label)] = value; } + + public string UserPrompt + { + get => this.Props.TryGetValue(nameof(this.UserPrompt), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.UserPrompt)] = value; + } + public AssistantDropdownItem Default { get diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs index 34b98341f..7f15f35dc 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -19,7 +19,7 @@ public static class ComponentPropSpecs ), [AssistantUiCompontentType.DROPDOWN] = new( required: ["Name", "Label", "Default", "Items"], - optional: [] + optional: ["UserPrompt"] ), [AssistantUiCompontentType.PROVIDER_SELECTION] = new( required: ["Name", "Label"], From 0775b03529a89f0664b4781f1a20b157e5e7b7e8 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 11 Nov 2025 19:06:44 +0100 Subject: [PATCH 17/20] added a switch component for boolean user inputs --- .../Assistants/Dynamic/AssistantDynamic.razor | 7 ++- .../Dynamic/AssistantDynamic.razor.cs | 23 ++++++-- .../Assistants/AssistantComponentFactory.cs | 2 + .../Assistants/DataModel/AssistantSwitch.cs | 54 +++++++++++++++++++ .../DataModel/AssistantUiCompontentType.cs | 1 + .../DataModel/ComponentPropSpecs.cs | 4 ++ 6 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantSwitch.cs diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index 7dbef4bc6..a5b2feacb 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -24,12 +24,17 @@ Icon="@Icons.Material.Filled.Translate"/> } break; - case AssistantUiCompontentType.PROVIDER_SELECTION: if (component is AssistantProviderSelection providerSelection) { <ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/> } break; + case AssistantUiCompontentType.SWITCH: + if (component is AssistantSwitch assistantSwitch) + { + <MudTextSwitch Label="@assistantSwitch.Label" @bind-Value="@this.switchFields[assistantSwitch.Name]" LabelOn="@assistantSwitch.LabelOn" LabelOff="@assistantSwitch.LabelOff" /> + } + break; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs index f506cdfd0..433afeb4a 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs @@ -30,6 +30,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic> private Dictionary<string, string> inputFields = new(); private Dictionary<string, string> dropdownFields = new(); + private Dictionary<string, bool> switchFields = new(); protected override void OnInitialized() { @@ -61,6 +62,12 @@ protected override void OnInitialized() this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value); } break; + case AssistantUiCompontentType.SWITCH: + if (component is AssistantSwitch switchComponent) + { + this.switchFields.Add(switchComponent.Name, switchComponent.Value); + } + break; } } base.OnInitialized(); @@ -89,12 +96,13 @@ private string CollectUserPrompt() foreach (var component in this.RootComponent!.Children) { var userInput = string.Empty; + var userDecision = false; switch (component.Type) { case AssistantUiCompontentType.TEXT_AREA: if (component is AssistantTextArea textArea) { - prompt += $"context:{Environment.NewLine}{textArea.UserPrompt}{Environment.NewLine}{Environment.NewLine}---{Environment.NewLine}"; + prompt += $"context:{Environment.NewLine}{textArea.UserPrompt}{Environment.NewLine}---{Environment.NewLine}"; if (this.inputFields.TryGetValue(textArea.Name, out userInput)) { prompt += $"user prompt:{Environment.NewLine}{userInput}"; @@ -104,20 +112,29 @@ private string CollectUserPrompt() case AssistantUiCompontentType.DROPDOWN: if (component is AssistantDropdown dropdown) { - prompt += $"{Environment.NewLine}context:{Environment.NewLine}{dropdown.UserPrompt}{Environment.NewLine}{Environment.NewLine}---{Environment.NewLine}"; + prompt += $"{Environment.NewLine}context:{Environment.NewLine}{dropdown.UserPrompt}{Environment.NewLine}---{Environment.NewLine}"; if (this.dropdownFields.TryGetValue(dropdown.Name, out userInput)) { prompt += $"user prompt:{Environment.NewLine}{userInput}"; } } break; + case AssistantUiCompontentType.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; default: prompt += $"{userInput}{Environment.NewLine}"; break; } } - Console.WriteLine(prompt); return prompt; } diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs index c558d6b9f..3efe0e48a 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs @@ -23,6 +23,8 @@ public static IAssistantComponent CreateComponent( return new AssistantDropdown { Props = props, Children = children }; case AssistantUiCompontentType.PROVIDER_SELECTION: return new AssistantProviderSelection { Props = props, Children = children }; + case AssistantUiCompontentType.SWITCH: + return new AssistantSwitch { Props = props, Children = children }; default: LOGGER.LogError($"Unknown assistant component type!\n{type} is not a supported assistant component type"); throw new Exception($"Unknown assistant component type: {type}"); diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantSwitch.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantSwitch.cs new file mode 100644 index 000000000..33082508e --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantSwitch.cs @@ -0,0 +1,54 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public class AssistantSwitch : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.SWITCH; + public Dictionary<string, object> Props { get; set; } = new(); + public List<IAssistantComponent> Children { get; set; } = new(); + + public string Name + { + get => this.Props.TryGetValue(nameof(this.Name), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.Name)] = value; + } + + public string Label + { + get => this.Props.TryGetValue(nameof(this.Label), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.Label)] = value; + } + + public bool Value + { + get => this.Props.TryGetValue(nameof(this.Value), out var val) && val is true; + set => this.Props[nameof(this.Value)] = value; + } + + public string UserPrompt + { + get => this.Props.TryGetValue(nameof(this.UserPrompt), out var val) + ? val.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.UserPrompt)] = value; + } + + public string LabelOn + { + get => this.Props.TryGetValue(nameof(this.LabelOn), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.LabelOn)] = value; + } + + public string LabelOff + { + get => this.Props.TryGetValue(nameof(this.LabelOff), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.LabelOff)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs index 73973145a..742542055 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs @@ -7,4 +7,5 @@ public enum AssistantUiCompontentType BUTTON, DROPDOWN, PROVIDER_SELECTION, + SWITCH, } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs index 7f15f35dc..fe5b03281 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -25,5 +25,9 @@ public static class ComponentPropSpecs required: ["Name", "Label"], optional: [] ), + [AssistantUiCompontentType.SWITCH] = new( + required: ["Name", "Label", "LabelOn", "LabelOff", "Value"], + optional: ["UserPrompt"] + ), }; } \ No newline at end of file From 9374b567895a1bb4456eee04065b17791c526f47 Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 11 Nov 2025 20:01:22 +0100 Subject: [PATCH 18/20] updated example plugin.lua for dynamic assistants --- .../Plugins/assistants/plugin.lua | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index bb0e48e99..5330dc2f6 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -53,47 +53,57 @@ ASSISTANT = { }, } --- An example of a assistant that resembles AI Studios translation assistant: +-- usage example with the full feature set: ASSISTANT = { - ["Title"] = "Grammatik- und Rechtschreibprüfung", - ["Description"] = "Grammatik und Rechtschreibung eines Textes überprüfen.", + ["Title"] = "<main title of assistant>", -- required + ["Description"] = "<assitant description>", -- required + ["SystemPrompt"] = "<prompt that fudamentally changes behaviour, personality and task focus of your assistant. Invisible to the user>", -- required + ["SubmitText"] = "<label for submit button>", -- required + ["AllowProfiles"] = true, -- if true, allows AiStudios profiles; required ["UI"] = { ["Type"] = "FORM", ["Children"] = { { - ["Type"] = "TEXT_AREA", + ["Type"] = "TEXT_AREA", -- required ["Props"] = { - ["Name"] = "input", - ["Label"] = "Ihre Eingabe zur Überprüfung" + ["Name"] = "<unique identifier of this component>", -- required + ["Label"] = "<heading of your component>", -- required + ["UserPrompt"] = "<direct input of instructions, questions, or tasks by a user>", + ["PrefillText"] = "<text to show in the field initially>", + ["IsSingleLine"] = false, -- if true, shows a text field instead of an area + ["ReadOnly"] = false -- if true, deactivates user input (make sure to provide a PrefillText) } }, { - ["Type"] = "DROPDOWN", - ["ValueType"] = "string", - ["Default"] = { ["Value"] = "", ["Display"] = "Sprache nicht angeben." }, - ["Items"] = { - { ["Value"] = "de-DE", ["Display"] = "Deutsch" }, - { ["Value"] = "en-UK", ["Display"] = "Englisch (UK)" }, - { ["Value"] = "en-US", ["Display"] = "Englisch (US)" }, - }, + ["Type"] = "DROPDOWN", -- required ["Props"] = { - ["Name"] = "language", - ["Label"] = "Sprache", + ["Name"] = "<unique identifier of this component>", -- required + ["Label"] = "<heading of your component>", -- required + ["UserPrompt"] = "<direct input of instructions, questions, or tasks by a user>", + ["ValueType"] = "<data type of item values>", -- required + ["Default"] = { ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" }, -- required + ["Items"] = { + { ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" }, + { ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" }, + } -- required } }, { - ["Type"] = "PROVIDER_SELECTION", + ["Type"] = "SWITCH", ["Props"] = { - ["Name"] = "Anbieter", - ["Label"] = "LLM auswählen" + ["Name"] = "<unique identifier of this component>", -- required + ["Label"] = "<heading of your component>", -- required + ["Value"] = true, -- intial switch state + ["UserPrompt"] = "<direct input of instructions, questions, or tasks by a user>", + ["LabelOn"] = "<text if state is true>", -- required + ["LabelOff"] = "<text if state is false>" -- required } }, { - ["Type"] = "BUTTON", + ["Type"] = "PROVIDER_SELECTION", -- required ["Props"] = { - ["Name"] = "submit", - ["Text"] = "Korrekturlesen", - ["Action"] = "OnSubmit" + ["Name"] = "Anbieter", + ["Label"] = "LLM auswählen" } }, } From bc50b3728cad64a14f2560f4d117e1ac2e4b704a Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 10 Feb 2026 16:12:59 +0100 Subject: [PATCH 19/20] Added new descriptive heading and text components --- .../Assistants/Dynamic/AssistantDynamic.razor | 28 +++++++++++++++++++ .../Plugins/assistants/plugin.lua | 13 +++++++++ .../Assistants/AssistantComponentFactory.cs | 4 +++ .../Assistants/DataModel/AssistantHeading.cs | 27 ++++++++++++++++++ .../Assistants/DataModel/AssistantText.cs | 18 ++++++++++++ .../DataModel/AssistantUiCompontentType.cs | 2 ++ .../DataModel/ComponentPropSpecs.cs | 8 ++++++ 7 files changed, 100 insertions(+) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantHeading.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantText.cs diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index a5b2feacb..a138dc5b4 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -36,5 +36,33 @@ <MudTextSwitch Label="@assistantSwitch.Label" @bind-Value="@this.switchFields[assistantSwitch.Name]" LabelOn="@assistantSwitch.LabelOn" LabelOff="@assistantSwitch.LabelOff" /> } break; + case AssistantUiCompontentType.HEADING: + if (component is AssistantHeading assistantHeading) + { + var heading = assistantHeading; + @switch (assistantHeading.Level) + { + case 1: + <MudText Typo="Typo.h4">@heading.Text</MudText> + break; + case 2: + <MudText Typo="Typo.h5">@heading.Text</MudText> + break; + case 3: + <MudText Typo="Typo.h6">@heading.Text</MudText> + break; + default: + <MudText Typo="Typo.h4">@heading.Text</MudText> + break; + } + } + break; + case AssistantUiCompontentType.TEXT: + if (component is AssistantText assistantText) + { + var text = assistantText; + <MudText Typo="Typo.body1" Class="mb-3">@text.Content</MudText> + } + break; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index 5330dc2f6..ca1b6d535 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -106,6 +106,19 @@ ASSISTANT = { ["Label"] = "LLM auswählen" } }, + { + ["Type"] = "HEADING", -- descriptive component for headings + ["Props"] = { + ["Text"] = "This is a Section Heading", -- The heading text + ["Level"] = 2 -- Heading level, 1 - 3 + } + }, + { + ["Type"] = "TEXT", -- descriptive component for normal text + ["Props"] = { + ["Content"] = "This is a paragraph of descriptive text that explains something about the assistant or provides additional information." + } + }, } }, } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs index 3efe0e48a..32f50c40e 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs @@ -25,6 +25,10 @@ public static IAssistantComponent CreateComponent( return new AssistantProviderSelection { Props = props, Children = children }; case AssistantUiCompontentType.SWITCH: return new AssistantSwitch { Props = props, Children = children }; + case AssistantUiCompontentType.HEADING: + return new AssistantHeading { Props = props, Children = children }; + case AssistantUiCompontentType.TEXT: + return new AssistantText { Props = props, Children = children }; default: LOGGER.LogError($"Unknown assistant component type!\n{type} is not a supported assistant component type"); throw new Exception($"Unknown assistant component type: {type}"); diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantHeading.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantHeading.cs new file mode 100644 index 000000000..68f6f4502 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantHeading.cs @@ -0,0 +1,27 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public class AssistantHeading : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.HEADING; + + public Dictionary<string, object> Props { get; set; } = new(); + + public List<IAssistantComponent> Children { get; set; } = new(); + + public string Text + { + get => this.Props.TryGetValue(nameof(this.Text), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.Text)] = value; + } + + public int Level + { + get => this.Props.TryGetValue(nameof(this.Level), out var v) + && int.TryParse(v.ToString(), out var i) + ? i + : 2; + set => this.Props[nameof(this.Level)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantText.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantText.cs new file mode 100644 index 000000000..01bec2683 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantText.cs @@ -0,0 +1,18 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public class AssistantText : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.TEXT; + + public Dictionary<string, object> Props { get; set; } = new(); + + public List<IAssistantComponent> Children { get; set; } = new(); + + public string Content + { + get => this.Props.TryGetValue(nameof(this.Content), out var v) + ? v.ToString() ?? string.Empty + : string.Empty; + set => this.Props[nameof(this.Content)] = value; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs index 742542055..e5ef6268d 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs @@ -8,4 +8,6 @@ public enum AssistantUiCompontentType DROPDOWN, PROVIDER_SELECTION, SWITCH, + HEADING, + TEXT, } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs index fe5b03281..0cbc4424f 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -29,5 +29,13 @@ public static class ComponentPropSpecs required: ["Name", "Label", "LabelOn", "LabelOff", "Value"], optional: ["UserPrompt"] ), + [AssistantUiCompontentType.HEADING] = new( + required: ["Text", "Level"], + optional: [] + ), + [AssistantUiCompontentType.TEXT] = new( + required: ["Content"], + optional: [] + ), }; } \ No newline at end of file From de1cf650f41f047f4ddb2dba414746aea9467f9f Mon Sep 17 00:00:00 2001 From: krut_ni <nils.kruthoff@dlr.de> Date: Tue, 10 Feb 2026 17:06:45 +0100 Subject: [PATCH 20/20] added a descriptive list component --- .../Assistants/Dynamic/AssistantDynamic.razor | 19 +++++++ .../Plugins/assistants/plugin.lua | 20 +++++++- .../Assistants/AssistantComponentFactory.cs | 2 + .../Assistants/DataModel/AssistantList.cs | 18 +++++++ .../Assistants/DataModel/AssistantListItem.cs | 8 +++ .../DataModel/AssistantUiCompontentType.cs | 1 + .../DataModel/ComponentPropSpecs.cs | 4 ++ .../Assistants/PluginAssistants.cs | 50 +++++++++++++++++++ 8 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantList.cs create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantListItem.cs diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index a138dc5b4..6f7e91b2f 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -64,5 +64,24 @@ <MudText Typo="Typo.body1" Class="mb-3">@text.Content</MudText> } break; + case AssistantUiCompontentType.LIST: + if (component is AssistantList assistantList) + { + var list = assistantList; + <MudList T="string" Class="mb-6"> + @foreach (var item in list.Items) + { + @if (item.Type == "LINK") + { + <MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="@item.Href">@item.Text</MudListItem> + } + else + { + <MudListItem T="string">@item.Text</MudListItem> + } + } + </MudList> + } + break; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index ca1b6d535..38c46b4aa 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -109,14 +109,30 @@ ASSISTANT = { { ["Type"] = "HEADING", -- descriptive component for headings ["Props"] = { - ["Text"] = "This is a Section Heading", -- The heading text + ["Text"] = "<heading content>", -- required ["Level"] = 2 -- Heading level, 1 - 3 } }, { ["Type"] = "TEXT", -- descriptive component for normal text ["Props"] = { - ["Content"] = "This is a paragraph of descriptive text that explains something about the assistant or provides additional information." + ["Content"] = "<text content>" + } + }, + { + ["Type"] = "LIST", -- descriptive list component + ["Props"] = { + ["Items"] = { + { + ["Type"] = "LINK", -- required + ["Text"] = "<user readable link text>", + ["Href"] = "<link>" -- required + }, + { + ["Type"] = "TEXT", -- required + ["Text"] = "<user readable text>" + } + } } }, } diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs index 32f50c40e..5dc67e7da 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs @@ -29,6 +29,8 @@ public static IAssistantComponent CreateComponent( return new AssistantHeading { Props = props, Children = children }; case AssistantUiCompontentType.TEXT: return new AssistantText { Props = props, Children = children }; + case AssistantUiCompontentType.LIST: + return new AssistantList { Props = props, Children = children }; default: LOGGER.LogError($"Unknown assistant component type!\n{type} is not a supported assistant component type"); throw new Exception($"Unknown assistant component type: {type}"); diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantList.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantList.cs new file mode 100644 index 000000000..f44cbf3dc --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantList.cs @@ -0,0 +1,18 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public class AssistantList : AssistantComponentBase +{ + public override AssistantUiCompontentType Type => AssistantUiCompontentType.LIST; + + public Dictionary<string, object> Props { get; set; } = new(); + + public 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; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantListItem.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantListItem.cs new file mode 100644 index 000000000..43bd60e13 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantListItem.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public class AssistantListItem +{ + public string Type { get; set; } = "TEXT"; + public string Text { get; set; } = string.Empty; + public string? Href { get; set; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs index e5ef6268d..9ec6e948e 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantUiCompontentType.cs @@ -10,4 +10,5 @@ public enum AssistantUiCompontentType SWITCH, HEADING, TEXT, + LIST, } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs index 0cbc4424f..5846dda2a 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -37,5 +37,9 @@ public static class ComponentPropSpecs required: ["Content"], optional: [] ), + [AssistantUiCompontentType.LIST] = new( + required: ["Items"], + optional: [] + ), }; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index ac8167cde..ba8bae8dd 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -273,6 +273,12 @@ private bool TryConvertLuaValue(LuaValue val, out object result) result = itemList; return true; } + + if (val.TryRead<LuaTable>(out var listItemListTable) && this.TryParseListItemList(listItemListTable, out var listItemList)) + { + result = listItemList; + return true; + } result = null!; return false; @@ -316,4 +322,48 @@ private bool TryParseDropdownItemList(LuaTable table, out List<AssistantDropdown return true; } + + private bool TryParseListItem(LuaTable table, out AssistantListItem item) + { + item = new AssistantListItem(); + + if (!table.TryGetValue("Text", out var textVal) || !textVal.TryRead<string>(out var text)) + return false; + + if (!table.TryGetValue("Type", out var typeVal) || !typeVal.TryRead<string>(out var type)) + return false; + + item.Text = text; + item.Type = type; + + if (table.TryGetValue("Href", out var hrefVal) && hrefVal.TryRead<string>(out var href)) + { + item.Href = href; + } + + return true; + } + + private bool TryParseListItemList(LuaTable table, out List<AssistantListItem> items) + { + items = new List<AssistantListItem>(); + + var length = table.ArrayLength; + for (var i = 1; i <= length; i++) + { + var value = table[i]; + + if (value.TryRead<LuaTable>(out var subTable) && this.TryParseListItem(subTable, out var item)) + { + items.Add(item); + } + else + { + items = null!; + return false; + } + } + + return true; + } } \ No newline at end of file