mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-08-15 16:42:11 +00:00
Added Hetzner as an experimental LLM provider
This commit is contained in:
parent
2d0d938f3d
commit
aa11e1c352
@ -8896,6 +8896,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T1014558951"] = "The trust leve
|
||||
-- You or your organization operate the LLM locally or within your trusted network. In terms of data processing and security, this is the best possible way.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T2124364471"] = "You or your organization operate the LLM locally or within your trusted network. In terms of data processing and security, this is the best possible way."
|
||||
|
||||
-- The provider operates its service in the EU and is subject to the **GDPR** (General Data Protection Regulation). It provides access to **open source models**. However, the service is currently **experimental**, and performance and availability are not guaranteed. We have no provider-specific information about whether submitted data is used for training.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T2930312134"] = "The provider operates its service in the EU and is subject to the **GDPR** (General Data Protection Regulation). It provides access to **open source models**. However, the service is currently **experimental**, and performance and availability are not guaranteed. We have no provider-specific information about whether submitted data is used for training."
|
||||
|
||||
-- The provider is located in the EU and is subject to the **GDPR** (General Data Protection Regulation). Additionally, the provider states that **your data is not used for training**.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T3010553924"] = "The provider is located in the EU and is subject to the **GDPR** (General Data Protection Regulation). Additionally, the provider states that **your data is not used for training**."
|
||||
|
||||
|
||||
@ -634,7 +634,7 @@ CONFIG["SETTINGS"] = {}
|
||||
-- Configure a custom confidence scheme.
|
||||
-- This is used when DataConfidence.ConfidenceScheme is set to CUSTOM.
|
||||
-- Allowed provider keys are: OPEN_AI, ANTHROPIC, MISTRAL, GOOGLE, X, DEEP_SEEK, ALIBABA_CLOUD,
|
||||
-- PERPLEXITY, OPEN_ROUTER, FIREWORKS, GROQ, HUGGINGFACE, SELF_HOSTED, HELMHOLTZ, GWDG
|
||||
-- PERPLEXITY, OPEN_ROUTER, HETZNER, FIREWORKS, GROQ, HUGGINGFACE, SELF_HOSTED, HELMHOLTZ, GWDG
|
||||
-- Allowed confidence values are: UNTRUSTED, VERY_LOW, LOW, MODERATE, MEDIUM, HIGH
|
||||
--
|
||||
-- Replaces, does not merge: a configuration with a higher priority replaces the whole
|
||||
@ -651,6 +651,7 @@ CONFIG["SETTINGS"] = {}
|
||||
-- ["ALIBABA_CLOUD"] = "LOW",
|
||||
-- ["PERPLEXITY"] = "MODERATE",
|
||||
-- ["OPEN_ROUTER"] = "MODERATE",
|
||||
-- ["HETZNER"] = "HIGH",
|
||||
-- ["FIREWORKS"] = "MODERATE",
|
||||
-- ["GROQ"] = "MODERATE",
|
||||
-- ["HUGGINGFACE"] = "MODERATE",
|
||||
|
||||
@ -64,6 +64,12 @@ public sealed record Confidence
|
||||
Level = ConfidenceLevel.MEDIUM,
|
||||
Description = TB("The provider is located in the EU and is subject to the **GDPR** (General Data Protection Regulation). Additionally, the provider states that **your data is not used for training**."),
|
||||
};
|
||||
|
||||
public static readonly Confidence GDPR_EXPERIMENTAL_OPEN_SOURCE = new()
|
||||
{
|
||||
Level = ConfidenceLevel.MEDIUM,
|
||||
Description = TB("The provider operates its service in the EU and is subject to the **GDPR** (General Data Protection Regulation). It provides access to **open source models**. However, the service is currently **experimental**, and performance and availability are not guaranteed. We have no provider-specific information about whether submitted data is used for training."),
|
||||
};
|
||||
|
||||
public static readonly Confidence SELF_HOSTED = new()
|
||||
{
|
||||
|
||||
93
app/MindWork AI Studio/Provider/Hetzner/ProviderHetzner.cs
Normal file
93
app/MindWork AI Studio/Provider/Hetzner/ProviderHetzner.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Hetzner;
|
||||
|
||||
public sealed class ProviderHetzner() : BaseProvider(LLMProviders.HETZNER, new Uri("https://inference.hetzner.com/api/v1/"), ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY, LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderHetzner> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderHetzner>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Id => LLMProviders.HETZNER.ToSecretId();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string InstanceName { get; set; } = "Hetzner (Experimental)";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasModelLoadingCapability => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||
"Hetzner",
|
||||
chatModel,
|
||||
chatThread,
|
||||
settingsManager,
|
||||
async (systemPrompt, apiParameters) =>
|
||||
{
|
||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||
|
||||
return new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
Messages = [systemPrompt, ..messages],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
};
|
||||
},
|
||||
token: token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<TranscriptionResult> TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(TranscriptionResult.Failure());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<IReadOnlyList<float>>>([]);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ModelLoadResult> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return this.LoadModelsResponse<ModelsResponse>(SecretStoreType.LLM_PROVIDER, "models", modelResponse => modelResponse.Data, token, apiKeyProvisional);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ModelLoadResult> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(ModelLoadResult.FromModels([]));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ModelLoadResult> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(ModelLoadResult.FromModels([]));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<ModelLoadResult> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(ModelLoadResult.FromModels([]));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -16,6 +16,7 @@ public enum LLMProviders
|
||||
ALIBABA_CLOUD = 12,
|
||||
PERPLEXITY = 14,
|
||||
OPEN_ROUTER = 15,
|
||||
HETZNER = 16,
|
||||
|
||||
FIREWORKS = 5,
|
||||
GROQ = 6,
|
||||
|
||||
@ -6,6 +6,7 @@ using AIStudio.Provider.Google;
|
||||
using AIStudio.Provider.Groq;
|
||||
using AIStudio.Provider.GWDG;
|
||||
using AIStudio.Provider.Helmholtz;
|
||||
using AIStudio.Provider.Hetzner;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Provider.Mistral;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
@ -56,6 +57,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => "Alibaba Cloud",
|
||||
LLMProviders.PERPLEXITY => "Perplexity",
|
||||
LLMProviders.OPEN_ROUTER => "OpenRouter",
|
||||
LLMProviders.HETZNER => "Hetzner (Experimental)",
|
||||
|
||||
LLMProviders.GROQ => "Groq",
|
||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||
@ -91,6 +93,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => "Alibaba Cloud",
|
||||
LLMProviders.PERPLEXITY => "Perplexity",
|
||||
LLMProviders.OPEN_ROUTER => "OpenRouter",
|
||||
LLMProviders.HETZNER => "Hetzner",
|
||||
|
||||
LLMProviders.GROQ => "Groq",
|
||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||
@ -144,6 +147,12 @@ public static class LLMProvidersExtensions
|
||||
|
||||
LLMProviders.OPEN_ROUTER => Confidence.USA_HUB.WithRegion("America, U.S.").WithSources("https://openrouter.ai/privacy", "https://openrouter.ai/terms").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.HETZNER => Confidence.GDPR_EXPERIMENTAL_OPEN_SOURCE.WithRegion("Europe, Germany").WithSources(
|
||||
"https://experiments.hetzner.com/docs/inference",
|
||||
"https://www.hetzner.com/legal/privacy-policy/",
|
||||
"https://www.hetzner.com/legal/terms-and-conditions/"
|
||||
).WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
@ -180,6 +189,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.HUGGINGFACE => false,
|
||||
LLMProviders.PERPLEXITY => false,
|
||||
LLMProviders.OPEN_ROUTER => true,
|
||||
LLMProviders.HETZNER => false,
|
||||
|
||||
//
|
||||
// Self-hosted providers are treated as a special case anyway.
|
||||
@ -209,6 +219,7 @@ public static class LLMProvidersExtensions
|
||||
// Providers that do not support transcription:
|
||||
//
|
||||
LLMProviders.OPEN_ROUTER => false,
|
||||
LLMProviders.HETZNER => false,
|
||||
LLMProviders.GROQ => false,
|
||||
LLMProviders.ANTHROPIC => false,
|
||||
LLMProviders.X => false,
|
||||
@ -271,6 +282,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.OPEN_ROUTER => new ProviderOpenRouter { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.HETZNER => new ProviderHetzner { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
|
||||
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, ConfiguredProviderId = configuredProviderId, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
@ -302,6 +314,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => "https://account.alibabacloud.com/register/intl_register.htm",
|
||||
LLMProviders.PERPLEXITY => "https://www.perplexity.ai/account/api",
|
||||
LLMProviders.OPEN_ROUTER => "https://openrouter.ai/keys",
|
||||
LLMProviders.HETZNER => "https://experiments.hetzner.com",
|
||||
|
||||
LLMProviders.GROQ => "https://console.groq.com/",
|
||||
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
||||
@ -327,6 +340,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.PERPLEXITY => "https://www.perplexity.ai/account/api/",
|
||||
LLMProviders.OPEN_ROUTER => "https://openrouter.ai/activity",
|
||||
LLMProviders.HUGGINGFACE => "https://huggingface.co/settings/billing",
|
||||
LLMProviders.HETZNER => "https://experiments.hetzner.com",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
@ -345,6 +359,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.PERPLEXITY => true,
|
||||
LLMProviders.OPEN_ROUTER => true,
|
||||
LLMProviders.HUGGINGFACE => true,
|
||||
LLMProviders.HETZNER => true,
|
||||
|
||||
_ => false,
|
||||
};
|
||||
@ -422,6 +437,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => true,
|
||||
LLMProviders.PERPLEXITY => true,
|
||||
LLMProviders.OPEN_ROUTER => true,
|
||||
LLMProviders.HETZNER => true,
|
||||
|
||||
LLMProviders.GROQ => true,
|
||||
LLMProviders.FIREWORKS => true,
|
||||
@ -445,6 +461,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => true,
|
||||
LLMProviders.PERPLEXITY => true,
|
||||
LLMProviders.OPEN_ROUTER => true,
|
||||
LLMProviders.HETZNER => true,
|
||||
|
||||
LLMProviders.GROQ => true,
|
||||
LLMProviders.FIREWORKS => true,
|
||||
|
||||
@ -90,6 +90,7 @@ public static partial class ProviderExtensions
|
||||
GetQwenReasoningState(parameters)),
|
||||
|
||||
LLMProviders.OPEN_ROUTER or
|
||||
LLMProviders.HETZNER or
|
||||
LLMProviders.X or
|
||||
LLMProviders.DEEP_SEEK or
|
||||
LLMProviders.GROQ or
|
||||
|
||||
@ -54,6 +54,7 @@ public static partial class ProviderExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => GetModelCapabilitiesAlibaba(model),
|
||||
LLMProviders.PERPLEXITY => GetModelCapabilitiesPerplexity(model),
|
||||
LLMProviders.OPEN_ROUTER => GetModelCapabilitiesOpenRouter(model),
|
||||
LLMProviders.HETZNER => GetModelCapabilitiesOpenSource(model),
|
||||
|
||||
LLMProviders.GROQ => GetModelCapabilitiesOpenSource(model),
|
||||
LLMProviders.FIREWORKS => GetModelCapabilitiesOpenSource(model),
|
||||
|
||||
@ -599,8 +599,9 @@ public sealed class SettingsManager
|
||||
{
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
LLMProviders.DEEP_SEEK => ConfidenceLevel.LOW,
|
||||
LLMProviders.ALIBABA_CLOUD => ConfidenceLevel.LOW,
|
||||
|
||||
_ => ConfidenceLevel.MEDIUM,
|
||||
_ => ConfidenceLevel.MEDIUM,
|
||||
};
|
||||
|
||||
case ConfidenceSchemes.TRUST_USA:
|
||||
@ -610,7 +611,9 @@ public sealed class SettingsManager
|
||||
LLMProviders.MISTRAL => ConfidenceLevel.LOW,
|
||||
LLMProviders.HELMHOLTZ => ConfidenceLevel.LOW,
|
||||
LLMProviders.GWDG => ConfidenceLevel.LOW,
|
||||
LLMProviders.HETZNER => ConfidenceLevel.LOW,
|
||||
LLMProviders.DEEP_SEEK => ConfidenceLevel.LOW,
|
||||
LLMProviders.ALIBABA_CLOUD => ConfidenceLevel.LOW,
|
||||
|
||||
_ => ConfidenceLevel.MEDIUM,
|
||||
};
|
||||
@ -622,6 +625,7 @@ public sealed class SettingsManager
|
||||
LLMProviders.MISTRAL => ConfidenceLevel.MEDIUM,
|
||||
LLMProviders.HELMHOLTZ => ConfidenceLevel.MEDIUM,
|
||||
LLMProviders.GWDG => ConfidenceLevel.MEDIUM,
|
||||
LLMProviders.HETZNER => ConfidenceLevel.MEDIUM,
|
||||
|
||||
_ => ConfidenceLevel.LOW,
|
||||
};
|
||||
@ -631,6 +635,7 @@ public sealed class SettingsManager
|
||||
{
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
LLMProviders.DEEP_SEEK => ConfidenceLevel.MEDIUM,
|
||||
LLMProviders.ALIBABA_CLOUD => ConfidenceLevel.MEDIUM,
|
||||
|
||||
_ => ConfidenceLevel.LOW,
|
||||
};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
# v26.8.1, build 251 (2026-08-xx xx:xx UTC)
|
||||
- Added Hetzner's experimental inference API as an LLM provider. It runs open-source models in the EU and supports text and image chats through its OpenAI-compatible API.
|
||||
- Added a prototype Visual Briefing Assistant that turns documents, data, images, audio, and video into self-contained interactive HTML briefings. When you want to test it, you have to enable this preview feature in your app settings.
|
||||
- Added organization-configurable defaults and visibility controls for the Visual Briefing Assistant.
|
||||
- Added a share button for assistants, configurations, and language plugins. It uses the native share dialog on Windows and macOS. For Linux, we added an export option, which stores the plugin archive at a location of your choice. When you work on a translation for a new language, you can now hand your current state to testers or to us with one click.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user