Added the option to create an assistants plugin that just opens a chat in a specific workspace

This commit is contained in:
Peer Schütt 2026-06-22 14:39:42 +02:00
parent 2acb6f2a57
commit c41a0cf74c
13 changed files with 242 additions and 6 deletions

View File

@ -7516,6 +7516,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANT
-- Button
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPEEXTENSIONS::T864557713"] = "Button"
-- The ASSISTANT table contains an invalid LaunchBehavior value.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T109828905"] = "The ASSISTANT table contains an invalid LaunchBehavior value."
-- The ASSISTANT table contains an unsupported LaunchBehavior value.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1194373781"] = "The ASSISTANT table contains an unsupported LaunchBehavior value."
-- Failed to parse the UI render tree from the ASSISTANT lua table.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Failed to parse the UI render tree from the ASSISTANT lua table."
@ -7531,12 +7537,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2
-- The ASSISTANT lua table does not exist or is not a valid table.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3017816936"] = "The ASSISTANT lua table does not exist or is not a valid table."
-- The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3233001282"] = "The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'."
-- The provided ASSISTANT lua table does not contain a valid system prompt.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3402798667"] = "The provided ASSISTANT lua table does not contain a valid system prompt."
-- 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 contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T4215554842"] = "The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName."
-- ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T683382975"] = "ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax."

View File

@ -24,9 +24,18 @@
<MudCardActions>
<MudStack Row="@true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Style="width: 100%;">
<MudButtonGroup Variant="Variant.Outlined">
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link" Disabled="@this.Disabled">
@this.ButtonText
</MudButton>
@if (this.HasStartAction)
{
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" OnClick="@this.OnClick" Disabled="@this.Disabled">
@this.ButtonText
</MudButton>
}
else
{
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link" Disabled="@this.Disabled">
@this.ButtonText
</MudButton>
}
@if (this.HasSettingsPanel)
{
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" Color="Color.Default" OnClick="@this.OpenSettingsDialog"/>

View File

@ -22,6 +22,9 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
[Parameter]
public string Link { get; set; } = string.Empty;
[Parameter]
public EventCallback OnClick { get; set; }
[Parameter]
public bool Disabled { get; set; }
@ -61,4 +64,6 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
private bool IsVisible => this.SettingsManager.IsAssistantVisible(this.Component, assistantName: this.Name, requiredPreviewFeature: this.RequiredPreviewFeature);
private bool HasSettingsPanel => typeof(TSettings) != typeof(NoSettingsPanel);
private bool HasStartAction => this.OnClick.HasDelegate;
}

View File

@ -42,12 +42,14 @@
@foreach (var assistantPlugin in this.AssistantPlugins)
{
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
var launchLink = assistantPlugin.StartsChatDirectly ? string.Empty : $"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}";
<AssistantBlock TSettings="NoSettingsPanel"
Name="@T(assistantPlugin.AssistantTitle)"
Description="@T(assistantPlugin.Description)"
Icon="@Icons.Material.Filled.FindInPage"
Disabled="@(!securityState.CanStartAssistant)"
Link="@($"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}")">
Link="@launchLink"
OnClick="@(() => this.StartAssistantPluginAsync(assistantPlugin))">
<SecurityBadge>
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true" />
</SecurityBadge>

View File

@ -1,3 +1,4 @@
using AIStudio.Chat;
using AIStudio.Components;
using AIStudio.Agents.AssistantAudit;
using AIStudio.Tools.PluginSystem;
@ -12,6 +13,12 @@ public partial class Assistants : MSGComponentBase
[Inject]
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
[Inject]
private NavigationManager NavigationManager { get; init; } = null!;
[Inject]
private ILogger<Assistants> Logger { get; init; } = null!;
protected override async Task OnInitializedAsync()
{
@ -81,6 +88,50 @@ public partial class Assistants : MSGComponentBase
audits.Add(audit);
}
private async Task StartAssistantPluginAsync(PluginAssistants assistantPlugin)
{
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
if (!securityState.CanStartAssistant)
return;
if (!assistantPlugin.StartsChatDirectly)
{
this.NavigationManager.NavigateTo($"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}");
return;
}
var chatThread = await this.TryCreateDirectChatThreadAsync(assistantPlugin);
if (chatThread is null)
return;
MessageBus.INSTANCE.DeferMessage(this, Event.SEND_TO_CHAT, chatThread);
this.NavigationManager.NavigateTo(Routes.CHAT);
}
private async Task<ChatThread?> TryCreateDirectChatThreadAsync(PluginAssistants assistantPlugin)
{
var workspaceId = await WorkspaceBehaviour.ResolveOrCreateWorkspaceIdByNameAsync(assistantPlugin.LaunchWorkspaceName);
if (workspaceId == Guid.Empty)
{
this.Logger.LogWarning("Assistant plugin '{PluginName}' could not resolve or create workspace '{WorkspaceName}'.", assistantPlugin.Name, assistantPlugin.LaunchWorkspaceName);
return null;
}
return new ChatThread
{
IncludeDateTime = true,
SelectedProvider = string.Empty,
SelectedProfile = string.Empty,
SelectedChatTemplate = string.Empty,
SystemPrompt = SystemPrompts.DEFAULT,
WorkspaceId = workspaceId,
ChatId = Guid.NewGuid(),
Name = assistantPlugin.AssistantTitle,
DataSourceOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy(),
Blocks = [],
};
}
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
if (triggeredEvent is Event.PLUGINS_RELOADED)

View File

@ -81,6 +81,9 @@ Each assistant plugin lives in its own directory under the assistants plugin roo
## Structure
- `ASSISTANT` is the root table. It must contain `Title`, `Description`, `SystemPrompt`, `SubmitText`, `AllowProfiles`, and the nested `UI` definition.
- `ASSISTANT` may optionally define direct-launch metadata for assistant tiles:
- `LaunchBehavior = "OPEN_WORKSPACE_CHAT_BY_NAME"`
- `WorkspaceName = "<target workspace name>"`
- `UI.Type` is always `"FORM"` and `UI.Children` is a list of component tables.
- Each component table declares `Type`, an optional `Children` array, and a `Props` table that feeds the components parameters.
@ -92,6 +95,8 @@ ASSISTANT = {
["SystemPrompt"] = "",
["SubmitText"] = "",
["AllowProfiles"] = true,
["LaunchBehavior"] = "OPEN_WORKSPACE_CHAT_BY_NAME",
["WorkspaceName"] = "",
["UI"] = {
["Type"] = "FORM",
["Children"] = {
@ -101,6 +106,29 @@ ASSISTANT = {
}
```
## Direct Launch to Workspace Chat
Assistant plugins can optionally skip the normal assistant page and open a chat directly from the tile.
```lua
ASSISTANT = {
["Title"] = "Open Chat",
["Description"] = "Open a new chat in the XXX workspace.",
["SystemPrompt"] = "",
["SubmitText"] = "Start",
["AllowProfiles"] = true,
["LaunchBehavior"] = "OPEN_WORKSPACE_CHAT_BY_NAME",
["WorkspaceName"] = "XXX",
["UI"] = {
["Type"] = "FORM",
["Children"] = {}
}
}
```
- `WorkspaceName` is resolved case-insensitively after trimming.
- If the workspace does not exist yet, AI Studio creates it automatically.
- The opened chat uses the normal default chat settings of AI Studio.
#### Supported types (matching the Blazor UI components):

View File

@ -62,6 +62,8 @@ ASSISTANT = {
["SystemPrompt"] = "<prompt that fundamentally 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
["LaunchBehavior"] = "<NONE|OPEN_WORKSPACE_CHAT_BY_NAME>", -- optional; when set to OPEN_WORKSPACE_CHAT_BY_NAME the tile opens a chat directly
["WorkspaceName"] = "<name of the workspace to open or create>", -- optional; required for OPEN_WORKSPACE_CHAT_BY_NAME
["UI"] = {
["Type"] = "FORM",
["Children"] = {

View File

@ -7518,6 +7518,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANT
-- Button
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPEEXTENSIONS::T864557713"] = "Schaltfläche"
-- The ASSISTANT table contains an invalid LaunchBehavior value.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T109828905"] = "Die Tabelle ASSISTANT enthält einen ungültigen Wert für LaunchBehavior."
-- The ASSISTANT table contains an unsupported LaunchBehavior value.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1194373781"] = "Die ASSISTANT-Tabelle enthält einen nicht unterstützten LaunchBehavior-Wert."
-- Failed to parse the UI render tree from the ASSISTANT lua table.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Der UI-Render-Baum konnte nicht aus der ASSISTANT-Lua-Tabelle geparst werden."
@ -7533,12 +7539,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2
-- The ASSISTANT lua table does not exist or is not a valid table.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3017816936"] = "Die Lua-Tabelle **ASSISTANT** existiert nicht oder ist keine gültige Tabelle."
-- The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3233001282"] = "Die ASSISTANT-Tabelle enthält einen leeren Arbeitsbereichsnamen für das LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'."
-- The provided ASSISTANT lua table does not contain a valid system prompt.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3402798667"] = "Die bereitgestellte ASSISTANT-Lua-Tabelle enthält keine gültige Systemaufforderung."
-- The ASSISTANT table does not contain a valid system prompt.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3723171842"] = "Die Tabelle **ASSISTANT** enthält keine gültige Systemanweisung."
-- The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T4215554842"] = "Die ASSISTANT-Tabelle enthält das LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME', aber keinen gültigen WorkspaceNamen."
-- ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T683382975"] = "`ASSISTANT.BuildPrompt` ist vorhanden, aber keine Lua-Funktion oder hat eine ungültige Syntax."

View File

@ -7518,6 +7518,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANT
-- Button
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPEEXTENSIONS::T864557713"] = "Button"
-- The ASSISTANT table contains an invalid LaunchBehavior value.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T109828905"] = "The ASSISTANT table contains an invalid LaunchBehavior value."
-- The ASSISTANT table contains an unsupported LaunchBehavior value.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1194373781"] = "The ASSISTANT table contains an unsupported LaunchBehavior value."
-- Failed to parse the UI render tree from the ASSISTANT lua table.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Failed to parse the UI render tree from the ASSISTANT lua table."
@ -7533,12 +7539,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T2
-- The ASSISTANT lua table does not exist or is not a valid table.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3017816936"] = "The ASSISTANT lua table does not exist or is not a valid table."
-- The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3233001282"] = "The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'."
-- The provided ASSISTANT lua table does not contain a valid system prompt.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T3402798667"] = "The provided ASSISTANT lua table does not contain a valid system prompt."
-- 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 contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T4215554842"] = "The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName."
-- ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T683382975"] = "ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax."

View File

@ -39,5 +39,5 @@ public sealed class DataAssistantPluginAudit(Expression<Func<Data, DataAssistant
/// <summary>
/// If true, the security audit will be hidden from the user and done in the background
/// </summary>
public bool AutomaticallyAuditAssistants { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AutomaticallyAuditAssistants, false);
public bool AutomaticallyAuditAssistants { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AutomaticallyAuditAssistants, true);
}

View File

@ -0,0 +1,7 @@
namespace AIStudio.Tools.PluginSystem.Assistants;
public enum AssistantPluginLaunchBehavior
{
NONE,
OPEN_WORKSPACE_CHAT_BY_NAME,
}

View File

@ -36,6 +36,9 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
public bool AllowProfiles { get; private set; } = true;
public bool HasEmbeddedProfileSelection { get; private set; }
public bool HasCustomPromptBuilder => this.buildPromptFunction is not null;
public AssistantPluginLaunchBehavior LaunchBehavior { get; private set; }
public string LaunchWorkspaceName { get; private set; } = string.Empty;
public bool StartsChatDirectly => this.LaunchBehavior is AssistantPluginLaunchBehavior.OPEN_WORKSPACE_CHAT_BY_NAME;
public const int TEXT_AREA_MAX_VALUE = 524288;
private LuaFunction? buildPromptFunction;
@ -61,6 +64,8 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
message = string.Empty;
this.HasEmbeddedProfileSelection = false;
this.buildPromptFunction = null;
this.LaunchBehavior = AssistantPluginLaunchBehavior.NONE;
this.LaunchWorkspaceName = string.Empty;
this.RegisterLuaHelpers();
@ -123,6 +128,12 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
this.SubmitText = assistantSubmitText;
this.AllowProfiles = assistantAllowProfiles;
if (!this.TryReadLaunchConfiguration(assistantTable, out var launchConfigIssue))
{
message = launchConfigIssue;
return false;
}
// 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))
{
@ -140,6 +151,51 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
return true;
}
private bool TryReadLaunchConfiguration(LuaTable assistantTable, out string message)
{
message = string.Empty;
if (!assistantTable.TryGetValue("LaunchBehavior", out var launchBehaviorValue))
return true;
if (!launchBehaviorValue.TryRead<string>(out var launchBehaviorText) ||
!Enum.TryParse<AssistantPluginLaunchBehavior>(launchBehaviorText, true, out var launchBehavior))
{
message = TB("The ASSISTANT table contains an invalid LaunchBehavior value.");
return false;
}
this.LaunchBehavior = launchBehavior;
if (launchBehavior is AssistantPluginLaunchBehavior.NONE)
return true;
switch (launchBehavior)
{
case AssistantPluginLaunchBehavior.OPEN_WORKSPACE_CHAT_BY_NAME:
if (!assistantTable.TryGetValue("WorkspaceName", out var workspaceNameValue) ||
!workspaceNameValue.TryRead<string>(out var workspaceName))
{
message = TB("The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName.");
return false;
}
workspaceName = workspaceName.Trim();
if (string.IsNullOrWhiteSpace(workspaceName))
{
message = TB("The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'.");
return false;
}
this.LaunchWorkspaceName = workspaceName;
return true;
default:
message = TB("The ASSISTANT table contains an unsupported LaunchBehavior value.");
return false;
}
}
public async Task<string?> TryBuildPromptAsync(LuaTable input, CancellationToken cancellationToken = default)
{
if (this.buildPromptFunction is null)

View File

@ -617,6 +617,46 @@ public static class WorkspaceBehaviour
}
}
public static async Task<Guid> ResolveOrCreateWorkspaceIdByNameAsync(string workspaceName)
{
var normalizedWorkspaceName = NormalizeWorkspaceName(workspaceName);
if (string.IsNullOrWhiteSpace(normalizedWorkspaceName))
return Guid.Empty;
await WORKSPACE_TREE_CACHE_SEMAPHORE.WaitAsync();
try
{
await EnsureTreeShellLoadedCoreAsync();
var existingWorkspace = WORKSPACE_TREE_CACHE.Workspaces.Values.FirstOrDefault(workspace =>
string.Equals(workspace.WorkspaceName.Trim(), normalizedWorkspaceName, StringComparison.OrdinalIgnoreCase));
if (existingWorkspace is not null)
return existingWorkspace.WorkspaceId;
}
finally
{
WORKSPACE_TREE_CACHE_SEMAPHORE.Release();
}
var result = await TryCreateWorkspaceAsync(normalizedWorkspaceName);
if (result.Success)
return result.Workspace.WorkspaceId;
await WORKSPACE_TREE_CACHE_SEMAPHORE.WaitAsync();
try
{
await EnsureTreeShellLoadedCoreAsync();
var existingWorkspace = WORKSPACE_TREE_CACHE.Workspaces.Values.FirstOrDefault(workspace =>
string.Equals(workspace.WorkspaceName.Trim(), normalizedWorkspaceName, StringComparison.OrdinalIgnoreCase));
return existingWorkspace?.WorkspaceId ?? Guid.Empty;
}
finally
{
WORKSPACE_TREE_CACHE_SEMAPHORE.Release();
}
}
public static async Task<TryCreateWorkspaceResult> TryCreateWorkspaceAsync(string workspaceName)
{
var normalizedWorkspaceName = NormalizeWorkspaceName(workspaceName);
@ -889,4 +929,4 @@ public static class WorkspaceBehaviour
public static async Task EnsureBiasWorkspace() => await EnsureWorkspace(KnownWorkspaces.BIAS_WORKSPACE_ID, "Bias of the Day");
public static async Task EnsureERIServerWorkspace() => await EnsureWorkspace(KnownWorkspaces.ERI_SERVER_WORKSPACE_ID, "ERI Servers");
}
}