mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-09-02 09:42:56 +00:00
Refactored configuration objects (#538)
Some checks are pending
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Some checks are pending
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
parent
be11efed67
commit
c1826426d9
@ -54,7 +54,7 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
|||||||
|
|
||||||
#region Implementation of IAgent
|
#region Implementation of IAgent
|
||||||
|
|
||||||
public abstract AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
public abstract AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||||
|
|
||||||
public abstract Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
|
public abstract Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
|
||||||
|
|
||||||
@ -103,10 +103,9 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
|||||||
|
|
||||||
protected async Task AddAIResponseAsync(ChatThread thread, IContent lastUserPrompt, DateTimeOffset time)
|
protected async Task AddAIResponseAsync(ChatThread thread, IContent lastUserPrompt, DateTimeOffset time)
|
||||||
{
|
{
|
||||||
if(this.ProviderSettings is null)
|
if(this.ProviderSettings == Settings.Provider.NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var providerSettings = this.ProviderSettings.Value;
|
|
||||||
var aiText = new ContentText
|
var aiText = new ContentText
|
||||||
{
|
{
|
||||||
// We have to wait for the remote
|
// We have to wait for the remote
|
||||||
@ -127,6 +126,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
|||||||
// Use the selected provider to get the AI response.
|
// Use the selected provider to get the AI response.
|
||||||
// By awaiting this line, we wait for the entire
|
// By awaiting this line, we wait for the entire
|
||||||
// content to be streamed.
|
// content to be streamed.
|
||||||
await aiText.CreateFromProviderAsync(providerSettings.CreateProvider(this.Logger), providerSettings.Model, lastUserPrompt, thread);
|
await aiText.CreateFromProviderAsync(this.ProviderSettings.CreateProvider(this.Logger), this.ProviderSettings.Model, lastUserPrompt, thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -86,7 +86,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
|||||||
""";
|
""";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Settings.Provider? ProviderSettings { get; set; }
|
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data source selection agent does not work with context. Use
|
/// The data source selection agent does not work with context. Use
|
||||||
@ -141,6 +141,11 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
|||||||
|
|
||||||
// We start with the provider currently selected by the user:
|
// We start with the provider currently selected by the user:
|
||||||
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_DATA_SOURCE_SELECTION, provider.Id, true);
|
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_DATA_SOURCE_SELECTION, provider.Id, true);
|
||||||
|
if (agentProvider == Settings.Provider.NONE)
|
||||||
|
{
|
||||||
|
logger.LogWarning("No provider is selected for the agent. The agent cannot select data sources.");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
// Assign the provider settings to the agent:
|
// Assign the provider settings to the agent:
|
||||||
logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
||||||
|
@ -71,7 +71,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
|||||||
""";
|
""";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Settings.Provider? ProviderSettings { get; set; }
|
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The retrieval context validation agent does not work with context. Use
|
/// The retrieval context validation agent does not work with context. Use
|
||||||
@ -133,6 +133,11 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
|||||||
{
|
{
|
||||||
// We start with the provider currently selected by the user:
|
// We start with the provider currently selected by the user:
|
||||||
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, provider.Id, true);
|
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, provider.Id, true);
|
||||||
|
if (agentProvider == Settings.Provider.NONE)
|
||||||
|
{
|
||||||
|
logger.LogWarning("No provider is selected for the agent.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Assign the provider settings to the agent:
|
// Assign the provider settings to the agent:
|
||||||
logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
||||||
|
@ -11,7 +11,7 @@ public sealed class AgentTextContentCleaner(ILogger<AgentBase> logger, SettingsM
|
|||||||
|
|
||||||
#region Overrides of AgentBase
|
#region Overrides of AgentBase
|
||||||
|
|
||||||
public override AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
public override AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||||
|
|
||||||
protected override Type Type => Type.SYSTEM;
|
protected override Type Type => Type.SYSTEM;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ public interface IAgent
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The provider to use for this agent.
|
/// The provider to use for this agent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
public Settings.Provider ProviderSettings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes a chat thread (i.e., context) and returns the updated thread.
|
/// Processes a chat thread (i.e., context) and returns the updated thread.
|
||||||
|
@ -85,7 +85,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
|
|
||||||
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected AIStudio.Settings.Provider providerSettings;
|
protected AIStudio.Settings.Provider providerSettings = Settings.Provider.NONE;
|
||||||
protected MudForm? form;
|
protected MudForm? form;
|
||||||
protected bool inputIsValid;
|
protected bool inputIsValid;
|
||||||
protected Profile currentProfile = Profile.NO_PROFILE;
|
protected Profile currentProfile = Profile.NO_PROFILE;
|
||||||
@ -352,7 +352,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
private async Task InnerResetForm()
|
private async Task InnerResetForm()
|
||||||
{
|
{
|
||||||
this.resultingContentBlock = null;
|
this.resultingContentBlock = null;
|
||||||
this.providerSettings = default;
|
this.providerSettings = Settings.Provider.NONE;
|
||||||
|
|
||||||
await this.JsRuntime.ClearDiv(RESULT_DIV_ID);
|
await this.JsRuntime.ClearDiv(RESULT_DIV_ID);
|
||||||
await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID);
|
await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID);
|
||||||
|
@ -5440,11 +5440,14 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCATEGORYEXTENSIONS::T91464
|
|||||||
-- The SETTINGS table does not exist or is not a valid table.
|
-- The SETTINGS table does not exist or is not a valid table.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011"] = "The SETTINGS table does not exist or is not a valid table."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011"] = "The SETTINGS table does not exist or is not a valid table."
|
||||||
|
|
||||||
|
-- At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3262676428"] = "At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist."
|
||||||
|
|
||||||
-- The CONFIG table does not exist or is not a valid table.
|
-- The CONFIG table does not exist or is not a valid table.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
||||||
|
|
||||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
-- At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "The LLM_PROVIDERS table does not exist or is not a valid table."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T445358428"] = "At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist."
|
||||||
|
|
||||||
-- The field IETF_TAG does not exist or is not a valid string.
|
-- The field IETF_TAG does not exist or is not a valid string.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
||||||
|
@ -18,9 +18,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<ChatThread?> ChatThreadChanged { get; set; }
|
public EventCallback<ChatThread?> ChatThreadChanged { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public AIStudio.Settings.Provider Provider { get; set; }
|
public AIStudio.Settings.Provider Provider { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<AIStudio.Settings.Provider> ProviderChanged { get; set; }
|
public EventCallback<AIStudio.Settings.Provider> ProviderChanged { get; set; }
|
||||||
@ -634,7 +634,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
case AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER:
|
case AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER:
|
||||||
if(this.Provider == default)
|
if(this.Provider == AIStudio.Settings.Provider.NONE)
|
||||||
{
|
{
|
||||||
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||||
await this.ProviderChanged.InvokeAsync(this.Provider);
|
await this.ProviderChanged.InvokeAsync(this.Provider);
|
||||||
@ -797,7 +797,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER:
|
case LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER:
|
||||||
if(this.Provider == default)
|
if(this.Provider == AIStudio.Settings.Provider.NONE)
|
||||||
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,9 @@ public partial class ConfigurationProviderSelection : MSGComponentBase
|
|||||||
foreach (var providerId in this.Data)
|
foreach (var providerId in this.Data)
|
||||||
{
|
{
|
||||||
var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value);
|
var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value);
|
||||||
|
if (provider is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||||
yield return providerId;
|
yield return providerId;
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ public partial class ProviderSelection : MSGComponentBase
|
|||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public AssistantBase<NoComponent>? AssistantBase { get; set; }
|
public AssistantBase<NoComponent>? AssistantBase { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<AIStudio.Settings.Provider> ProviderSettingsChanged { get; set; }
|
public EventCallback<AIStudio.Settings.Provider> ProviderSettingsChanged { get; set; }
|
||||||
@ -32,7 +32,8 @@ public partial class ProviderSelection : MSGComponentBase
|
|||||||
{
|
{
|
||||||
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE);
|
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE);
|
||||||
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
||||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
if (provider.UsedLLMProvider != LLMProviders.NONE)
|
||||||
yield return provider;
|
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||||
|
yield return provider;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ public partial class ReadWebContent : MSGComponentBase
|
|||||||
public EventCallback<string> ContentChanged { get; set; }
|
public EventCallback<string> ContentChanged { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool AgentIsRunning { get; set; }
|
public bool AgentIsRunning { get; set; }
|
||||||
@ -34,7 +34,7 @@ public partial class ReadWebContent : MSGComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool PreselectContentCleanerAgent { get; set; }
|
public bool PreselectContentCleanerAgent { get; set; }
|
||||||
|
|
||||||
private Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
|
private readonly Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
|
||||||
private ProcessStepValue processStep;
|
private ProcessStepValue processStep;
|
||||||
|
|
||||||
private bool showWebContentReader;
|
private bool showWebContentReader;
|
||||||
@ -43,7 +43,7 @@ public partial class ReadWebContent : MSGComponentBase
|
|||||||
private bool urlIsValid;
|
private bool urlIsValid;
|
||||||
private bool isProviderValid;
|
private bool isProviderValid;
|
||||||
|
|
||||||
private AIStudio.Settings.Provider providerSettings;
|
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ public partial class ReadWebContent : MSGComponentBase
|
|||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
markdown = this.HTMLParser.ParseToMarkdown(html);
|
markdown = this.HTMLParser.ParseToMarkdown(html);
|
||||||
|
|
||||||
if (this.useContentCleanerAgent)
|
if (this.useContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE)
|
||||||
{
|
{
|
||||||
this.AgentTextContentCleaner.ProviderSettings = this.providerSettings;
|
this.AgentTextContentCleaner.ProviderSettings = this.providerSettings;
|
||||||
var additionalData = new Dictionary<string, string>
|
var additionalData = new Dictionary<string, string>
|
||||||
@ -149,7 +149,7 @@ public partial class ReadWebContent : MSGComponentBase
|
|||||||
|
|
||||||
private string? ValidateProvider(bool shouldUseAgent)
|
private string? ValidateProvider(bool shouldUseAgent)
|
||||||
{
|
{
|
||||||
if(shouldUseAgent && this.providerSettings == default)
|
if(shouldUseAgent && this.providerSettings == AIStudio.Settings.Provider.NONE)
|
||||||
{
|
{
|
||||||
this.isProviderValid = false;
|
this.isProviderValid = false;
|
||||||
return T("Please select a provider to use the cleanup agent.");
|
return T("Please select a provider to use the cleanup agent.");
|
||||||
|
@ -54,6 +54,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
|||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||||
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
|
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
|
||||||
{
|
{
|
||||||
|
if(provider == AIStudio.Settings.Provider.NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
if (provider.IsEnterpriseConfiguration)
|
if (provider.IsEnterpriseConfiguration)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ public partial class Chat : MSGComponentBase
|
|||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
private ChatThread? chatThread;
|
private ChatThread? chatThread;
|
||||||
private AIStudio.Settings.Provider providerSettings;
|
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||||
private bool workspaceOverlayVisible;
|
private bool workspaceOverlayVisible;
|
||||||
private string currentWorkspaceName = string.Empty;
|
private string currentWorkspaceName = string.Empty;
|
||||||
private Workspaces? workspaces;
|
private Workspaces? workspaces;
|
||||||
|
@ -18,7 +18,7 @@ public partial class Writer : MSGComponentBase
|
|||||||
private readonly Timer typeTimer = new(TimeSpan.FromMilliseconds(1_500));
|
private readonly Timer typeTimer = new(TimeSpan.FromMilliseconds(1_500));
|
||||||
|
|
||||||
private MudTextField<string> textField = null!;
|
private MudTextField<string> textField = null!;
|
||||||
private AIStudio.Settings.Provider providerSettings;
|
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||||
private ChatThread? chatThread;
|
private ChatThread? chatThread;
|
||||||
private bool isStreaming;
|
private bool isStreaming;
|
||||||
private string userInput = string.Empty;
|
private string userInput = string.Empty;
|
||||||
|
@ -5442,11 +5442,14 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCATEGORYEXTENSIONS::T91464
|
|||||||
-- The SETTINGS table does not exist or is not a valid table.
|
-- The SETTINGS table does not exist or is not a valid table.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011"] = "Die Tabelle SETTINGS existiert nicht oder ist keine gültige Tabelle."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011"] = "Die Tabelle SETTINGS existiert nicht oder ist keine gültige Tabelle."
|
||||||
|
|
||||||
|
-- At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3262676428"] = "Mindestens ein konfigurierter LLM-Anbieter ist ungültig oder konnte nicht verarbeitet werden, oder die Tabelle LLM_PROVIDERS existiert nicht."
|
||||||
|
|
||||||
-- The CONFIG table does not exist or is not a valid table.
|
-- The CONFIG table does not exist or is not a valid table.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "Die Tabelle CONFIG existiert nicht oder ist keine gültige Tabelle."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "Die Tabelle CONFIG existiert nicht oder ist keine gültige Tabelle."
|
||||||
|
|
||||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
-- At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "Die Tabelle LLM_PROVIDERS existiert nicht oder ist keine gültige Tabelle."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T445358428"] = "Mindestens eine konfigurierte Chat-Vorlage ist ungültig oder konnte nicht verarbeitet werden, oder die Tabelle CHAT_TEMPLATES existiert nicht."
|
||||||
|
|
||||||
-- The field IETF_TAG does not exist or is not a valid string.
|
-- The field IETF_TAG does not exist or is not a valid string.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "Das Feld IETF_TAG existiert nicht oder ist keine gültige Zeichenkette."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "Das Feld IETF_TAG existiert nicht oder ist keine gültige Zeichenkette."
|
||||||
|
@ -5442,11 +5442,14 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCATEGORYEXTENSIONS::T91464
|
|||||||
-- The SETTINGS table does not exist or is not a valid table.
|
-- The SETTINGS table does not exist or is not a valid table.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011"] = "The SETTINGS table does not exist or is not a valid table."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011"] = "The SETTINGS table does not exist or is not a valid table."
|
||||||
|
|
||||||
|
-- At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3262676428"] = "At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist."
|
||||||
|
|
||||||
-- The CONFIG table does not exist or is not a valid table.
|
-- The CONFIG table does not exist or is not a valid table.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
||||||
|
|
||||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
-- At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "The LLM_PROVIDERS table does not exist or is not a valid table."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T445358428"] = "At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist."
|
||||||
|
|
||||||
-- The field IETF_TAG does not exist or is not a valid string.
|
-- The field IETF_TAG does not exist or is not a valid string.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Tools.PluginSystem;
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
using Lua;
|
||||||
|
|
||||||
namespace AIStudio.Settings;
|
namespace AIStudio.Settings;
|
||||||
|
|
||||||
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 record ChatTemplate(
|
||||||
|
uint Num,
|
||||||
|
string Id,
|
||||||
|
string Name,
|
||||||
|
string SystemPrompt,
|
||||||
|
string PredefinedUserPrompt,
|
||||||
|
List<ContentBlock> ExampleConversation,
|
||||||
|
bool AllowProfileUsage,
|
||||||
|
bool IsEnterpriseConfiguration = false,
|
||||||
|
Guid EnterpriseConfigurationPluginId = default) : ConfigurationBaseObject
|
||||||
{
|
{
|
||||||
public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
|
public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
|
||||||
{
|
{
|
||||||
@ -11,6 +22,8 @@ public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt
|
|||||||
|
|
||||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate));
|
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate));
|
||||||
|
|
||||||
|
private static readonly ILogger<ChatTemplate> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ChatTemplate>();
|
||||||
|
|
||||||
public static readonly ChatTemplate NO_CHAT_TEMPLATE = new()
|
public static readonly ChatTemplate NO_CHAT_TEMPLATE = new()
|
||||||
{
|
{
|
||||||
Name = TB("Use no chat template"),
|
Name = TB("Use no chat template"),
|
||||||
@ -41,4 +54,96 @@ public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt
|
|||||||
|
|
||||||
return this.SystemPrompt;
|
return this.SystemPrompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryParseChatTemplateTable(int idx, LuaTable table, Guid configPluginId, out ConfigurationBaseObject template)
|
||||||
|
{
|
||||||
|
template = NO_CHAT_TEMPLATE;
|
||||||
|
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid ID. The ID must be a valid GUID.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("Name", out var nameValue) || !nameValue.TryRead<string>(out var name))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid name.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("SystemPrompt", out var sysPromptValue) || !sysPromptValue.TryRead<string>(out var systemPrompt))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid system prompt.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var predefinedUserPrompt = string.Empty;
|
||||||
|
if (table.TryGetValue("PredefinedUserPrompt", out var preUserValue) && preUserValue.TryRead<string>(out var preUser))
|
||||||
|
predefinedUserPrompt = preUser;
|
||||||
|
|
||||||
|
var allowProfileUsage = false;
|
||||||
|
if (table.TryGetValue("AllowProfileUsage", out var allowProfileValue) && allowProfileValue.TryRead<bool>(out var allow))
|
||||||
|
allowProfileUsage = allow;
|
||||||
|
|
||||||
|
template = new ChatTemplate
|
||||||
|
{
|
||||||
|
Num = 0,
|
||||||
|
Id = id.ToString(),
|
||||||
|
Name = name,
|
||||||
|
SystemPrompt = systemPrompt,
|
||||||
|
PredefinedUserPrompt = predefinedUserPrompt,
|
||||||
|
ExampleConversation = ParseExampleConversation(idx, table),
|
||||||
|
AllowProfileUsage = allowProfileUsage,
|
||||||
|
IsEnterpriseConfiguration = true,
|
||||||
|
EnterpriseConfigurationPluginId = configPluginId,
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ContentBlock> ParseExampleConversation(int idx, LuaTable table)
|
||||||
|
{
|
||||||
|
var exampleConversation = new List<ContentBlock>();
|
||||||
|
if (!table.TryGetValue("ExampleConversation", out var exConvValue) || !exConvValue.TryRead<LuaTable>(out var exConvTable))
|
||||||
|
return exampleConversation;
|
||||||
|
|
||||||
|
var numBlocks = exConvTable.ArrayLength;
|
||||||
|
for (var j = 1; j <= numBlocks; j++)
|
||||||
|
{
|
||||||
|
var blockValue = exConvTable[j];
|
||||||
|
if (!blockValue.TryRead<LuaTable>(out var blockTable))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} is not a valid table.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blockTable.TryGetValue("Role", out var roleValue) || !roleValue.TryRead<string>(out var roleText) || !Enum.TryParse<ChatRole>(roleText, true, out var parsedRole))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} does not contain a valid role.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blockTable.TryGetValue("Content", out var contentValue) || !contentValue.TryRead<string>(out var content))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} does not contain a valid content message.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} contains an empty content message.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
exampleConversation.Add(new ContentBlock
|
||||||
|
{
|
||||||
|
Time = DateTimeOffset.UtcNow,
|
||||||
|
Role = parsedRole,
|
||||||
|
Content = new ContentText { Text = content },
|
||||||
|
ContentType = ContentType.TEXT,
|
||||||
|
HideFromUser = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return exampleConversation;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,26 +0,0 @@
|
|||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace AIStudio.Settings;
|
|
||||||
|
|
||||||
public static class ExpressionExtensions
|
|
||||||
{
|
|
||||||
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(typeof(ExpressionExtensions));
|
|
||||||
|
|
||||||
public static MemberExpression GetMemberExpression<TIn, TOut>(this Expression<Func<TIn, TOut>> expression)
|
|
||||||
{
|
|
||||||
switch (expression.Body)
|
|
||||||
{
|
|
||||||
// Case for value types, which are wrapped in UnaryExpression:
|
|
||||||
case UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression:
|
|
||||||
return (MemberExpression)unaryExpression.Operand;
|
|
||||||
|
|
||||||
// Case for reference types, which are directly MemberExpressions:
|
|
||||||
case MemberExpression memberExpression:
|
|
||||||
return memberExpression;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOGGER.LogError($"Expression '{expression}' is not a valid property expression.");
|
|
||||||
throw new ArgumentException($"Expression '{expression}' is not a valid property expression.", nameof(expression));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,10 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Provider.HuggingFace;
|
using AIStudio.Provider.HuggingFace;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
using Lua;
|
||||||
|
|
||||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||||
|
|
||||||
namespace AIStudio.Settings;
|
namespace AIStudio.Settings;
|
||||||
@ -16,7 +20,7 @@ namespace AIStudio.Settings;
|
|||||||
/// <param name="IsSelfHosted">Whether the provider is self-hosted.</param>
|
/// <param name="IsSelfHosted">Whether the provider is self-hosted.</param>
|
||||||
/// <param name="Hostname">The hostname of the provider. Useful for self-hosted providers.</param>
|
/// <param name="Hostname">The hostname of the provider. Useful for self-hosted providers.</param>
|
||||||
/// <param name="Model">The LLM model to use for chat.</param>
|
/// <param name="Model">The LLM model to use for chat.</param>
|
||||||
public readonly record struct Provider(
|
public sealed record Provider(
|
||||||
uint Num,
|
uint Num,
|
||||||
string Id,
|
string Id,
|
||||||
string InstanceName,
|
string InstanceName,
|
||||||
@ -27,8 +31,24 @@ public readonly record struct Provider(
|
|||||||
Guid EnterpriseConfigurationPluginId = default,
|
Guid EnterpriseConfigurationPluginId = default,
|
||||||
string Hostname = "http://localhost:1234",
|
string Hostname = "http://localhost:1234",
|
||||||
Host Host = Host.NONE,
|
Host Host = Host.NONE,
|
||||||
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ISecretId
|
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ConfigurationBaseObject, ISecretId
|
||||||
{
|
{
|
||||||
|
private static readonly ILogger<Provider> LOGGER = Program.LOGGER_FACTORY.CreateLogger<Provider>();
|
||||||
|
|
||||||
|
public static readonly Provider NONE = new();
|
||||||
|
|
||||||
|
public Provider() : this(
|
||||||
|
0,
|
||||||
|
Guid.Empty.ToString(),
|
||||||
|
string.Empty,
|
||||||
|
LLMProviders.NONE,
|
||||||
|
default,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
Guid.Empty)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#region Overrides of ValueType
|
#region Overrides of ValueType
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -57,4 +77,95 @@ public readonly record struct Provider(
|
|||||||
public string SecretName => this.InstanceName;
|
public string SecretName => this.InstanceName;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IConfigurationObject
|
||||||
|
|
||||||
|
public override string Name
|
||||||
|
{
|
||||||
|
get => this.InstanceName;
|
||||||
|
init => this.InstanceName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static bool TryParseProviderTable(int idx, LuaTable table, Guid configPluginId, out ConfigurationBaseObject provider)
|
||||||
|
{
|
||||||
|
provider = NONE;
|
||||||
|
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid ID. The ID must be a valid GUID.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("InstanceName", out var instanceNameValue) || !instanceNameValue.TryRead<string>(out var instanceName))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid instance name.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("UsedLLMProvider", out var usedLLMProviderValue) || !usedLLMProviderValue.TryRead<string>(out var usedLLMProviderText) || !Enum.TryParse<LLMProviders>(usedLLMProviderText, true, out var usedLLMProvider))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid LLM provider enum value.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("Host", out var hostValue) || !hostValue.TryRead<string>(out var hostText) || !Enum.TryParse<Host>(hostText, true, out var host))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid host enum value.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("Hostname", out var hostnameValue) || !hostnameValue.TryRead<string>(out var hostname))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid hostname.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("Model", out var modelValue) || !modelValue.TryRead<LuaTable>(out var modelTable))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model table.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryReadModelTable(idx, modelTable, out var model))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model configuration.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
provider = new Provider
|
||||||
|
{
|
||||||
|
Num = 0,
|
||||||
|
Id = id.ToString(),
|
||||||
|
InstanceName = instanceName,
|
||||||
|
UsedLLMProvider = usedLLMProvider,
|
||||||
|
Model = model,
|
||||||
|
IsSelfHosted = usedLLMProvider is LLMProviders.SELF_HOSTED,
|
||||||
|
IsEnterpriseConfiguration = true,
|
||||||
|
EnterpriseConfigurationPluginId = configPluginId,
|
||||||
|
Hostname = hostname,
|
||||||
|
Host = host
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadModelTable(int idx, LuaTable table, out Model model)
|
||||||
|
{
|
||||||
|
model = default;
|
||||||
|
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var id))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model ID.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.TryGetValue("DisplayName", out var displayNameValue) || !displayNameValue.TryRead<string>(out var displayName))
|
||||||
|
{
|
||||||
|
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model display name.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
model = new(id, displayName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
@ -215,18 +215,18 @@ public sealed class SettingsManager
|
|||||||
return this.ConfigurationData.Providers[0];
|
return this.ConfigurationData.Providers[0];
|
||||||
|
|
||||||
// Is there a current provider with a sufficiently high confidence level?
|
// Is there a current provider with a sufficiently high confidence level?
|
||||||
Provider currentProvider = default;
|
var currentProvider = Provider.NONE;
|
||||||
if (currentProviderId is not null && !string.IsNullOrWhiteSpace(currentProviderId))
|
if (currentProviderId is not null && !string.IsNullOrWhiteSpace(currentProviderId))
|
||||||
{
|
{
|
||||||
var currentProviderProbe = this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == currentProviderId);
|
var currentProviderProbe = this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == currentProviderId);
|
||||||
if (currentProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
|
if (currentProviderProbe is not null && currentProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
|
||||||
currentProvider = currentProviderProbe;
|
currentProvider = currentProviderProbe;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is there a component-preselected provider with a sufficiently high confidence level?
|
// Is there a component-preselected provider with a sufficiently high confidence level?
|
||||||
Provider preselectedProvider = default;
|
var preselectedProvider = Provider.NONE;
|
||||||
var preselectedProviderProbe = component.PreselectedProvider(this);
|
var preselectedProviderProbe = component.PreselectedProvider(this);
|
||||||
if(preselectedProviderProbe != default && preselectedProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
|
if(preselectedProviderProbe != Provider.NONE && preselectedProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel)
|
||||||
preselectedProvider = preselectedProviderProbe;
|
preselectedProvider = preselectedProviderProbe;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -234,14 +234,14 @@ public sealed class SettingsManager
|
|||||||
// and the preselected provider is available and has a confidence level
|
// and the preselected provider is available and has a confidence level
|
||||||
// that is high enough.
|
// that is high enough.
|
||||||
//
|
//
|
||||||
if(usePreselectionBeforeCurrentProvider && preselectedProvider != default)
|
if(usePreselectionBeforeCurrentProvider && preselectedProvider != Provider.NONE)
|
||||||
return preselectedProvider;
|
return preselectedProvider;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Case: The current provider is available and has a confidence level that is
|
// Case: The current provider is available and has a confidence level that is
|
||||||
// high enough.
|
// high enough.
|
||||||
//
|
//
|
||||||
if(currentProvider != default)
|
if(currentProvider != Provider.NONE)
|
||||||
return currentProvider;
|
return currentProvider;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -250,11 +250,11 @@ public sealed class SettingsManager
|
|||||||
// level that is high enough. The preselected provider is available and
|
// level that is high enough. The preselected provider is available and
|
||||||
// has a confidence level that is high enough.
|
// has a confidence level that is high enough.
|
||||||
//
|
//
|
||||||
if(preselectedProvider != default)
|
if(preselectedProvider != Provider.NONE)
|
||||||
return preselectedProvider;
|
return preselectedProvider;
|
||||||
|
|
||||||
// When there is an app-wide preselected provider, and it has a confidence level that is high enough, we return it:
|
// When there is an app-wide preselected provider, and it has a confidence level that is high enough, we return it:
|
||||||
return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider && x.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel);
|
return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider && x.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) ?? Provider.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Profile GetPreselectedProfile(Tools.Components component)
|
public Profile GetPreselectedProfile(Tools.Components component)
|
||||||
|
@ -89,32 +89,37 @@ public static class ComponentsExtensions
|
|||||||
};
|
};
|
||||||
|
|
||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||||
public static AIStudio.Settings.Provider PreselectedProvider(this Components component, SettingsManager settingsManager) => component switch
|
public static AIStudio.Settings.Provider PreselectedProvider(this Components component, SettingsManager settingsManager)
|
||||||
{
|
{
|
||||||
Components.GRAMMAR_SPELLING_ASSISTANT => settingsManager.ConfigurationData.GrammarSpelling.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider) : default,
|
var preselectedProvider = component switch
|
||||||
Components.ICON_FINDER_ASSISTANT => settingsManager.ConfigurationData.IconFinder.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.IconFinder.PreselectedProvider) : default,
|
{
|
||||||
Components.REWRITE_ASSISTANT => settingsManager.ConfigurationData.RewriteImprove.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.RewriteImprove.PreselectedProvider) : default,
|
Components.GRAMMAR_SPELLING_ASSISTANT => settingsManager.ConfigurationData.GrammarSpelling.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider) : null,
|
||||||
Components.TRANSLATION_ASSISTANT => settingsManager.ConfigurationData.Translation.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Translation.PreselectedProvider) : default,
|
Components.ICON_FINDER_ASSISTANT => settingsManager.ConfigurationData.IconFinder.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.IconFinder.PreselectedProvider) : null,
|
||||||
Components.AGENDA_ASSISTANT => settingsManager.ConfigurationData.Agenda.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Agenda.PreselectedProvider) : default,
|
Components.REWRITE_ASSISTANT => settingsManager.ConfigurationData.RewriteImprove.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.RewriteImprove.PreselectedProvider) : null,
|
||||||
Components.CODING_ASSISTANT => settingsManager.ConfigurationData.Coding.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Coding.PreselectedProvider) : default,
|
Components.TRANSLATION_ASSISTANT => settingsManager.ConfigurationData.Translation.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Translation.PreselectedProvider) : null,
|
||||||
Components.TEXT_SUMMARIZER_ASSISTANT => settingsManager.ConfigurationData.TextSummarizer.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.TextSummarizer.PreselectedProvider) : default,
|
Components.AGENDA_ASSISTANT => settingsManager.ConfigurationData.Agenda.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Agenda.PreselectedProvider) : null,
|
||||||
Components.EMAIL_ASSISTANT => settingsManager.ConfigurationData.EMail.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.EMail.PreselectedProvider) : default,
|
Components.CODING_ASSISTANT => settingsManager.ConfigurationData.Coding.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Coding.PreselectedProvider) : null,
|
||||||
Components.LEGAL_CHECK_ASSISTANT => settingsManager.ConfigurationData.LegalCheck.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.LegalCheck.PreselectedProvider) : default,
|
Components.TEXT_SUMMARIZER_ASSISTANT => settingsManager.ConfigurationData.TextSummarizer.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.TextSummarizer.PreselectedProvider) : null,
|
||||||
Components.SYNONYMS_ASSISTANT => settingsManager.ConfigurationData.Synonyms.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Synonyms.PreselectedProvider) : default,
|
Components.EMAIL_ASSISTANT => settingsManager.ConfigurationData.EMail.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.EMail.PreselectedProvider) : null,
|
||||||
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.MyTasks.PreselectedProvider) : default,
|
Components.LEGAL_CHECK_ASSISTANT => settingsManager.ConfigurationData.LegalCheck.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.LegalCheck.PreselectedProvider) : null,
|
||||||
Components.JOB_POSTING_ASSISTANT => settingsManager.ConfigurationData.JobPostings.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.JobPostings.PreselectedProvider) : default,
|
Components.SYNONYMS_ASSISTANT => settingsManager.ConfigurationData.Synonyms.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Synonyms.PreselectedProvider) : null,
|
||||||
Components.BIAS_DAY_ASSISTANT => settingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider) : default,
|
Components.MY_TASKS_ASSISTANT => settingsManager.ConfigurationData.MyTasks.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.MyTasks.PreselectedProvider) : null,
|
||||||
Components.ERI_ASSISTANT => settingsManager.ConfigurationData.ERI.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.ERI.PreselectedProvider) : default,
|
Components.JOB_POSTING_ASSISTANT => settingsManager.ConfigurationData.JobPostings.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.JobPostings.PreselectedProvider) : null,
|
||||||
Components.I18N_ASSISTANT => settingsManager.ConfigurationData.I18N.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.I18N.PreselectedProvider) : default,
|
Components.BIAS_DAY_ASSISTANT => settingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider) : null,
|
||||||
|
Components.ERI_ASSISTANT => settingsManager.ConfigurationData.ERI.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.ERI.PreselectedProvider) : null,
|
||||||
|
Components.I18N_ASSISTANT => settingsManager.ConfigurationData.I18N.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.I18N.PreselectedProvider) : null,
|
||||||
|
|
||||||
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : default,
|
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedProvider) : null,
|
||||||
|
|
||||||
|
Components.AGENT_TEXT_CONTENT_CLEANER => settingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider) : null,
|
||||||
|
Components.AGENT_DATA_SOURCE_SELECTION => settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider) : null,
|
||||||
|
Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION => settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider) : null,
|
||||||
|
|
||||||
|
_ => Settings.Provider.NONE,
|
||||||
|
};
|
||||||
|
|
||||||
Components.AGENT_TEXT_CONTENT_CLEANER => settingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider) : default,
|
return preselectedProvider ?? Settings.Provider.NONE;
|
||||||
Components.AGENT_DATA_SOURCE_SELECTION => settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider) : default,
|
}
|
||||||
Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION => settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider) : default,
|
|
||||||
|
|
||||||
_ => default,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Profile PreselectedProfile(this Components component, SettingsManager settingsManager) => component switch
|
public static Profile PreselectedProfile(this Components component, SettingsManager settingsManager) => component switch
|
||||||
{
|
{
|
||||||
|
96
app/MindWork AI Studio/Tools/ExpressionExtensions.cs
Normal file
96
app/MindWork AI Studio/Tools/ExpressionExtensions.cs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools;
|
||||||
|
|
||||||
|
public static class ExpressionExtensions
|
||||||
|
{
|
||||||
|
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(typeof(ExpressionExtensions));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the member expression from a given lambda expression representing a property.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expression">A lambda expression specifying the property for which the member expression is to be extracted.
|
||||||
|
/// The lambda expression body must represent member access.</param>
|
||||||
|
/// <typeparam name="TIn">The type of the object containing the property referenced in the lambda expression.</typeparam>
|
||||||
|
/// <typeparam name="TOut">The type of the property being accessed in the lambda expression.</typeparam>
|
||||||
|
/// <returns>The member expression that represents the property access.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the provided lambda expression does not represent a valid property expression.</exception>
|
||||||
|
public static MemberExpression GetMemberExpression<TIn, TOut>(this Expression<Func<TIn, TOut>> expression)
|
||||||
|
{
|
||||||
|
switch (expression.Body)
|
||||||
|
{
|
||||||
|
// Case for value types, which are wrapped in UnaryExpression:
|
||||||
|
case UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression:
|
||||||
|
return (MemberExpression)unaryExpression.Operand;
|
||||||
|
|
||||||
|
// Case for reference types, which are directly MemberExpressions:
|
||||||
|
case MemberExpression memberExpression:
|
||||||
|
return memberExpression;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOGGER.LogError($"Expression '{expression}' is not a valid property expression.");
|
||||||
|
throw new ArgumentException($"Expression '{expression}' is not a valid property expression.", nameof(expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to increment the value of an uint property for a specified object using a
|
||||||
|
/// provided expression.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expression">An expression representing the property to be incremented. The property
|
||||||
|
/// must be of type uint and belong to the provided object.</param>
|
||||||
|
/// <param name="data">The object that contains the property referenced by the expression.</param>
|
||||||
|
/// <param name="type">The type of increment operation to perform (e.g., prefix or postfix).</param>
|
||||||
|
/// <typeparam name="TIn">The type of the object that contains the property to be incremented.</typeparam>
|
||||||
|
/// <typeparam name="TOut">The type of the property to be incremented.</typeparam>
|
||||||
|
/// <returns>An IncrementResult object containing the result of the increment operation.</returns>
|
||||||
|
public static IncrementResult<TOut> TryIncrement<TIn, TOut>(this Expression<Func<TIn, TOut>> expression, TIn data, IncrementType type) where TOut : IBinaryInteger<TOut>
|
||||||
|
{
|
||||||
|
// Ensure that the expression body is a member expression:
|
||||||
|
if (expression.Body is not MemberExpression memberExpression)
|
||||||
|
return new(false, TOut.Zero);
|
||||||
|
|
||||||
|
// Ensure that the member expression is a property:
|
||||||
|
if (memberExpression.Member is not PropertyInfo propertyInfo)
|
||||||
|
return new(false, TOut.Zero);
|
||||||
|
|
||||||
|
// Ensure that the member expression has a target object:
|
||||||
|
if (memberExpression.Expression is null)
|
||||||
|
return new(false, TOut.Zero);
|
||||||
|
|
||||||
|
// Get the target object for the expression, which is the object containing the property to increment:
|
||||||
|
var targetObjectExpression = Expression.Lambda(memberExpression.Expression, expression.Parameters);
|
||||||
|
|
||||||
|
// Compile the lambda expression to get the target object
|
||||||
|
// (which is the object containing the property to increment):
|
||||||
|
var targetObject = targetObjectExpression.Compile().DynamicInvoke(data);
|
||||||
|
|
||||||
|
// Was the compilation successful?
|
||||||
|
if (targetObject is null)
|
||||||
|
return new(false, TOut.Zero);
|
||||||
|
|
||||||
|
// Read the current value of the property:
|
||||||
|
if (propertyInfo.GetValue(targetObject) is not TOut value)
|
||||||
|
return new(false, TOut.Zero);
|
||||||
|
|
||||||
|
// Increment the value:
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IncrementType.PRE:
|
||||||
|
var nextValue = value + TOut.CreateChecked(1);
|
||||||
|
propertyInfo.SetValue(targetObject, nextValue);
|
||||||
|
return new(true, nextValue);
|
||||||
|
|
||||||
|
case IncrementType.POST:
|
||||||
|
var currentValue = value;
|
||||||
|
var incrementedValue = value + TOut.CreateChecked(1);
|
||||||
|
propertyInfo.SetValue(targetObject, incrementedValue);
|
||||||
|
return new(true, currentValue);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new(false, TOut.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
app/MindWork AI Studio/Tools/IncrementResult.cs
Normal file
10
app/MindWork AI Studio/Tools/IncrementResult.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the result of an increment operation. It encapsulates whether the operation
|
||||||
|
/// was successful and the increased value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TOut">The type of the incremented value, constrained to implement the IBinaryInteger interface.</typeparam>
|
||||||
|
public sealed record IncrementResult<TOut>(bool Success, TOut UpdatedValue) where TOut : IBinaryInteger<TOut>;
|
14
app/MindWork AI Studio/Tools/IncrementType.cs
Normal file
14
app/MindWork AI Studio/Tools/IncrementType.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace AIStudio.Tools;
|
||||||
|
|
||||||
|
public enum IncrementType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the value before returning it. So, the incremented value is returned.
|
||||||
|
/// </summary>
|
||||||
|
PRE,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the value after returning it. So, the original value is returned.
|
||||||
|
/// </summary>
|
||||||
|
POST,
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
namespace AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
public abstract record ConfigurationBaseObject : IConfigurationObject
|
||||||
|
{
|
||||||
|
#region Implementation of IConfigurationObject
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract string Id { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract uint Num { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract string Name { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract bool IsEnterpriseConfiguration { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract Guid EnterpriseConfigurationPluginId { get; init; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
namespace AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a configuration object, such as a chat template or a LLM provider.
|
||||||
|
/// </summary>
|
||||||
|
public interface IConfigurationObject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The unique ID of the configuration object.
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The continuous number of the configuration object.
|
||||||
|
/// </summary>
|
||||||
|
public uint Num { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the configuration object.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this configuration object an enterprise configuration?
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnterpriseConfiguration { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the enterprise configuration plugin.
|
||||||
|
/// </summary>
|
||||||
|
public Guid EnterpriseConfigurationPluginId { get; }
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
namespace AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
public sealed record NoConfigurationObject : ConfigurationBaseObject
|
||||||
|
{
|
||||||
|
public static readonly NoConfigurationObject INSTANCE = new();
|
||||||
|
|
||||||
|
private NoConfigurationObject()
|
||||||
|
{
|
||||||
|
this.Id = Guid.Empty.ToString();
|
||||||
|
this.Name = "No Configuration";
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overrides of ConfigurationBaseObject
|
||||||
|
|
||||||
|
public override string Id { get; init; }
|
||||||
|
|
||||||
|
public override uint Num { get; init; }
|
||||||
|
|
||||||
|
public override string Name { get; init; }
|
||||||
|
|
||||||
|
public override bool IsEnterpriseConfiguration { get; init; }
|
||||||
|
|
||||||
|
public override Guid EnterpriseConfigurationPluginId { get; init; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
@ -1,21 +1,15 @@
|
|||||||
using AIStudio.Provider;
|
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Chat;
|
|
||||||
|
|
||||||
using Lua;
|
using Lua;
|
||||||
|
|
||||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
|
||||||
using Model = AIStudio.Provider.Model;
|
|
||||||
|
|
||||||
namespace AIStudio.Tools.PluginSystem;
|
namespace AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginType type) : PluginBase(isInternal, state, type)
|
public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginType type) : PluginBase(isInternal, state, type)
|
||||||
{
|
{
|
||||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginConfiguration).Namespace, nameof(PluginConfiguration));
|
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginConfiguration).Namespace, nameof(PluginConfiguration));
|
||||||
private static readonly ILogger<PluginConfiguration> LOGGER = Program.LOGGER_FACTORY.CreateLogger<PluginConfiguration>();
|
|
||||||
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||||
|
|
||||||
private readonly List<PluginConfigurationObject> configObjects = [];
|
private List<PluginConfigurationObject> configObjects = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of configuration objects. Configuration objects are, e.g., providers or chat templates.
|
/// The list of configuration objects. Configuration objects are, e.g., providers or chat templates.
|
||||||
@ -51,317 +45,34 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Check for the main SETTINGS table:
|
||||||
// ===========================================
|
|
||||||
// Configured settings
|
|
||||||
// ===========================================
|
|
||||||
//
|
|
||||||
if (!mainTable.TryGetValue("SETTINGS", out var settingsValue) || !settingsValue.TryRead<LuaTable>(out var settingsTable))
|
if (!mainTable.TryGetValue("SETTINGS", out var settingsValue) || !settingsValue.TryRead<LuaTable>(out var settingsTable))
|
||||||
{
|
{
|
||||||
message = TB("The SETTINGS table does not exist or is not a valid table.");
|
message = TB("The SETTINGS table does not exist or is not a valid table.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for updates, and if so, how often?
|
// Config: check for updates, and if so, how often?
|
||||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.UpdateBehavior, this.Id, settingsTable, dryRun);
|
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.UpdateBehavior, this.Id, settingsTable, dryRun);
|
||||||
|
|
||||||
// Allow the user to add providers?
|
// Config: allow the user to add providers?
|
||||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToAddProvider, this.Id, settingsTable, dryRun);
|
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.AllowUserToAddProvider, this.Id, settingsTable, dryRun);
|
||||||
|
|
||||||
//
|
// Handle configured LLM providers:
|
||||||
// Configured providers:
|
if (!PluginConfigurationObject.TryParse(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, x => x.NextProviderNum, mainTable, this.Id, ref this.configObjects, dryRun))
|
||||||
//
|
|
||||||
if (!mainTable.TryGetValue("LLM_PROVIDERS", out var providersValue) || !providersValue.TryRead<LuaTable>(out var providersTable))
|
|
||||||
{
|
{
|
||||||
message = TB("The LLM_PROVIDERS table does not exist or is not a valid table.");
|
message = TB("At least one configured LLM provider is not valid or could not be parsed, or the LLM_PROVIDERS table does not exist.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle configured chat templates:
|
||||||
|
if (!PluginConfigurationObject.TryParse(PluginConfigurationObjectType.CHAT_TEMPLATE, x => x.ChatTemplates, x => x.NextChatTemplateNum, mainTable, this.Id, ref this.configObjects, dryRun))
|
||||||
|
{
|
||||||
|
message = TB("At least one configured chat template is not valid or could not be parsed, or the CHAT_TEMPLATES table does not exist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
message = string.Empty;
|
message = string.Empty;
|
||||||
var numberProviders = providersTable.ArrayLength;
|
|
||||||
var configuredProviders = new List<Settings.Provider>(numberProviders);
|
|
||||||
for (var i = 1; i <= numberProviders; i++)
|
|
||||||
{
|
|
||||||
var providerLuaTableValue = providersTable[i];
|
|
||||||
if (!providerLuaTableValue.TryRead<LuaTable>(out var providerLuaTable))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The LLM_PROVIDERS table at index {i} is not a valid table.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.TryReadProviderTable(i, providerLuaTable, out var provider))
|
|
||||||
configuredProviders.Add(provider);
|
|
||||||
else
|
|
||||||
LOGGER.LogWarning($"The LLM_PROVIDERS table at index {i} does not contain a valid provider configuration.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Apply the configured providers to the system settings:
|
|
||||||
//
|
|
||||||
#pragma warning disable MWAIS0001
|
|
||||||
foreach (var configuredProvider in configuredProviders)
|
|
||||||
{
|
|
||||||
// The iterating variable is immutable, so we need to create a local copy:
|
|
||||||
var provider = configuredProvider;
|
|
||||||
|
|
||||||
// Store this provider in the config object list:
|
|
||||||
this.configObjects.Add(new()
|
|
||||||
{
|
|
||||||
ConfigPluginId = this.Id,
|
|
||||||
Id = Guid.Parse(provider.Id),
|
|
||||||
Type = PluginConfigurationObjectType.LLM_PROVIDER,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dryRun)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var providerIndex = SETTINGS_MANAGER.ConfigurationData.Providers.FindIndex(p => p.Id == provider.Id);
|
|
||||||
if (providerIndex > -1)
|
|
||||||
{
|
|
||||||
// Case: The provider already exists, we update it:
|
|
||||||
var existingProvider = SETTINGS_MANAGER.ConfigurationData.Providers[providerIndex];
|
|
||||||
provider = provider with { Num = existingProvider.Num }; // Keep the original number
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.Providers[providerIndex] = provider;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Case: The provider does not exist, we add it:
|
|
||||||
provider = provider with { Num = SETTINGS_MANAGER.ConfigurationData.NextProviderNum++ };
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.Providers.Add(provider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning restore MWAIS0001
|
|
||||||
|
|
||||||
//
|
|
||||||
// Configured chat templates:
|
|
||||||
//
|
|
||||||
if (mainTable.TryGetValue("CHAT_TEMPLATES", out var templatesValue) && templatesValue.TryRead<LuaTable>(out var templatesTable))
|
|
||||||
{
|
|
||||||
var numberTemplates = templatesTable.ArrayLength;
|
|
||||||
var configuredTemplates = new List<ChatTemplate>(numberTemplates);
|
|
||||||
for (var i = 1; i <= numberTemplates; i++)
|
|
||||||
{
|
|
||||||
var templateLuaTableValue = templatesTable[i];
|
|
||||||
if (!templateLuaTableValue.TryRead<LuaTable>(out var templateLuaTable))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The CHAT_TEMPLATES table at index {i} is not a valid table.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.TryReadChatTemplateTable(i, templateLuaTable, out var template) && template != ChatTemplate.NO_CHAT_TEMPLATE)
|
|
||||||
configuredTemplates.Add(template);
|
|
||||||
else
|
|
||||||
LOGGER.LogWarning($"The CHAT_TEMPLATES table at index {i} does not contain a valid chat template configuration.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply configured chat templates to the system settings:
|
|
||||||
foreach (var configuredTemplate in configuredTemplates)
|
|
||||||
{
|
|
||||||
// The iterating variable is immutable, so we need to create a local copy:
|
|
||||||
var template = configuredTemplate;
|
|
||||||
|
|
||||||
// Store this provider in the config object list:
|
|
||||||
this.configObjects.Add(new()
|
|
||||||
{
|
|
||||||
ConfigPluginId = this.Id,
|
|
||||||
Id = Guid.Parse(template.Id),
|
|
||||||
Type = PluginConfigurationObjectType.CHAT_TEMPLATE,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dryRun)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var tplIndex = SETTINGS_MANAGER.ConfigurationData.ChatTemplates.FindIndex(t => t.Id == template.Id);
|
|
||||||
if (tplIndex > -1)
|
|
||||||
{
|
|
||||||
// Case: The template already exists, we update it:
|
|
||||||
var existingTemplate = SETTINGS_MANAGER.ConfigurationData.ChatTemplates[tplIndex];
|
|
||||||
template = template with { Num = existingTemplate.Num };
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.ChatTemplates[tplIndex] = template;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Case: The template does not exist, we add it:
|
|
||||||
template = template with { Num = SETTINGS_MANAGER.ConfigurationData.NextChatTemplateNum++ };
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Add(template);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryReadProviderTable(int idx, LuaTable table, out Settings.Provider provider)
|
|
||||||
{
|
|
||||||
provider = default;
|
|
||||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid ID. The ID must be a valid GUID.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("InstanceName", out var instanceNameValue) || !instanceNameValue.TryRead<string>(out var instanceName))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid instance name.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("UsedLLMProvider", out var usedLLMProviderValue) || !usedLLMProviderValue.TryRead<string>(out var usedLLMProviderText) || !Enum.TryParse<LLMProviders>(usedLLMProviderText, true, out var usedLLMProvider))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid LLM provider enum value.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Host", out var hostValue) || !hostValue.TryRead<string>(out var hostText) || !Enum.TryParse<Host>(hostText, true, out var host))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid host enum value.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Hostname", out var hostnameValue) || !hostnameValue.TryRead<string>(out var hostname))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid hostname.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Model", out var modelValue) || !modelValue.TryRead<LuaTable>(out var modelTable))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model table.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.TryReadModelTable(idx, modelTable, out var model))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model configuration.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
provider = new()
|
|
||||||
{
|
|
||||||
Num = 0,
|
|
||||||
Id = id.ToString(),
|
|
||||||
InstanceName = instanceName,
|
|
||||||
UsedLLMProvider = usedLLMProvider,
|
|
||||||
Model = model,
|
|
||||||
IsSelfHosted = usedLLMProvider is LLMProviders.SELF_HOSTED,
|
|
||||||
IsEnterpriseConfiguration = true,
|
|
||||||
EnterpriseConfigurationPluginId = this.Id,
|
|
||||||
Hostname = hostname,
|
|
||||||
Host = host
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryReadModelTable(int idx, LuaTable table, out Model model)
|
|
||||||
{
|
|
||||||
model = default;
|
|
||||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var id))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model ID.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("DisplayName", out var displayNameValue) || !displayNameValue.TryRead<string>(out var displayName))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured provider {idx} does not contain a valid model display name.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
model = new(id, displayName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryReadChatTemplateTable(int idx, LuaTable table, out ChatTemplate template)
|
|
||||||
{
|
|
||||||
template = ChatTemplate.NO_CHAT_TEMPLATE;
|
|
||||||
if (!table.TryGetValue("Id", out var idValue) || !idValue.TryRead<string>(out var idText) || !Guid.TryParse(idText, out var id))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid ID. The ID must be a valid GUID.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Name", out var nameValue) || !nameValue.TryRead<string>(out var name))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid name.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!table.TryGetValue("SystemPrompt", out var sysPromptValue) || !sysPromptValue.TryRead<string>(out var systemPrompt))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The configured chat template {idx} does not contain a valid system prompt.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var predefinedUserPrompt = string.Empty;
|
|
||||||
if (table.TryGetValue("PredefinedUserPrompt", out var preUserValue) && preUserValue.TryRead<string>(out var preUser))
|
|
||||||
predefinedUserPrompt = preUser;
|
|
||||||
|
|
||||||
var allowProfileUsage = false;
|
|
||||||
if (table.TryGetValue("AllowProfileUsage", out var allowProfileValue) && allowProfileValue.TryRead<bool>(out var allow))
|
|
||||||
allowProfileUsage = allow;
|
|
||||||
|
|
||||||
template = new()
|
|
||||||
{
|
|
||||||
Num = 0,
|
|
||||||
Id = id.ToString(),
|
|
||||||
Name = name,
|
|
||||||
SystemPrompt = systemPrompt,
|
|
||||||
PredefinedUserPrompt = predefinedUserPrompt,
|
|
||||||
ExampleConversation = ParseExampleConversation(idx, table),
|
|
||||||
AllowProfileUsage = allowProfileUsage,
|
|
||||||
IsEnterpriseConfiguration = true,
|
|
||||||
EnterpriseConfigurationPluginId = this.Id
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<ContentBlock> ParseExampleConversation(int idx, LuaTable table)
|
|
||||||
{
|
|
||||||
var exampleConversation = new List<ContentBlock>();
|
|
||||||
if (!table.TryGetValue("ExampleConversation", out var exConvValue) || !exConvValue.TryRead<LuaTable>(out var exConvTable))
|
|
||||||
return exampleConversation;
|
|
||||||
|
|
||||||
var numBlocks = exConvTable.ArrayLength;
|
|
||||||
for (var j = 1; j <= numBlocks; j++)
|
|
||||||
{
|
|
||||||
var blockValue = exConvTable[j];
|
|
||||||
if (!blockValue.TryRead<LuaTable>(out var blockTable))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} is not a valid table.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blockTable.TryGetValue("Role", out var roleValue) || !roleValue.TryRead<string>(out var roleText) || !Enum.TryParse<ChatRole>(roleText, true, out var parsedRole))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} does not contain a valid role.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blockTable.TryGetValue("Content", out var contentValue) || !contentValue.TryRead<string>(out var content))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} does not contain a valid content message.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(content))
|
|
||||||
{
|
|
||||||
LOGGER.LogWarning($"The ExampleConversation entry {j} in chat template {idx} contains an empty content message.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
exampleConversation.Add(new ContentBlock
|
|
||||||
{
|
|
||||||
Time = DateTimeOffset.UtcNow,
|
|
||||||
Role = parsedRole,
|
|
||||||
Content = new ContentText { Text = content },
|
|
||||||
ContentType = ContentType.TEXT,
|
|
||||||
HideFromUser = true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return exampleConversation;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,3 +1,10 @@
|
|||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
|
using Lua;
|
||||||
|
|
||||||
namespace AIStudio.Tools.PluginSystem;
|
namespace AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -6,6 +13,9 @@ namespace AIStudio.Tools.PluginSystem;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record PluginConfigurationObject
|
public sealed record PluginConfigurationObject
|
||||||
{
|
{
|
||||||
|
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||||
|
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<PluginConfigurationObject>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The id of the configuration plugin to which this configuration object belongs.
|
/// The id of the configuration plugin to which this configuration object belongs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,4 +30,178 @@ public sealed record PluginConfigurationObject
|
|||||||
/// The type of the configuration object.
|
/// The type of the configuration object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required PluginConfigurationObjectType Type { get; init; } = PluginConfigurationObjectType.NONE;
|
public required PluginConfigurationObjectType Type { get; init; } = PluginConfigurationObjectType.NONE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses Lua table entries into configuration objects of the specified type, populating the
|
||||||
|
/// provided list with results.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TClass">The type of configuration object to parse, which must
|
||||||
|
/// inherit from <see cref="ConfigurationBaseObject"/>.</typeparam>
|
||||||
|
/// <param name="configObjectType">The type of configuration object to process, as specified
|
||||||
|
/// in <see cref="PluginConfigurationObjectType"/>.</param>
|
||||||
|
/// <param name="configObjectSelection">An expression to retrieve existing configuration objects from
|
||||||
|
/// the main configuration data.</param>
|
||||||
|
/// <param name="nextConfigObjectNumSelection">An expression to retrieve the next available configuration
|
||||||
|
/// object number from the main configuration data.</param>
|
||||||
|
/// <param name="mainTable">The Lua table containing entries to parse into configuration objects.</param>
|
||||||
|
/// <param name="configPluginId">The unique identifier of the plugin associated with the configuration
|
||||||
|
/// objects being parsed.</param>
|
||||||
|
/// <param name="configObjects">The list to populate with the parsed configuration objects.
|
||||||
|
/// This parameter is passed by reference.</param>
|
||||||
|
/// <param name="dryRun">Specifies whether to perform the operation as a dry run, where changes
|
||||||
|
/// are not persisted.</param>
|
||||||
|
/// <returns>Returns true if parsing succeeds and configuration objects are added
|
||||||
|
/// to the list; otherwise, false.</returns>
|
||||||
|
public static bool TryParse<TClass>(
|
||||||
|
PluginConfigurationObjectType configObjectType,
|
||||||
|
Expression<Func<Data, List<TClass>>> configObjectSelection,
|
||||||
|
Expression<Func<Data, uint>> nextConfigObjectNumSelection,
|
||||||
|
LuaTable mainTable,
|
||||||
|
Guid configPluginId,
|
||||||
|
ref List<PluginConfigurationObject> configObjects,
|
||||||
|
bool dryRun
|
||||||
|
) where TClass : ConfigurationBaseObject
|
||||||
|
{
|
||||||
|
var luaTableName = configObjectType switch
|
||||||
|
{
|
||||||
|
PluginConfigurationObjectType.LLM_PROVIDER => "LLM_PROVIDERS",
|
||||||
|
PluginConfigurationObjectType.CHAT_TEMPLATE => "CHAT_TEMPLATES",
|
||||||
|
PluginConfigurationObjectType.DATA_SOURCE => "DATA_SOURCES",
|
||||||
|
PluginConfigurationObjectType.EMBEDDING_PROVIDER => "EMBEDDING_PROVIDERS",
|
||||||
|
PluginConfigurationObjectType.PROFILE => "PROFILES",
|
||||||
|
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (luaTableName is null)
|
||||||
|
{
|
||||||
|
LOG.LogError($"The configuration object type '{configObjectType}' is not supported yet.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainTable.TryGetValue(luaTableName, out var luaValue) || !luaValue.TryRead<LuaTable>(out var luaTable))
|
||||||
|
{
|
||||||
|
LOG.LogWarning($"The {luaTableName} table does not exist or is not a valid table.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var storedObjects = configObjectSelection.Compile()(SETTINGS_MANAGER.ConfigurationData);
|
||||||
|
var numberObjects = luaTable.ArrayLength;
|
||||||
|
ThreadSafeRandom? random = null;
|
||||||
|
for (var i = 1; i <= numberObjects; i++)
|
||||||
|
{
|
||||||
|
var luaObjectTableValue = luaTable[i];
|
||||||
|
if (!luaObjectTableValue.TryRead<LuaTable>(out var luaObjectTable))
|
||||||
|
{
|
||||||
|
LOG.LogWarning($"The {luaObjectTable} table at index {i} is not a valid table.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (wasParsingSuccessful, configObject) = configObjectType switch
|
||||||
|
{
|
||||||
|
PluginConfigurationObjectType.LLM_PROVIDER => (Settings.Provider.TryParseProviderTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != Settings.Provider.NONE, configurationObject),
|
||||||
|
PluginConfigurationObjectType.CHAT_TEMPLATE => (ChatTemplate.TryParseChatTemplateTable(i, luaObjectTable, configPluginId, out var configurationObject) && configurationObject != ChatTemplate.NO_CHAT_TEMPLATE, configurationObject),
|
||||||
|
|
||||||
|
_ => (false, NoConfigurationObject.INSTANCE)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (wasParsingSuccessful)
|
||||||
|
{
|
||||||
|
// Store it in the config object list:
|
||||||
|
configObjects.Add(new()
|
||||||
|
{
|
||||||
|
ConfigPluginId = configPluginId,
|
||||||
|
Id = Guid.Parse(configObject.Id),
|
||||||
|
Type = configObjectType,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dryRun)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var objectIndex = storedObjects.FindIndex(t => t.Id == configObject.Id);
|
||||||
|
|
||||||
|
// Case: The object already exists, we update it:
|
||||||
|
if (objectIndex > -1)
|
||||||
|
{
|
||||||
|
var existingObject = storedObjects[objectIndex];
|
||||||
|
configObject = configObject with { Num = existingObject.Num };
|
||||||
|
storedObjects[objectIndex] = (TClass)configObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case: The object does not exist, we have to add it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (nextConfigObjectNumSelection.TryIncrement(SETTINGS_MANAGER.ConfigurationData, IncrementType.POST) is { Success: true, UpdatedValue: var nextNum })
|
||||||
|
{
|
||||||
|
// Case: Increment the next number was successful
|
||||||
|
configObject = configObject with { Num = nextNum };
|
||||||
|
storedObjects.Add((TClass)configObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Case: The next number could not be incremented, we use a random number
|
||||||
|
random ??= new ThreadSafeRandom();
|
||||||
|
configObject = configObject with { Num = (uint)random.Next(500_000, 1_000_000) };
|
||||||
|
storedObjects.Add((TClass)configObject);
|
||||||
|
LOG.LogWarning($"The next number for the configuration object '{configObject.Name}' (id={configObject.Id}) could not be incremented. Using a random number instead.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LOG.LogWarning($"The {luaObjectTable} table at index {i} does not contain a valid chat template configuration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans up configuration objects of a specified type that are no longer associated with any available plugin.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TClass">The type of configuration object to clean up.</typeparam>
|
||||||
|
/// <param name="configObjectType">The type of configuration object to process.</param>
|
||||||
|
/// <param name="configObjectSelection">A selection expression to retrieve the configuration objects from the main configuration.</param>
|
||||||
|
/// <param name="availablePlugins">A list of currently available plugins.</param>
|
||||||
|
/// <param name="configObjectList">A list of all existing configuration objects.</param>
|
||||||
|
/// <returns>Returns true if the configuration was altered during cleanup; otherwise, false.</returns>
|
||||||
|
public static bool CleanLeftOverConfigurationObjects<TClass>(
|
||||||
|
PluginConfigurationObjectType configObjectType,
|
||||||
|
Expression<Func<Data, List<TClass>>> configObjectSelection,
|
||||||
|
IList<IAvailablePlugin> availablePlugins,
|
||||||
|
IList<PluginConfigurationObject> configObjectList) where TClass : IConfigurationObject
|
||||||
|
{
|
||||||
|
var configuredObjects = configObjectSelection.Compile()(SETTINGS_MANAGER.ConfigurationData);
|
||||||
|
var leftOverObjects = new List<TClass>();
|
||||||
|
foreach (var configuredObject in configuredObjects)
|
||||||
|
{
|
||||||
|
if(!configuredObject.IsEnterpriseConfiguration)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var configObjectSourcePluginId = configuredObject.EnterpriseConfigurationPluginId;
|
||||||
|
if(configObjectSourcePluginId == Guid.Empty)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var templateSourcePlugin = availablePlugins.FirstOrDefault(plugin => plugin.Id == configObjectSourcePluginId);
|
||||||
|
if(templateSourcePlugin is null)
|
||||||
|
{
|
||||||
|
LOG.LogWarning($"The configured object '{configuredObject.Name}' (id={configuredObject.Id}) is based on a plugin that is not available anymore. Removing the chat template from the settings.");
|
||||||
|
leftOverObjects.Add(configuredObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!configObjectList.Any(configObject =>
|
||||||
|
configObject.Type == configObjectType &&
|
||||||
|
configObject.ConfigPluginId == configObjectSourcePluginId &&
|
||||||
|
configObject.Id.ToString() == configuredObject.Id))
|
||||||
|
{
|
||||||
|
LOG.LogWarning($"The configured object '{configuredObject.Name}' (id={configuredObject.Id}) is not present in the configuration plugin anymore. Removing the chat template from the settings.");
|
||||||
|
leftOverObjects.Add(configuredObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove collected items after enumeration to avoid modifying the collection during iteration:
|
||||||
|
var wasConfigurationChanged = leftOverObjects.Count > 0;
|
||||||
|
foreach (var item in leftOverObjects.Distinct())
|
||||||
|
configuredObjects.Remove(item);
|
||||||
|
|
||||||
|
return wasConfigurationChanged;
|
||||||
|
}
|
||||||
}
|
}
|
@ -92,10 +92,10 @@ public static partial class PluginFactory
|
|||||||
|
|
||||||
case { IsValid: false }:
|
case { IsValid: false }:
|
||||||
LOG.LogError($"Was not able to load plugin '{pluginMainFile}', because the Lua code is not a valid AI Studio plugin. There are {plugin.Issues.Count()} issues to fix. First issue is: {plugin.Issues.FirstOrDefault()}");
|
LOG.LogError($"Was not able to load plugin '{pluginMainFile}', because the Lua code is not a valid AI Studio plugin. There are {plugin.Issues.Count()} issues to fix. First issue is: {plugin.Issues.FirstOrDefault()}");
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
foreach (var pluginIssue in plugin.Issues)
|
foreach (var pluginIssue in plugin.Issues)
|
||||||
LOG.LogError($"Plugin issue: {pluginIssue}");
|
LOG.LogError($"Plugin issue: {pluginIssue}");
|
||||||
#endif
|
#endif
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case { IsMaintained: false }:
|
case { IsMaintained: false }:
|
||||||
@ -125,89 +125,24 @@ public static partial class PluginFactory
|
|||||||
|
|
||||||
//
|
//
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Next, we have to clean up our settings. It is possible that a configuration plugin was removed.
|
// Next, we have to clean up our settings. It is possible
|
||||||
// We have to remove the related settings as well:
|
// that a configuration plugin was removed. We have to
|
||||||
|
// remove the related settings as well:
|
||||||
// =========================================================
|
// =========================================================
|
||||||
//
|
//
|
||||||
var wasConfigurationChanged = false;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check LLM providers:
|
// Check LLM providers:
|
||||||
//
|
var wasConfigurationChanged = PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, AVAILABLE_PLUGINS, configObjectList);
|
||||||
#pragma warning disable MWAIS0001
|
|
||||||
var configuredProviders = SETTINGS_MANAGER.ConfigurationData.Providers.ToList();
|
|
||||||
foreach (var configuredProvider in configuredProviders)
|
|
||||||
{
|
|
||||||
if(!configuredProvider.IsEnterpriseConfiguration)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var providerSourcePluginId = configuredProvider.EnterpriseConfigurationPluginId;
|
|
||||||
if(providerSourcePluginId == Guid.Empty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var providerSourcePlugin = AVAILABLE_PLUGINS.FirstOrDefault(plugin => plugin.Id == providerSourcePluginId);
|
|
||||||
if(providerSourcePlugin is null)
|
|
||||||
{
|
|
||||||
LOG.LogWarning($"The configured LLM provider '{configuredProvider.InstanceName}' (id={configuredProvider.Id}) is based on a plugin that is not available anymore. Removing the provider from the settings.");
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.Providers.Remove(configuredProvider);
|
|
||||||
wasConfigurationChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!configObjectList.Any(configObject =>
|
|
||||||
configObject.Type is PluginConfigurationObjectType.LLM_PROVIDER &&
|
|
||||||
configObject.ConfigPluginId == providerSourcePluginId &&
|
|
||||||
configObject.Id.ToString() == configuredProvider.Id))
|
|
||||||
{
|
|
||||||
LOG.LogWarning($"The configured LLM provider '{configuredProvider.InstanceName}' (id={configuredProvider.Id}) is not present in the configuration plugin anymore. Removing the provider from the settings.");
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.Providers.Remove(configuredProvider);
|
|
||||||
wasConfigurationChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma warning restore MWAIS0001
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check chat templates:
|
// Check chat templates:
|
||||||
//
|
if(PluginConfigurationObject.CleanLeftOverConfigurationObjects(PluginConfigurationObjectType.CHAT_TEMPLATE, x => x.ChatTemplates, AVAILABLE_PLUGINS, configObjectList))
|
||||||
var configuredTemplates = SETTINGS_MANAGER.ConfigurationData.ChatTemplates.ToList();
|
wasConfigurationChanged = true;
|
||||||
foreach (var configuredTemplate in configuredTemplates)
|
|
||||||
{
|
|
||||||
if(!configuredTemplate.IsEnterpriseConfiguration)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var templateSourcePluginId = configuredTemplate.EnterpriseConfigurationPluginId;
|
|
||||||
if(templateSourcePluginId == Guid.Empty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var templateSourcePlugin = AVAILABLE_PLUGINS.FirstOrDefault(plugin => plugin.Id == templateSourcePluginId);
|
|
||||||
if(templateSourcePlugin is null)
|
|
||||||
{
|
|
||||||
LOG.LogWarning($"The configured chat template '{configuredTemplate.Name}' (id={configuredTemplate.Id}) is based on a plugin that is not available anymore. Removing the chat template from the settings.");
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Remove(configuredTemplate);
|
|
||||||
wasConfigurationChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!configObjectList.Any(configObject =>
|
|
||||||
configObject.Type is PluginConfigurationObjectType.CHAT_TEMPLATE &&
|
|
||||||
configObject.ConfigPluginId == templateSourcePluginId &&
|
|
||||||
configObject.Id.ToString() == configuredTemplate.Id))
|
|
||||||
{
|
|
||||||
LOG.LogWarning($"The configured chat template '{configuredTemplate.Name}' (id={configuredTemplate.Id}) is not present in the configuration plugin anymore. Removing the chat template from the settings.");
|
|
||||||
SETTINGS_MANAGER.ConfigurationData.ChatTemplates.Remove(configuredTemplate);
|
|
||||||
wasConfigurationChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
// Check for update behavior:
|
||||||
// ==========================================================
|
|
||||||
// Check all possible settings:
|
|
||||||
// ==========================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
// Check for updates, and if so, how often?
|
|
||||||
if(ManagedConfiguration.IsConfigurationLeftOver<DataApp, UpdateBehavior>(x => x.App, x => x.UpdateBehavior, AVAILABLE_PLUGINS))
|
if(ManagedConfiguration.IsConfigurationLeftOver<DataApp, UpdateBehavior>(x => x.App, x => x.UpdateBehavior, AVAILABLE_PLUGINS))
|
||||||
wasConfigurationChanged = true;
|
wasConfigurationChanged = true;
|
||||||
|
|
||||||
// Allow the user to add providers?
|
// Check for users allowed to added providers:
|
||||||
if(ManagedConfiguration.IsConfigurationLeftOver<DataApp, bool>(x => x.App, x => x.AllowUserToAddProvider, AVAILABLE_PLUGINS))
|
if(ManagedConfiguration.IsConfigurationLeftOver<DataApp, bool>(x => x.App, x => x.AllowUserToAddProvider, AVAILABLE_PLUGINS))
|
||||||
wasConfigurationChanged = true;
|
wasConfigurationChanged = true;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ public sealed class DataSourceService
|
|||||||
// does not mean anything. We cannot filter the data sources by any means.
|
// does not mean anything. We cannot filter the data sources by any means.
|
||||||
// We return an empty list. Better safe than sorry.
|
// We return an empty list. Better safe than sorry.
|
||||||
//
|
//
|
||||||
if (selectedLLMProvider == default)
|
if (selectedLLMProvider == Settings.Provider.NONE)
|
||||||
{
|
{
|
||||||
this.logger.LogWarning("The selected LLM provider is not set. We cannot filter the data sources by any means.");
|
this.logger.LogWarning("The selected LLM provider is not set. We cannot filter the data sources by any means.");
|
||||||
return new([], []);
|
return new([], []);
|
||||||
|
Loading…
Reference in New Issue
Block a user