Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/MindWork AI Studio/Assistants/AssistantBase.razor
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,4 @@
</MudStack>
</FooterContent>
</InnerScrolling>
</PathDropZone>
</PathDropZone>
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
{
<MudTextField T="string" @bind-Text="@this.typicalInput" AdornmentIcon="@Icons.Material.Filled.Login" Adornment="Adornment.Start" IconSize="Size.Small" Label="@T("Typical input (Optional)")" Placeholder="@T("What users provide, e.g. text, notes, files, or a URL")" Variant="Variant.Outlined" Margin="Margin.Dense" Lines="3" AutoGrow="@true" MaxLines="8" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.expectedOutput" AdornmentIcon="@Icons.Material.Filled.Logout" Adornment="Adornment.Start" IconSize="Size.Small" Label="@T("Expected output (Optional)")" Placeholder="@T("What users should get, e.g. a summary or checklist")" Variant="Variant.Outlined" Margin="Margin.Dense" Lines="3" AutoGrow="@true" MaxLines="8" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.expectedDataSourceContent" AdornmentIcon="@AppIcons.DATABASE" Adornment="Adornment.Start" IconSize="Size.Small" Label="@T("Expected data from data sources (Optional)")" Placeholder="@T("If your assistant is dependant on a data source, describe what information a selected data source (e.g. ERI) will provide. This field does not select or configure a data source.")" Variant="Variant.Outlined" Margin="Margin.Dense" Lines="3" AutoGrow="@true" MaxLines="10" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudSelect T="AssistantComponentType" Label="@T("Input and UI components (Optional)")" MultiSelection="@true" @bind-SelectedValues="@this.selectedAssistantComponents" MultiSelectionTextFunc="@this.GetSelectedAssistantComponentText" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ViewDay" IconSize="Size.Small">
@foreach (var component in ASSISTANT_COMPONENT_OPTIONS)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Transform user-provided requirements into transparent assistant behavior.
private string assistantName = string.Empty;
private string typicalInput = string.Empty;
private string expectedOutput = string.Empty;
private string expectedDataSourceContent = string.Empty;
private bool createChatLauncher;
private string descriptionSuggestion = string.Empty;
private string launcherWorkspaceName = string.Empty;
Expand Down Expand Up @@ -134,6 +135,7 @@ Transform user-provided requirements into transparent assistant behavior.
private static readonly AssistantSessionStateKey<string> ASSISTANT_NAME_STATE_KEY = new(nameof(assistantName));
private static readonly AssistantSessionStateKey<string> TYPICAL_INPUT_STATE_KEY = new(nameof(typicalInput));
private static readonly AssistantSessionStateKey<string> EXPECTED_OUTPUT_STATE_KEY = new(nameof(expectedOutput));
private static readonly AssistantSessionStateKey<string> EXPECTED_DATA_SOURCE_CONTENT_STATE_KEY = new(nameof(expectedDataSourceContent));
private static readonly AssistantSessionStateKey<bool> CREATE_CHAT_LAUNCHER_STATE_KEY = new(nameof(createChatLauncher));
private static readonly AssistantSessionStateKey<string> DESCRIPTION_SUGGESTION_STATE_KEY = new(nameof(descriptionSuggestion));
private static readonly AssistantSessionStateKey<string> LAUNCHER_WORKSPACE_NAME_STATE_KEY = new(nameof(launcherWorkspaceName));
Expand Down Expand Up @@ -240,6 +242,7 @@ protected override void ResetForm()
this.assistantName = string.Empty;
this.typicalInput = string.Empty;
this.expectedOutput = string.Empty;
this.expectedDataSourceContent = string.Empty;
this.createChatLauncher = false;
this.descriptionSuggestion = string.Empty;
this.launcherWorkspaceName = string.Empty;
Expand Down Expand Up @@ -277,6 +280,7 @@ protected override void CaptureCustomAssistantSessionState(AssistantSessionState
state.Set(ASSISTANT_NAME_STATE_KEY, this.assistantName);
state.Set(TYPICAL_INPUT_STATE_KEY, this.typicalInput);
state.Set(EXPECTED_OUTPUT_STATE_KEY, this.expectedOutput);
state.Set(EXPECTED_DATA_SOURCE_CONTENT_STATE_KEY, this.expectedDataSourceContent);
state.Set(CREATE_CHAT_LAUNCHER_STATE_KEY, this.createChatLauncher);
state.Set(DESCRIPTION_SUGGESTION_STATE_KEY, this.descriptionSuggestion);
state.Set(LAUNCHER_WORKSPACE_NAME_STATE_KEY, this.launcherWorkspaceName);
Expand Down Expand Up @@ -319,6 +323,7 @@ protected override void RestoreCustomAssistantSessionState(AssistantSessionState
state.Restore(ASSISTANT_NAME_STATE_KEY, value => this.assistantName = value);
state.Restore(TYPICAL_INPUT_STATE_KEY, value => this.typicalInput = value);
state.Restore(EXPECTED_OUTPUT_STATE_KEY, value => this.expectedOutput = value);
state.Restore(EXPECTED_DATA_SOURCE_CONTENT_STATE_KEY, value => this.expectedDataSourceContent = value);
state.Restore(CREATE_CHAT_LAUNCHER_STATE_KEY, value => this.createChatLauncher = value);
state.Restore(DESCRIPTION_SUGGESTION_STATE_KEY, value => this.descriptionSuggestion = value);
state.Restore(LAUNCHER_WORKSPACE_NAME_STATE_KEY, value => this.launcherWorkspaceName = value);
Expand Down Expand Up @@ -391,6 +396,7 @@ private async Task GenerateAssistantSpec()
this.assistantName,
this.createChatLauncher ? string.Empty : this.typicalInput,
this.createChatLauncher ? string.Empty : this.expectedOutput,
this.createChatLauncher ? string.Empty : this.expectedDataSourceContent,
this.createChatLauncher ? string.Empty : this.GetSelectedAssistantComponentTypes(),
this.createChatLauncher ? string.Empty : this.GetSelectedOutputLanguageName(),
!this.createChatLauncher && this.allowGeneratedAssistantProfiles,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@attribute [Route(Routes.ASSISTANT_DYNAMIC)]
@using AIStudio.Agents.AssistantAudit
@using AIStudio.Settings.DataModel
@using AIStudio.Tools.PluginSystem.Assistants.DataModel
@using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.NoSettingsPanel>
Expand Down Expand Up @@ -55,10 +56,26 @@ else
}

@code {
private protected override RenderFragment? HeaderActions => this.CanReviseCurrentAssistant
? @<MudTooltip Text="@T("Revise assistant")">
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.AutoMode" OnClick="@(async () => await this.OpenRevisionDialogAsync())"/>
</MudTooltip>
private bool ShowDataSourceSelection => PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager);

private protected override RenderFragment? HeaderActions => this.CanReviseCurrentAssistant || this.ShowDataSourceSelection
? @<text>
@if (this.CanReviseCurrentAssistant)
{
<MudTooltip Text="@T("Revise assistant")">
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.AutoMode" OnClick="@(async () => await this.OpenRevisionDialogAsync())"/>
</MudTooltip>
}

@if (this.ShowDataSourceSelection)
{
<DataSourceSelection LLMProvider="@this.ProviderSettings"
DataSourceOptions="@this.dataSourceOptions"
DataSourceOptionsChanged="@this.DataSourceOptionsChanged"
DataSourcesAISelected="@(this.ChatThread?.AISelectedDataSources ?? [])"
PopoverTriggerMode="PopoverTriggerMode.ICON" />
}
</text>
: null;

private RenderFragment RenderSwitch(AssistantSwitch assistantSwitch) => @<MudSwitch T="bool"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using AIStudio.Dialogs;
using AIStudio.Dialogs.Settings;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.AssistantSessions;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.PluginSystem.Assistants;
Expand Down Expand Up @@ -69,6 +70,7 @@ public partial class AssistantDynamic : AssistantBaseCore<NoSettingsPanel>
private PluginAssistantAudit? audit;
private string securityMessage = string.Empty;
private bool isSecurityBlocked;
private DataSourceOptions dataSourceOptions = new();
private PluginAssistants? pendingChatLauncher;
private const string ASSISTANT_QUERY_KEY = "assistantId";
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
Expand All @@ -88,6 +90,7 @@ public partial class AssistantDynamic : AssistantBaseCore<NoSettingsPanel>
private static readonly AssistantSessionStateKey<PluginAssistantAudit?> AUDIT_STATE_KEY = new(nameof(audit));
private static readonly AssistantSessionStateKey<string> SECURITY_MESSAGE_STATE_KEY = new(nameof(securityMessage));
private static readonly AssistantSessionStateKey<bool> IS_SECURITY_BLOCKED_STATE_KEY = new(nameof(isSecurityBlocked));
private static readonly AssistantSessionStateKey<DataSourceOptions> DATA_SOURCE_OPTIONS_STATE_KEY = new(nameof(dataSourceOptions));

private bool CanReviseCurrentAssistant => this.assistantPlugin is { IsInternal: false, IsManagedByConfigServer: false } && !string.IsNullOrWhiteSpace(this.assistantPlugin.PluginPath);

Expand All @@ -110,6 +113,7 @@ protected override void CaptureCustomAssistantSessionState(AssistantSessionState
state.Set(AUDIT_STATE_KEY, this.audit);
state.Set(SECURITY_MESSAGE_STATE_KEY, this.securityMessage);
state.Set(IS_SECURITY_BLOCKED_STATE_KEY, this.isSecurityBlocked);
state.Set(DATA_SOURCE_OPTIONS_STATE_KEY, this.dataSourceOptions.CreateCopy());
}

/// <inheritdoc />
Expand All @@ -131,6 +135,7 @@ protected override void RestoreCustomAssistantSessionState(AssistantSessionState
state.Restore(AUDIT_STATE_KEY, value => this.audit = value);
state.Restore(SECURITY_MESSAGE_STATE_KEY, value => this.securityMessage = value);
state.Restore(IS_SECURITY_BLOCKED_STATE_KEY, value => this.isSecurityBlocked = value);
state.Restore(DATA_SOURCE_OPTIONS_STATE_KEY, value => this.dataSourceOptions = value.CreateCopy());
}

#region Implementation of AssistantBase
Expand Down Expand Up @@ -207,6 +212,12 @@ protected override async Task OnInitializedAsync()
await this.OpenChatLauncherAsync(launcherPlugin);
}

protected override Task OnDefaultsAppliedAsync()
{
this.dataSourceOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy();
return Task.CompletedTask;
}

protected override void ResetForm()
{
this.assistantState.Clear();
Expand Down Expand Up @@ -693,10 +704,17 @@ private async Task Submit()
}

this.CreateChatThread();
this.ChatThread!.DataSourceOptions = this.dataSourceOptions.CreateCopy();
var time = this.AddUserRequest(await this.CollectUserPromptAsync(), false, this.CollectFileAttachments());
await this.AddAIResponseAsync(time);
}

private async Task DataSourceOptionsChanged(DataSourceOptions options)
{
this.dataSourceOptions = options.CreateCopy();
await this.CheckpointAssistantSession();
}

private string CollectUserPromptFallback(IEnumerable<IAssistantComponent> components)
{
var prompt = new StringBuilder();
Expand Down
6 changes: 6 additions & 0 deletions app/MindWork AI Studio/Assistants/I18N/allTexts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T254606977"] =
-- Load description from file
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T2686336585"] = "Load description from file"

-- Expected data from data sources (Optional)
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T2689396712"] = "Expected data from data sources (Optional)"

-- I need an assistant that turns meeting notes into clear tasks with owners and deadlines.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T2703350865"] = "I need an assistant that turns meeting notes into clear tasks with owners and deadlines."

Expand Down Expand Up @@ -877,6 +880,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3163704605"]
-- Regenerate Assistant
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3171038735"] = "Regenerate Assistant"

-- If your assistant is dependant on a data source, describe what information a selected data source (e.g. ERI) will provide. This field does not select or configure a data source.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3201382042"] = "If your assistant is dependant on a data source, describe what information a selected data source (e.g. ERI) will provide. This field does not select or configure a data source."

-- What kind of assistant should this be?
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3238517263"] = "What kind of assistant should this be?"

Expand Down
8 changes: 8 additions & 0 deletions app/MindWork AI Studio/Plugins/assistants/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This folder keeps the Lua manifest (`plugin.lua`) that defines a custom assistan
- [Directory Structure](#directory-structure)
- [Structure](#structure)
- [Minimal Requirements Assistant Table](#example-minimal-requirements-assistant-table)
- [Data Sources in Form Assistants](#data-sources-in-form-assistants)
- [Supported types (matching the Blazor UI components):](#supported-types-matching-the-blazor-ui-components)
- [Component References](#component-references)
- [`TEXT_AREA` reference](#text_area-reference)
Expand Down Expand Up @@ -108,6 +109,13 @@ ASSISTANT = {
}
```

## Data Sources in Form Assistants
Form assistants start with the data-source defaults configured for new chats. This includes disabled data sources, manually preselected sources, automatic source selection, and automatic context validation. AI Studio automatically displays its standard data-source icon in the assistant header. Users can override the selection there for the current open assistant without changing the chat defaults or any other assistant. Resetting the form reloads the current chat defaults.

The Assistant Builder's optional expected-data field describes what kind of retrieval content the generated assistant may receive. The Builder uses it to add interpretation guidance to the `SystemPrompt`; it does not configure or select a data source. Generated assistants must treat that content as optional and remain usable when no matching retrieval content is available.

Do not add a custom data-source form component: AI Studio provides the selector automatically. Form assistants cannot define individual data sources or ERI servers in `plugin.lua`. `DataSourceIds` belongs exclusively to direct chat launchers. Do not implement a separate ERI connection, network access, or retrieval pipeline in an assistant plugin. AI Studio owns authentication, provider and permission checks, retrieval, citations, warnings, and fallback behavior. A form assistant's `SystemPrompt` may use retrieval content supplied by AI Studio, but it must not promise that such content will be available.

## Direct Launch into a Chat
Assistant plugins can optionally skip the normal assistant page and open a chat directly from the tile. The chat either lives in a workspace or in none at all; everything else about the two behaviors is the same.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T254606977"] =
-- Load description from file
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T2686336585"] = "Beschreibung aus Datei laden"

-- Erwartete Daten aus Datenquellen (optional)
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T2689396712"] = "Erwartete Daten aus Datenquellen (optional)"

-- I need an assistant that turns meeting notes into clear tasks with owners and deadlines.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T2703350865"] = "Ich brauche einen Assistenten, der Besprechungsnotizen in klare Aufgaben mit Verantwortlichen und Fristen umwandelt."

Expand Down Expand Up @@ -879,6 +882,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3163704605"]
-- Regenerate Assistant
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3171038735"] = "Assistent neu erstellen"

-- Wenn Ihr Assistent von einer Datenquelle abhängig ist, beschreiben Sie, welche Informationen die ausgewählte Datenquelle (z. B. ERI) bereitstellt. In diesem Feld wird keine Datenquelle ausgewählt oder konfiguriert.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3201382042"] = "Wenn Ihr Assistent von einer Datenquelle abhängig ist, beschreiben Sie, welche Informationen die ausgewählte Datenquelle (z. B. ERI) bereitstellt. In diesem Feld wird keine Datenquelle ausgewählt oder konfiguriert."

-- What kind of assistant should this be?
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BUILDER::ASSISTANTBUILDER::T3238517263"] = "Was für eine Art von Assistent soll dies sein?"

Expand Down Expand Up @@ -11670,6 +11676,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINTYPEEXTENSIONS::T335338363
-- Standard augmentation process
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::AUGMENTATIONPROCESSES::AUGMENTATIONONE::T1072508429"] = "Standardmäßiger Erweiterungsprozess"

-- Keinem Anbieter wird ausreichend vertraut, um zu prüfen, welche Passagen zu Ihrer Frage passen. Diese Antwort verwendet alle gefundenen Passagen.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::AUGMENTATIONPROCESSES::AUGMENTATIONONE::T2710880477"] = "Keinem Anbieter wird ausreichend vertraut, um zu prüfen, welche Passagen zu Ihrer Frage passen. Diese Antwort verwendet alle gefundenen Passagen."

-- This is the standard augmentation process, which uses all retrieval contexts to augment the chat thread.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::AUGMENTATIONPROCESSES::AUGMENTATIONONE::T3240406069"] = "Dies ist der Standard-Erweiterungsprozess, bei dem alle abgerufenen Kontexte verwendet werden, um den Chatverlauf zu ergänzen."

Expand All @@ -11682,6 +11691,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::DATASOURCESELECTIONPROCESSES::AGENTICSRCS
-- Automatically selects the appropriate data sources based on the last prompt. Applies a heuristic reduction at the end to reduce the number of data sources.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::DATASOURCESELECTIONPROCESSES::AGENTICSRCSELWITHDYNHEUR::T648937779"] = "Wählt automatisch die passenden Datenquellen basierend auf der letzten Eingabe aus. Wendet am Ende eine heuristische Reduzierung an, um die Anzahl der Datenquellen zu verringern."

-- Keine der ausgewählten Datenquellen ist für den gewählten Anbieter verfügbar. Diese Antwort wurde daher ohne sie erstellt.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T1696726639"] = "Keine der ausgewählten Datenquellen ist für den gewählten Anbieter verfügbar. Diese Antwort wurde daher ohne sie erstellt."

-- This RAG process filters data sources, automatically selects appropriate sources, optionally allows manual source selection, retrieves data, and automatically validates the retrieval context.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T3047786484"] = "Dieser RAG-Prozess filtert Datenquellen, wählt automatisch passende Quellen aus, ermöglicht optional die manuelle Auswahl von Quellen, ruft Daten ab und überprüft den Abrufkontext automatisch."

Expand Down
Loading
Loading