Skip to content

Allow chat templates in config plugins #537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
17 changes: 17 additions & 0 deletions .junie/guidelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Project Guidelines

## Repository Structure
- The repository and the app consist of a Rust project in the `runtime` folder and a .NET solution in the `app` folder.
- The .NET solution then contains 4 .NET projects:
- `Build Script` is not required for running the app; instead, it contains the build script for creating new releases, for example.
- `MindWork AI Studio` contains the actual app code.
- `SharedTools` contains types that are needed in the build script and in the app, for example.
- `SourceCodeRules` is a Roslyn analyzer project. It contains analyzers and code fixes that we use to enforce code style rules within the team.

## Changelogs
- There is a changelog in Markdown format for each version.
- All changelogs are located in the folder `app/MindWork AI Studio/wwwroot/changelog`.
- These changelogs are intended for end users, not for developers.
- Therefore, we don't mention all changes in the changelog: changes that end users wouldn't understand remain unmentioned. For complex refactorings, for example, we mention a generic point that the code quality has been improved to enhance future maintenance.
- The changelog is always written in US English.
- The changelog doesn't mention bug fixes if the bug was never shipped and users don't know about it.
3 changes: 3 additions & 0 deletions app/MindWork AI Studio/Assistants/I18N/allTexts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3415,6 +3415,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Close"

-- This template is managed by your organization.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "This template is managed by your organization."

-- Edit Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Edit Chat Template"

Expand Down
2 changes: 1 addition & 1 deletion app/MindWork AI Studio/Chat/ChatThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread ch
else
{
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
if(chatTemplate == default)
if(chatTemplate == null)
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
else
{
Expand Down
13 changes: 7 additions & 6 deletions app/MindWork AI Studio/Components/ChatComponent.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,9 @@ private async Task ProfileWasChanged(Profile profile)
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
{
this.currentChatTemplate = chatTemplate;
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
if(!string.IsNullOrWhiteSpace(this.currentChatTemplate.PredefinedUserPrompt))
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;

if(this.ChatThread is null)
return;

Expand Down Expand Up @@ -435,7 +437,7 @@ private async Task SendMessage(bool reuseLastUserPrompt = false)
DataSourceOptions = this.earlyDataSourceOptions,
Name = this.ExtractThreadName(this.userInput),
Seed = this.RNG.Next(),
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
};

await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
Expand Down Expand Up @@ -673,7 +675,7 @@ private async Task StartNewChat(bool useSameWorkspace = false, bool deletePrevio
ChatId = Guid.NewGuid(),
Name = string.Empty,
Seed = this.RNG.Next(),
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
};
}

Expand Down Expand Up @@ -813,9 +815,8 @@ private async Task SelectProviderWhenLoadingChat()
// Try to select the chat template:
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
{
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
if(this.currentChatTemplate == default)
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
var selectedTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
this.currentChatTemplate = selectedTemplate ?? ChatTemplate.NO_CHAT_TEMPLATE;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ private async Task AddLLMProvider()
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
{
if (provider.IsEnterpriseConfiguration)
return;

var dialogParameters = new DialogParameters<ProviderDialog>
{
{ x => x.DataNum, provider.Num },
Expand Down
3 changes: 3 additions & 0 deletions app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
PredefinedUserPrompt = this.PredefinedUserPrompt,
ExampleConversation = this.dataExampleConversation,
AllowProfileUsage = this.AllowProfileUsage,

EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
};

private void RemoveMessage(ContentBlock item)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,23 @@
<MudTd>@context.Num</MudTd>
<MudTd>@context.Name</MudTd>
<MudTd>
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditChatTemplate(context)"/>
</MudTooltip>
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteChatTemplate(context)"/>
</MudTooltip>
</MudStack>
@if (context.IsEnterpriseConfiguration)
{
<MudTooltip Text="@T("This template is managed by your organization.")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
</MudTooltip>
}
else
{
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
<MudTooltip Text="@T("Edit")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditChatTemplate(context)"/>
</MudTooltip>
<MudTooltip Text="@T("Delete")">
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteChatTemplate(context)"/>
</MudTooltip>
</MudStack>
}
</MudTd>
</RowTemplate>
</MudTable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ private async Task AddChatTemplate()

private async Task EditChatTemplate(ChatTemplate chatTemplate)
{
if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration)
return;

var dialogParameters = new DialogParameters<ChatTemplateDialog>
{
{ x => x.DataNum, chatTemplate.Num },
Expand Down
26 changes: 25 additions & 1 deletion app/MindWork AI Studio/Plugins/configuration/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,28 @@ CONFIG["SETTINGS"] = {}

-- Configure the user permission to add providers:
-- Allowed values are: true, false
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddProvider"] = false
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddProvider"] = false

-- Example chat templates for this configuration:
CONFIG["CHAT_TEMPLATES"] = {}

-- A simple example chat template:
CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
["Id"] = "00000000-0000-0000-0000-000000000000",
["Name"] = "<user-friendly name of the chat template>",
["SystemPrompt"] = "You are <Company Name>'s helpful AI assistant for <Department Name>. Your task is ...",
["PredefinedUserPrompt"] = "Please help me with ...",
["AllowProfileUsage"] = true,
["ExampleConversation"] = {
{
-- Allowed values are: USER, AI, SYSTEM
["Role"] = "USER",
["Content"] = "Hello! Can you help me with a quick task?"
},
{
-- Allowed values are: USER, AI, SYSTEM
["Role"] = "AI",
["Content"] = "Of course. What do you need?"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3417,6 +3417,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Schließen"

-- This template is managed by your organization.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "Diesee Vorlage wird von Ihrer Organisation verwaltet."

-- Edit Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Chat-Vorlage bearbeiten"

Expand Down Expand Up @@ -4584,7 +4587,7 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Letztes Änderungsproto
-- Choose the provider and model best suited for your current task.
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Wählen Sie den Anbieter und das Modell aus, die am besten zu ihrer aktuellen Aufgabe passen."

-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2900280782"] = "Sie sind an keinen einzelnen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu ihren Bedürfnissen passt. Derzeit unterstützen wir OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face und selbst gehostete Modelle mit vLLM, llama.cpp, ollama, LM Studio, Groq oder Fireworks. Für Wissenschaftler und Mitarbeiter von Forschungseinrichtungen unterstützen wir auch die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Anmeldungen wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen und viele internationale Universitäten verfügbar."

-- Quick Start Guide
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3417,6 +3417,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Close"

-- This template is managed by your organization.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "This template is managed by your organization."

-- Edit Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Edit Chat Template"

Expand Down Expand Up @@ -4584,7 +4587,7 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Last Changelog"
-- Choose the provider and model best suited for your current task.
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Choose the provider and model best suited for your current task."

-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2900280782"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."

-- Quick Start Guide
Expand Down
8 changes: 7 additions & 1 deletion app/MindWork AI Studio/Settings/ChatTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

namespace AIStudio.Settings;

public readonly record struct ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List<ContentBlock> ExampleConversation, bool AllowProfileUsage)
public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List<ContentBlock> ExampleConversation, bool AllowProfileUsage, bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default)
{
public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
{
}

private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate));

public static readonly ChatTemplate NO_CHAT_TEMPLATE = new()
Expand All @@ -16,6 +20,8 @@ public readonly record struct ChatTemplate(uint Num, string Id, string Name, str
Num = uint.MaxValue,
ExampleConversation = [],
AllowProfileUsage = true,
EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
};

#region Overrides of ValueType
Expand Down
4 changes: 2 additions & 2 deletions app/MindWork AI Studio/Settings/SettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,11 @@ public Profile GetPreselectedProfile(Tools.Components component)
public ChatTemplate GetPreselectedChatTemplate(Tools.Components component)
{
var preselection = component.PreselectedChatTemplate(this);
if (preselection != default)
if (preselection != ChatTemplate.NO_CHAT_TEMPLATE)
return preselection;

preselection = this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedChatTemplate);
return preselection != default ? preselection : ChatTemplate.NO_CHAT_TEMPLATE;
return preselection ?? ChatTemplate.NO_CHAT_TEMPLATE;
}

public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)
Expand Down
4 changes: 2 additions & 2 deletions app/MindWork AI Studio/Tools/ComponentsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ public static class ComponentsExtensions

public static ChatTemplate PreselectedChatTemplate(this Components component, SettingsManager settingsManager) => component switch
{
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) : default,
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) ?? ChatTemplate.NO_CHAT_TEMPLATE : ChatTemplate.NO_CHAT_TEMPLATE,

_ => default,
_ => ChatTemplate.NO_CHAT_TEMPLATE,
};
}
Loading