diff --git a/README.md b/README.md index 7a157fa..84f945a 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi **Key advantages:** - **Free of charge**: The app is free to use, both for personal and commercial purposes. -- **Independence**: You are not tied to any single provider. Instead, you can choose the provider that best suits their needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), and self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), [Groq](https://groq.com/), or [Fireworks](https://fireworks.ai/). +- **Independence**: You are not tied to any single provider. Instead, you can choose the provider that best suits their needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, and self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), [Groq](https://groq.com/), or [Fireworks](https://fireworks.ai/). For scientists and employees of research institutions, we also support [Helmholtz](https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570) and [GWDG](https://gwdg.de/services/application-services/ai-services/) AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities. - **Assistants**: You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants. - **Unrestricted usage**: Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API. - **Cost-effective**: You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit. diff --git a/app/MindWork AI Studio/Pages/Home.razor.cs b/app/MindWork AI Studio/Pages/Home.razor.cs index 5b8d63e..026fafe 100644 --- a/app/MindWork AI Studio/Pages/Home.razor.cs +++ b/app/MindWork AI Studio/Pages/Home.razor.cs @@ -33,7 +33,7 @@ public partial class Home : ComponentBase private static readonly TextItem[] ITEMS_ADVANTAGES = [ new("Free of charge", "The app is free to use, both for personal and commercial purposes."), - new("Independence", "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks."), + new("Independence", "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."), new("Assistants", "You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants."), new("Unrestricted usage", "Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API."), new("Cost-effective", "You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit."), diff --git a/app/MindWork AI Studio/Provider/Confidence.cs b/app/MindWork AI Studio/Provider/Confidence.cs index ac5557a..087ac4e 100644 --- a/app/MindWork AI Studio/Provider/Confidence.cs +++ b/app/MindWork AI Studio/Provider/Confidence.cs @@ -53,6 +53,12 @@ public sealed record Confidence Description = "The provider operates its service from the USA and is subject to **US jurisdiction**. In case of suspicion, authorities in the USA can access your data. However, **your data is not used for training** purposes.", }; + public static readonly Confidence CHINA_NO_TRAINING = new() + { + Level = ConfidenceLevel.MODERATE, + Description = "The provider operates its service from China. In case of suspicion, authorities in the respective countries of operation may access your data. However, **your data is not used for training** purposes.", + }; + public static readonly Confidence GDPR_NO_TRAINING = new() { Level = ConfidenceLevel.MEDIUM, diff --git a/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs b/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs new file mode 100644 index 0000000..c58e914 --- /dev/null +++ b/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs @@ -0,0 +1,137 @@ +using System.Net.Http.Headers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; + +using AIStudio.Chat; +using AIStudio.Provider.OpenAI; +using AIStudio.Settings; + +namespace AIStudio.Provider.DeepSeek; + +public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api.deepseek.com/", logger) +{ + #region Implementation of IProvider + + /// + public override string Id => LLMProviders.DEEP_SEEK.ToName(); + + /// + public override string InstanceName { get; set; } = "DeepSeek"; + + /// + public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default) + { + // Get the API key: + var requestedSecret = await RUST_SERVICE.GetAPIKey(this); + if(!requestedSecret.Success) + yield break; + + // Prepare the system prompt: + var systemPrompt = new Message + { + Role = "system", + Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger), + }; + + // Prepare the DeepSeek HTTP chat request: + var deepSeekChatRequest = JsonSerializer.Serialize(new ChatRequest + { + Model = chatModel.Id, + + // Build the messages: + // - First of all the system prompt + // - Then none-empty user and AI messages + Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message + { + Role = n.Role switch + { + ChatRole.USER => "user", + ChatRole.AI => "assistant", + ChatRole.AGENT => "assistant", + ChatRole.RAG => "assistant", + ChatRole.SYSTEM => "system", + + _ => "user", + }, + + Content = n.Content switch + { + ContentText text => text.Text, + _ => string.Empty, + } + }).ToList()], + Stream = true, + }, JSON_SERIALIZER_OPTIONS); + + async Task RequestBuilder() + { + // Build the HTTP post request: + var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions"); + + // Set the authorization header: + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION)); + + // Set the content: + request.Content = new StringContent(deepSeekChatRequest, Encoding.UTF8, "application/json"); + return request; + } + + await foreach (var content in this.StreamChatCompletionInternal("Helmholtz", RequestBuilder, token)) + yield return content; + } + + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + /// + public override async IAsyncEnumerable 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 + + /// + public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default) + { + return this.LoadModels(token, apiKeyProvisional); + } + + /// + public override Task> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default) + { + return Task.FromResult(Enumerable.Empty()); + } + + /// + public override Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default) + { + return Task.FromResult(Enumerable.Empty()); + } + + #endregion + + private async Task> LoadModels(CancellationToken token, string? apiKeyProvisional = null) + { + var secretKey = apiKeyProvisional switch + { + not null => apiKeyProvisional, + _ => await RUST_SERVICE.GetAPIKey(this) switch + { + { Success: true } result => await result.Secret.Decrypt(ENCRYPTION), + _ => null, + } + }; + + if (secretKey is null) + return []; + + using var request = new HttpRequestMessage(HttpMethod.Get, "models"); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey); + + using var response = await this.httpClient.SendAsync(request, token); + if(!response.IsSuccessStatusCode) + return []; + + var modelResponse = await response.Content.ReadFromJsonAsync(token); + return modelResponse.Data; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Provider/LLMProviders.cs b/app/MindWork AI Studio/Provider/LLMProviders.cs index 6c56c6e..92c0873 100644 --- a/app/MindWork AI Studio/Provider/LLMProviders.cs +++ b/app/MindWork AI Studio/Provider/LLMProviders.cs @@ -12,6 +12,7 @@ public enum LLMProviders MISTRAL = 3, GOOGLE = 7, X = 8, + DEEP_SEEK = 11, FIREWORKS = 5, GROQ = 6, diff --git a/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs index ce435e1..7fb80e7 100644 --- a/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs +++ b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs @@ -1,4 +1,5 @@ using AIStudio.Provider.Anthropic; +using AIStudio.Provider.DeepSeek; using AIStudio.Provider.Fireworks; using AIStudio.Provider.Google; using AIStudio.Provider.Groq; @@ -30,6 +31,7 @@ public static class LLMProvidersExtensions LLMProviders.MISTRAL => "Mistral", LLMProviders.GOOGLE => "Google", LLMProviders.X => "xAI", + LLMProviders.DEEP_SEEK => "DeepSeek", LLMProviders.GROQ => "Groq", LLMProviders.FIREWORKS => "Fireworks.ai", @@ -71,6 +73,8 @@ public static class LLMProvidersExtensions LLMProviders.X => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources("https://x.ai/legal/terms-of-service-enterprise").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)), + LLMProviders.DEEP_SEEK => Confidence.CHINA_NO_TRAINING.WithRegion("Asia").WithSources("https://cdn.deepseek.com/policies/en-US/deepseek-open-platform-terms-of-service.html").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)), @@ -102,6 +106,7 @@ public static class LLMProvidersExtensions LLMProviders.FIREWORKS => false, LLMProviders.X => false, LLMProviders.GWDG => false, + LLMProviders.DEEP_SEEK => false, // // Self-hosted providers are treated as a special case anyway. @@ -144,6 +149,7 @@ public static class LLMProvidersExtensions LLMProviders.MISTRAL => new ProviderMistral(logger) { InstanceName = instanceName }, LLMProviders.GOOGLE => new ProviderGoogle(logger) { InstanceName = instanceName }, LLMProviders.X => new ProviderX(logger) { InstanceName = instanceName }, + LLMProviders.DEEP_SEEK => new ProviderDeepSeek(logger) { InstanceName = instanceName }, LLMProviders.GROQ => new ProviderGroq(logger) { InstanceName = instanceName }, LLMProviders.FIREWORKS => new ProviderFireworks(logger) { InstanceName = instanceName }, @@ -170,6 +176,7 @@ public static class LLMProvidersExtensions LLMProviders.ANTHROPIC => "https://console.anthropic.com/dashboard", LLMProviders.GOOGLE => "https://console.cloud.google.com/", LLMProviders.X => "https://accounts.x.ai/sign-up", + LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/sign_up", LLMProviders.GROQ => "https://console.groq.com/", LLMProviders.FIREWORKS => "https://fireworks.ai/login", @@ -189,6 +196,7 @@ public static class LLMProvidersExtensions LLMProviders.GROQ => "https://console.groq.com/settings/usage", LLMProviders.GOOGLE => "https://console.cloud.google.com/billing", LLMProviders.FIREWORKS => "https://fireworks.ai/account/billing", + LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/usage", _ => string.Empty, }; @@ -202,6 +210,7 @@ public static class LLMProvidersExtensions LLMProviders.GROQ => true, LLMProviders.FIREWORKS => true, LLMProviders.GOOGLE => true, + LLMProviders.DEEP_SEEK => true, _ => false, }; @@ -243,6 +252,7 @@ public static class LLMProvidersExtensions LLMProviders.ANTHROPIC => true, LLMProviders.GOOGLE => true, LLMProviders.X => true, + LLMProviders.DEEP_SEEK => true, LLMProviders.GROQ => true, LLMProviders.FIREWORKS => true, @@ -261,6 +271,7 @@ public static class LLMProvidersExtensions LLMProviders.ANTHROPIC => true, LLMProviders.GOOGLE => true, LLMProviders.X => true, + LLMProviders.DEEP_SEEK => true, LLMProviders.GROQ => true, LLMProviders.FIREWORKS => true, diff --git a/app/MindWork AI Studio/Settings/DataModel/DataLLMProviders.cs b/app/MindWork AI Studio/Settings/DataModel/DataLLMProviders.cs index a716476..30ad8ba 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataLLMProviders.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataLLMProviders.cs @@ -22,7 +22,7 @@ public sealed class DataLLMProviders /// /// Which confidence scheme to use. /// - public ConfidenceSchemes ConfidenceScheme { get; set; } = ConfidenceSchemes.TRUST_USA_EUROPE; + public ConfidenceSchemes ConfidenceScheme { get; set; } = ConfidenceSchemes.TRUST_ALL; /// /// Provide custom confidence levels for each LLM provider. diff --git a/app/MindWork AI Studio/Settings/SettingsManager.cs b/app/MindWork AI Studio/Settings/SettingsManager.cs index 29e9439..0930c9a 100644 --- a/app/MindWork AI Studio/Settings/SettingsManager.cs +++ b/app/MindWork AI Studio/Settings/SettingsManager.cs @@ -211,11 +211,19 @@ public sealed class SettingsManager(ILogger logger) switch (this.ConfigurationData.LLMProviders.ConfidenceScheme) { + case ConfidenceSchemes.TRUST_ALL: + return llmProvider switch + { + LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH, + + _ => ConfidenceLevel.MEDIUM, + }; + case ConfidenceSchemes.TRUST_USA_EUROPE: return llmProvider switch { LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH, - LLMProviders.FIREWORKS => ConfidenceLevel.UNTRUSTED, + LLMProviders.DEEP_SEEK => ConfidenceLevel.LOW, _ => ConfidenceLevel.MEDIUM, }; @@ -224,8 +232,10 @@ public sealed class SettingsManager(ILogger logger) return llmProvider switch { LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH, - LLMProviders.FIREWORKS => ConfidenceLevel.UNTRUSTED, LLMProviders.MISTRAL => ConfidenceLevel.LOW, + LLMProviders.HELMHOLTZ => ConfidenceLevel.LOW, + LLMProviders.GWDG => ConfidenceLevel.LOW, + LLMProviders.DEEP_SEEK => ConfidenceLevel.LOW, _ => ConfidenceLevel.MEDIUM, }; @@ -234,8 +244,18 @@ public sealed class SettingsManager(ILogger logger) return llmProvider switch { LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH, - LLMProviders.FIREWORKS => ConfidenceLevel.UNTRUSTED, LLMProviders.MISTRAL => ConfidenceLevel.MEDIUM, + LLMProviders.HELMHOLTZ => ConfidenceLevel.MEDIUM, + LLMProviders.GWDG => ConfidenceLevel.MEDIUM, + + _ => ConfidenceLevel.LOW, + }; + + case ConfidenceSchemes.TRUST_ASIA: + return llmProvider switch + { + LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH, + LLMProviders.DEEP_SEEK => ConfidenceLevel.MEDIUM, _ => ConfidenceLevel.LOW, }; @@ -244,7 +264,6 @@ public sealed class SettingsManager(ILogger logger) return llmProvider switch { LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH, - LLMProviders.FIREWORKS => ConfidenceLevel.UNTRUSTED, _ => ConfidenceLevel.VERY_LOW, }; diff --git a/app/MindWork AI Studio/Tools/ConfidenceSchemes.cs b/app/MindWork AI Studio/Tools/ConfidenceSchemes.cs index 97d4f4d..b508528 100644 --- a/app/MindWork AI Studio/Tools/ConfidenceSchemes.cs +++ b/app/MindWork AI Studio/Tools/ConfidenceSchemes.cs @@ -2,11 +2,13 @@ namespace AIStudio.Tools; public enum ConfidenceSchemes { - TRUST_USA_EUROPE = 0, - TRUST_USA = 1, - TRUST_EUROPE = 2, + TRUST_ALL, + TRUST_USA_EUROPE, + TRUST_USA, + TRUST_EUROPE, + TRUST_ASIA, - LOCAL_TRUST_ONLY = 3, + LOCAL_TRUST_ONLY, CUSTOM = 10_000, } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/ConfidenceSchemesExtensions.cs b/app/MindWork AI Studio/Tools/ConfidenceSchemesExtensions.cs index d36993f..035be94 100644 --- a/app/MindWork AI Studio/Tools/ConfidenceSchemesExtensions.cs +++ b/app/MindWork AI Studio/Tools/ConfidenceSchemesExtensions.cs @@ -4,9 +4,11 @@ public static class ConfidenceSchemesExtensions { public static string GetListDescription(this ConfidenceSchemes scheme) => scheme switch { + ConfidenceSchemes.TRUST_ALL => "Trust all LLM providers", ConfidenceSchemes.TRUST_USA_EUROPE => "Trust LLM providers from the USA and Europe", ConfidenceSchemes.TRUST_USA => "Trust LLM providers from the USA", ConfidenceSchemes.TRUST_EUROPE => "Trust LLM providers from Europe", + ConfidenceSchemes.TRUST_ASIA => "Trust LLM providers from Asia", ConfidenceSchemes.LOCAL_TRUST_ONLY => "Trust only local LLM providers", ConfidenceSchemes.CUSTOM => "Configure your own confidence scheme", diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.9.31.md b/app/MindWork AI Studio/wwwroot/changelog/v0.9.31.md index 3145150..51bb5e7 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v0.9.31.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v0.9.31.md @@ -1,3 +1,4 @@ # v0.9.31, build 206 (2025-02-xx xx:xx UTC) - Added Helmholtz (aka "Blablador") as provider. This provider is available to all researchers and employees of the 18 Helmholtz Centers as well as all eduGAIN organizations worldwide. -- Added GWDG SAIA as provider. This provider is available to all researchers and employees of the GWDG, the Max Planck Society, the 18 Helmholtz Centers, and most German universities. \ No newline at end of file +- Added GWDG SAIA as provider. This provider is available to all researchers and employees of the GWDG, the Max Planck Society, the 18 Helmholtz Centers, and most German universities. +- Added DeepSeek as provider.