mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-10-31 06:40:20 +00:00 
			
		
		
		
	Added GWDG provider (#301)
This commit is contained in:
		
							parent
							
								
									b984b4432e
								
							
						
					
					
						commit
						37a9046be4
					
				| @ -2,6 +2,7 @@ | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String> | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EDI/@EntryIndexedValue">EDI</s:String> | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ERI/@EntryIndexedValue">ERI</s:String> | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GWDG/@EntryIndexedValue">GWDG</s:String> | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String> | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LM/@EntryIndexedValue">LM</s:String> | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String> | ||||
| @ -9,6 +10,7 @@ | ||||
| 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String> | ||||
| 	<s:Boolean x:Key="/Default/UserDictionary/Words/=agentic/@EntryIndexedValue">True</s:Boolean> | ||||
| 	<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean> | ||||
| 	<s:Boolean x:Key="/Default/UserDictionary/Words/=gwdg/@EntryIndexedValue">True</s:Boolean> | ||||
| 	<s:Boolean x:Key="/Default/UserDictionary/Words/=mwais/@EntryIndexedValue">True</s:Boolean> | ||||
| 	<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean> | ||||
| 	<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> | ||||
							
								
								
									
										139
									
								
								app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | ||||
| 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.GWDG; | ||||
| 
 | ||||
| public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai.academiccloud.de/v1/", logger) | ||||
| { | ||||
|     #region Implementation of IProvider | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override string Id => LLMProviders.GWDG.ToName(); | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override string InstanceName { get; set; } = "GWDG SAIA"; | ||||
|      | ||||
|     /// <inheritdoc /> | ||||
|     public override async IAsyncEnumerable<string> 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 GWDG HTTP chat request: | ||||
|         var gwdgChatRequest = 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<HttpRequestMessage> 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(gwdgChatRequest, Encoding.UTF8, "application/json"); | ||||
|             return request; | ||||
|         } | ||||
|          | ||||
|         await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("GWDG", RequestBuilder, 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 async Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default) | ||||
|     { | ||||
|         var models = await this.LoadModels(token, apiKeyProvisional); | ||||
|         return models.Where(model => !model.Id.StartsWith("e5-mistral-7b-instruct", StringComparison.InvariantCultureIgnoreCase)); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc /> | ||||
|     public override Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default) | ||||
|     { | ||||
|         return Task.FromResult(Enumerable.Empty<Model>()); | ||||
|     } | ||||
|      | ||||
|     /// <inheritdoc /> | ||||
|     public override async Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default) | ||||
|     { | ||||
|         var models = await this.LoadModels(token, apiKeyProvisional); | ||||
|         return models.Where(model => model.Id.StartsWith("e5-", StringComparison.InvariantCultureIgnoreCase)); | ||||
|     } | ||||
| 
 | ||||
|     #endregion | ||||
| 
 | ||||
|     private async Task<IEnumerable<Model>> 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<ModelsResponse>(token); | ||||
|         return modelResponse.Data; | ||||
|     } | ||||
| } | ||||
| @ -19,4 +19,5 @@ public enum LLMProviders | ||||
|     SELF_HOSTED = 4, | ||||
|      | ||||
|     HELMHOLTZ = 9, | ||||
|     GWDG = 10, | ||||
| } | ||||
| @ -2,6 +2,7 @@ using AIStudio.Provider.Anthropic; | ||||
| using AIStudio.Provider.Fireworks; | ||||
| using AIStudio.Provider.Google; | ||||
| using AIStudio.Provider.Groq; | ||||
| using AIStudio.Provider.GWDG; | ||||
| using AIStudio.Provider.Helmholtz; | ||||
| using AIStudio.Provider.Mistral; | ||||
| using AIStudio.Provider.OpenAI; | ||||
| @ -36,6 +37,7 @@ public static class LLMProvidersExtensions | ||||
|         LLMProviders.SELF_HOSTED => "Self-hosted", | ||||
|          | ||||
|         LLMProviders.HELMHOLTZ => "Helmholtz Blablador", | ||||
|         LLMProviders.GWDG => "GWDG SAIA", | ||||
|          | ||||
|         _ => "Unknown", | ||||
|     }; | ||||
| @ -72,6 +74,7 @@ public static class LLMProvidersExtensions | ||||
|         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)), | ||||
|         LLMProviders.GWDG => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://docs.hpc.gwdg.de/services/chat-ai/data-privacy/index.html").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)), | ||||
|          | ||||
|         _ => Confidence.UNKNOWN.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)), | ||||
|     }; | ||||
| @ -98,6 +101,7 @@ public static class LLMProvidersExtensions | ||||
|         LLMProviders.ANTHROPIC => false, | ||||
|         LLMProviders.FIREWORKS => false, | ||||
|         LLMProviders.X => false, | ||||
|         LLMProviders.GWDG => false, | ||||
|          | ||||
|         // | ||||
|         // Self-hosted providers are treated as a special case anyway. | ||||
| @ -147,6 +151,7 @@ public static class LLMProvidersExtensions | ||||
|                 LLMProviders.SELF_HOSTED => new ProviderSelfHosted(logger, host, hostname) { InstanceName = instanceName }, | ||||
|                  | ||||
|                 LLMProviders.HELMHOLTZ => new ProviderHelmholtz(logger) { InstanceName = instanceName }, | ||||
|                 LLMProviders.GWDG => new ProviderGWDG(logger) { InstanceName = instanceName }, | ||||
|                  | ||||
|                 _ => new NoProvider(), | ||||
|             }; | ||||
| @ -170,6 +175,7 @@ public static class LLMProvidersExtensions | ||||
|         LLMProviders.FIREWORKS => "https://fireworks.ai/login", | ||||
|          | ||||
|         LLMProviders.HELMHOLTZ => "https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/#step-1-register-on-gitlab", | ||||
|         LLMProviders.GWDG => "https://docs.hpc.gwdg.de/services/saia/index.html#api-request", | ||||
|          | ||||
|         _ => string.Empty, | ||||
|     }; | ||||
| @ -241,6 +247,7 @@ public static class LLMProvidersExtensions | ||||
|         LLMProviders.GROQ => true, | ||||
|         LLMProviders.FIREWORKS => true, | ||||
|         LLMProviders.HELMHOLTZ => true, | ||||
|         LLMProviders.GWDG => true, | ||||
|          | ||||
|         LLMProviders.SELF_HOSTED => host is Host.OLLAMA, | ||||
|          | ||||
| @ -258,6 +265,7 @@ public static class LLMProvidersExtensions | ||||
|         LLMProviders.GROQ => true, | ||||
|         LLMProviders.FIREWORKS => true, | ||||
|         LLMProviders.HELMHOLTZ => true, | ||||
|         LLMProviders.GWDG => true, | ||||
|          | ||||
|         _ => false, | ||||
|     }; | ||||
|  | ||||
| @ -1,2 +1,3 @@ | ||||
| # 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 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. | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user