diff --git a/app/MindWork AI Studio.sln.DotSettings b/app/MindWork AI Studio.sln.DotSettings
index ac41b88..74de15b 100644
--- a/app/MindWork AI Studio.sln.DotSettings
+++ b/app/MindWork AI Studio.sln.DotSettings
@@ -1,4 +1,6 @@
AI
+ LM
MSG
+ True
True
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/AssistantBase.razor.cs b/app/MindWork AI Studio/Components/AssistantBase.razor.cs
index c298861..7fcbd37 100644
--- a/app/MindWork AI Studio/Components/AssistantBase.razor.cs
+++ b/app/MindWork AI Studio/Components/AssistantBase.razor.cs
@@ -26,8 +26,8 @@ public abstract partial class AssistantBase : ComponentBase
private protected virtual RenderFragment? Body => null;
protected static readonly Dictionary USER_INPUT_ATTRIBUTES = new();
-
- protected AIStudio.Settings.Provider selectedProvider;
+
+ protected AIStudio.Settings.Provider providerSettings;
protected MudForm? form;
protected bool inputIsValid;
@@ -109,6 +109,6 @@ public abstract partial class AssistantBase : ComponentBase
// Use the selected provider to get the AI response.
// By awaiting this line, we wait for the entire
// content to be streamed.
- await aiText.CreateFromProviderAsync(this.selectedProvider.UsedProvider.CreateProvider(this.selectedProvider.InstanceName, this.selectedProvider.Hostname), this.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread);
+ await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(), this.JsRuntime, this.SettingsManager, this.providerSettings.Model, this.chatThread);
}
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/Pages/Chat.razor b/app/MindWork AI Studio/Components/Pages/Chat.razor
index edd8551..d56106a 100644
--- a/app/MindWork AI Studio/Components/Pages/Chat.razor
+++ b/app/MindWork AI Studio/Components/Pages/Chat.razor
@@ -15,7 +15,7 @@
}
-
+
@foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
{
diff --git a/app/MindWork AI Studio/Components/Pages/Chat.razor.cs b/app/MindWork AI Studio/Components/Pages/Chat.razor.cs
index 6707637..09dd708 100644
--- a/app/MindWork AI Studio/Components/Pages/Chat.razor.cs
+++ b/app/MindWork AI Studio/Components/Pages/Chat.razor.cs
@@ -32,7 +32,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Bottom;
private static readonly Dictionary USER_INPUT_ATTRIBUTES = new();
- private AIStudio.Settings.Provider selectedProvider;
+ private AIStudio.Settings.Provider providerSettings;
private ChatThread? chatThread;
private bool hasUnsavedChanges;
private bool isStreaming;
@@ -61,11 +61,11 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
#endregion
- private bool IsProviderSelected => this.selectedProvider.UsedProvider != Providers.NONE;
+ private bool IsProviderSelected => this.providerSettings.UsedProvider != Providers.NONE;
private string ProviderPlaceholder => this.IsProviderSelected ? "Type your input here..." : "Select a provider first";
- private string InputLabel => this.IsProviderSelected ? $"Your Prompt (use selected instance '{this.selectedProvider.InstanceName}', provider '{this.selectedProvider.UsedProvider.ToName()}')" : "Select a provider first";
+ private string InputLabel => this.IsProviderSelected ? $"Your Prompt (use selected instance '{this.providerSettings.InstanceName}', provider '{this.providerSettings.UsedProvider.ToName()}')" : "Select a provider first";
private bool CanThreadBeSaved => this.chatThread is not null && this.chatThread.Blocks.Count > 0;
@@ -151,7 +151,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
// Use the selected provider to get the AI response.
// By awaiting this line, we wait for the entire
// content to be streamed.
- await aiText.CreateFromProviderAsync(this.selectedProvider.UsedProvider.CreateProvider(this.selectedProvider.InstanceName, this.selectedProvider.Hostname), this.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread);
+ await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(), this.JsRuntime, this.SettingsManager, this.providerSettings.Model, this.chatThread);
// Save the chat:
if (this.SettingsManager.ConfigurationData.WorkspaceStorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
diff --git a/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor b/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor
index e3d7b54..02a5993 100644
--- a/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor
+++ b/app/MindWork AI Studio/Components/Pages/IconFinder/AssistantIconFinder.razor
@@ -17,7 +17,7 @@
}
-
+
@foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
{
diff --git a/app/MindWork AI Studio/Components/Pages/Settings.razor b/app/MindWork AI Studio/Components/Pages/Settings.razor
index c8b827b..2becc03 100644
--- a/app/MindWork AI Studio/Components/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Components/Pages/Settings.razor
@@ -1,5 +1,6 @@
@page "/settings"
@using AIStudio.Provider
+@using Host = AIStudio.Provider.SelfHosted.Host
Settings
@@ -26,10 +27,18 @@
@context.InstanceName
@context.UsedProvider
- @if(context.UsedProvider is not Providers.SELF_HOSTED)
+ @if (context.UsedProvider is not Providers.SELF_HOSTED)
+ {
@context.Model
+ }
+ else if (context.UsedProvider is Providers.SELF_HOSTED && context.Host is not Host.LLAMACPP)
+ {
+ @context.Model
+ }
else
+ {
@("as selected by provider")
+ }
diff --git a/app/MindWork AI Studio/Components/Pages/Settings.razor.cs b/app/MindWork AI Studio/Components/Pages/Settings.razor.cs
index e6df748..23c14d3 100644
--- a/app/MindWork AI Studio/Components/Pages/Settings.razor.cs
+++ b/app/MindWork AI Studio/Components/Pages/Settings.razor.cs
@@ -53,6 +53,7 @@ public partial class Settings : ComponentBase
{ x => x.DataHostname, provider.Hostname },
{ x => x.IsSelfHosted, provider.IsSelfHosted },
{ x => x.IsEditing, true },
+ { x => x.DataHost, provider.Host },
};
var dialogReference = await this.DialogService.ShowAsync("Edit Provider", dialogParameters, DialogOptions.FULLSCREEN);
@@ -83,7 +84,7 @@ public partial class Settings : ComponentBase
if (dialogResult.Canceled)
return;
- var providerInstance = provider.UsedProvider.CreateProvider(provider.InstanceName, provider.Hostname);
+ var providerInstance = provider.CreateProvider();
var deleteSecretResponse = await this.SettingsManager.DeleteAPIKey(this.JsRuntime, providerInstance);
if(deleteSecretResponse.Success)
{
diff --git a/app/MindWork AI Studio/Components/Pages/TextSummarizer/AssistantTextSummarizer.razor b/app/MindWork AI Studio/Components/Pages/TextSummarizer/AssistantTextSummarizer.razor
index 3f6ff48..d290541 100644
--- a/app/MindWork AI Studio/Components/Pages/TextSummarizer/AssistantTextSummarizer.razor
+++ b/app/MindWork AI Studio/Components/Pages/TextSummarizer/AssistantTextSummarizer.razor
@@ -31,7 +31,7 @@
}
-
+
@foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
{
diff --git a/app/MindWork AI Studio/Components/Pages/Translator/AssistantTranslator.razor b/app/MindWork AI Studio/Components/Pages/Translator/AssistantTranslator.razor
index b167317..ac06d21 100644
--- a/app/MindWork AI Studio/Components/Pages/Translator/AssistantTranslator.razor
+++ b/app/MindWork AI Studio/Components/Pages/Translator/AssistantTranslator.razor
@@ -38,7 +38,7 @@ else
}
-
+
@foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
{
diff --git a/app/MindWork AI Studio/Provider/Providers.cs b/app/MindWork AI Studio/Provider/Providers.cs
index db99cc3..3f0d88e 100644
--- a/app/MindWork AI Studio/Provider/Providers.cs
+++ b/app/MindWork AI Studio/Provider/Providers.cs
@@ -45,17 +45,15 @@ public static class ExtensionsProvider
///
/// Creates a new provider instance based on the provider value.
///
- /// The provider value.
- /// The used instance name.
- /// The hostname of the provider.
+ /// The provider settings.
/// The provider instance.
- public static IProvider CreateProvider(this Providers provider, string instanceName, string hostname = "http://localhost:1234") => provider switch
+ public static IProvider CreateProvider(this Settings.Provider providerSettings) => providerSettings.UsedProvider switch
{
- Providers.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName },
- Providers.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName },
- Providers.MISTRAL => new ProviderMistral { InstanceName = instanceName },
+ Providers.OPEN_AI => new ProviderOpenAI { InstanceName = providerSettings.InstanceName },
+ Providers.ANTHROPIC => new ProviderAnthropic { InstanceName = providerSettings.InstanceName },
+ Providers.MISTRAL => new ProviderMistral { InstanceName = providerSettings.InstanceName },
- Providers.SELF_HOSTED => new ProviderSelfHosted(hostname) { InstanceName = instanceName },
+ Providers.SELF_HOSTED => new ProviderSelfHosted(providerSettings) { InstanceName = providerSettings.InstanceName },
_ => new NoProvider(),
};
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/Host.cs b/app/MindWork AI Studio/Provider/SelfHosted/Host.cs
new file mode 100644
index 0000000..0e3a26d
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/SelfHosted/Host.cs
@@ -0,0 +1,42 @@
+namespace AIStudio.Provider.SelfHosted;
+
+public enum Host
+{
+ NONE,
+
+ LM_STUDIO,
+ LLAMACPP,
+ OLLAMA,
+}
+
+public static class HostExtensions
+{
+ public static string Name(this Host host) => host switch
+ {
+ Host.NONE => "None",
+
+ Host.LM_STUDIO => "LM Studio",
+ Host.LLAMACPP => "llama.cpp",
+ Host.OLLAMA => "ollama",
+
+ _ => "Unknown",
+ };
+
+ public static string BaseURL(this Host host) => host switch
+ {
+ Host.LM_STUDIO => "/v1/",
+ Host.LLAMACPP => "/v1/",
+ Host.OLLAMA => "/v1/",
+
+ _ => "/v1/",
+ };
+
+ public static string ChatURL(this Host host) => host switch
+ {
+ Host.LM_STUDIO => "chat/completions",
+ Host.LLAMACPP => "chat/completions",
+ Host.OLLAMA => "chat/completions",
+
+ _ => "chat/completions",
+ };
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs b/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs
index 4093224..7790d2e 100644
--- a/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs
+++ b/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs
@@ -8,7 +8,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.SelfHosted;
-public sealed class ProviderSelfHosted(string hostname) : BaseProvider($"{hostname}/v1/"), IProvider
+public sealed class ProviderSelfHosted(Settings.Provider provider) : BaseProvider($"{provider.Hostname}{provider.Host.BaseURL()}"), IProvider
{
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
{
@@ -33,7 +33,7 @@ public sealed class ProviderSelfHosted(string hostname) : BaseProvider($"{hostna
// Prepare the OpenAI HTTP chat request:
var providerChatRequest = JsonSerializer.Serialize(new ChatRequest
{
- Model = (await this.GetTextModels(jsRuntime, settings, token: token)).First().Id,
+ Model = chatModel.Id,
// Build the messages:
// - First of all the system prompt
@@ -62,7 +62,7 @@ public sealed class ProviderSelfHosted(string hostname) : BaseProvider($"{hostna
}, JSON_SERIALIZER_OPTIONS);
// Build the HTTP post request:
- var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
+ var request = new HttpRequestMessage(HttpMethod.Post, provider.Host.ChatURL());
// Set the content:
request.Content = new StringContent(providerChatRequest, Encoding.UTF8, "application/json");
@@ -137,17 +137,33 @@ public sealed class ProviderSelfHosted(string hostname) : BaseProvider($"{hostna
public async Task> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
{
- var request = new HttpRequestMessage(HttpMethod.Get, "models");
- var response = await this.httpClient.SendAsync(request, token);
- if(!response.IsSuccessStatusCode)
- return [];
+ try
+ {
+ switch (provider.Host)
+ {
+ case Host.LLAMACPP:
+ // Right now, llama.cpp only supports one model.
+ // There is no API to list the model(s).
+ return [ new Provider.Model("as configured by llama.cpp") ];
+
+ case Host.LM_STUDIO:
+ case Host.OLLAMA:
+ var lmStudioRequest = new HttpRequestMessage(HttpMethod.Get, "models");
+ var lmStudioResponse = await this.httpClient.SendAsync(lmStudioRequest, token);
+ if(!lmStudioResponse.IsSuccessStatusCode)
+ return [];
- var modelResponse = await response.Content.ReadFromJsonAsync(token);
- if (modelResponse.Data.Length > 1)
- Console.WriteLine("Warning: multiple models found; using the first one.");
-
- var firstModel = modelResponse.Data.First();
- return [ new Provider.Model(firstModel.Id) ];
+ var lmStudioModelResponse = await lmStudioResponse.Content.ReadFromJsonAsync(token);
+ return lmStudioModelResponse.Data.Select(n => new Provider.Model(n.Id));
+ }
+
+ return [];
+ }
+ catch(Exception e)
+ {
+ Console.WriteLine($"Failed to load text models from self-hosted provider: {e.Message}");
+ return [];
+ }
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
diff --git a/app/MindWork AI Studio/Settings/Data.cs b/app/MindWork AI Studio/Settings/Data.cs
index e4efb07..ea77487 100644
--- a/app/MindWork AI Studio/Settings/Data.cs
+++ b/app/MindWork AI Studio/Settings/Data.cs
@@ -9,7 +9,7 @@ public sealed class Data
/// The version of the settings file. Allows us to upgrade the settings
/// when a new version is available.
///
- public Version Version { get; init; } = Version.V2;
+ public Version Version { get; init; } = Version.V3;
///
/// List of configured providers.
diff --git a/app/MindWork AI Studio/Settings/Provider.cs b/app/MindWork AI Studio/Settings/Provider.cs
index 4813326..a59eb9b 100644
--- a/app/MindWork AI Studio/Settings/Provider.cs
+++ b/app/MindWork AI Studio/Settings/Provider.cs
@@ -1,5 +1,7 @@
using AIStudio.Provider;
+using Host = AIStudio.Provider.SelfHosted.Host;
+
namespace AIStudio.Settings;
///
@@ -12,7 +14,15 @@ namespace AIStudio.Settings;
/// Whether the provider is self-hosted.
/// The hostname of the provider. Useful for self-hosted providers.
/// The LLM model to use for chat.
-public readonly record struct Provider(uint Num, string Id, string InstanceName, Providers UsedProvider, Model Model, bool IsSelfHosted = false, string Hostname = "http://localhost:1234")
+public readonly record struct Provider(
+ uint Num,
+ string Id,
+ string InstanceName,
+ Providers UsedProvider,
+ Model Model,
+ bool IsSelfHosted = false,
+ string Hostname = "http://localhost:1234",
+ Host Host = Host.NONE)
{
#region Overrides of ValueType
@@ -24,7 +34,7 @@ public readonly record struct Provider(uint Num, string Id, string InstanceName,
public override string ToString()
{
if(this.IsSelfHosted)
- return $"{this.InstanceName} ({this.UsedProvider.ToName()}, {this.Hostname}, {this.Model})";
+ return $"{this.InstanceName} ({this.UsedProvider.ToName()}, {this.Host}, {this.Hostname}, {this.Model})";
return $"{this.InstanceName} ({this.UsedProvider.ToName()}, {this.Model})";
}
diff --git a/app/MindWork AI Studio/Settings/ProviderDialog.razor b/app/MindWork AI Studio/Settings/ProviderDialog.razor
index 82bb5fc..e83740e 100644
--- a/app/MindWork AI Studio/Settings/ProviderDialog.razor
+++ b/app/MindWork AI Studio/Settings/ProviderDialog.razor
@@ -1,4 +1,5 @@
@using AIStudio.Provider
+@using AIStudio.Provider.SelfHosted
@using MudBlazor
@@ -39,11 +40,19 @@
AdornmentIcon="@Icons.Material.Filled.Dns"
AdornmentColor="Color.Info"
Validation="@this.ValidatingHostname"
+ UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
+
+ @foreach (Host host in Enum.GetValues(typeof(Host)))
+ {
+ @host.Name()
+ }
+
+
- Load
-
+ Load
+
@foreach (var model in this.availableModels)
{
@model
@@ -61,7 +70,7 @@
AdornmentIcon="@Icons.Material.Filled.Lightbulb"
AdornmentColor="Color.Info"
Validation="@this.ValidatingInstanceName"
- UserAttributes="@INSTANCE_NAME_ATTRIBUTES"
+ UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
diff --git a/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs b/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs
index 3febabc..edd00e9 100644
--- a/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs
+++ b/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs
@@ -4,6 +4,8 @@ using AIStudio.Provider;
using Microsoft.AspNetCore.Components;
+using Host = AIStudio.Provider.SelfHosted.Host;
+
namespace AIStudio.Settings;
///
@@ -38,6 +40,12 @@ public partial class ProviderDialog : ComponentBase
[Parameter]
public string DataHostname { get; set; } = string.Empty;
+ ///
+ /// The local host to use, e.g., llama.cpp.
+ ///
+ [Parameter]
+ public Host DataHost { get; set; } = Host.NONE;
+
///
/// Is this provider self-hosted?
///
@@ -68,7 +76,7 @@ public partial class ProviderDialog : ComponentBase
[Inject]
private IJSRuntime JsRuntime { get; set; } = null!;
- private static readonly Dictionary INSTANCE_NAME_ATTRIBUTES = new();
+ private static readonly Dictionary SPELLCHECK_ATTRIBUTES = new();
///
/// The list of used instance names. We need this to check for uniqueness.
@@ -85,13 +93,25 @@ public partial class ProviderDialog : ComponentBase
private MudForm form = null!;
private readonly List availableModels = new();
+
+ private Provider CreateProviderSettings() => new()
+ {
+ Num = this.DataNum,
+ Id = this.DataId,
+ InstanceName = this.DataInstanceName,
+ UsedProvider = this.DataProvider,
+ Model = this.DataModel,
+ IsSelfHosted = this.DataProvider is Providers.SELF_HOSTED,
+ Hostname = this.DataHostname,
+ Host = this.DataHost,
+ };
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
// Configure the spellchecking for the instance name input:
- this.SettingsManager.InjectSpellchecking(INSTANCE_NAME_ATTRIBUTES);
+ this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
// Load the used instance names:
this.UsedInstanceNames = this.SettingsManager.ConfigurationData.Providers.Select(x => x.InstanceName.ToLowerInvariant()).ToList();
@@ -100,9 +120,24 @@ public partial class ProviderDialog : ComponentBase
if(this.IsEditing)
{
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
- var provider = this.DataProvider.CreateProvider(this.DataInstanceName);
- if(provider is NoProvider)
+
+ //
+ // We cannot load the API key for self-hosted providers:
+ //
+ if (this.DataProvider is Providers.SELF_HOSTED)
+ {
+ await this.ReloadModels();
+ await base.OnInitializedAsync();
return;
+ }
+
+ var loadedProviderSettings = this.CreateProviderSettings();
+ var provider = loadedProviderSettings.CreateProvider();
+ if(provider is NoProvider)
+ {
+ await base.OnInitializedAsync();
+ return;
+ }
// Load the API key:
var requestedSecret = await this.SettingsManager.GetAPIKey(this.JsRuntime, provider);
@@ -111,8 +146,7 @@ public partial class ProviderDialog : ComponentBase
this.dataAPIKey = requestedSecret.Secret;
// Now, we try to load the list of available models:
- if(this.DataProvider is not Providers.SELF_HOSTED)
- await this.ReloadModels();
+ await this.ReloadModels();
}
else
{
@@ -148,30 +182,23 @@ public partial class ProviderDialog : ComponentBase
// Use the data model to store the provider.
// We just return this data to the parent component:
- var addedProvider = new Provider
+ var addedProviderSettings = this.CreateProviderSettings();
+ if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED)
{
- Num = this.DataNum,
- Id = this.DataId,
- InstanceName = this.DataInstanceName,
- UsedProvider = this.DataProvider,
- Model = this.DataModel,
- IsSelfHosted = this.DataProvider is Providers.SELF_HOSTED,
- Hostname = this.DataHostname,
- };
-
- // We need to instantiate the provider to store the API key:
- var provider = this.DataProvider.CreateProvider(this.DataInstanceName);
+ // We need to instantiate the provider to store the API key:
+ var provider = addedProviderSettings.CreateProvider();
- // Store the API key in the OS secure storage:
- var storeResponse = await this.SettingsManager.SetAPIKey(this.JsRuntime, provider, this.dataAPIKey);
- if (!storeResponse.Success)
- {
- this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
- await this.form.Validate();
- return;
+ // Store the API key in the OS secure storage:
+ var storeResponse = await this.SettingsManager.SetAPIKey(this.JsRuntime, provider, this.dataAPIKey);
+ if (!storeResponse.Success)
+ {
+ this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
+ await this.form.Validate();
+ return;
+ }
}
-
- this.MudDialog.Close(DialogResult.Ok(addedProvider));
+
+ this.MudDialog.Close(DialogResult.Ok(addedProviderSettings));
}
private string? ValidatingProvider(Providers provider)
@@ -182,9 +209,20 @@ public partial class ProviderDialog : ComponentBase
return null;
}
+ private string? ValidatingHost(Host host)
+ {
+ if(this.DataProvider is not Providers.SELF_HOSTED)
+ return null;
+
+ if (host == Host.NONE)
+ return "Please select a host.";
+
+ return null;
+ }
+
private string? ValidatingModel(Model model)
{
- if(this.DataProvider is Providers.SELF_HOSTED)
+ if(this.DataProvider is Providers.SELF_HOSTED && this.DataHost == Host.LLAMACPP)
return null;
if (model == default)
@@ -196,7 +234,7 @@ public partial class ProviderDialog : ComponentBase
[GeneratedRegex(@"^[a-zA-Z0-9\-_. ]+$")]
private static partial Regex InstanceNameRegex();
- private static readonly string[] RESERVED_NAMES = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
+ private static readonly string[] RESERVED_NAMES = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"];
private string? ValidatingInstanceName(string instanceName)
{
@@ -270,7 +308,8 @@ public partial class ProviderDialog : ComponentBase
private async Task ReloadModels()
{
- var provider = this.DataProvider.CreateProvider("temp");
+ var currentProviderSettings = this.CreateProviderSettings();
+ var provider = currentProviderSettings.CreateProvider();
if(provider is NoProvider)
return;
@@ -283,11 +322,43 @@ public partial class ProviderDialog : ComponentBase
this.availableModels.AddRange(orderedModels);
}
- private bool CanLoadModels => !string.IsNullOrWhiteSpace(this.dataAPIKey) && this.DataProvider != Providers.NONE && this.DataProvider != Providers.SELF_HOSTED;
-
+ private bool CanLoadModels()
+ {
+ if (this.DataProvider is Providers.SELF_HOSTED)
+ {
+ switch (this.DataHost)
+ {
+ case Host.NONE:
+ return false;
+
+ case Host.LLAMACPP:
+ return false;
+
+ case Host.LM_STUDIO:
+ return true;
+
+ case Host.OLLAMA:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ if(this.DataProvider is Providers.NONE)
+ return false;
+
+ if(string.IsNullOrWhiteSpace(this.dataAPIKey))
+ return false;
+
+ return true;
+ }
+
private bool IsCloudProvider => this.DataProvider is not Providers.SELF_HOSTED;
private bool IsSelfHostedOrNone => this.DataProvider is Providers.SELF_HOSTED or Providers.NONE;
+
+ private bool IsNoneProvider => this.DataProvider is Providers.NONE;
private string GetProviderCreationURL() => this.DataProvider switch
{
diff --git a/app/MindWork AI Studio/Settings/SettingsMigrations.cs b/app/MindWork AI Studio/Settings/SettingsMigrations.cs
index e578378..516a1c1 100644
--- a/app/MindWork AI Studio/Settings/SettingsMigrations.cs
+++ b/app/MindWork AI Studio/Settings/SettingsMigrations.cs
@@ -1,3 +1,5 @@
+using Host = AIStudio.Provider.SelfHosted.Host;
+
namespace AIStudio.Settings;
public static class SettingsMigrations
@@ -7,7 +9,11 @@ public static class SettingsMigrations
switch (previousData.Version)
{
case Version.V1:
- return MigrateFromV1(previousData);
+ previousData = MigrateV1ToV2(previousData);
+ return MigrateV2ToV3(previousData);
+
+ case Version.V2:
+ return MigrateV2ToV3(previousData);
default:
Console.WriteLine("No migration needed.");
@@ -15,7 +21,7 @@ public static class SettingsMigrations
}
}
- private static Data MigrateFromV1(Data previousData)
+ private static Data MigrateV1ToV2(Data previousData)
{
//
// Summary:
@@ -36,4 +42,33 @@ public static class SettingsMigrations
UpdateBehavior = previousData.UpdateBehavior,
};
}
+
+ private static Data MigrateV2ToV3(Data previousData)
+ {
+ //
+ // Summary:
+ // In v2, self-hosted providers had no host (LM Studio, llama.cpp, ollama, etc.)
+ //
+
+ Console.WriteLine("Migrating from v2 to v3...");
+ return new()
+ {
+ Version = Version.V3,
+ Providers = previousData.Providers.Select(provider =>
+ {
+ if(provider.IsSelfHosted)
+ return provider with { Host = Host.LM_STUDIO };
+
+ return provider with { Host = Host.NONE };
+ }).ToList(),
+
+ EnableSpellchecking = previousData.EnableSpellchecking,
+ IsSavingEnergy = previousData.IsSavingEnergy,
+ NextProviderNum = previousData.NextProviderNum,
+ ShortcutSendBehavior = previousData.ShortcutSendBehavior,
+ UpdateBehavior = previousData.UpdateBehavior,
+ WorkspaceStorageBehavior = previousData.WorkspaceStorageBehavior,
+ WorkspaceStorageTemporaryMaintenancePolicy = previousData.WorkspaceStorageTemporaryMaintenancePolicy,
+ };
+ }
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/Version.cs b/app/MindWork AI Studio/Settings/Version.cs
index 04e54ef..cb30052 100644
--- a/app/MindWork AI Studio/Settings/Version.cs
+++ b/app/MindWork AI Studio/Settings/Version.cs
@@ -10,4 +10,5 @@ public enum Version
V1,
V2,
+ V3,
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.8.1.md b/app/MindWork AI Studio/wwwroot/changelog/v0.8.1.md
index 83d09dd..9a7bded 100644
--- a/app/MindWork AI Studio/wwwroot/changelog/v0.8.1.md
+++ b/app/MindWork AI Studio/wwwroot/changelog/v0.8.1.md
@@ -1,2 +1,4 @@
-# v0.8.1, build 163 (2024-07-15 14:56 UTC)
+# v0.8.1, build 163 (2024-07-16 08:28 UTC)
+- Added support for ollama as a self-hosted provider
+- Added support for model selection of self-hosted providers
- Fixed a bug where the spellchecking setting was not applied to assistants
\ No newline at end of file