diff --git a/app/MindWork AI Studio/Components/Pages/Chat.razor.cs b/app/MindWork AI Studio/Components/Pages/Chat.razor.cs
index 53c22aa0..08275bd1 100644
--- a/app/MindWork AI Studio/Components/Pages/Chat.razor.cs
+++ b/app/MindWork AI Studio/Components/Pages/Chat.razor.cs
@@ -101,7 +101,7 @@ public partial class Chat : 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.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread);
+ await aiText.CreateFromProviderAsync(this.selectedProvider.UsedProvider.CreateProvider(this.selectedProvider.InstanceName, this.selectedProvider.Hostname), this.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread);
// Disable the stream state:
this.isStreaming = false;
diff --git a/app/MindWork AI Studio/Components/Pages/Settings.razor b/app/MindWork AI Studio/Components/Pages/Settings.razor
index cff1db0b..2a9414e4 100644
--- a/app/MindWork AI Studio/Components/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Components/Pages/Settings.razor
@@ -25,7 +25,12 @@
@context.Num
@context.InstanceName
@context.UsedProvider
- @context.Model
+
+ @if(context.UsedProvider is not Providers.SELF_HOSTED)
+ @context.Model
+ else
+ @("as selected by provider")
+
Open Dashboard
diff --git a/app/MindWork AI Studio/Components/Pages/Settings.razor.cs b/app/MindWork AI Studio/Components/Pages/Settings.razor.cs
index d1b32517..e6df7480 100644
--- a/app/MindWork AI Studio/Components/Pages/Settings.razor.cs
+++ b/app/MindWork AI Studio/Components/Pages/Settings.razor.cs
@@ -50,6 +50,8 @@ public partial class Settings : ComponentBase
{ x => x.DataInstanceName, provider.InstanceName },
{ x => x.DataProvider, provider.UsedProvider },
{ x => x.DataModel, provider.Model },
+ { x => x.DataHostname, provider.Hostname },
+ { x => x.IsSelfHosted, provider.IsSelfHosted },
{ x => x.IsEditing, true },
};
diff --git a/app/MindWork AI Studio/Provider/Providers.cs b/app/MindWork AI Studio/Provider/Providers.cs
index 6c8326f2..db99cc3e 100644
--- a/app/MindWork AI Studio/Provider/Providers.cs
+++ b/app/MindWork AI Studio/Provider/Providers.cs
@@ -1,6 +1,7 @@
using AIStudio.Provider.Anthropic;
using AIStudio.Provider.Mistral;
using AIStudio.Provider.OpenAI;
+using AIStudio.Provider.SelfHosted;
namespace AIStudio.Provider;
@@ -10,9 +11,12 @@ namespace AIStudio.Provider;
public enum Providers
{
NONE,
+
OPEN_AI,
ANTHROPIC,
MISTRAL,
+
+ SELF_HOSTED,
}
///
@@ -27,11 +31,14 @@ public static class ExtensionsProvider
/// The human-readable name of the provider.
public static string ToName(this Providers provider) => provider switch
{
+ Providers.NONE => "No provider selected",
+
Providers.OPEN_AI => "OpenAI",
Providers.ANTHROPIC => "Anthropic",
Providers.MISTRAL => "Mistral",
- Providers.NONE => "No provider selected",
+ Providers.SELF_HOSTED => "Self-hosted",
+
_ => "Unknown",
};
@@ -40,13 +47,16 @@ public static class ExtensionsProvider
///
/// The provider value.
/// The used instance name.
+ /// The hostname of the provider.
/// The provider instance.
- public static IProvider CreateProvider(this Providers provider, string instanceName) => provider switch
+ public static IProvider CreateProvider(this Providers provider, string instanceName, string hostname = "http://localhost:1234") => provider switch
{
Providers.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName },
Providers.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName },
Providers.MISTRAL => new ProviderMistral { InstanceName = instanceName },
+ Providers.SELF_HOSTED => new ProviderSelfHosted(hostname) { InstanceName = instanceName },
+
_ => new NoProvider(),
};
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/ProviderDialog.razor b/app/MindWork AI Studio/Settings/ProviderDialog.razor
index 0a58a9e2..82bb5fc1 100644
--- a/app/MindWork AI Studio/Settings/ProviderDialog.razor
+++ b/app/MindWork AI Studio/Settings/ProviderDialog.razor
@@ -12,7 +12,7 @@
@provider
}
- Create account
+ Create account
@* ReSharper disable once CSharpWarnings::CS8974 *@
@@ -20,6 +20,7 @@
T="string"
@bind-Text="@this.dataAPIKey"
Label="API Key"
+ Disabled="@this.IsSelfHostedOrNone"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.VpnKey"
@@ -27,10 +28,22 @@
InputType="InputType.Password"
Validation="@this.ValidatingAPIKey"
/>
+
+
Load
-
+
@foreach (var model in this.availableModels)
{
@model
diff --git a/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs b/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs
index 8e391348..b752cc04 100644
--- a/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs
+++ b/app/MindWork AI Studio/Settings/ProviderDialog.razor.cs
@@ -32,6 +32,18 @@ public partial class ProviderDialog : ComponentBase
[Parameter]
public string DataInstanceName { get; set; } = string.Empty;
+ ///
+ /// The chosen hostname for self-hosted providers.
+ ///
+ [Parameter]
+ public string DataHostname { get; set; } = string.Empty;
+
+ ///
+ /// Is this provider self-hosted?
+ ///
+ [Parameter]
+ public bool IsSelfHosted { get; set; }
+
///
/// The provider to use.
///
@@ -99,7 +111,8 @@ public partial class ProviderDialog : ComponentBase
this.dataAPIKey = requestedSecret.Secret;
// Now, we try to load the list of available models:
- await this.ReloadModels();
+ if(this.DataProvider is not Providers.SELF_HOSTED)
+ await this.ReloadModels();
}
else
{
@@ -142,6 +155,8 @@ public partial class ProviderDialog : ComponentBase
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:
@@ -169,6 +184,9 @@ public partial class ProviderDialog : ComponentBase
private string? ValidatingModel(Model model)
{
+ if(this.DataProvider is Providers.SELF_HOSTED)
+ return null;
+
if (model == default)
return "Please select a model.";
@@ -206,6 +224,9 @@ public partial class ProviderDialog : ComponentBase
private string? ValidatingAPIKey(string apiKey)
{
+ if(this.DataProvider is Providers.SELF_HOSTED)
+ return null;
+
if(!string.IsNullOrWhiteSpace(this.dataAPIKeyStorageIssue))
return this.dataAPIKeyStorageIssue;
@@ -215,9 +236,24 @@ public partial class ProviderDialog : ComponentBase
return null;
}
- private void Cancel() => this.MudDialog.Cancel();
+ private string? ValidatingHostname(string hostname)
+ {
+ if(this.DataProvider != Providers.SELF_HOSTED)
+ return null;
+
+ if(string.IsNullOrWhiteSpace(hostname))
+ return "Please enter a hostname, e.g., http://localhost:1234";
+
+ if(!hostname.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
+ return "The hostname must start with either http:// or https://";
- private bool CanLoadModels => !string.IsNullOrWhiteSpace(this.dataAPIKey) && this.DataProvider != Providers.NONE;
+ if(!Uri.TryCreate(hostname, UriKind.Absolute, out _))
+ return "The hostname is not a valid HTTP(S) URL.";
+
+ return null;
+ }
+
+ private void Cancel() => this.MudDialog.Cancel();
private async Task ReloadModels()
{
@@ -233,6 +269,12 @@ public partial class ProviderDialog : ComponentBase
this.availableModels.Clear();
this.availableModels.AddRange(orderedModels);
}
+
+ private bool CanLoadModels => !string.IsNullOrWhiteSpace(this.dataAPIKey) && this.DataProvider != Providers.NONE && this.DataProvider != Providers.SELF_HOSTED;
+
+ private bool IsCloudProvider => this.DataProvider is not Providers.SELF_HOSTED or Providers.NONE;
+
+ private bool IsSelfHostedOrNone => this.DataProvider is Providers.SELF_HOSTED or Providers.NONE;
private string GetProviderCreationURL() => this.DataProvider switch
{