From 65e2d202d710e405fe8c0ebf491853aa7f47fcf1 Mon Sep 17 00:00:00 2001 From: Andrei Alistar Date: Wed, 31 Jul 2024 16:52:11 +0300 Subject: [PATCH 1/2] stabilize Gatekeeper API --- .../Services/AzureContentSafetyService.cs | 14 +++++++------- src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj | 1 + src/dotnet/GatekeeperAPI/Program.cs | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs b/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs index b6a02d7c56..fbeb5f0ea5 100644 --- a/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs +++ b/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs @@ -47,17 +47,17 @@ public async Task AnalyzeText(string content) { var client = await _httpClientFactoryService.CreateClient(HttpClientNames.AzureContentSafety, _callContext.CurrentUserIdentity); - Response? results = null; + AnalyzeTextResult? results = null; try { var response = await client.PostAsync("/contentsafety/text:analyze?api-version=2023-10-01", - new StringContent(JsonSerializer.Serialize(new AnalyzeTextOptions(content)), + new StringContent(JsonSerializer.Serialize(new { text = content }), Encoding.UTF8, "application/json")); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); - results = JsonSerializer.Deserialize>(responseContent); + results = JsonSerializer.Deserialize(responseContent); } } catch (RequestFailedException ex) @@ -72,28 +72,28 @@ public async Task AnalyzeText(string content) var safe = true; var reason = "The prompt text did not pass the content safety filter. Reason:"; - var hateSeverity = results.Value.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.Hate)?.Severity ?? 0; + var hateSeverity = results.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.Hate)?.Severity ?? 0; if (hateSeverity > _settings.HateSeverity) { reason += $" hate"; safe = false; } - var violenceSeverity = results.Value.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.Violence)?.Severity ?? 0; + var violenceSeverity = results.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.Violence)?.Severity ?? 0; if (violenceSeverity > _settings.ViolenceSeverity) { reason += $" violence"; safe = false; } - var selfHarmSeverity = results.Value.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.SelfHarm)?.Severity ?? 0; + var selfHarmSeverity = results.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.SelfHarm)?.Severity ?? 0; if (selfHarmSeverity > _settings.SelfHarmSeverity) { reason += $" self-harm"; safe = false; } - var sexualSeverity = results.Value.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.Sexual)?.Severity ?? 0; + var sexualSeverity = results.CategoriesAnalysis.FirstOrDefault(a => a.Category == TextCategory.Sexual)?.Severity ?? 0; if (sexualSeverity > _settings.SexualSeverity) { reason += $" sexual"; diff --git a/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj b/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj index 3b5806dcdc..e6fcae9481 100644 --- a/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj +++ b/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj @@ -56,6 +56,7 @@ + diff --git a/src/dotnet/GatekeeperAPI/Program.cs b/src/dotnet/GatekeeperAPI/Program.cs index ab14a32a03..21a88382d8 100644 --- a/src/dotnet/GatekeeperAPI/Program.cs +++ b/src/dotnet/GatekeeperAPI/Program.cs @@ -8,7 +8,9 @@ using FoundationaLLM.Common.Models.Configuration.Instance; using FoundationaLLM.Common.Models.Context; using FoundationaLLM.Common.OpenAPI; +using FoundationaLLM.Common.Services.Azure; using FoundationaLLM.Common.Services.Security; +using FoundationaLLM.Common.Validation; using FoundationaLLM.Gatekeeper.Core.Interfaces; using FoundationaLLM.Gatekeeper.Core.Models.ConfigurationOptions; using FoundationaLLM.Gatekeeper.Core.Services; @@ -46,6 +48,10 @@ public static void Main(string[] args) }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatekeeperAPI_Configuration); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatekeeperAPI_Essentials); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_ResourceProviders_Configuration_Storage); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Events_Profiles_CoreAPI); //TODO: Replace this with a more granular approach that would only bring in the configuration namespaces that are actually needed. options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints); @@ -73,6 +79,18 @@ public static void Main(string[] args) builder.Services.AddOptions() .Bind(builder.Configuration.GetSection(AppConfigurationKeySections.FoundationaLLM_Instance)); + // Add Azure ARM services + builder.Services.AddAzureResourceManager(); + + // Add event services + builder.Services.AddAzureEventGridEvents( + builder.Configuration, + AppConfigurationKeySections.FoundationaLLM_Events_Profiles_CoreAPI); + + // Add resource providers + builder.Services.AddSingleton(); + builder.AddConfigurationResourceProvider(); + // Register the downstream services and HTTP clients. builder.AddHttpClientFactoryService(); builder.AddDownstreamAPIService(HttpClientNames.GatekeeperIntegrationAPI); From 64bda2f72ab364473dc02e588fb6a552c5b440ae Mon Sep 17 00:00:00 2001 From: Andrei Alistar Date: Thu, 1 Aug 2024 10:29:39 +0300 Subject: [PATCH 2/2] Gatekeeper API stabilization --- .../AppConfigurationKeyFilters.cs | 6 +++ .../AppConfigurationKeySections.cs | 6 +++ .../Configuration/AppConfigurationKeys.cs | 11 ++++ .../Constants/Data/AppConfiguration.json | 16 ++++++ src/dotnet/Gatekeeper/Gatekeeper.csproj | 1 - .../Models/ContentSafety/AnalyzeTextResult.cs | 54 +++++++++++++++++++ .../Services/AzureContentSafetyService.cs | 1 - .../Gatekeeper/Services/GatekeeperService.cs | 4 +- src/dotnet/GatekeeperAPI/Program.cs | 4 +- 9 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 src/dotnet/Gatekeeper/Models/ContentSafety/AnalyzeTextResult.cs diff --git a/src/dotnet/Common/Constants/Configuration/AppConfigurationKeyFilters.cs b/src/dotnet/Common/Constants/Configuration/AppConfigurationKeyFilters.cs index d2210bbde9..3f62bd3068 100644 --- a/src/dotnet/Common/Constants/Configuration/AppConfigurationKeyFilters.cs +++ b/src/dotnet/Common/Constants/Configuration/AppConfigurationKeyFilters.cs @@ -335,5 +335,11 @@ public static partial class AppConfigurationKeyFilters /// public const string FoundationaLLM_Events_Profiles_VectorizationWorker = "FoundationaLLM:Events:Profiles:VectorizationWorker:*"; + + /// + /// Filter for the configuration section used to identify the settings for the events infrastructure used by the Gatekeeper API. + /// + public const string FoundationaLLM_Events_Profiles_GatekeeperAPI = + "FoundationaLLM:Events:Profiles:GatekeeperAPI:*"; } } diff --git a/src/dotnet/Common/Constants/Configuration/AppConfigurationKeySections.cs b/src/dotnet/Common/Constants/Configuration/AppConfigurationKeySections.cs index 6bef6a5718..523872d68d 100644 --- a/src/dotnet/Common/Constants/Configuration/AppConfigurationKeySections.cs +++ b/src/dotnet/Common/Constants/Configuration/AppConfigurationKeySections.cs @@ -335,5 +335,11 @@ public static partial class AppConfigurationKeySections /// public const string FoundationaLLM_Events_Profiles_VectorizationWorker = "FoundationaLLM:Events:Profiles:VectorizationWorker"; + + /// + /// Configuration section used to identify the settings for the events infrastructure used by the Gatekeeper API. + /// + public const string FoundationaLLM_Events_Profiles_GatekeeperAPI = + "FoundationaLLM:Events:Profiles:GatekeeperAPI"; } } diff --git a/src/dotnet/Common/Constants/Configuration/AppConfigurationKeys.cs b/src/dotnet/Common/Constants/Configuration/AppConfigurationKeys.cs index ef0eda53d2..9d374cb393 100644 --- a/src/dotnet/Common/Constants/Configuration/AppConfigurationKeys.cs +++ b/src/dotnet/Common/Constants/Configuration/AppConfigurationKeys.cs @@ -1083,6 +1083,13 @@ public static class AppConfigurationKeys /// public const string FoundationaLLM_Events_Profiles_VectorizationWorker = "FoundationaLLM:Events:Profiles:VectorizationWorker"; + + /// + /// The app configuration key for the FoundationaLLM:Events:Profiles:GatekeeperAPI setting. + /// Value description:
The settings used by the Gatekeeper API to process Azure Event Grid events.
+ ///
+ public const string FoundationaLLM_Events_Profiles_GatekeeperAPI = + "FoundationaLLM:Events:Profiles:GatekeeperAPI"; #endregion @@ -1105,5 +1112,9 @@ public static class AppConfigurationKeys #region FoundationaLLM:Events:Profiles:VectorizationWorker #endregion + + #region FoundationaLLM:Events:Profiles:GatekeeperAPI + + #endregion } } diff --git a/src/dotnet/Common/Constants/Data/AppConfiguration.json b/src/dotnet/Common/Constants/Data/AppConfiguration.json index 682d96f3f1..0719efc8bf 100644 --- a/src/dotnet/Common/Constants/Data/AppConfiguration.json +++ b/src/dotnet/Common/Constants/Data/AppConfiguration.json @@ -1480,6 +1480,14 @@ "value": "${env:FOUNDATIONALLM_VECTORIZATION_WORKER_EVENT_GRID_PROFILE}", "content_type": "application/json", "first_version": "0.8.0" + }, + { + "name": "GatekeeperAPI", + "description": "The settings used by the Gatekeeper API to process Azure Event Grid events.", + "secret": "", + "value": "${env:FOUNDATIONALLM_GATEKEEPER_API_EVENT_GRID_PROFILE}", + "content_type": "application/json", + "first_version": "0.8.0" } ] }, @@ -1522,5 +1530,13 @@ "description": "Configuration section used to identify the settings for the events infrastructure used by the Vectorization Worker service." }, "configuration_keys": [] + }, + { + "namespace": "Events:Profiles:GatekeeperAPI", + "dependency_injection_key": null, + "configuration_section": { + "description": "Configuration section used to identify the settings for the events infrastructure used by the Gatekeeper API." + }, + "configuration_keys": [] } ] diff --git a/src/dotnet/Gatekeeper/Gatekeeper.csproj b/src/dotnet/Gatekeeper/Gatekeeper.csproj index 1b77bb0225..83a5c56f84 100644 --- a/src/dotnet/Gatekeeper/Gatekeeper.csproj +++ b/src/dotnet/Gatekeeper/Gatekeeper.csproj @@ -11,7 +11,6 @@ - diff --git a/src/dotnet/Gatekeeper/Models/ContentSafety/AnalyzeTextResult.cs b/src/dotnet/Gatekeeper/Models/ContentSafety/AnalyzeTextResult.cs new file mode 100644 index 0000000000..848bf5b989 --- /dev/null +++ b/src/dotnet/Gatekeeper/Models/ContentSafety/AnalyzeTextResult.cs @@ -0,0 +1,54 @@ +using System.Text.Json.Serialization; + +namespace FoundationaLLM.Gatekeeper.Core.Models.ContentSafety +{ + /// + /// Text analysis results. + /// + public class AnalyzeTextResult + { + /// Analysis result for categories. + [JsonPropertyName("categoriesAnalysis")] + public required List CategoriesAnalysis { get; set; } + } + + /// + /// Text category results. + /// + public class TextCategoryResult + { + /// The text analysis category. + [JsonPropertyName("category")] + public required string Category { get; set; } + + /// The value increases with the severity of the input content. The value of this field is determined by the output type specified in the request. The output type could be ‘FourSeverityLevels’ or ‘EightSeverity Levels’, and the output value can be 0, 2, 4, 6 or 0, 1, 2, 3, 4, 5, 6, or 7. + [JsonPropertyName("severity")] + public int? Severity { get; set; } + } + + /// + /// Text category constants. + /// + public static class TextCategory + { + /// + /// Hate. + /// + public const string Hate = "Hate"; + + /// + /// Violence. + /// + public const string Violence = "Violence"; + + /// + /// SelfHarm. + /// + public const string SelfHarm = "SelfHarm"; + + /// + /// Sexual. + /// + public const string Sexual = "Sexual"; + } +} diff --git a/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs b/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs index fbeb5f0ea5..2b8c9bb712 100644 --- a/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs +++ b/src/dotnet/Gatekeeper/Services/AzureContentSafetyService.cs @@ -1,5 +1,4 @@ using Azure; -using Azure.AI.ContentSafety; using FoundationaLLM.Common.Constants; using FoundationaLLM.Common.Interfaces; using FoundationaLLM.Gatekeeper.Core.Interfaces; diff --git a/src/dotnet/Gatekeeper/Services/GatekeeperService.cs b/src/dotnet/Gatekeeper/Services/GatekeeperService.cs index e02b665994..77f7e726bf 100644 --- a/src/dotnet/Gatekeeper/Services/GatekeeperService.cs +++ b/src/dotnet/Gatekeeper/Services/GatekeeperService.cs @@ -72,7 +72,7 @@ public async Task GetCompletion(string instanceId, Completio var promptInjectionResult = await _contentSafetyService.DetectPromptInjection(completionRequest.UserPrompt!); if (!string.IsNullOrWhiteSpace(promptInjectionResult)) - return new CompletionResponse() { OperationId=completionRequest.OperationId, Completion = promptInjectionResult }; + return new CompletionResponse() { OperationId = completionRequest.OperationId, Completion = promptInjectionResult }; } if (_gatekeeperServiceSettings.EnableAzureContentSafety) @@ -103,6 +103,6 @@ public async Task StartCompletionOperation(string instance public async Task GetCompletionOperationResult(string instanceId, string operationId) => // TODO: Need to call State API to get the operation. throw new NotImplementedException(); - + } } diff --git a/src/dotnet/GatekeeperAPI/Program.cs b/src/dotnet/GatekeeperAPI/Program.cs index 21a88382d8..a92e4a83e2 100644 --- a/src/dotnet/GatekeeperAPI/Program.cs +++ b/src/dotnet/GatekeeperAPI/Program.cs @@ -51,7 +51,7 @@ public static void Main(string[] args) options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatekeeperAPI_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatekeeperAPI_Essentials); options.Select(AppConfigurationKeyFilters.FoundationaLLM_ResourceProviders_Configuration_Storage); - options.Select(AppConfigurationKeyFilters.FoundationaLLM_Events_Profiles_CoreAPI); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Events_Profiles_GatekeeperAPI); //TODO: Replace this with a more granular approach that would only bring in the configuration namespaces that are actually needed. options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints); @@ -85,7 +85,7 @@ public static void Main(string[] args) // Add event services builder.Services.AddAzureEventGridEvents( builder.Configuration, - AppConfigurationKeySections.FoundationaLLM_Events_Profiles_CoreAPI); + AppConfigurationKeySections.FoundationaLLM_Events_Profiles_GatekeeperAPI); // Add resource providers builder.Services.AddSingleton();