mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 04:27:21 +00:00
Added a home page prompt for missing API keys of enterprise providers (#1056)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
1f8d72aa45
commit
7d35536c87
29 files changed
+920
-31
No files matched your search
@@ -0,0 +1,63 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// What a configuration plugin tells the user while AI Studio asks for the API key of the providers
|
||||
/// which that plugin marked with "PromptForUserAPIKey".
|
||||
/// </summary>
|
||||
public sealed record DataUserAPIKeyPrompt : ILivePluginContent
|
||||
{
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<DataUserAPIKeyPrompt>();
|
||||
|
||||
/// <summary>
|
||||
/// The stable ID of the prompt. A configuration plugin has at most one, so this is the plugin's ID.
|
||||
/// </summary>
|
||||
public string Id { get; private init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the enterprise configuration plugin that provides this prompt.
|
||||
/// </summary>
|
||||
public Guid EnterpriseConfigurationPluginId { get; private init; } = Guid.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The optional title shown to the user. When empty, AI Studio shows a title of its own.
|
||||
/// </summary>
|
||||
public string Title { get; private init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The Markdown instructions which explain how to obtain the API key.
|
||||
/// </summary>
|
||||
public string Markdown { get; private init; } = string.Empty;
|
||||
|
||||
public static bool TryParseConfiguration(LuaTable table, Guid configPluginId, out DataUserAPIKeyPrompt prompt)
|
||||
{
|
||||
prompt = new DataUserAPIKeyPrompt();
|
||||
if (!table.TryGetValue("Markdown", out var markdownValue) || !markdownValue.TryRead<string>(out var markdown) || string.IsNullOrWhiteSpace(markdown))
|
||||
{
|
||||
LOG.LogWarning("The configured user API key prompt does not contain a valid Markdown field (config plugin id: {ConfigPluginId}).", configPluginId);
|
||||
return false;
|
||||
}
|
||||
|
||||
var title = string.Empty;
|
||||
if (table.TryGetValue("Title", out var titleValue))
|
||||
{
|
||||
if (titleValue.TryRead<string>(out var titleText))
|
||||
title = titleText;
|
||||
else
|
||||
LOG.LogWarning("The configured user API key prompt does not contain a valid Title field. Falling back to the built-in title (config plugin id: {ConfigPluginId}).", configPluginId);
|
||||
}
|
||||
|
||||
prompt = new DataUserAPIKeyPrompt
|
||||
{
|
||||
Id = configPluginId.ToString(),
|
||||
Title = title.Trim(),
|
||||
Markdown = AIStudio.Tools.Markdown.RemoveSharedIndentation(markdown),
|
||||
EnterpriseConfigurationPluginId = configPluginId,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,8 @@ public sealed record EmbeddingProvider(
|
||||
int TokenLimit = 0,
|
||||
bool AllowUserProvidedAPIKey = false,
|
||||
string CustomIconDataUrl = "",
|
||||
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey
|
||||
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE,
|
||||
bool PromptForUserAPIKey = false) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey
|
||||
{
|
||||
public const int DEFAULT_TOKEN_LIMIT = 8192;
|
||||
public const int DEFAULT_EMBEDDING_BATCH_SIZE = 1;
|
||||
@@ -128,9 +129,7 @@ public sealed record EmbeddingProvider(
|
||||
embeddingBatchSize = DEFAULT_EMBEDDING_BATCH_SIZE;
|
||||
}
|
||||
|
||||
var allowUserProvidedApiKey = false;
|
||||
if (table.TryGetValue("AllowUserProvidedAPIKey", out var allowUserProvidedApiKeyValue) && allowUserProvidedApiKeyValue.TryRead<bool>(out var allowUserProvidedApiKeyBool))
|
||||
allowUserProvidedApiKey = allowUserProvidedApiKeyBool;
|
||||
var apiKeyOptions = UserProvidedAPIKeyOptions.Read(table);
|
||||
|
||||
var hfInferenceProvider = HFInferenceProvider.NONE;
|
||||
if (table.TryGetValue("HFInferenceProvider", out var hfInferenceProviderValue) && hfInferenceProviderValue.TryRead<string>(out var hfInferenceProviderText))
|
||||
@@ -166,7 +165,8 @@ public sealed record EmbeddingProvider(
|
||||
TokenizerPath = tokenizerPath,
|
||||
EmbeddingBatchSize = embeddingBatchSize,
|
||||
TokenLimit = tokenLimit,
|
||||
AllowUserProvidedAPIKey = allowUserProvidedApiKey,
|
||||
AllowUserProvidedAPIKey = apiKeyOptions.AllowUserProvidedAPIKey,
|
||||
PromptForUserAPIKey = apiKeyOptions.PromptForUserAPIKey,
|
||||
CustomIconDataUrl = customIconDataUrl,
|
||||
HFInferenceProvider = hfInferenceProvider,
|
||||
};
|
||||
@@ -174,7 +174,7 @@ public sealed record EmbeddingProvider(
|
||||
// Handle an encrypted API key if present. When the user manages their own key for this
|
||||
// embedding provider, we must never enqueue an embedded key: doing so would overwrite the
|
||||
// user's key in the OS keyring on every configuration reload.
|
||||
if (allowUserProvidedApiKey)
|
||||
if (apiKeyOptions.AllowUserProvidedAPIKey)
|
||||
{
|
||||
if (table.TryGetValue("APIKey", out var ignoredApiKeyValue) && ignoredApiKeyValue.TryRead<string>(out var ignoredApiKeyText) && !string.IsNullOrWhiteSpace(ignoredApiKeyText))
|
||||
LOGGER.LogWarning($"The configured embedding provider {idx} sets both AllowUserProvidedAPIKey and an embedded APIKey. Ignoring the embedded key: the user manages their own key for this provider. (Plugin ID: {configPluginId})");
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace AIStudio.Settings;
|
||||
/// <param name="Model">The LLM model to use for chat.</param>
|
||||
/// <param name="AllowUserProvidedAPIKey">When set by a configuration plugin, the user may set their own API key for this otherwise locked, enterprise-managed provider.</param>
|
||||
/// <param name="CustomIconDataUrl">The validated custom SVG icon supplied by a configuration plugin.</param>
|
||||
/// <param name="PromptForUserAPIKey">When set by a configuration plugin, AI Studio asks the user for the API key of this provider on the home page as long as none is stored.</param>
|
||||
public sealed record Provider(
|
||||
uint Num,
|
||||
string Id,
|
||||
@@ -40,7 +41,8 @@ public sealed record Provider(
|
||||
[property: JsonConverter(typeof(DataDirectoryRelativePathConverter))] string TokenizerPath = "",
|
||||
ProviderCapabilityOverrides? CapabilityOverrides = null,
|
||||
bool AllowUserProvidedAPIKey = false,
|
||||
string CustomIconDataUrl = "") : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey
|
||||
string CustomIconDataUrl = "",
|
||||
bool PromptForUserAPIKey = false) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey
|
||||
{
|
||||
private static readonly ILogger<Provider> LOGGER = Program.LOGGER_FACTORY.CreateLogger<Provider>();
|
||||
|
||||
@@ -159,9 +161,7 @@ public sealed record Provider(
|
||||
}
|
||||
var capabilityOverrides = ProviderCapabilityOverrides.TryParseFromLuaTable(idx, table, configPluginId, LOGGER);
|
||||
|
||||
var allowUserProvidedApiKey = false;
|
||||
if (table.TryGetValue("AllowUserProvidedAPIKey", out var allowUserProvidedApiKeyValue) && allowUserProvidedApiKeyValue.TryRead<bool>(out var allowUserProvidedApiKeyBool))
|
||||
allowUserProvidedApiKey = allowUserProvidedApiKeyBool;
|
||||
var apiKeyOptions = UserProvidedAPIKeyOptions.Read(table);
|
||||
|
||||
var customIconDataUrl = string.Empty;
|
||||
if (table.TryGetValue("IconPath", out var iconPathValue))
|
||||
@@ -188,14 +188,15 @@ public sealed record Provider(
|
||||
AdditionalJsonApiParameters = additionalJsonApiParameters,
|
||||
TokenizerPath = tokenizerPath,
|
||||
CapabilityOverrides = capabilityOverrides,
|
||||
AllowUserProvidedAPIKey = allowUserProvidedApiKey,
|
||||
AllowUserProvidedAPIKey = apiKeyOptions.AllowUserProvidedAPIKey,
|
||||
PromptForUserAPIKey = apiKeyOptions.PromptForUserAPIKey,
|
||||
CustomIconDataUrl = customIconDataUrl,
|
||||
};
|
||||
|
||||
// Handle an encrypted API key if present. When the user manages their own key for this
|
||||
// provider, we must never enqueue an embedded key: doing so would overwrite the user's
|
||||
// key in the OS keyring on every configuration reload.
|
||||
if (allowUserProvidedApiKey)
|
||||
if (apiKeyOptions.AllowUserProvidedAPIKey)
|
||||
{
|
||||
if (table.TryGetValue("APIKey", out var ignoredApiKeyValue) && ignoredApiKeyValue.TryRead<string>(out var ignoredApiKeyText) && !string.IsNullOrWhiteSpace(ignoredApiKeyText))
|
||||
LOGGER.LogWarning($"The configured provider {idx} sets both AllowUserProvidedAPIKey and an embedded APIKey. Ignoring the embedded key: the user manages their own key for this provider. (Plugin ID: {configPluginId})");
|
||||
|
||||
@@ -24,7 +24,8 @@ public sealed record TranscriptionProvider(
|
||||
Host Host = Host.NONE,
|
||||
bool AllowUserProvidedAPIKey = false,
|
||||
string CustomIconDataUrl = "",
|
||||
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey
|
||||
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE,
|
||||
bool PromptForUserAPIKey = false) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey
|
||||
{
|
||||
private static readonly ILogger<TranscriptionProvider> LOGGER = Program.LOGGER_FACTORY.CreateLogger<TranscriptionProvider>();
|
||||
|
||||
@@ -101,9 +102,7 @@ public sealed record TranscriptionProvider(
|
||||
return false;
|
||||
}
|
||||
|
||||
var allowUserProvidedApiKey = false;
|
||||
if (table.TryGetValue("AllowUserProvidedAPIKey", out var allowUserProvidedApiKeyValue) && allowUserProvidedApiKeyValue.TryRead<bool>(out var allowUserProvidedApiKeyBool))
|
||||
allowUserProvidedApiKey = allowUserProvidedApiKeyBool;
|
||||
var apiKeyOptions = UserProvidedAPIKeyOptions.Read(table);
|
||||
|
||||
var hfInferenceProvider = HFInferenceProvider.NONE;
|
||||
if (table.TryGetValue("HFInferenceProvider", out var hfInferenceProviderValue) && hfInferenceProviderValue.TryRead<string>(out var hfInferenceProviderText))
|
||||
@@ -136,7 +135,8 @@ public sealed record TranscriptionProvider(
|
||||
EnterpriseConfigurationPluginId = configPluginId,
|
||||
Hostname = hostname,
|
||||
Host = host,
|
||||
AllowUserProvidedAPIKey = allowUserProvidedApiKey,
|
||||
AllowUserProvidedAPIKey = apiKeyOptions.AllowUserProvidedAPIKey,
|
||||
PromptForUserAPIKey = apiKeyOptions.PromptForUserAPIKey,
|
||||
CustomIconDataUrl = customIconDataUrl,
|
||||
HFInferenceProvider = hfInferenceProvider,
|
||||
};
|
||||
@@ -144,7 +144,7 @@ public sealed record TranscriptionProvider(
|
||||
// Handle an encrypted API key if present. When the user manages their own key for this
|
||||
// transcription provider, we must never enqueue an embedded key: doing so would overwrite
|
||||
// the user's key in the OS keyring on every configuration reload.
|
||||
if (allowUserProvidedApiKey)
|
||||
if (apiKeyOptions.AllowUserProvidedAPIKey)
|
||||
{
|
||||
if (table.TryGetValue("APIKey", out var ignoredApiKeyValue) && ignoredApiKeyValue.TryRead<string>(out var ignoredApiKeyText) && !string.IsNullOrWhiteSpace(ignoredApiKeyText))
|
||||
LOGGER.LogWarning($"The configured transcription provider {idx} sets both AllowUserProvidedAPIKey and an embedded APIKey. Ignoring the embedded key: the user manages their own key for this provider. (Plugin ID: {configPluginId})");
|
||||
|
||||
Reference in new issue
Block a user