Add configurable timeout for LLM provider requests

This commit is contained in:
hart_s3 2026-04-28 14:04:10 +02:00
parent 9ec687e22e
commit 57d60a7b85
5 changed files with 50 additions and 3 deletions

View File

@ -212,6 +212,10 @@ CONFIG["SETTINGS"] = {}
-- Examples are: "CmdOrControl+Shift+D", "Alt+F9", "F8"
-- CONFIG["SETTINGS"]["DataApp.ShortcutVoiceRecording"] = "CmdOrControl+1"
-- Configure the HTTP timeout for requests to LLM providers, in seconds.
-- The default is 3600 (1 hour).
-- CONFIG["SETTINGS"]["DataApp.ProviderHttpTimeoutSeconds"] = 3600
-- Example chat templates for this configuration:
CONFIG["CHAT_TEMPLATES"] = {}

View File

@ -24,9 +24,10 @@ namespace AIStudio.Provider;
/// </summary>
public abstract class BaseProvider : IProvider, ISecretId
{
private static readonly TimeSpan HTTP_TIMEOUT = TimeSpan.FromHours(1);
private const int DEFAULT_HTTP_TIMEOUT_SECONDS = 3600;
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(BaseProvider).Namespace, nameof(BaseProvider));
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
/// <summary>
/// The HTTP client to use it for all requests.
@ -76,7 +77,7 @@ public abstract class BaseProvider : IProvider, ISecretId
// Set the base URL:
this.HttpClient.BaseAddress = new(url);
this.HttpClient.Timeout = HTTP_TIMEOUT;
this.HttpClient.Timeout = GetProviderHttpTimeout();
}
#region Handling of IProvider, which all providers must implement
@ -156,11 +157,41 @@ public abstract class BaseProvider : IProvider, ISecretId
protected Task SendTimeoutError(string action) => MessageBus.INSTANCE.SendError(new(
Icons.Material.Filled.HourglassTop,
string.Format(
TB("The request to the LLM provider '{0}' (type={1}) timed out after 1 hour while {2}. Please try again or check whether the provider is still responding."),
TB("The request to the LLM provider '{0}' (type={1}) timed out after {2} while {3}. Please try again or check whether the provider is still responding."),
this.InstanceName,
this.Provider,
GetProviderHttpTimeoutDescription(),
action)));
private static TimeSpan GetProviderHttpTimeout()
{
var seconds = SETTINGS_MANAGER.ConfigurationData.App.ProviderHttpTimeoutSeconds;
if (seconds <= 0)
seconds = DEFAULT_HTTP_TIMEOUT_SECONDS;
return TimeSpan.FromSeconds(seconds);
}
private static string GetProviderHttpTimeoutDescription()
{
var timeout = GetProviderHttpTimeout();
if (timeout.TotalHours >= 1 && timeout.TotalMinutes % 60 == 0)
{
var hours = (int)timeout.TotalHours;
return hours == 1 ? "1 hour" : $"{hours} hours";
}
if (timeout.TotalMinutes >= 1 && timeout.TotalSeconds % 60 == 0)
{
var minutes = (int)timeout.TotalMinutes;
return minutes == 1 ? "1 minute" : $"{minutes} minutes";
}
var seconds = (int)timeout.TotalSeconds;
return seconds == 1 ? "1 second" : $"{seconds} seconds";
}
protected async Task<string?> GetModelLoadingSecretKey(SecretStoreType storeType, string? apiKeyProvisional = null, bool isTryingSecret = false) => apiKeyProvisional switch
{
not null => apiKeyProvisional,

View File

@ -94,6 +94,11 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
/// </summary>
public string ShortcutVoiceRecording { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ShortcutVoiceRecording, string.Empty);
/// <summary>
/// The HTTP timeout in seconds for requests to LLM providers.
/// </summary>
public int ProviderHttpTimeoutSeconds { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ProviderHttpTimeoutSeconds, 3600);
/// <summary>
/// Should the user be allowed to add providers?
/// </summary>

View File

@ -141,6 +141,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
// Config: global voice recording shortcut
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.ShortcutVoiceRecording, this.Id, settingsTable, dryRun);
// Config: timeout for HTTP requests to providers
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.ProviderHttpTimeoutSeconds, this.Id, settingsTable, dryRun);
// Handle configured LLM providers:
PluginConfigurationObject.TryParse(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, x => x.NextProviderNum, mainTable, this.Id, ref this.configObjects, dryRun);

View File

@ -241,6 +241,10 @@ public static partial class PluginFactory
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShortcutVoiceRecording, AVAILABLE_PLUGINS))
wasConfigurationChanged = true;
// Check for the provider HTTP timeout:
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ProviderHttpTimeoutSeconds, AVAILABLE_PLUGINS))
wasConfigurationChanged = true;
// Check if audit is required before it can be activated
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.RequireAuditBeforeActivation, AVAILABLE_PLUGINS))
wasConfigurationChanged = true;