From 60e78daf5452b514427322d786e76d9574b9b853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 23 Jan 2025 14:10:39 +0100 Subject: [PATCH 01/13] Add ADI --- src/System Application/App/AI/app.json | 2 +- .../DocumentIntelligence/ADIModelType.Enum.al | 29 +++++ .../DocumentIntelligence/AzureDI.Codeunit.al | 49 ++++++++ .../AzureDIImpl.Codeunit.al | 115 ++++++++++++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al create mode 100644 src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al create mode 100644 src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al diff --git a/src/System Application/App/AI/app.json b/src/System Application/App/AI/app.json index cc9675aba2..8b14735b3f 100644 --- a/src/System Application/App/AI/app.json +++ b/src/System Application/App/AI/app.json @@ -90,7 +90,7 @@ "idRanges": [ { "from": 7758, - "to": 7778 + "to": 7780 } ], "target": "OnPrem", diff --git a/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al b/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al new file mode 100644 index 0000000000..ab5ae7bb4d --- /dev/null +++ b/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.AI; + +/// +/// The supported model types for Azure OpenAI. +/// +enum 7779 "ADI Model Type" +{ + Access = Public; + Extensible = false; + + /// + /// Invoice model type. + /// + value(0; Invoice) + { + } + + /// + /// Recepit model type. + /// + value(1; Recepit) + { + } + +} \ No newline at end of file diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al new file mode 100644 index 0000000000..5f99f6479c --- /dev/null +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.Azure.DI; + +/// +/// Azure Document Intelligence implementation. +/// +codeunit 9970 "Azure DI" +{ + Access = Public; + InherentEntitlements = X; + InherentPermissions = X; + + var + AzureOpenAIImpl: Codeunit "Azure DI Impl."; + + + /// + /// Analyze the invoice. + /// + /// Data to analyze. + /// The analyzed result. + [Scope('OnPrem')] + procedure AnalyzeInvoice(Base64Data: Text): Text + var + CallerModuleInfo: ModuleInfo; + begin + NavApp.GetCallerModuleInfo(CallerModuleInfo); + exit(AzureOpenAIImpl.AnalyzeInvoice(Base64Data, CallerModuleInfo)); + end; + + /// + /// Analyze the Receipt. + /// + /// Data to analyze. + /// The analyzed result. + [Scope('OnPrem')] + procedure AnalyzeReceipt(Base64Data: Text): Text + var + CallerModuleInfo: ModuleInfo; + begin + NavApp.GetCallerModuleInfo(CallerModuleInfo); + exit(AzureOpenAIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo)); + end; + + +} \ No newline at end of file diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al new file mode 100644 index 0000000000..dc3e376d51 --- /dev/null +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al @@ -0,0 +1,115 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.Azure.DI; +using System.AI; +using System.Globalization; +using System.Telemetry; + +/// +/// Azure Document Intelligence implementation. +/// +codeunit 7779 "Azure DI Impl." +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + /// + /// Analyze the invoice. + /// + /// Data to analyze. + /// The module info of the caller. + /// The analyzed result. + [NonDebuggable] + procedure AnalyzeInvoice(Base64Data: Text; CallerModuleInfo: ModuleInfo) Result: Text + var + CustomDimensions: Dictionary of [Text, Text]; + begin + AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + + if not SendRequest(Base64Data, Enum::"ADI Model Type"::Invoice, CallerModuleInfo, Result) then begin + FeatureTelemetry.LogError('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + exit; + end; + + FeatureTelemetry.LogUsage('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + + end; + + /// + /// Analyze the receipt. + /// + /// Data to analyze. + /// The module info of the caller. + /// The analyzed result. + [NonDebuggable] + procedure AnalyzeReceipt(Base64Data: Text; CallerModuleInfo: ModuleInfo) Result: Text + var + CustomDimensions: Dictionary of [Text, Text]; + begin + AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + + if not SendRequest(Base64Data, Enum::"ADI Model Type"::Recepit, CallerModuleInfo, Result) then begin + FeatureTelemetry.LogError('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + exit; + end; + + FeatureTelemetry.LogUsage('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + end; + + [TryFunction] + [NonDebuggable] + local procedure SendRequest(Base64Data: Text; ModelType: Enum "ADI Model Type"; CallerModuleInfo: ModuleInfo; var Result: Text) + var + ALCopilotFunctions: DotNet ALCopilotFunctions; + ALCopilotCapability: DotNet ALCopilotCapability; + ALCopilotResponse: DotNet ALCopilotOperationResponse; + ErrorMsg: Text; + begin + ClearLastError(); + ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), AzureDocumentIntelligenceCapabilityTok); + case ModelType of + Enum::"ADI Model Type"::Invoice: + ALCopilotResponse := ALCopilotFunctions.GenerateInvoiceIntelligence(Base64Data, ALCopilotCapability); + Enum::"ADI Model Type"::Recepit: + ALCopilotResponse := ALCopilotFunctions.GenerateReceiptIntelligence(Base64Data, ALCopilotCapability); + end; + ErrorMsg := ALCopilotResponse.ErrorText(); + if ErrorMsg <> '' then + Error(ErrorMsg); + + if not ALCopilotResponse.IsSuccess() then + Error(GenerateRequestFailedErr); + + Result := ALCopilotResponse.Result(); + end; + + + local procedure AddTelemetryCustomDimensions(var CustomDimensions: Dictionary of [Text, Text]; CallerModuleInfo: ModuleInfo) + var + Language: Codeunit Language; + SavedGlobalLanguageId: Integer; + begin + SavedGlobalLanguageId := GlobalLanguage(); + GlobalLanguage(Language.GetDefaultApplicationLanguageId()); + + CustomDimensions.Add('Capability', AzureDocumentIntelligenceCapabilityTok); + CustomDimensions.Add('AppId', CallerModuleInfo.Id); + CustomDimensions.Add('Publisher', CallerModuleInfo.Publisher); + CustomDimensions.Add('UserLanguage', Format(GlobalLanguage())); + + GlobalLanguage(SavedGlobalLanguageId); + end; + + var + FeatureTelemetry: Codeunit "Feature Telemetry"; + AzureDocumentIntelligenceCapabilityTok: Label 'ADI', Locked = true; + TelemetryAnalyzeInvoiceFailureLbl: Label 'Analyze invoice failed.', Locked = true; + TelemetryAnalyzeInvoiceCompletedLbl: Label 'Analyze invoice completed.', Locked = true; + TelemetryAnalyzeReceiptFailureLbl: Label 'Analyze receipt failed.', Locked = true; + TelemetryAnalyzeReceiptCompletedLbl: Label 'Analyze receipt completed.', Locked = true; + GenerateRequestFailedErr: Label 'The request did not return a success status code.'; + +} \ No newline at end of file From 6e860fbd7ac5aea4f1755f7df90ced7eaf800ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 23 Jan 2025 14:25:34 +0100 Subject: [PATCH 02/13] tags --- .../AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al index dc3e376d51..488ece111a 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al @@ -30,11 +30,11 @@ codeunit 7779 "Azure DI Impl." AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); if not SendRequest(Base64Data, Enum::"ADI Model Type"::Invoice, CallerModuleInfo, Result) then begin - FeatureTelemetry.LogError('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000OLK', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; - FeatureTelemetry.LogUsage('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogUsage('0000OLM', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); end; @@ -52,11 +52,11 @@ codeunit 7779 "Azure DI Impl." AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); if not SendRequest(Base64Data, Enum::"ADI Model Type"::Recepit, CallerModuleInfo, Result) then begin - FeatureTelemetry.LogError('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000OLL', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; - FeatureTelemetry.LogUsage('', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogUsage('0000OLN', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); end; [TryFunction] From 838fba7fabe2e696fec37307ef93c4a21ebfe87c Mon Sep 17 00:00:00 2001 From: Jose Antonio Garcia Garcia Date: Thu, 23 Jan 2025 19:33:42 +0100 Subject: [PATCH 03/13] Minor corrections --- .../App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al | 1 + 1 file changed, 1 insertion(+) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al index 488ece111a..7fd606aafa 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al @@ -6,6 +6,7 @@ namespace System.Azure.DI; using System.AI; using System.Globalization; using System.Telemetry; +using System; /// /// Azure Document Intelligence implementation. From b91bdb9acaabb7085f4c3cdf82b064b615cff470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Fri, 24 Jan 2025 08:32:58 +0100 Subject: [PATCH 04/13] Fix unused label --- .../App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al index 488ece111a..4db45919e7 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al @@ -52,11 +52,11 @@ codeunit 7779 "Azure DI Impl." AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); if not SendRequest(Base64Data, Enum::"ADI Model Type"::Recepit, CallerModuleInfo, Result) then begin - FeatureTelemetry.LogError('0000OLL', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000OLL', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeReceiptFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; - FeatureTelemetry.LogUsage('0000OLN', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogUsage('0000OLN', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeReceiptCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); end; [TryFunction] From e351912beca6578198a7b2dc95655f00eae270c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Fri, 24 Jan 2025 10:03:04 +0100 Subject: [PATCH 05/13] pack into json --- .../AzureDIImpl.Codeunit.al | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al index c061f10831..aff7e8ef0d 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al @@ -73,9 +73,9 @@ codeunit 7779 "Azure DI Impl." ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), AzureDocumentIntelligenceCapabilityTok); case ModelType of Enum::"ADI Model Type"::Invoice: - ALCopilotResponse := ALCopilotFunctions.GenerateInvoiceIntelligence(Base64Data, ALCopilotCapability); + ALCopilotResponse := ALCopilotFunctions.GenerateInvoiceIntelligence(GenerateJson(Base64Data), ALCopilotCapability); Enum::"ADI Model Type"::Recepit: - ALCopilotResponse := ALCopilotFunctions.GenerateReceiptIntelligence(Base64Data, ALCopilotCapability); + ALCopilotResponse := ALCopilotFunctions.GenerateReceiptIntelligence(GenerateJson(Base64Data), ALCopilotCapability); end; ErrorMsg := ALCopilotResponse.ErrorText(); if ErrorMsg <> '' then @@ -104,6 +104,25 @@ codeunit 7779 "Azure DI Impl." GlobalLanguage(SavedGlobalLanguageId); end; + local procedure GenerateJson(Base64: Text): Text + var + JsonObject: JsonObject; + InputsObject: JsonObject; + InnerObject: JsonObject; + JsonText: Text; + begin + // Create the inner object with the base64Encoded property + InnerObject.Add('base64_encoded', Base64); + // Create the inputs object and add the inner object to it + InputsObject.Add('1', InnerObject); + // Create the main JSON object and add the inputs object to it + JsonObject.Add('inputs', InputsObject); + // Convert the JSON object to text + JsonObject.WriteTo(JsonText); + // Return the JSON text + exit(JsonText); + end; + var FeatureTelemetry: Codeunit "Feature Telemetry"; AzureDocumentIntelligenceCapabilityTok: Label 'ADI', Locked = true; From b0026362bcc48f755d2b9d7adc169ece09448ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Sat, 25 Jan 2025 11:29:49 +0100 Subject: [PATCH 06/13] Fix id --- .../App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al index 5f99f6479c..d63a3b7076 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al @@ -7,7 +7,7 @@ namespace System.Azure.DI; /// /// Azure Document Intelligence implementation. /// -codeunit 9970 "Azure DI" +codeunit 7780 "Azure DI" { Access = Public; InherentEntitlements = X; From 8b1a43212a9d7e46e2a7c49ba4f84b8aceacfc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 30 Jan 2025 11:23:46 +0100 Subject: [PATCH 07/13] Update namespell --- .../src/DocumentIntelligence/ADIModelType.Enum.al | 4 ++-- .../DocumentIntelligence/AzureDIImpl.Codeunit.al | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al b/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al index ab5ae7bb4d..0eff502a0a 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al @@ -20,9 +20,9 @@ enum 7779 "ADI Model Type" } /// - /// Recepit model type. + /// Receipt model type. /// - value(1; Recepit) + value(1; Receipt) { } diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al index aff7e8ef0d..51b0d708ae 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al @@ -18,7 +18,7 @@ codeunit 7779 "Azure DI Impl." InherentPermissions = X; /// - /// Analyze the invoice. + /// Analyze a single invoice. /// /// Data to analyze. /// The module info of the caller. @@ -40,7 +40,7 @@ codeunit 7779 "Azure DI Impl." end; /// - /// Analyze the receipt. + /// Analyze a single receipt. /// /// Data to analyze. /// The module info of the caller. @@ -52,7 +52,7 @@ codeunit 7779 "Azure DI Impl." begin AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); - if not SendRequest(Base64Data, Enum::"ADI Model Type"::Recepit, CallerModuleInfo, Result) then begin + if not SendRequest(Base64Data, Enum::"ADI Model Type"::Receipt, CallerModuleInfo, Result) then begin FeatureTelemetry.LogError('0000OLL', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeReceiptFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; @@ -73,9 +73,9 @@ codeunit 7779 "Azure DI Impl." ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), AzureDocumentIntelligenceCapabilityTok); case ModelType of Enum::"ADI Model Type"::Invoice: - ALCopilotResponse := ALCopilotFunctions.GenerateInvoiceIntelligence(GenerateJson(Base64Data), ALCopilotCapability); - Enum::"ADI Model Type"::Recepit: - ALCopilotResponse := ALCopilotFunctions.GenerateReceiptIntelligence(GenerateJson(Base64Data), ALCopilotCapability); + ALCopilotResponse := ALCopilotFunctions.GenerateInvoiceIntelligence(GenerateJsonForSingleInput(Base64Data), ALCopilotCapability); + Enum::"ADI Model Type"::Receipt: + ALCopilotResponse := ALCopilotFunctions.GenerateReceiptIntelligence(GenerateJsonForSingleInput(Base64Data), ALCopilotCapability); end; ErrorMsg := ALCopilotResponse.ErrorText(); if ErrorMsg <> '' then @@ -104,7 +104,7 @@ codeunit 7779 "Azure DI Impl." GlobalLanguage(SavedGlobalLanguageId); end; - local procedure GenerateJson(Base64: Text): Text + local procedure GenerateJsonForSingleInput(Base64: Text): Text var JsonObject: JsonObject; InputsObject: JsonObject; From e0ea02621a2597a5c366b4783d6e47f9e606d9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 30 Jan 2025 15:14:40 +0100 Subject: [PATCH 08/13] Can we decouple OpenAI and Copilot module --- .../ADIModelType.Enum.al | 0 .../AzureDI.Codeunit.al | 22 +++++- .../AzureDIImpl.Codeunit.al | 23 +++++- .../src/Azure OpenAI/AzureOpenAI.Codeunit.al | 4 +- .../Azure OpenAI/AzureOpenAIImpl.Codeunit.al | 76 +++++++------------ .../src/Copilot/CopilotAICapabilities.Page.al | 7 +- .../Copilot/CopilotCapabilityImpl.Codeunit.al | 51 +++++++++---- 7 files changed, 112 insertions(+), 71 deletions(-) rename src/System Application/App/AI/src/{DocumentIntelligence => Azure AI Document Intelligence}/ADIModelType.Enum.al (100%) rename src/System Application/App/AI/src/{DocumentIntelligence => Azure AI Document Intelligence}/AzureDI.Codeunit.al (61%) rename src/System Application/App/AI/src/{DocumentIntelligence => Azure AI Document Intelligence}/AzureDIImpl.Codeunit.al (82%) diff --git a/src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al similarity index 100% rename from src/System Application/App/AI/src/DocumentIntelligence/ADIModelType.Enum.al rename to src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al similarity index 61% rename from src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al rename to src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al index d63a3b7076..5718f51e91 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al +++ b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al @@ -4,6 +4,8 @@ // ------------------------------------------------------------------------------------------------ namespace System.Azure.DI; +using System.AI; + /// /// Azure Document Intelligence implementation. /// @@ -14,7 +16,7 @@ codeunit 7780 "Azure DI" InherentPermissions = X; var - AzureOpenAIImpl: Codeunit "Azure DI Impl."; + AzureDIImpl: Codeunit "Azure DI Impl."; /// @@ -28,7 +30,7 @@ codeunit 7780 "Azure DI" CallerModuleInfo: ModuleInfo; begin NavApp.GetCallerModuleInfo(CallerModuleInfo); - exit(AzureOpenAIImpl.AnalyzeInvoice(Base64Data, CallerModuleInfo)); + exit(AzureDIImpl.AnalyzeInvoice(Base64Data, CallerModuleInfo)); end; /// @@ -42,8 +44,22 @@ codeunit 7780 "Azure DI" CallerModuleInfo: ModuleInfo; begin NavApp.GetCallerModuleInfo(CallerModuleInfo); - exit(AzureOpenAIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo)); + exit(AzureDIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo)); end; + /// + /// Sets the copilot capability that the API is running for. + /// + /// The copilot capability to set. + [NonDebuggable] + procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability") + var + CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; + CallerModuleInfo: ModuleInfo; + begin + NavApp.GetCallerModuleInfo(CallerModuleInfo); + CopilotCapabilityImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureDIImpl.GetAzureAIDocumentIntelligenceCategory()); + end; + } \ No newline at end of file diff --git a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al similarity index 82% rename from src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al rename to src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al index 51b0d708ae..9301e9f468 100644 --- a/src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al @@ -5,6 +5,7 @@ namespace System.Azure.DI; using System.AI; using System.Globalization; +using System.Privacy; using System.Telemetry; using System; @@ -87,7 +88,6 @@ codeunit 7779 "Azure DI Impl." Result := ALCopilotResponse.Result(); end; - local procedure AddTelemetryCustomDimensions(var CustomDimensions: Dictionary of [Text, Text]; CallerModuleInfo: ModuleInfo) var Language: Codeunit Language; @@ -123,13 +123,34 @@ codeunit 7779 "Azure DI Impl." exit(JsonText); end; + procedure GetAzureAIDocumentIntelligenceCategory(): Code[50] + begin + exit(AzureAiDocumentIntelligenceTxt); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Privacy Notice", OnRegisterPrivacyNotices, '', false, false)] + local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary) + begin + TempPrivacyNotice.Init(); + TempPrivacyNotice.ID := AzureAiDocumentIntelligenceTxt; + TempPrivacyNotice."Integration Service Name" := AzureAiDocumentIntelligenceTxt; + if not TempPrivacyNotice.Insert() then; + end; + var + // CopilotSettings: Record "Copilot Settings"; + // CopilotCapabilityCU: Codeunit "Copilot Capability"; + // CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; FeatureTelemetry: Codeunit "Feature Telemetry"; + // TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true; + // TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true; + // CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability'; AzureDocumentIntelligenceCapabilityTok: Label 'ADI', Locked = true; TelemetryAnalyzeInvoiceFailureLbl: Label 'Analyze invoice failed.', Locked = true; TelemetryAnalyzeInvoiceCompletedLbl: Label 'Analyze invoice completed.', Locked = true; TelemetryAnalyzeReceiptFailureLbl: Label 'Analyze receipt failed.', Locked = true; TelemetryAnalyzeReceiptCompletedLbl: Label 'Analyze receipt completed.', Locked = true; GenerateRequestFailedErr: Label 'The request did not return a success status code.'; + AzureAiDocumentIntelligenceTxt: Label 'Azure AI Document Intelligence', Locked = true; } \ No newline at end of file diff --git a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al index 7992af7d3f..9c12e924a2 100644 --- a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al +++ b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al @@ -265,11 +265,13 @@ codeunit 7771 "Azure OpenAI" [NonDebuggable] procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability") var + CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; CallerModuleInfo: ModuleInfo; begin NavApp.GetCallerModuleInfo(CallerModuleInfo); - AzureOpenAIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo); + CopilotCapabilityImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureOpenAIImpl.GetAzureOpenAICategory()); end; + #if not CLEAN24 /// /// Gets the approximate token count for the input. diff --git a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al index 932a32220b..25c5099284 100644 --- a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al @@ -22,7 +22,6 @@ codeunit 7772 "Azure OpenAI Impl" var CopilotSettings: Record "Copilot Settings"; CopilotCapabilityCU: Codeunit "Copilot Capability"; - CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; ChatCompletionsAOAIAuthorization: Codeunit "AOAI Authorization"; TextCompletionsAOAIAuthorization: Codeunit "AOAI Authorization"; EmbeddingsAOAIAuthorization: Codeunit "AOAI Authorization"; @@ -39,8 +38,6 @@ codeunit 7772 "Azure OpenAI Impl" CopilotCapabilityNotSetErr: Label 'Copilot capability has not been set.'; CapabilityBackgroundErr: Label 'Microsoft Copilot Capabilities are not allowed in the background.'; CopilotDisabledForTenantErr: Label 'Copilot is not enabled for the tenant. Please contact your system administrator.'; - CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability'; - CapabilityNotEnabledErr: Label 'Copilot capability ''%1'' has not been enabled. Please contact your system administrator.', Comment = '%1 is the name of the Copilot Capability'; MessagesMustContainJsonWordWhenResponseFormatIsJsonErr: Label 'The messages must contain the word ''json'' in some form, to use ''response format'' of type ''json_object''.'; EmptyMetapromptErr: Label 'The metaprompt has not been set, please provide a metaprompt.'; MetapromptLoadingErr: Label 'Metaprompt not found.'; @@ -52,8 +49,6 @@ codeunit 7772 "Azure OpenAI Impl" TelemetryGenerateChatCompletionLbl: Label 'Chat Completion generated.', Locked = true; TelemetryChatCompletionToolCallLbl: Label 'Tools called by chat completion.', Locked = true; TelemetryChatCompletionToolUsedLbl: Label 'Tools added to chat completion.', Locked = true; - TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true; - TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true; TelemetryIsEnabledLbl: Label 'Is Enabled', Locked = true; TelemetryUnableToCheckEnvironmentKVTxt: Label 'Unable to check if environment is allowed to run AOAI.', Locked = true; TelemetryEnvironmentNotAllowedtoRunCopilotTxt: Label 'Copilot is not allowed on this environment.', Locked = true; @@ -63,6 +58,7 @@ codeunit 7772 "Azure OpenAI Impl" TelemetryFunctionCallingFailedErr: Label 'Function calling failed for function: %1', Comment = '%1 is the name of the function', Locked = true; TelemetryEmptyTenantIdErr: Label 'Empty or malformed tenant ID.', Locked = true; TelemetryTenantAllowlistedMsg: Label 'Current tenant allowlisted for first party auth.', Locked = true; + AzureOpenAiTxt: Label 'Azure OpenAI', Locked = true; procedure IsEnabled(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo): Boolean begin @@ -109,12 +105,12 @@ codeunit 7772 "Azure OpenAI Impl" exit(true); if (not AzureKeyVault.GetAzureKeyVaultSecret(EnabledKeyTok, BlockList)) or (BlockList.Trim() = '') then begin - FeatureTelemetry.LogError('0000KYC', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt); + FeatureTelemetry.LogError('0000KYC', GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt); exit(false); end; if BlockList.Contains(AzureAdTenant.GetAadTenantId()) then begin - FeatureTelemetry.LogError('0000LFP', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryEnvironmentNotAllowedtoRunCopilotTxt); + FeatureTelemetry.LogError('0000LFP', GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryEnvironmentNotAllowedtoRunCopilotTxt); exit(false); end; @@ -126,7 +122,7 @@ codeunit 7772 "Azure OpenAI Impl" PrivacyNotice: Codeunit "Privacy Notice"; CopilotNotAvailable: Page "Copilot Not Available"; begin - case PrivacyNotice.GetPrivacyNoticeApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), false) of + case PrivacyNotice.GetPrivacyNoticeApprovalState(GetAzureOpenAICategory(), false) of Enum::"Privacy Notice Approval State"::Agreed: exit(true); Enum::"Privacy Notice Approval State"::Disagreed: @@ -256,11 +252,11 @@ codeunit 7772 "Azure OpenAI Impl" SendTokenCountTelemetry(AOAIToken.GetGPT4TokenCount(Metaprompt), AOAIToken.GetGPT4TokenCount(Prompt), CustomDimensions); if not SendRequest(Enum::"AOAI Model Type"::"Text Completions", TextCompletionsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin - FeatureTelemetry.LogError('0000KVD', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, CompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000KVD', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, CompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; - FeatureTelemetry.LogUsage('0000KVL', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogUsage('0000KVL', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); Result := AOAIOperationResponse.GetResult(); end; @@ -283,11 +279,11 @@ codeunit 7772 "Azure OpenAI Impl" AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); SendTokenCountTelemetry(0, AOAIToken.GetAdaTokenCount(Input), CustomDimensions); if not SendRequest(Enum::"AOAI Model Type"::Embeddings, EmbeddingsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin - FeatureTelemetry.LogError('0000KVE', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, EmbeddingsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000KVE', GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, EmbeddingsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; - FeatureTelemetry.LogUsage('0000KVM', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogUsage('0000KVM', GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); exit(ProcessEmbeddingResponse(AOAIOperationResponse)); end; @@ -356,13 +352,13 @@ codeunit 7772 "Azure OpenAI Impl" SendTokenCountTelemetry(MetapromptTokenCount, PromptTokenCount, CustomDimensions); if not SendRequest(Enum::"AOAI Model Type"::"Chat Completions", ChatCompletionsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin - FeatureTelemetry.LogError('0000KVF', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, ChatCompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000KVF', GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, ChatCompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); exit; end; ProcessChatCompletionResponse(ChatMessages, AOAIOperationResponse, CallerModuleInfo); - FeatureTelemetry.LogUsage('0000KVN', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogUsage('0000KVN', GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions); if (AOAIOperationResponse.GetFunctionResponses().Count() > 0) and (ChatMessages.GetToolInvokePreference() = Enum::"AOAI Tool Invoke Preference"::Automatic) then GenerateChatCompletion(ChatMessages, AOAIChatCompletionParams, AOAIOperationResponse, CallerModuleInfo); @@ -421,7 +417,7 @@ codeunit 7772 "Azure OpenAI Impl" AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); foreach AOAIFunctionResponse in AOAIOperationResponse.GetFunctionResponses() do if not AOAIFunctionResponse.IsSuccess() then - FeatureTelemetry.LogError('0000MTB', CopilotCapabilityImpl.GetAzureOpenAICategory(), StrSubstNo(TelemetryFunctionCallingFailedErr, AOAIFunctionResponse.GetFunctionName()), AOAIFunctionResponse.GetError(), AOAIFunctionResponse.GetErrorCallstack(), Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000MTB', GetAzureOpenAICategory(), StrSubstNo(TelemetryFunctionCallingFailedErr, AOAIFunctionResponse.GetFunctionName()), AOAIFunctionResponse.GetError(), AOAIFunctionResponse.GetErrorCallstack(), Enum::"AL Telemetry Scope"::All, CustomDimensions); if ChatMessages.GetToolInvokePreference() in [Enum::"AOAI Tool Invoke Preference"::"Invoke Tools Only", Enum::"AOAI Tool Invoke Preference"::Automatic] then AOAIOperationResponse.AppendFunctionResponsesToChatMessages(ChatMessages); @@ -600,36 +596,6 @@ codeunit 7772 "Azure OpenAI Impl" GlobalLanguage(SavedGlobalLanguageId); end; - procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo) - var - CopilotTelemetry: Codeunit "Copilot Telemetry"; - Language: Codeunit Language; - SavedGlobalLanguageId: Integer; - CustomDimensions: Dictionary of [Text, Text]; - ErrorMessage: Text; - begin - if not CopilotCapabilityCU.IsCapabilityRegistered(Capability, CallerModuleInfo.Id()) then begin - SavedGlobalLanguageId := GlobalLanguage(); - GlobalLanguage(Language.GetDefaultApplicationLanguageId()); - CustomDimensions.Add('Capability', Format(Capability)); - CustomDimensions.Add('AppId', Format(CallerModuleInfo.Id())); - GlobalLanguage(SavedGlobalLanguageId); - - FeatureTelemetry.LogError('0000LFN', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetrySetCapabilityLbl, TelemetryCopilotCapabilityNotRegisteredLbl, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); - ErrorMessage := StrSubstNo(CapabilityNotRegisteredErr, Capability); - Error(ErrorMessage); - end; - - CopilotSettings.ReadIsolation(IsolationLevel::ReadCommitted); - CopilotSettings.SetLoadFields(Status); - CopilotSettings.Get(Capability, CallerModuleInfo.Id()); - if CopilotSettings.Status = Enum::"Copilot Status"::Inactive then begin - ErrorMessage := StrSubstNo(CapabilityNotEnabledErr, Capability); - Error(ErrorMessage); - end; - CopilotTelemetry.SetCopilotCapability(Capability, CallerModuleInfo.Id()); - end; - local procedure CheckEnabled(CallerModuleInfo: ModuleInfo) begin if not IsEnabled(CopilotSettings.Capability, true, CallerModuleInfo) then @@ -688,7 +654,7 @@ codeunit 7772 "Azure OpenAI Impl" ModuleInfo: ModuleInfo; begin if Metaprompt.Unwrap().Trim() = '' then begin - FeatureTelemetry.LogError('0000LO8', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, EmptyMetapromptErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + FeatureTelemetry.LogError('0000LO8', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, EmptyMetapromptErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); NavApp.GetCurrentModuleInfo(ModuleInfo); if ModuleInfo.Publisher = 'Microsoft' then @@ -747,15 +713,29 @@ codeunit 7772 "Azure OpenAI Impl" EntraTenantIdAsText := AzureAdTenant.GetAadTenantId(); if (EntraTenantIdAsText = '') or not Evaluate(EntraTenantIdAsGuid, EntraTenantIdAsText) or IsNullGuid(EntraTenantIdAsGuid) then begin - Session.LogMessage('0000MLN', TelemetryEmptyTenantIdErr, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CopilotCapabilityImpl.GetAzureOpenAICategory()); + Session.LogMessage('0000MLN', TelemetryEmptyTenantIdErr, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GetAzureOpenAICategory()); exit(false); end; if not AllowlistedTenants.Contains(EntraTenantIdAsText) then exit(false); - Session.LogMessage('0000MLE', TelemetryTenantAllowlistedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CopilotCapabilityImpl.GetAzureOpenAICategory()); + Session.LogMessage('0000MLE', TelemetryTenantAllowlistedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GetAzureOpenAICategory()); exit(true); end; + procedure GetAzureOpenAICategory(): Code[50] + begin + exit(AzureOpenAiTxt); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Privacy Notice", 'OnRegisterPrivacyNotices', '', false, false)] + local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary) + begin + TempPrivacyNotice.Init(); + TempPrivacyNotice.ID := AzureOpenAiTxt; + TempPrivacyNotice."Integration Service Name" := AzureOpenAiTxt; + if not TempPrivacyNotice.Insert() then; + end; + } \ No newline at end of file diff --git a/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al b/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al index 7a7c160902..73c8f3aa17 100644 --- a/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al +++ b/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al @@ -256,7 +256,7 @@ page 7775 "Copilot AI Capabilities" CopilotCapabilityImpl.CheckGeoAndEUDB(WithinGeo, WithinEUDB); - case PrivacyNotice.GetPrivacyNoticeApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), false) of + case PrivacyNotice.GetPrivacyNoticeApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), false) of Enum::"Privacy Notice Approval State"::Agreed: AllowDataMovement := true; Enum::"Privacy Notice Approval State"::Disagreed: @@ -297,9 +297,9 @@ page 7775 "Copilot AI Capabilities" local procedure UpdateAllowDataMovement() begin if AllowDataMovement then - PrivacyNotice.SetApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Agreed) + PrivacyNotice.SetApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Agreed) else - PrivacyNotice.SetApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Disagreed); + PrivacyNotice.SetApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Disagreed); CurrPage.GenerallyAvailableCapabilities.Page.SetDataMovement(AllowDataMovement); CurrPage.PreviewCapabilities.Page.SetDataMovement(AllowDataMovement); @@ -313,6 +313,7 @@ page 7775 "Copilot AI Capabilities" end; var + AzureOpenAIImpl: Codeunit "Azure OpenAI Impl"; CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; PrivacyNotice: Codeunit "Privacy Notice"; WithinEUDBArea: Boolean; diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al index 58800c420b..8c44d3d46d 100644 --- a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al @@ -23,12 +23,15 @@ codeunit 7774 "Copilot Capability Impl" var Telemetry: Codeunit Telemetry; CopilotCategoryLbl: Label 'Copilot', Locked = true; - AzureOpenAiTxt: Label 'Azure OpenAI', Locked = true; AlreadyRegisteredErr: Label 'Capability has already been registered.'; NotRegisteredErr: Label 'Copilot capability has not been registered by the module.'; ReviewPrivacyNoticeLbl: Label 'Review the privacy notice'; PrivacyNoticeDisagreedNotificationMessageLbl: Label 'To enable Copilot, please review and accept the privacy notice.'; CapabilitiesNotAvailableOnPremNotificationMessageLbl: Label 'Copilot capabilities published by Microsoft are not available on-premises. You can extend Copilot with custom capabilities and use them on-premises for development purposes only.'; + CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability'; + CapabilityNotEnabledErr: Label 'Copilot capability ''%1'' has not been enabled. Please contact your system administrator.', Comment = '%1 is the name of the Copilot Capability'; + TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true; + TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true; TelemetryRegisteredNewCopilotCapabilityLbl: Label 'New copilot capability registered.', Locked = true; TelemetryModifiedCopilotCapabilityLbl: Label 'Copilot capability modified', Locked = true; TelemetryUnregisteredCopilotCapabilityLbl: Label 'Copilot capability unregistered.', Locked = true; @@ -68,6 +71,38 @@ codeunit 7774 "Copilot Capability Impl" Telemetry.LogMessage('0000LDV', TelemetryRegisteredNewCopilotCapabilityLbl, Verbosity::Normal, DataClassification::OrganizationIdentifiableInformation, Enum::"AL Telemetry Scope"::All, CustomDimensions); end; + procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo; AzureAIServiceName: Text) + var + CopilotSettings: Record "Copilot Settings"; + CopilotTelemetry: Codeunit "Copilot Telemetry"; + FeatureTelemetry: Codeunit "Feature Telemetry"; + Language: Codeunit Language; + SavedGlobalLanguageId: Integer; + CustomDimensions: Dictionary of [Text, Text]; + ErrorMessage: Text; + begin + if not IsCapabilityRegistered(Capability, CallerModuleInfo.Id()) then begin + SavedGlobalLanguageId := GlobalLanguage(); + GlobalLanguage(Language.GetDefaultApplicationLanguageId()); + CustomDimensions.Add('Capability', Format(Capability)); + CustomDimensions.Add('AppId', Format(CallerModuleInfo.Id())); + GlobalLanguage(SavedGlobalLanguageId); + + FeatureTelemetry.LogError('0000LFN', AzureAIServiceName, TelemetrySetCapabilityLbl, TelemetryCopilotCapabilityNotRegisteredLbl, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); + ErrorMessage := StrSubstNo(CapabilityNotRegisteredErr, Capability); + Error(ErrorMessage); + end; + + CopilotSettings.ReadIsolation(IsolationLevel::ReadCommitted); + CopilotSettings.SetLoadFields(Status); + CopilotSettings.Get(Capability, CallerModuleInfo.Id()); + if CopilotSettings.Status = Enum::"Copilot Status"::Inactive then begin + ErrorMessage := StrSubstNo(CapabilityNotEnabledErr, Capability); + Error(ErrorMessage); + end; + CopilotTelemetry.SetCopilotCapability(Capability, CallerModuleInfo.Id()); + end; + procedure ModifyCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; LearnMoreUrl: Text[2048]; CallerModuleInfo: ModuleInfo) var CopilotSettings: Record "Copilot Settings"; @@ -209,11 +244,6 @@ codeunit 7774 "Copilot Capability Impl" Page.Run(Page::"Privacy Notices"); end; - procedure GetAzureOpenAICategory(): Code[50] - begin - exit(AzureOpenAiTxt); - end; - procedure GetCopilotCategory(): Code[50] begin exit(CopilotCategoryLbl); @@ -276,15 +306,6 @@ codeunit 7774 "Copilot Capability Impl" GuidedExperience.ResetAssistedSetup(ObjectType::Page, Page::"Copilot AI Capabilities"); end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Privacy Notice", 'OnRegisterPrivacyNotices', '', false, false)] - local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary) - begin - TempPrivacyNotice.Init(); - TempPrivacyNotice.ID := AzureOpenAiTxt; - TempPrivacyNotice."Integration Service Name" := AzureOpenAiTxt; - if not TempPrivacyNotice.Insert() then; - end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"System Action Triggers", 'GetCopilotCapabilityStatus', '', false, false)] local procedure GetCopilotCapabilityStatus(Capability: Integer; var IsEnabled: Boolean; AppId: Guid; Silent: Boolean) var From 893bb6bf85579dcb515bf99d79d1fdebe93e3a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 5 Feb 2025 15:01:07 +0100 Subject: [PATCH 09/13] Move abstraction around --- .../ADIModelType.Enum.al | 2 +- .../AzureDIImpl.Codeunit.al | 69 ++++---- ... => AzureDocumentIntelligence.Codeunit.al} | 7 +- .../src/Azure OpenAI/AzureOpenAI.Codeunit.al | 3 +- .../Azure OpenAI/AzureOpenAIImpl.Codeunit.al | 153 ++--------------- .../AI/src/Copilot/AzureAIServiceType.Enum.al | 30 ++++ .../Copilot/CopilotCapabilityImpl.Codeunit.al | 161 +++++++++++++++++- 7 files changed, 235 insertions(+), 190 deletions(-) rename src/System Application/App/AI/src/Azure AI Document Intelligence/{AzureDI.Codeunit.al => AzureDocumentIntelligence.Codeunit.al} (87%) create mode 100644 src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al diff --git a/src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al index 0eff502a0a..f41e6e80cf 100644 --- a/src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al +++ b/src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ -namespace System.AI; +namespace System.AI.DocumentIntelligence; /// /// The supported model types for Azure OpenAI. diff --git a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al index 9301e9f468..50b5bc8d63 100644 --- a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al @@ -2,12 +2,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ -namespace System.Azure.DI; -using System.AI; -using System.Globalization; +namespace System.AI.DocumentIntelligence; + using System.Privacy; using System.Telemetry; using System; +using System.AI; /// /// Azure Document Intelligence implementation. @@ -18,18 +18,36 @@ codeunit 7779 "Azure DI Impl." InherentEntitlements = X; InherentPermissions = X; + var + CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; + FeatureTelemetry: Codeunit "Feature Telemetry"; + AzureDocumentIntelligenceCapabilityTok: Label 'ADI', Locked = true; + TelemetryAnalyzeInvoiceFailureLbl: Label 'Analyze invoice failed.', Locked = true; + TelemetryAnalyzeInvoiceCompletedLbl: Label 'Analyze invoice completed.', Locked = true; + TelemetryAnalyzeReceiptFailureLbl: Label 'Analyze receipt failed.', Locked = true; + TelemetryAnalyzeReceiptCompletedLbl: Label 'Analyze receipt completed.', Locked = true; + GenerateRequestFailedErr: Label 'The request did not return a success status code.'; + AzureAiDocumentIntelligenceTxt: Label 'Azure AI Document Intelligence', Locked = true; + + + procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo; AzureAIServiceName: Text) + begin + CopilotCapabilityImpl.SetCopilotCapability(Capability, CallerModuleInfo, AzureAIServiceName); + end; + /// /// Analyze a single invoice. /// /// Data to analyze. /// The module info of the caller. /// The analyzed result. - [NonDebuggable] procedure AnalyzeInvoice(Base64Data: Text; CallerModuleInfo: ModuleInfo) Result: Text var CustomDimensions: Dictionary of [Text, Text]; begin - AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + CopilotCapabilityImpl.CheckCapabilitySet(); + CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure Document Intelligence"); + CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); if not SendRequest(Base64Data, Enum::"ADI Model Type"::Invoice, CallerModuleInfo, Result) then begin FeatureTelemetry.LogError('0000OLK', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); @@ -46,12 +64,13 @@ codeunit 7779 "Azure DI Impl." /// Data to analyze. /// The module info of the caller. /// The analyzed result. - [NonDebuggable] procedure AnalyzeReceipt(Base64Data: Text; CallerModuleInfo: ModuleInfo) Result: Text var CustomDimensions: Dictionary of [Text, Text]; begin - AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + CopilotCapabilityImpl.CheckCapabilitySet(); + CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure Document Intelligence"); + CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); if not SendRequest(Base64Data, Enum::"ADI Model Type"::Receipt, CallerModuleInfo, Result) then begin FeatureTelemetry.LogError('0000OLL', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeReceiptFailureLbl, GetLastErrorText(), '', Enum::"AL Telemetry Scope"::All, CustomDimensions); @@ -88,22 +107,6 @@ codeunit 7779 "Azure DI Impl." Result := ALCopilotResponse.Result(); end; - local procedure AddTelemetryCustomDimensions(var CustomDimensions: Dictionary of [Text, Text]; CallerModuleInfo: ModuleInfo) - var - Language: Codeunit Language; - SavedGlobalLanguageId: Integer; - begin - SavedGlobalLanguageId := GlobalLanguage(); - GlobalLanguage(Language.GetDefaultApplicationLanguageId()); - - CustomDimensions.Add('Capability', AzureDocumentIntelligenceCapabilityTok); - CustomDimensions.Add('AppId', CallerModuleInfo.Id); - CustomDimensions.Add('Publisher', CallerModuleInfo.Publisher); - CustomDimensions.Add('UserLanguage', Format(GlobalLanguage())); - - GlobalLanguage(SavedGlobalLanguageId); - end; - local procedure GenerateJsonForSingleInput(Base64: Text): Text var JsonObject: JsonObject; @@ -132,25 +135,11 @@ codeunit 7779 "Azure DI Impl." local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary) begin TempPrivacyNotice.Init(); - TempPrivacyNotice.ID := AzureAiDocumentIntelligenceTxt; - TempPrivacyNotice."Integration Service Name" := AzureAiDocumentIntelligenceTxt; + TempPrivacyNotice.ID := GetAzureAIDocumentIntelligenceCategory(); + TempPrivacyNotice."Integration Service Name" := GetAzureAIDocumentIntelligenceCategory(); if not TempPrivacyNotice.Insert() then; end; - var - // CopilotSettings: Record "Copilot Settings"; - // CopilotCapabilityCU: Codeunit "Copilot Capability"; - // CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; - FeatureTelemetry: Codeunit "Feature Telemetry"; - // TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true; - // TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true; - // CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability'; - AzureDocumentIntelligenceCapabilityTok: Label 'ADI', Locked = true; - TelemetryAnalyzeInvoiceFailureLbl: Label 'Analyze invoice failed.', Locked = true; - TelemetryAnalyzeInvoiceCompletedLbl: Label 'Analyze invoice completed.', Locked = true; - TelemetryAnalyzeReceiptFailureLbl: Label 'Analyze receipt failed.', Locked = true; - TelemetryAnalyzeReceiptCompletedLbl: Label 'Analyze receipt completed.', Locked = true; - GenerateRequestFailedErr: Label 'The request did not return a success status code.'; - AzureAiDocumentIntelligenceTxt: Label 'Azure AI Document Intelligence', Locked = true; + } \ No newline at end of file diff --git a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDocumentIntelligence.Codeunit.al similarity index 87% rename from src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al rename to src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDocumentIntelligence.Codeunit.al index 5718f51e91..9de24eb663 100644 --- a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al +++ b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDocumentIntelligence.Codeunit.al @@ -2,14 +2,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // ------------------------------------------------------------------------------------------------ -namespace System.Azure.DI; +namespace System.AI.DocumentIntelligence; using System.AI; /// /// Azure Document Intelligence implementation. /// -codeunit 7780 "Azure DI" +codeunit 7780 "Azure Document Intelligence" { Access = Public; InherentEntitlements = X; @@ -55,11 +55,10 @@ codeunit 7780 "Azure DI" [NonDebuggable] procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability") var - CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; CallerModuleInfo: ModuleInfo; begin NavApp.GetCallerModuleInfo(CallerModuleInfo); - CopilotCapabilityImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureDIImpl.GetAzureAIDocumentIntelligenceCategory()); + AzureDIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureDIImpl.GetAzureAIDocumentIntelligenceCategory()); end; } \ No newline at end of file diff --git a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al index 9c12e924a2..2f916b73db 100644 --- a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al +++ b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al @@ -265,11 +265,10 @@ codeunit 7771 "Azure OpenAI" [NonDebuggable] procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability") var - CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; CallerModuleInfo: ModuleInfo; begin NavApp.GetCallerModuleInfo(CallerModuleInfo); - CopilotCapabilityImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureOpenAIImpl.GetAzureOpenAICategory()); + AzureOpenAIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureOpenAIImpl.GetAzureOpenAICategory()); end; #if not CLEAN24 diff --git a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al index 25c5099284..68f90edb22 100644 --- a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al @@ -8,7 +8,6 @@ using System; using System.Azure.Identity; using System.Azure.KeyVault; using System.Environment; -using System.Globalization; using System.Privacy; using System.Telemetry; @@ -20,8 +19,7 @@ codeunit 7772 "Azure OpenAI Impl" Permissions = tabledata "Copilot Settings" = r; var - CopilotSettings: Record "Copilot Settings"; - CopilotCapabilityCU: Codeunit "Copilot Capability"; + CopilotCapabilityImpl: Codeunit "Copilot Capability Impl"; ChatCompletionsAOAIAuthorization: Codeunit "AOAI Authorization"; TextCompletionsAOAIAuthorization: Codeunit "AOAI Authorization"; EmbeddingsAOAIAuthorization: Codeunit "AOAI Authorization"; @@ -34,14 +32,10 @@ codeunit 7772 "Azure OpenAI Impl" EmbeddingsFailedWithCodeErr: Label 'Embeddings failed to be generated.'; ChatCompletionsFailedWithCodeErr: Label 'Chat completions failed to be generated.'; AuthenticationNotConfiguredErr: Label 'The authentication was not configured.'; - CopilotNotEnabledErr: Label 'Copilot is not enabled. Please contact your system administrator.'; - CopilotCapabilityNotSetErr: Label 'Copilot capability has not been set.'; CapabilityBackgroundErr: Label 'Microsoft Copilot Capabilities are not allowed in the background.'; - CopilotDisabledForTenantErr: Label 'Copilot is not enabled for the tenant. Please contact your system administrator.'; MessagesMustContainJsonWordWhenResponseFormatIsJsonErr: Label 'The messages must contain the word ''json'' in some form, to use ''response format'' of type ''json_object''.'; EmptyMetapromptErr: Label 'The metaprompt has not been set, please provide a metaprompt.'; MetapromptLoadingErr: Label 'Metaprompt not found.'; - EnabledKeyTok: Label 'AOAI-Enabled', Locked = true; FunctionCallingFunctionNotFoundErr: Label 'Function call not found, %1.', Comment = '%1 is the name of the function'; AllowlistedTenantsAkvKeyTok: Label 'AOAI-Allow-1P-Auth', Locked = true; TelemetryGenerateTextCompletionLbl: Label 'Text completion generated.', Locked = true; @@ -49,9 +43,6 @@ codeunit 7772 "Azure OpenAI Impl" TelemetryGenerateChatCompletionLbl: Label 'Chat Completion generated.', Locked = true; TelemetryChatCompletionToolCallLbl: Label 'Tools called by chat completion.', Locked = true; TelemetryChatCompletionToolUsedLbl: Label 'Tools added to chat completion.', Locked = true; - TelemetryIsEnabledLbl: Label 'Is Enabled', Locked = true; - TelemetryUnableToCheckEnvironmentKVTxt: Label 'Unable to check if environment is allowed to run AOAI.', Locked = true; - TelemetryEnvironmentNotAllowedtoRunCopilotTxt: Label 'Copilot is not allowed on this environment.', Locked = true; TelemetryProhibitedCharactersTxt: Label 'Prohibited characters removed from the prompt.', Locked = true; TelemetryTokenCountLbl: Label 'Metaprompt token count: %1, Prompt token count: %2, Total token count: %3', Comment = '%1 is the number of tokens in the metaprompt, %2 is the number of tokens in the prompt, %3 is the total number of tokens', Locked = true; TelemetryMetapromptRetrievalErr: Label 'Unable to retrieve metaprompt from Azure Key Vault.', Locked = true; @@ -62,81 +53,17 @@ codeunit 7772 "Azure OpenAI Impl" procedure IsEnabled(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo): Boolean begin - exit(IsEnabled(Capability, false, CallerModuleInfo)); + CopilotCapabilityImpl.IsCapabilityEnabled(Capability, CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure OpenAI"); end; procedure IsEnabled(Capability: Enum "Copilot Capability"; Silent: Boolean; CallerModuleInfo: ModuleInfo): Boolean - var - CopilotNotAvailable: Page "Copilot Not Available"; begin - if not IsTenantAllowed() then begin - if not Silent then - Error(CopilotDisabledForTenantErr); // Copilot capabilities cannot be run on this environment. - - exit(false); - end; - - if not CopilotCapabilityCU.IsCapabilityActive(Capability, CallerModuleInfo.Id()) then begin - if not Silent then begin - CopilotNotAvailable.SetCopilotCapability(Capability); - CopilotNotAvailable.Run(); - end; - - exit(false); - end; - - exit(CheckPrivacyNoticeState(Silent, Capability)); + CopilotCapabilityImpl.IsCapabilityEnabled(Capability, Silent, CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure OpenAI"); end; - [NonDebuggable] - local procedure IsTenantAllowed(): Boolean - var - EnvironmentInformation: Codeunit "Environment Information"; - AzureKeyVault: Codeunit "Azure Key Vault"; - AzureAdTenant: Codeunit "Azure AD Tenant"; - ModuleInfo: ModuleInfo; - BlockList: Text; + procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo; AzureAIServiceName: Text) begin - if not EnvironmentInformation.IsSaaSInfrastructure() then - exit(true); - - NavApp.GetCurrentModuleInfo(ModuleInfo); - if ModuleInfo.Publisher <> 'Microsoft' then - exit(true); - - if (not AzureKeyVault.GetAzureKeyVaultSecret(EnabledKeyTok, BlockList)) or (BlockList.Trim() = '') then begin - FeatureTelemetry.LogError('0000KYC', GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt); - exit(false); - end; - - if BlockList.Contains(AzureAdTenant.GetAadTenantId()) then begin - FeatureTelemetry.LogError('0000LFP', GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryEnvironmentNotAllowedtoRunCopilotTxt); - exit(false); - end; - - exit(true); - end; - - local procedure CheckPrivacyNoticeState(Silent: Boolean; Capability: Enum "Copilot Capability"): Boolean - var - PrivacyNotice: Codeunit "Privacy Notice"; - CopilotNotAvailable: Page "Copilot Not Available"; - begin - case PrivacyNotice.GetPrivacyNoticeApprovalState(GetAzureOpenAICategory(), false) of - Enum::"Privacy Notice Approval State"::Agreed: - exit(true); - Enum::"Privacy Notice Approval State"::Disagreed: - begin - if not Silent then begin - CopilotNotAvailable.SetCopilotCapability(Capability); - CopilotNotAvailable.Run(); - end; - - exit(false); - end; - else - exit(true); - end; + CopilotCapabilityImpl.SetCopilotCapability(Capability, CallerModuleInfo, AzureAIServiceName); end; procedure IsAuthorizationConfigured(ModelType: Enum "AOAI Model Type"; CallerModule: ModuleInfo): Boolean @@ -235,11 +162,11 @@ codeunit 7772 "Azure OpenAI Impl" begin GuiCheck(TextCompletionsAOAIAuthorization); - CheckCapabilitySet(); - CheckEnabled(CallerModuleInfo); + CopilotCapabilityImpl.CheckCapabilitySet(); + CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure OpenAI"); CheckAuthorizationEnabled(TextCompletionsAOAIAuthorization, CallerModuleInfo); - AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); CheckTextCompletionMetaprompt(Metaprompt, CustomDimensions); UnwrappedPrompt := Metaprompt.Unwrap() + Prompt.Unwrap(); @@ -269,14 +196,14 @@ codeunit 7772 "Azure OpenAI Impl" begin GuiCheck(EmbeddingsAOAIAuthorization); - CheckCapabilitySet(); - CheckEnabled(CallerModuleInfo); + CopilotCapabilityImpl.CheckCapabilitySet(); + CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure OpenAI"); CheckAuthorizationEnabled(EmbeddingsAOAIAuthorization, CallerModuleInfo); Payload.Add('input', Input.Unwrap()); Payload.WriteTo(PayloadText); - AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); SendTokenCountTelemetry(0, AOAIToken.GetAdaTokenCount(Input), CustomDimensions); if not SendRequest(Enum::"AOAI Model Type"::Embeddings, EmbeddingsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin FeatureTelemetry.LogError('0000KVE', GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, EmbeddingsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions); @@ -323,10 +250,10 @@ codeunit 7772 "Azure OpenAI Impl" begin GuiCheck(ChatCompletionsAOAIAuthorization); - CheckCapabilitySet(); - CheckEnabled(CallerModuleInfo); + CopilotCapabilityImpl.CheckCapabilitySet(); + CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure OpenAI"); CheckAuthorizationEnabled(ChatCompletionsAOAIAuthorization, CallerModuleInfo); - AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); AOAIChatCompletionParams.AddChatCompletionsParametersToPayload(Payload); Payload.Add('messages', ChatMessages.AssembleHistory(MetapromptTokenCount, PromptTokenCount)); @@ -414,7 +341,7 @@ codeunit 7772 "Azure OpenAI Impl" AOAIOperationResponse.AddFunctionResponse(AOAIFunctionResponse); end; - AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); + CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo); foreach AOAIFunctionResponse in AOAIOperationResponse.GetFunctionResponses() do if not AOAIFunctionResponse.IsSuccess() then FeatureTelemetry.LogError('0000MTB', GetAzureOpenAICategory(), StrSubstNo(TelemetryFunctionCallingFailedErr, AOAIFunctionResponse.GetFunctionName()), AOAIFunctionResponse.GetError(), AOAIFunctionResponse.GetErrorCallstack(), Enum::"AL Telemetry Scope"::All, CustomDimensions); @@ -526,7 +453,7 @@ codeunit 7772 "Azure OpenAI Impl" ALCopilotAuthorization := ALCopilotAuthorization.Create(AOAIAuthorization.GetEndpoint(), AOAIAuthorization.GetDeployment(), AOAIAuthorization.GetApiKey()); end; - ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), GetCapabilityName()); + ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), CopilotCapabilityImpl.GetCapabilityName()); case ModelType of Enum::"AOAI Model Type"::"Text Completions": @@ -548,22 +475,6 @@ codeunit 7772 "Azure OpenAI Impl" Error(GenerateRequestFailedErr); end; - local procedure GetCapabilityName(): Text - var - CapabilityIndex: Integer; - CapabilityName: Text; - begin - CheckCapabilitySet(); - - CapabilityIndex := CopilotSettings.Capability.Ordinals.IndexOf(CopilotSettings.Capability.AsInteger()); - CapabilityName := CopilotSettings.Capability.Names.Get(CapabilityIndex); - - if CapabilityName.Trim() = '' then - exit(Format(CopilotSettings.Capability, 0, 9)); - - exit(CapabilityName); - end; - local procedure SendTokenCountTelemetry(Metaprompt: Integer; Prompt: Integer; CustomDimensions: Dictionary of [Text, Text]) begin Telemetry.LogMessage('0000LT4', StrSubstNo(TelemetryTokenCountLbl, Metaprompt, Prompt, Metaprompt + Prompt), Verbosity::Normal, DataClassification::OrganizationIdentifiableInformation, Enum::"AL Telemetry Scope"::All, CustomDimensions); @@ -580,40 +491,12 @@ codeunit 7772 "Azure OpenAI Impl" Error(CapabilityBackgroundErr); end; - local procedure AddTelemetryCustomDimensions(var CustomDimensions: Dictionary of [Text, Text]; CallerModuleInfo: ModuleInfo) - var - Language: Codeunit Language; - SavedGlobalLanguageId: Integer; - begin - SavedGlobalLanguageId := GlobalLanguage(); - GlobalLanguage(Language.GetDefaultApplicationLanguageId()); - - CustomDimensions.Add('Capability', Format(CopilotSettings.Capability)); - CustomDimensions.Add('AppId', Format(CopilotSettings."App Id")); - CustomDimensions.Add('Publisher', CallerModuleInfo.Publisher); - CustomDimensions.Add('UserLanguage', Format(GlobalLanguage())); - - GlobalLanguage(SavedGlobalLanguageId); - end; - - local procedure CheckEnabled(CallerModuleInfo: ModuleInfo) - begin - if not IsEnabled(CopilotSettings.Capability, true, CallerModuleInfo) then - Error(CopilotNotEnabledErr); - end; - local procedure CheckAuthorizationEnabled(AOAIAuthorization: Codeunit "AOAI Authorization"; CallerModuleInfo: ModuleInfo) begin if not AOAIAuthorization.IsConfigured(CallerModuleInfo) then Error(AuthenticationNotConfiguredErr); end; - local procedure CheckCapabilitySet() - begin - if CopilotSettings.Capability.AsInteger() = 0 then - Error(CopilotCapabilityNotSetErr); - end; - [NonDebuggable] procedure RemoveProhibitedCharacters(Prompt: Text) Result: Text begin @@ -733,8 +616,8 @@ codeunit 7772 "Azure OpenAI Impl" local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary) begin TempPrivacyNotice.Init(); - TempPrivacyNotice.ID := AzureOpenAiTxt; - TempPrivacyNotice."Integration Service Name" := AzureOpenAiTxt; + TempPrivacyNotice.ID := GetAzureOpenAICategory(); + TempPrivacyNotice."Integration Service Name" := GetAzureOpenAICategory(); if not TempPrivacyNotice.Insert() then; end; diff --git a/src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al b/src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al new file mode 100644 index 0000000000..905531fae0 --- /dev/null +++ b/src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.AI; + +/// +/// The supported service types for Azure AI. +/// +enum 7778 "Azure AI Service Type" +{ + Access = Public; + Extensible = false; + + /// + /// Azure OpenAI service type. + /// + value(0; "Azure OpenAI") + { + Caption = 'Azure OpenAI'; + } + + /// + /// Azure Document Intelligence service type. + /// + value(1; "Azure Document Intelligence") + { + Caption = 'Azure Document Intelligence'; + } +} \ No newline at end of file diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al index 8c44d3d46d..48768be327 100644 --- a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al @@ -6,10 +6,12 @@ namespace System.AI; using System; using System.Azure.Identity; +using System.Azure.KeyVault; using System.Environment; using System.Environment.Configuration; using System.Globalization; using System.Privacy; +using System.AI.DocumentIntelligence; using System.Security.User; using System.Telemetry; @@ -21,6 +23,8 @@ codeunit 7774 "Copilot Capability Impl" Permissions = tabledata "Copilot Settings" = rimd; var + CopilotSettings: Record "Copilot Settings"; + FeatureTelemetry: Codeunit "Feature Telemetry"; Telemetry: Codeunit Telemetry; CopilotCategoryLbl: Label 'Copilot', Locked = true; AlreadyRegisteredErr: Label 'Capability has already been registered.'; @@ -31,6 +35,13 @@ codeunit 7774 "Copilot Capability Impl" CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability'; CapabilityNotEnabledErr: Label 'Copilot capability ''%1'' has not been enabled. Please contact your system administrator.', Comment = '%1 is the name of the Copilot Capability'; TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true; + CopilotNotEnabledErr: Label 'Copilot is not enabled. Please contact your system administrator.'; + CopilotCapabilityNotSetErr: Label 'Copilot capability has not been set.'; + CopilotDisabledForTenantErr: Label 'Copilot is not enabled for the tenant. Please contact your system administrator.'; + TelemetryIsEnabledLbl: Label 'Is Enabled', Locked = true; + TelemetryUnableToCheckEnvironmentKVTxt: Label 'Unable to check if environment is allowed to run AOAI.', Locked = true; + TelemetryEnvironmentNotAllowedtoRunCopilotTxt: Label 'Copilot is not allowed on this environment.', Locked = true; + EnabledKeyTok: Label 'AOAI-Enabled', Locked = true; TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true; TelemetryRegisteredNewCopilotCapabilityLbl: Label 'New copilot capability registered.', Locked = true; TelemetryModifiedCopilotCapabilityLbl: Label 'Copilot capability modified', Locked = true; @@ -48,7 +59,6 @@ codeunit 7774 "Copilot Capability Impl" procedure RegisterCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; LearnMoreUrl: Text[2048]; CallerModuleInfo: ModuleInfo) var - CopilotSettings: Record "Copilot Settings"; CustomDimensions: Dictionary of [Text, Text]; begin if IsCapabilityRegistered(CopilotCapability, CallerModuleInfo) then @@ -73,9 +83,7 @@ codeunit 7774 "Copilot Capability Impl" procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo; AzureAIServiceName: Text) var - CopilotSettings: Record "Copilot Settings"; CopilotTelemetry: Codeunit "Copilot Telemetry"; - FeatureTelemetry: Codeunit "Feature Telemetry"; Language: Codeunit Language; SavedGlobalLanguageId: Integer; CustomDimensions: Dictionary of [Text, Text]; @@ -105,7 +113,6 @@ codeunit 7774 "Copilot Capability Impl" procedure ModifyCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; LearnMoreUrl: Text[2048]; CallerModuleInfo: ModuleInfo) var - CopilotSettings: Record "Copilot Settings"; CustomDimensions: Dictionary of [Text, Text]; begin if not IsCapabilityRegistered(CopilotCapability, CallerModuleInfo) then @@ -129,7 +136,6 @@ codeunit 7774 "Copilot Capability Impl" procedure UnregisterCapability(CopilotCapability: Enum "Copilot Capability"; var CallerModuleInfo: ModuleInfo) var - CopilotSettings: Record "Copilot Settings"; CustomDimensions: Dictionary of [Text, Text]; begin if not IsCapabilityRegistered(CopilotCapability, CallerModuleInfo) then @@ -151,8 +157,6 @@ codeunit 7774 "Copilot Capability Impl" end; procedure IsCapabilityRegistered(CopilotCapability: Enum "Copilot Capability"; AppId: Guid): Boolean - var - CopilotSettings: Record "Copilot Settings"; begin CopilotSettings.ReadIsolation(IsolationLevel::ReadCommitted); CopilotSettings.SetRange("Capability", CopilotCapability); @@ -167,7 +171,6 @@ codeunit 7774 "Copilot Capability Impl" procedure IsCapabilityActive(CopilotCapability: Enum "Copilot Capability"; AppId: Guid): Boolean var - CopilotSettings: Record "Copilot Settings"; CopilotCapabilityCU: Codeunit "Copilot Capability"; PrivacyNotice: Codeunit "Privacy Notice"; RequiredPrivacyNotices: List of [Code[50]]; @@ -191,6 +194,148 @@ codeunit 7774 "Copilot Capability Impl" exit(true); end; + procedure GetCapabilityName(): Text + var + CapabilityIndex: Integer; + CapabilityName: Text; + begin + CheckCapabilitySet(); + + CapabilityIndex := CopilotSettings.Capability.Ordinals.IndexOf(CopilotSettings.Capability.AsInteger()); + CapabilityName := CopilotSettings.Capability.Names.Get(CapabilityIndex); + + if CapabilityName.Trim() = '' then + exit(Format(CopilotSettings.Capability, 0, 9)); + + exit(CapabilityName); + end; + + procedure CheckCapabilitySet() + begin + if CopilotSettings.Capability.AsInteger() = 0 then + Error(CopilotCapabilityNotSetErr); + end; + + procedure CheckEnabled(CallerModuleInfo: ModuleInfo; ServiceType: Enum "Azure AI Service Type") + begin + if not IsCapabilityEnabled(CopilotSettings.Capability, true, CallerModuleInfo, ServiceType) then + Error(CopilotNotEnabledErr); + end; + + procedure IsCapabilityEnabled(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo; ServiceType: Enum "Azure AI Service Type"): Boolean + begin + exit(IsCapabilityEnabled(Capability, false, CallerModuleInfo, ServiceType)); + end; + + procedure IsCapabilityEnabled(Capability: Enum "Copilot Capability"; Silent: Boolean; CallerModuleInfo: ModuleInfo; ServiceType: Enum "Azure AI Service Type"): Boolean + var + CopilotNotAvailable: Page "Copilot Not Available"; + begin + if not IsTenantAllowed(ServiceType) then begin + if not Silent then + Error(CopilotDisabledForTenantErr); // Copilot capabilities cannot be run on this environment. + + exit(false); + end; + + if not IsCapabilityActive(Capability, CallerModuleInfo.Id()) then begin + if not Silent then begin + CopilotNotAvailable.SetCopilotCapability(Capability); + CopilotNotAvailable.Run(); + end; + + exit(false); + end; + + exit(CheckPrivacyNoticeState(Silent, Capability, ServiceType)); + end; + + [NonDebuggable] + local procedure IsTenantAllowed(ServiceType: Enum "Azure AI Service Type"): Boolean + var + EnvironmentInformation: Codeunit "Environment Information"; + AzureOpenAIImpl: Codeunit "Azure OpenAI Impl"; + AzureDIImpl: Codeunit "Azure DI Impl."; + AzureKeyVault: Codeunit "Azure Key Vault"; + AzureAdTenant: Codeunit "Azure AD Tenant"; + ModuleInfo: ModuleInfo; + BlockList, TelemtryTok : Text; + begin + if not EnvironmentInformation.IsSaaSInfrastructure() then + exit(true); + + NavApp.GetCurrentModuleInfo(ModuleInfo); + if ModuleInfo.Publisher <> 'Microsoft' then + exit(true); + + case ServiceType of + Enum::"Azure AI Service Type"::"Azure OpenAI": + TelemtryTok := AzureOpenAIImpl.GetAzureOpenAICategory(); + Enum::"Azure AI Service Type"::"Azure Document Intelligence": + TelemtryTok := AzureDIImpl.GetAzureAIDocumentIntelligenceCategory() + end; + + if (not AzureKeyVault.GetAzureKeyVaultSecret(EnabledKeyTok, BlockList)) or (BlockList.Trim() = '') then begin + FeatureTelemetry.LogError('0000KYC', TelemtryTok, TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt); + exit(false); + end; + + if BlockList.Contains(AzureAdTenant.GetAadTenantId()) then begin + FeatureTelemetry.LogError('0000LFP', TelemtryTok, TelemetryIsEnabledLbl, TelemetryEnvironmentNotAllowedtoRunCopilotTxt); + exit(false); + end; + + exit(true); + end; + + local procedure CheckPrivacyNoticeState(Silent: Boolean; Capability: Enum "Copilot Capability"; ServiceType: Enum "Azure AI Service Type"): Boolean + var + PrivacyNotice: Codeunit "Privacy Notice"; + AzureOpenAIImpl: Codeunit "Azure OpenAI Impl"; + AzureDIImpl: Codeunit "Azure DI Impl."; + CopilotNotAvailable: Page "Copilot Not Available"; + PrivacyNoticeApprovalState: Enum "Privacy Notice Approval State"; + begin + case ServiceType of + Enum::"Azure AI Service Type"::"Azure OpenAI": + PrivacyNoticeApprovalState := PrivacyNotice.GetPrivacyNoticeApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), false); + Enum::"Azure AI Service Type"::"Azure Document Intelligence": + PrivacyNoticeApprovalState := PrivacyNotice.GetPrivacyNoticeApprovalState(AzureDIImpl.GetAzureAIDocumentIntelligenceCategory(), false); + end; + + case PrivacyNoticeApprovalState of + Enum::"Privacy Notice Approval State"::Agreed: + exit(true); + Enum::"Privacy Notice Approval State"::Disagreed: + begin + if not Silent then begin + CopilotNotAvailable.SetCopilotCapability(Capability); + CopilotNotAvailable.Run(); + end; + + exit(false); + end; + else + exit(true); + end; + end; + + procedure AddTelemetryCustomDimensions(var CustomDimensions: Dictionary of [Text, Text]; CallerModuleInfo: ModuleInfo) + var + Language: Codeunit Language; + SavedGlobalLanguageId: Integer; + begin + SavedGlobalLanguageId := GlobalLanguage(); + GlobalLanguage(Language.GetDefaultApplicationLanguageId()); + + CustomDimensions.Add('Capability', Format(CopilotSettings.Capability)); + CustomDimensions.Add('AppId', Format(CopilotSettings."App Id")); + CustomDimensions.Add('Publisher', CallerModuleInfo.Publisher); + CustomDimensions.Add('UserLanguage', Format(GlobalLanguage())); + + GlobalLanguage(SavedGlobalLanguageId); + end; + procedure SendActivateTelemetry(CopilotCapability: Enum "Copilot Capability"; AppId: Guid) var CustomDimensions: Dictionary of [Text, Text]; From 6dc545b13ee6c2474cb3002aa916b14914aa2248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 5 Feb 2025 15:03:20 +0100 Subject: [PATCH 10/13] Update text --- .../AI/src/Copilot/CopilotAICapabilities.Page.al | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al b/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al index 73c8f3aa17..6784b465fe 100644 --- a/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al +++ b/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al @@ -34,7 +34,7 @@ page 7775 "Copilot AI Capabilities" { ShowCaption = false; Visible = WithinAOAIServicesInRegionArea; - InstructionalText = 'Copilot and agents use the Azure OpenAI Service. Your environment connects to this service in your own region.'; + InstructionalText = 'Copilot and agents use the Azure AI Services. Your environment connects to this service in your own region.'; field(DataSecurityAndPrivacy; FAQForDataSecurityAndPrivacyLbl) { @@ -74,13 +74,13 @@ page 7775 "Copilot AI Capabilities" { ShowCaption = false; Visible = WithinEUDBArea and (not AllowDataMovement); - InstructionalText = 'Copilot and agents use the Azure OpenAI Service available within the EU Data Boundary. To activate these capabilities, you must allow data movement within this boundary.'; + InstructionalText = 'Copilot and agents use the Azure AI Services available within the EU Data Boundary. To activate these capabilities, you must allow data movement within this boundary.'; } group(AllowedDataMovementOnInfo) { ShowCaption = false; Visible = WithinEUDBArea and AllowDataMovement; - InstructionalText = 'Copilot and agents use the Azure OpenAI Service available within the EU Data Boundary. To keep using these capabilities, you must allow data movement within this boundary.'; + InstructionalText = 'Copilot and agents use the Azure AI Services available within the EU Data Boundary. To keep using these capabilities, you must allow data movement within this boundary.'; } field(EUDBAreaDataSecurityAndPrivacy; FAQForDataSecurityAndPrivacyLbl) { @@ -97,7 +97,7 @@ page 7775 "Copilot AI Capabilities" label(EUDBAreaCaption) { ApplicationArea = All; - Caption = 'By allowing data movement, you agree to data being processed by the Azure OpenAI Service within the EU Data Boundary.'; + Caption = 'By allowing data movement, you agree to data being processed by the Azure AI Services within the EU Data Boundary.'; } field(EUDBAreaDataMovement; AllowDataMovement) { @@ -140,13 +140,13 @@ page 7775 "Copilot AI Capabilities" { ShowCaption = false; Visible = WithinAOAIOutOfRegionArea and (not AllowDataMovement); - InstructionalText = 'Copilot and agents use the Azure OpenAI Service, which isn''t available in your region. To activate these capabilities, you must allow data movement.'; + InstructionalText = 'Copilot and agents use the Azure AI Services, which isn''t available in your region. To activate these capabilities, you must allow data movement.'; } group(AllowedDataMovementOnInfo2) { ShowCaption = false; Visible = WithinAOAIOutOfRegionArea and AllowDataMovement; - InstructionalText = 'Copilot and agents use the Azure OpenAI Service, which isn''t available in your region. To keep using these capabilities, you must allow data movement.'; + InstructionalText = 'Copilot and agents use the Azure AI Services, which isn''t available in your region. To keep using these capabilities, you must allow data movement.'; } field(AOAIOutOfRegionAreaDataSecurityAndPrivacy; FAQForDataSecurityAndPrivacyLbl) { @@ -163,7 +163,7 @@ page 7775 "Copilot AI Capabilities" label(AOAIOutOfRegionAreaCaption) { ApplicationArea = All; - Caption = 'By allowing data movement, you agree to data being processed by the Azure OpenAI Service outside of your environment''s geographic region or compliance boundary.'; + Caption = 'By allowing data movement, you agree to data being processed by the Azure AI Services outside of your environment''s geographic region or compliance boundary.'; } field(AOAIOutOfRegionAreaDataMovement; AllowDataMovement) { @@ -324,7 +324,7 @@ page 7775 "Copilot AI Capabilities" HasEarlyPreview: Boolean; CopilotGovernDataLbl: Label 'How do I govern my Copilot data?'; FAQForDataSecurityAndPrivacyLbl: Label 'FAQ for data security and privacy'; - DataProcessByAOAILbl: Label 'What data is processed by Azure OpenAI Service?'; + DataProcessByAOAILbl: Label 'What data is processed by Azure AI Services?'; AOAIServiceLocatedLbl: Label 'In which region will my data be processed?'; CopilotGovernDataDocLinkLbl: Label 'https://go.microsoft.com/fwlink/?linkid=2249575', Locked = true; FAQForDataSecurityAndPrivacyDocLinkLbl: Label 'https://go.microsoft.com/fwlink/?linkid=2298505', Locked = true; From f791b25793c480d49e1e769ebcd628713448ce07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Wed, 5 Feb 2025 17:11:21 +0100 Subject: [PATCH 11/13] Clear before insert. --- .../App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al | 1 + 1 file changed, 1 insertion(+) diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al index 48768be327..31e50dc8bf 100644 --- a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al @@ -64,6 +64,7 @@ codeunit 7774 "Copilot Capability Impl" if IsCapabilityRegistered(CopilotCapability, CallerModuleInfo) then Error(AlreadyRegisteredErr); + Clear(CopilotSettings); CopilotSettings.Init(); CopilotSettings.Capability := CopilotCapability; CopilotSettings."App Id" := CallerModuleInfo.Id(); From a61ba614fda4ebfa81067525d697eaea9dc0c71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 6 Feb 2025 08:22:17 +0100 Subject: [PATCH 12/13] Update src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al --- .../App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al index 68f90edb22..9186430edb 100644 --- a/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al @@ -617,7 +617,7 @@ codeunit 7772 "Azure OpenAI Impl" begin TempPrivacyNotice.Init(); TempPrivacyNotice.ID := GetAzureOpenAICategory(); - TempPrivacyNotice."Integration Service Name" := GetAzureOpenAICategory(); + TempPrivacyNotice."Integration Service Name" := AzureOpenAiTxt; if not TempPrivacyNotice.Insert() then; end; From 5ca1c58f5a648c5dd0fa011315eaa92e9b127dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Hartvig=20Gr=C3=B8nbech?= Date: Thu, 6 Feb 2025 08:22:47 +0100 Subject: [PATCH 13/13] Update src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al --- .../src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al index 50b5bc8d63..ca133865c3 100644 --- a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al +++ b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al @@ -136,7 +136,7 @@ codeunit 7779 "Azure DI Impl." begin TempPrivacyNotice.Init(); TempPrivacyNotice.ID := GetAzureAIDocumentIntelligenceCategory(); - TempPrivacyNotice."Integration Service Name" := GetAzureAIDocumentIntelligenceCategory(); + TempPrivacyNotice."Integration Service Name" := AzureAiDocumentIntelligenceTxt; if not TempPrivacyNotice.Insert() then; end;