mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 20:07:20 +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
@@ -76,19 +76,20 @@ public static class ConfigurationSnippetImport
|
||||
return true;
|
||||
|
||||
//
|
||||
// A user-provided API key is what an organization grants for a provider it manages. A
|
||||
// provider of the user's own has nothing else, so the flag carries no meaning here:
|
||||
// A user-provided API key, and asking the user for it, is what an organization grants for a
|
||||
// provider it manages. A provider of the user's own has nothing else, so neither option carries
|
||||
// any meaning here:
|
||||
//
|
||||
case Settings.Provider provider:
|
||||
result = new(MakeLocal(provider) with { TokenizerPath = KeepLocalTokenizer(provider.TokenizerPath, hints), AllowUserProvidedAPIKey = false }, ReadAPIKey(table, hints), hints, []);
|
||||
result = new(MakeLocal(provider) with { TokenizerPath = KeepLocalTokenizer(provider.TokenizerPath, hints), AllowUserProvidedAPIKey = false, PromptForUserAPIKey = false }, ReadAPIKey(table, hints), hints, []);
|
||||
return true;
|
||||
|
||||
case EmbeddingProvider provider:
|
||||
result = new(MakeLocal(provider) with { TokenizerPath = KeepLocalTokenizer(provider.TokenizerPath, hints), AllowUserProvidedAPIKey = false }, ReadAPIKey(table, hints), hints, []);
|
||||
result = new(MakeLocal(provider) with { TokenizerPath = KeepLocalTokenizer(provider.TokenizerPath, hints), AllowUserProvidedAPIKey = false, PromptForUserAPIKey = false }, ReadAPIKey(table, hints), hints, []);
|
||||
return true;
|
||||
|
||||
case TranscriptionProvider provider:
|
||||
result = new(MakeLocal(provider) with { AllowUserProvidedAPIKey = false }, ReadAPIKey(table, hints), hints, []);
|
||||
result = new(MakeLocal(provider) with { AllowUserProvidedAPIKey = false, PromptForUserAPIKey = false }, ReadAPIKey(table, hints), hints, []);
|
||||
return true;
|
||||
|
||||
//
|
||||
|
||||
@@ -3,7 +3,8 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
/// <summary>
|
||||
/// Represents a configuration object whose API key is managed by the user, although the object
|
||||
/// itself is managed by a configuration plugin. Implemented by all provider kinds which support
|
||||
/// the "AllowUserProvidedAPIKey" option, i.e., LLM, embedding, and transcription providers.
|
||||
/// the options "AllowUserProvidedAPIKey" and "PromptForUserAPIKey", i.e., LLM, embedding, and
|
||||
/// transcription providers. UserProvidedAPIKeyOptions reads both options from a plugin.
|
||||
/// </summary>
|
||||
public interface IUserProvidedAPIKey
|
||||
{
|
||||
@@ -12,4 +13,10 @@ public interface IUserProvidedAPIKey
|
||||
/// locked, enterprise-managed object.
|
||||
/// </summary>
|
||||
public bool AllowUserProvidedAPIKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// When set by a configuration plugin, AI Studio asks the user for the API key of this object
|
||||
/// on the home page as long as none is stored. Implies AllowUserProvidedAPIKey.
|
||||
/// </summary>
|
||||
public bool PromptForUserAPIKey { get; }
|
||||
}
|
||||
@@ -18,6 +18,7 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
private List<DataMandatoryInfo> mandatoryInfos = [];
|
||||
private List<DataIntroduction> introductions = [];
|
||||
private List<DataMailboxProvider> mailboxProviders = [];
|
||||
private List<DataUserAPIKeyPrompt> userAPIKeyPrompts = [];
|
||||
|
||||
/// <summary>
|
||||
/// The list of configuration objects. Configuration objects are, e.g., providers or chat templates.
|
||||
@@ -42,6 +43,12 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
/// </summary>
|
||||
public IReadOnlyList<DataMailboxProvider> MailboxProviders => this.mailboxProviders;
|
||||
|
||||
/// <summary>
|
||||
/// What this configuration plugin tells the user while AI Studio asks for the API key of its providers;
|
||||
/// at most one entry. The prompt is live plugin content and is not persisted to ConfigurationData.
|
||||
/// </summary>
|
||||
public IReadOnlyList<DataUserAPIKeyPrompt> UserAPIKeyPrompts => this.userAPIKeyPrompts;
|
||||
|
||||
/// <summary>
|
||||
/// True/false when explicitly configured in the plugin, otherwise null.
|
||||
/// </summary>
|
||||
@@ -204,6 +211,7 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
this.mandatoryInfos.Clear();
|
||||
this.introductions.Clear();
|
||||
this.mailboxProviders.Clear();
|
||||
this.userAPIKeyPrompts.Clear();
|
||||
|
||||
// Ensure that the main CONFIG table exists and is a valid Lua table:
|
||||
if (!this.State.Environment["CONFIG"].TryRead<LuaTable>(out var mainTable))
|
||||
@@ -400,6 +408,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
|
||||
// Handle configured mail servers:
|
||||
this.TryReadMailboxProviders(mainTable);
|
||||
|
||||
// Handle the configured prompt for the API key of the user:
|
||||
this.TryReadUserAPIKeyPrompt(mainTable);
|
||||
|
||||
// Config: preselected provider?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
|
||||
@@ -872,4 +883,13 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
LOG.LogWarning("The table 'MAILBOX_PROVIDERS' entry at index {Index} does not contain a valid mail server (config plugin id: {ConfigPluginId}).", i, this.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void TryReadUserAPIKeyPrompt(LuaTable mainTable)
|
||||
{
|
||||
if (!mainTable.TryGetValue("USER_API_KEY_PROMPT", out var promptValue) || !promptValue.TryRead<LuaTable>(out var promptTable))
|
||||
return;
|
||||
|
||||
if (DataUserAPIKeyPrompt.TryParseConfiguration(promptTable, this.Id, out var prompt))
|
||||
this.userAPIKeyPrompts.Add(prompt);
|
||||
}
|
||||
}
|
||||
@@ -449,6 +449,15 @@ public static partial class PluginFactory
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects what the running configuration plugins tell the user while AI Studio asks for an API key.
|
||||
/// </summary>
|
||||
/// <returns>The prompts of all configuration plugins, at most one per plugin.</returns>
|
||||
public static IReadOnlyList<DataUserAPIKeyPrompt> GetUserAPIKeyPrompts()
|
||||
{
|
||||
return ResolveLivePluginContent<PluginConfiguration, DataUserAPIKeyPrompt>("user API key prompt", plugin => plugin.UserAPIKeyPrompts).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects the mail servers which the running configuration plugins offer for new mailboxes.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
/// <summary>
|
||||
/// What a configuration plugin allows for the API key of a provider it manages: whether the user may set
|
||||
/// their own key, and whether AI Studio asks the user for it on the home page.
|
||||
/// </summary>
|
||||
/// <param name="AllowUserProvidedAPIKey">The user may set their own API key for the otherwise locked provider.</param>
|
||||
/// <param name="PromptForUserAPIKey">AI Studio asks the user for the API key on the home page as long as none is stored.</param>
|
||||
public readonly record struct UserProvidedAPIKeyOptions(bool AllowUserProvidedAPIKey, bool PromptForUserAPIKey)
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads both options from the Lua table of a provider. An option which is missing or not a boolean counts as false.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Asking the user for the key only makes sense when the user may set it. Therefore, PromptForUserAPIKey
|
||||
/// implies AllowUserProvidedAPIKey, and a configuration needs to name only the one option. The implied
|
||||
/// option also keeps the key of the user in the OS keyring when the configuration is withdrawn.
|
||||
/// </remarks>
|
||||
/// <param name="table">The Lua table of the provider.</param>
|
||||
/// <returns>The options of the provider.</returns>
|
||||
public static UserProvidedAPIKeyOptions Read(LuaTable table)
|
||||
{
|
||||
var promptForUserAPIKey = ReadOption(table, "PromptForUserAPIKey");
|
||||
var allowUserProvidedAPIKey = promptForUserAPIKey || ReadOption(table, "AllowUserProvidedAPIKey");
|
||||
return new UserProvidedAPIKeyOptions(allowUserProvidedAPIKey, promptForUserAPIKey);
|
||||
}
|
||||
|
||||
private static bool ReadOption(LuaTable table, string name) => table.TryGetValue(name, out var value) && value.TryRead<bool>(out var isSet) && isSet;
|
||||
}
|
||||
Reference in new issue
Block a user