diff --git a/src/System Application/App/AI/app.json b/src/System Application/App/AI/app.json
index a4de64d7de..aee58d9565 100644
--- a/src/System Application/App/AI/app.json
+++ b/src/System Application/App/AI/app.json
@@ -90,7 +90,7 @@
"idRanges": [
{
"from": 7757,
- "to": 7780
+ "to": 7778
}
],
"target": "OnPrem",
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
deleted file mode 100644
index 00df30b255..0000000000
--- a/src/System Application/App/AI/src/Azure AI Document Intelligence/ADIModelType.Enum.al
+++ /dev/null
@@ -1,29 +0,0 @@
-// ------------------------------------------------------------------------------------------------
-// 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.DocumentIntelligence;
-
-///
-/// The supported model types for Azure Document Intelligence.
-///
-enum 7779 "ADI Model Type"
-{
- Access = Public;
- Extensible = false;
-
- ///
- /// Invoice model type.
- ///
- value(0; Invoice)
- {
- }
-
- ///
- /// Receipt model type.
- ///
- value(1; Receipt)
- {
- }
-
-}
\ No newline at end of file
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
deleted file mode 100644
index cf70ecb106..0000000000
--- a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al
+++ /dev/null
@@ -1,147 +0,0 @@
-// ------------------------------------------------------------------------------------------------
-// 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.DocumentIntelligence;
-
-using System.Telemetry;
-using System;
-using System.AI;
-
-///
-/// Azure Document Intelligence implementation.
-///
-codeunit 7779 "Azure DI Impl." implements "AI Service Name"
-{
- Access = Internal;
- 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;
- CapabilityNotEnabledErr: Label 'Copilot capability ''%1'' has not been enabled. Please contact your system administrator.', Comment = '%1 is the name of the Copilot Capability';
-
- procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo)
- begin
- CopilotCapabilityImpl.SetCopilotCapability(Capability, CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure Document Intelligence");
- end;
-
- procedure RegisterCopilotCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; LearnMoreUrl: Text[2048]; CallerModuleInfo: ModuleInfo)
- begin
- CopilotCapabilityImpl.RegisterCapability(CopilotCapability, CopilotAvailability, Enum::"Azure AI Service Type"::"Azure Document Intelligence", LearnMoreUrl, CallerModuleInfo);
- end;
-
- ///
- /// Analyze a single invoice.
- ///
- /// Data to analyze.
- /// The module info of the caller.
- /// The analyzed result.
- procedure AnalyzeInvoice(Base64Data: Text; CallerModuleInfo: ModuleInfo) Result: Text
- var
- CustomDimensions: Dictionary of [Text, Text];
- begin
- CopilotCapabilityImpl.CheckCapabilitySet();
- if not CopilotCapabilityImpl.IsCapabilityActive(CallerModuleInfo) then
- Error(CapabilityNotEnabledErr, CopilotCapabilityImpl.GetCapabilityName());
-
- CopilotCapabilityImpl.CheckCapabilityServiceType(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);
- exit;
- end;
-
- FeatureTelemetry.LogUsage('0000OLM', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeInvoiceCompletedLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
- end;
-
- ///
- /// Analyze a single receipt.
- ///
- /// Data to analyze.
- /// The module info of the caller.
- /// The analyzed result.
- procedure AnalyzeReceipt(Base64Data: Text; CallerModuleInfo: ModuleInfo) Result: Text
- var
- CustomDimensions: Dictionary of [Text, Text];
- begin
- CopilotCapabilityImpl.CheckCapabilitySet();
- if not CopilotCapabilityImpl.IsCapabilityActive(CallerModuleInfo) then
- Error(CapabilityNotEnabledErr, CopilotCapabilityImpl.GetCapabilityName());
-
- 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);
- exit;
- end;
-
- FeatureTelemetry.LogUsage('0000OLN', AzureDocumentIntelligenceCapabilityTok, TelemetryAnalyzeReceiptCompletedLbl, 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(GenerateJsonForSingleInput(Base64Data), ALCopilotCapability);
- Enum::"ADI Model Type"::Receipt:
- ALCopilotResponse := ALCopilotFunctions.GenerateReceiptIntelligence(GenerateJsonForSingleInput(Base64Data), ALCopilotCapability);
- end;
- ErrorMsg := ALCopilotResponse.ErrorText();
- if ErrorMsg <> '' then
- Error(ErrorMsg);
-
- if not ALCopilotResponse.IsSuccess() then
- Error(GenerateRequestFailedErr);
-
- Result := ALCopilotResponse.Result();
- end;
-
- local procedure GenerateJsonForSingleInput(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;
-
- procedure GetServiceName(): Text[250]
- begin
- exit(AzureAiDocumentIntelligenceTxt);
- end;
-
- procedure GetServiceId(): Code[50];
- begin
- exit(AzureAiDocumentIntelligenceTxt);
- end;
-
-}
\ No newline at end of file
diff --git a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDocumentIntelligence.Codeunit.al b/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDocumentIntelligence.Codeunit.al
deleted file mode 100644
index d9ca35a59b..0000000000
--- a/src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDocumentIntelligence.Codeunit.al
+++ /dev/null
@@ -1,75 +0,0 @@
-// ------------------------------------------------------------------------------------------------
-// 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.DocumentIntelligence;
-
-using System.AI;
-
-///
-/// Provides functionality to invoke Azure Document Intelligence services.
-///
-codeunit 7780 "Azure Document Intelligence"
-{
- Access = Public;
- InherentEntitlements = X;
- InherentPermissions = X;
-
- var
- AzureDIImpl: 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(AzureDIImpl.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(AzureDIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo));
- end;
-
- ///
- /// Register a capability for Azure Document Intelligence.
- ///
- /// The capability.
- /// The availability.
- /// The learn more url.
- procedure RegisterCopilotCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; LearnMoreUrl: Text[2048])
- var
- CallerModuleInfo: ModuleInfo;
- begin
- NavApp.GetCallerModuleInfo(CallerModuleInfo);
- AzureDIImpl.RegisterCopilotCapability(CopilotCapability, CopilotAvailability, LearnMoreUrl, CallerModuleInfo);
- end;
-
- ///
- /// Sets the copilot capability that the API is running for.
- ///
- /// The copilot capability to set.
- procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability")
- var
- CallerModuleInfo: ModuleInfo;
- begin
- NavApp.GetCallerModuleInfo(CallerModuleInfo);
- AzureDIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo);
- 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 21579c9bab..7992af7d3f 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
@@ -270,7 +270,6 @@ codeunit 7771 "Azure OpenAI"
NavApp.GetCallerModuleInfo(CallerModuleInfo);
AzureOpenAIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo);
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 89bfadd8c1..1e03e39e13 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,10 +8,11 @@ using System;
using System.Azure.Identity;
using System.Azure.KeyVault;
using System.Environment;
+using System.Globalization;
using System.Privacy;
using System.Telemetry;
-codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
+codeunit 7772 "Azure OpenAI Impl"
{
Access = Internal;
InherentEntitlements = X;
@@ -19,6 +20,8 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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";
@@ -32,10 +35,16 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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.';
+ 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.';
+ 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;
@@ -43,27 +52,95 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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;
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;
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
- exit(CopilotCapabilityImpl.IsCapabilityEnabled(Capability, CallerModuleInfo));
+ exit(IsEnabled(Capability, false, CallerModuleInfo));
end;
procedure IsEnabled(Capability: Enum "Copilot Capability"; Silent: Boolean; CallerModuleInfo: ModuleInfo): Boolean
+ var
+ CopilotNotAvailable: Page "Copilot Not Available";
begin
- exit(CopilotCapabilityImpl.IsCapabilityEnabled(Capability, Silent, CallerModuleInfo));
+ 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));
end;
- procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo)
+ [NonDebuggable]
+ local procedure IsTenantAllowed(): Boolean
+ var
+ EnvironmentInformation: Codeunit "Environment Information";
+ AzureKeyVault: Codeunit "Azure Key Vault";
+ AzureAdTenant: Codeunit "Azure AD Tenant";
+ ModuleInfo: ModuleInfo;
+ BlockList: Text;
begin
- CopilotCapabilityImpl.SetCopilotCapability(Capability, CallerModuleInfo, Enum::"Azure AI Service Type"::"Azure OpenAI");
+ 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', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt);
+ exit(false);
+ end;
+
+ if BlockList.Contains(AzureAdTenant.GetAadTenantId()) then begin
+ FeatureTelemetry.LogError('0000LFP', CopilotCapabilityImpl.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(CopilotCapabilityImpl.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;
end;
procedure IsAuthorizationConfigured(ModelType: Enum "AOAI Model Type"; CallerModule: ModuleInfo): Boolean
@@ -162,11 +239,11 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
begin
GuiCheck(TextCompletionsAOAIAuthorization);
- CopilotCapabilityImpl.CheckCapabilitySet();
- CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo);
+ CheckCapabilitySet();
+ CheckEnabled(CallerModuleInfo);
CheckAuthorizationEnabled(TextCompletionsAOAIAuthorization, CallerModuleInfo);
- CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
+ AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
CheckTextCompletionMetaprompt(Metaprompt, CustomDimensions);
UnwrappedPrompt := Metaprompt.Unwrap() + Prompt.Unwrap();
@@ -179,11 +256,11 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, CompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
+ FeatureTelemetry.LogError('0000KVD', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, CompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
exit;
end;
- FeatureTelemetry.LogUsage('0000KVL', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
+ FeatureTelemetry.LogUsage('0000KVL', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
Result := AOAIOperationResponse.GetResult();
end;
@@ -196,21 +273,21 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
begin
GuiCheck(EmbeddingsAOAIAuthorization);
- CopilotCapabilityImpl.CheckCapabilitySet();
- CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo);
+ CheckCapabilitySet();
+ CheckEnabled(CallerModuleInfo);
CheckAuthorizationEnabled(EmbeddingsAOAIAuthorization, CallerModuleInfo);
Payload.Add('input', Input.Unwrap());
Payload.WriteTo(PayloadText);
- CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
+ 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);
+ FeatureTelemetry.LogError('0000KVE', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, EmbeddingsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
exit;
end;
- FeatureTelemetry.LogUsage('0000KVM', GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
+ FeatureTelemetry.LogUsage('0000KVM', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
exit(ProcessEmbeddingResponse(AOAIOperationResponse));
end;
@@ -250,10 +327,10 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
begin
GuiCheck(ChatCompletionsAOAIAuthorization);
- CopilotCapabilityImpl.CheckCapabilitySet();
- CopilotCapabilityImpl.CheckEnabled(CallerModuleInfo);
+ CheckCapabilitySet();
+ CheckEnabled(CallerModuleInfo);
CheckAuthorizationEnabled(ChatCompletionsAOAIAuthorization, CallerModuleInfo);
- CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
+ AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
AOAIChatCompletionParams.AddChatCompletionsParametersToPayload(Payload);
Payload.Add('messages', ChatMessages.AssembleHistory(MetapromptTokenCount, PromptTokenCount));
@@ -279,13 +356,13 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
SendTokenCountTelemetry(MetapromptTokenCount, PromptTokenCount, CustomDimensions);
if not SendRequest(Enum::"AOAI Model Type"::"Chat Completions", ChatCompletionsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin
- FeatureTelemetry.LogError('0000KVF', GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, ChatCompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
+ FeatureTelemetry.LogError('0000KVF', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, ChatCompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
exit;
end;
ProcessChatCompletionResponse(ChatMessages, AOAIOperationResponse, CallerModuleInfo);
- FeatureTelemetry.LogUsage('0000KVN', GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
+ FeatureTelemetry.LogUsage('0000KVN', CopilotCapabilityImpl.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);
@@ -341,10 +418,10 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
AOAIOperationResponse.AddFunctionResponse(AOAIFunctionResponse);
end;
- CopilotCapabilityImpl.AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
+ 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);
+ FeatureTelemetry.LogError('0000MTB', CopilotCapabilityImpl.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);
@@ -454,7 +531,7 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
ALCopilotAuthorization := ALCopilotAuthorization.Create(AOAIAuthorization.GetEndpoint(), AOAIAuthorization.GetDeployment(), AOAIAuthorization.GetApiKey());
end;
- ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), CopilotCapabilityImpl.GetCapabilityName());
+ ALCopilotCapability := ALCopilotCapability.ALCopilotCapability(CallerModuleInfo.Publisher(), CallerModuleInfo.Id(), Format(CallerModuleInfo.AppVersion()), GetCapabilityName());
case ModelType of
Enum::"AOAI Model Type"::"Text Completions":
@@ -479,6 +556,22 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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);
@@ -495,12 +588,70 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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;
+
+ 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
+ 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
@@ -541,7 +692,7 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
ModuleInfo: ModuleInfo;
begin
if Metaprompt.Unwrap().Trim() = '' then begin
- FeatureTelemetry.LogError('0000LO8', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, EmptyMetapromptErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
+ FeatureTelemetry.LogError('0000LO8', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, EmptyMetapromptErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
NavApp.GetCurrentModuleInfo(ModuleInfo);
if ModuleInfo.Publisher = 'Microsoft' then
@@ -600,39 +751,15 @@ codeunit 7772 "Azure OpenAI Impl" implements "AI Service Name"
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', GetAzureOpenAICategory());
+ Session.LogMessage('0000MLN', TelemetryEmptyTenantIdErr, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CopilotCapabilityImpl.GetAzureOpenAICategory());
exit(false);
end;
if not AllowlistedTenants.Contains(EntraTenantIdAsText) then
exit(false);
- Session.LogMessage('0000MLE', TelemetryTenantAllowlistedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GetAzureOpenAICategory());
+ Session.LogMessage('0000MLE', TelemetryTenantAllowlistedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CopilotCapabilityImpl.GetAzureOpenAICategory());
exit(true);
end;
- procedure GetAzureOpenAICategory(): Code[50]
- begin
- exit(AzureOpenAiTxt);
- end;
-
- procedure GetServiceName(): Text[250];
- begin
- exit(AzureOpenAiTxt);
- end;
-
- procedure GetServiceId(): 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 := GetAzureOpenAICategory();
- TempPrivacyNotice."Integration Service Name" := GetServiceName();
- if not TempPrivacyNotice.Insert() then;
- end;
-
}
\ No newline at end of file
diff --git a/src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al b/src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al
deleted file mode 100644
index 3705990822..0000000000
--- a/src/System Application/App/AI/src/Copilot/AzureAIServiceType.Enum.al
+++ /dev/null
@@ -1,33 +0,0 @@
-// ------------------------------------------------------------------------------------------------
-// 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;
-using System.AI.DocumentIntelligence;
-
-///
-/// The supported service types for Azure AI.
-///
-enum 7778 "Azure AI Service Type" implements "AI Service Name"
-{
- Access = Public;
- Extensible = false;
-
- ///
- /// Azure OpenAI service type.
- ///
- value(0; "Azure OpenAI")
- {
- Caption = 'Azure OpenAI';
- Implementation = "AI Service Name" = "Azure OpenAI Impl";
- }
-
- ///
- /// Azure Document Intelligence service type.
- ///
- value(1; "Azure Document Intelligence")
- {
- Caption = 'Azure Document Intelligence';
- Implementation = "AI Service Name" = "Azure DI Impl.";
- }
-}
\ 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 2ecd9c7ed1..cfda74d386 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotAICapabilities.Page.al
@@ -257,7 +257,7 @@ page 7775 "Copilot AI Capabilities"
CopilotCapabilityImpl.CheckGeoAndEUDB(WithinGeo, WithinEUDB);
- case PrivacyNotice.GetPrivacyNoticeApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), false) of
+ case PrivacyNotice.GetPrivacyNoticeApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), false) of
Enum::"Privacy Notice Approval State"::Agreed:
AllowDataMovement := true;
Enum::"Privacy Notice Approval State"::Disagreed:
@@ -297,16 +297,15 @@ page 7775 "Copilot AI Capabilities"
CopilotSettings: Record "Copilot Settings";
begin
CopilotSettings.SetRange(Availability, Enum::"Copilot Availability"::"Early Preview");
- CopilotSettings.SetRange("Service Type", Enum::"Azure AI Service Type"::"Azure OpenAI");
exit(not CopilotSettings.IsEmpty());
end;
local procedure UpdateAllowDataMovement()
begin
if AllowDataMovement then
- PrivacyNotice.SetApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Agreed)
+ PrivacyNotice.SetApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Agreed)
else
- PrivacyNotice.SetApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Disagreed);
+ PrivacyNotice.SetApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), Enum::"Privacy Notice Approval State"::Disagreed);
CurrPage.GenerallyAvailableCapabilities.Page.SetDataMovement(AllowDataMovement);
CurrPage.PreviewCapabilities.Page.SetDataMovement(AllowDataMovement);
@@ -320,7 +319,6 @@ 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/CopilotCapEarlyPreview.Page.al b/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al
index f044a41d1f..f322325fb9 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapEarlyPreview.Page.al
@@ -16,7 +16,7 @@ page 7770 "Copilot Cap. Early Preview"
Editable = false;
Extensible = false;
SourceTable = "Copilot Settings";
- SourceTableView = where(Availability = const("Early Preview"), "Service Type" = const("Azure AI Service Type"::"Azure OpenAI"));
+ SourceTableView = where(Availability = const("Early Preview"));
Permissions = tabledata "Copilot Settings" = rm;
InherentEntitlements = X;
InherentPermissions = X;
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al
index 241e75d182..b76b8e4350 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesGA.Page.al
@@ -16,7 +16,7 @@ page 7774 "Copilot Capabilities GA"
Editable = false;
Extensible = false;
SourceTable = "Copilot Settings";
- SourceTableView = where(Availability = const("Generally Available"), "Service Type" = const("Azure AI Service Type"::"Azure OpenAI"));
+ SourceTableView = where(Availability = const("Generally Available"));
Permissions = tabledata "Copilot Settings" = rm;
InherentEntitlements = X;
InherentPermissions = X;
diff --git a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al
index 5c528a18a6..38e90454e1 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilitiesPreview.Page.al
@@ -16,7 +16,7 @@ page 7773 "Copilot Capabilities Preview"
Editable = false;
Extensible = false;
SourceTable = "Copilot Settings";
- SourceTableView = where(Availability = const(Preview), "Service Type" = const("Azure AI Service Type"::"Azure OpenAI"));
+ SourceTableView = where(Availability = const(Preview));
Permissions = tabledata "Copilot Settings" = rm;
InherentEntitlements = X;
InherentPermissions = X;
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 1989341ee8..366a56b901 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotCapabilityImpl.Codeunit.al
@@ -6,7 +6,6 @@ namespace System.AI;
using System;
using System.Azure.Identity;
-using System.Azure.KeyVault;
using System.Environment;
using System.Environment.Configuration;
using System.Globalization;
@@ -22,23 +21,11 @@ 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;
+ AzureOpenAiTxt: Label 'Azure OpenAI', Locked = true;
AlreadyRegisteredErr: Label 'Capability has already been registered.';
NotRegisteredErr: Label 'Copilot capability has not been registered by the module.';
- 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;
TelemetryUnregisteredCopilotCapabilityLbl: Label 'Copilot capability unregistered.', Locked = true;
@@ -51,25 +38,19 @@ codeunit 7774 "Copilot Capability Impl"
end;
procedure RegisterCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; LearnMoreUrl: Text[2048]; CallerModuleInfo: ModuleInfo)
- begin
- RegisterCapability(CopilotCapability, CopilotAvailability, Enum::"Azure AI Service Type"::"Azure OpenAI", LearnMoreUrl, CallerModuleInfo);
- end;
-
- procedure RegisterCapability(CopilotCapability: Enum "Copilot Capability"; CopilotAvailability: Enum "Copilot Availability"; AzureAIServiceType: Enum "Azure AI Service Type"; LearnMoreUrl: Text[2048]; CallerModuleInfo: ModuleInfo)
var
+ CopilotSettings: Record "Copilot Settings";
CustomDimensions: Dictionary of [Text, Text];
begin
if IsCapabilityRegistered(CopilotCapability, CallerModuleInfo) then
Error(AlreadyRegisteredErr);
- Clear(CopilotSettings);
CopilotSettings.Init();
CopilotSettings.Capability := CopilotCapability;
CopilotSettings."App Id" := CallerModuleInfo.Id();
CopilotSettings.Publisher := CopyStr(CallerModuleInfo.Publisher, 1, MaxStrLen(CopilotSettings.Publisher));
CopilotSettings.Availability := CopilotAvailability;
CopilotSettings."Learn More Url" := LearnMoreUrl;
- CopilotSettings."Service Type" := AzureAIServiceType;
if CopilotSettings.Availability = Enum::"Copilot Availability"::"Early Preview" then
CopilotSettings.Status := Enum::"Copilot Status"::Inactive
else
@@ -81,40 +62,9 @@ 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; AIServiceType: Enum "Azure AI Service Type")
- var
- CopilotTelemetry: Codeunit "Copilot Telemetry";
- Language: Codeunit Language;
- IAIServicename: Interface "AI Service Name";
- 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);
-
- IAIServicename := AIServiceType;
- FeatureTelemetry.LogError('0000LFN', IAIServicename.GetServiceName(), 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";
CustomDimensions: Dictionary of [Text, Text];
begin
if not IsCapabilityRegistered(CopilotCapability, CallerModuleInfo) then
@@ -138,6 +88,7 @@ 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
@@ -159,6 +110,8 @@ 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);
@@ -166,11 +119,6 @@ codeunit 7774 "Copilot Capability Impl"
exit(not CopilotSettings.IsEmpty());
end;
- procedure IsCapabilityActive(CallerModuleInfo: ModuleInfo): Boolean
- begin
- exit(IsCapabilityActive(CopilotSettings.Capability, CallerModuleInfo.Id()));
- end;
-
procedure IsCapabilityActive(CopilotCapability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo): Boolean
begin
exit(IsCapabilityActive(CopilotCapability, CallerModuleInfo.Id()));
@@ -178,6 +126,7 @@ 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]];
@@ -201,140 +150,6 @@ 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 CheckCapabilityServiceType(ServiceType: Enum "Azure AI Service Type")
- begin
- if CopilotSettings."Service Type" <> ServiceType then
- Error(CopilotCapabilityNotSetErr);
- end;
-
- procedure CheckEnabled(CallerModuleInfo: ModuleInfo)
- begin
- if not IsCapabilityEnabled(CopilotSettings.Capability, true, CallerModuleInfo) then
- Error(CopilotNotEnabledErr);
- end;
-
- procedure IsCapabilityEnabled(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo): Boolean
- begin
- exit(IsCapabilityEnabled(Capability, false, CallerModuleInfo));
- end;
-
- procedure IsCapabilityEnabled(Capability: Enum "Copilot Capability"; Silent: Boolean; CallerModuleInfo: ModuleInfo): Boolean
- var
- CopilotNotAvailable: Page "Copilot Not Available";
- begin
- if not IsTenantAllowedToUseAOAI() 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));
- end;
-
- [NonDebuggable]
- local procedure IsTenantAllowedToUseAOAI(): Boolean
- var
- EnvironmentInformation: Codeunit "Environment Information";
- AzureOpenAIImpl: Codeunit "Azure OpenAI 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);
-
- TelemtryTok := AzureOpenAIImpl.GetAzureOpenAICategory();
- 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"): Boolean
- var
- PrivacyNotice: Codeunit "Privacy Notice";
- AzureOpenAIImpl: Codeunit "Azure OpenAI Impl";
- CopilotNotAvailable: Page "Copilot Not Available";
- PrivacyNoticeApprovalState: Enum "Privacy Notice Approval State";
- begin
- PrivacyNoticeApprovalState := PrivacyNotice.GetPrivacyNoticeApprovalState(AzureOpenAIImpl.GetAzureOpenAICategory(), false);
- 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];
@@ -360,6 +175,11 @@ codeunit 7774 "Copilot Capability Impl"
GlobalLanguage(SavedGlobalLanguageId);
end;
+ procedure GetAzureOpenAICategory(): Code[50]
+ begin
+ exit(AzureOpenAiTxt);
+ end;
+
procedure GetCopilotCategory(): Code[50]
begin
exit(CopilotCategoryLbl);
@@ -422,6 +242,15 @@ 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
diff --git a/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al b/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
index e86861ff8f..9ee6c06bfb 100644
--- a/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
+++ b/src/System Application/App/AI/src/Copilot/CopilotSettings.Table.al
@@ -44,10 +44,6 @@ table 7775 "Copilot Settings"
{
DataClassification = SystemMetadata;
}
- field(7; "Service Type"; Enum "Azure AI Service Type")
- {
- DataClassification = SystemMetadata;
- }
}
keys
diff --git a/src/System Application/App/AI/src/Copilot/Interfaces/AIServiceName.Interface.al b/src/System Application/App/AI/src/Copilot/Interfaces/AIServiceName.Interface.al
deleted file mode 100644
index f7ac245c07..0000000000
--- a/src/System Application/App/AI/src/Copilot/Interfaces/AIServiceName.Interface.al
+++ /dev/null
@@ -1,25 +0,0 @@
-// ------------------------------------------------------------------------------------------------
-// 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;
-
-///
-/// Interface for providing naming information for a given AI service.
-///
-interface "AI Service Name"
-{
-
- ///
- /// Get the name of the service.
- ///
- /// The name of the service.
- procedure GetServiceName(): Text[250];
-
- ///
- /// Get the id of the service. Will often be the service name in Code form.
- ///
- /// The id of the service.
- procedure GetServiceId(): Code[50];
-
-}
\ No newline at end of file
diff --git a/src/System Application/Test/AI/src/AzureDITest.Codeunit.al b/src/System Application/Test/AI/src/AzureDITest.Codeunit.al
deleted file mode 100644
index 767f6b6dd5..0000000000
--- a/src/System Application/Test/AI/src/AzureDITest.Codeunit.al
+++ /dev/null
@@ -1,95 +0,0 @@
-// ------------------------------------------------------------------------------------------------
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License. See License.txt in the project root for license information.
-// ------------------------------------------------------------------------------------------------
-
-namespace System.Test.AI;
-
-using System.AI;
-using System.TestLibraries.AI;
-using System.TestLibraries.Environment;
-using System.AI.DocumentIntelligence;
-using System.TestLibraries.Utilities;
-
-codeunit 132685 "Azure DI Test"
-{
- Subtype = Test;
-
- var
- CopilotTestLibrary: Codeunit "Copilot Test Library";
- EnvironmentInfoTestLibrary: Codeunit "Environment Info Test Library";
- LibraryAssert: Codeunit "Library Assert";
-
- [Test]
- procedure TestSetCopilotCapabilityInactive()
- var
- AzureDI: Codeunit "Azure Document Intelligence";
- begin
-
- // [GIVEN] Capability is set
- RegisterCapability(Enum::"Copilot Capability"::"Text Capability");
- CopilotTestLibrary.SetCopilotStatus(Enum::"Copilot Capability"::"Text Capability", GetModuleAppId(), Enum::"Copilot Status"::Inactive);
-
- // [WHEN] SetCopilotCapability is called
- asserterror AzureDI.SetCopilotCapability(Enum::"Copilot Capability"::"Text Capability");
-
- // [THEN] SetCopilotCapability returns an error
- LibraryAssert.ExpectedError('Copilot capability ''Text Capability'' has not been enabled. Please contact your system administrator.');
- end;
-
- [Test]
- procedure AnalyzeInvoiceCopilotCapabilityNotSet()
- var
- AzureDI: Codeunit "Azure Document Intelligence";
- begin
- // [SCENARIO] AnalyzeInvoice returns an error when capability is not set
-
- EnvironmentInfoTestLibrary.SetTestabilitySoftwareAsAService(false);
-
- // [WHEN] AnalyzeInvoice is called
- asserterror AzureDI.AnalyzeInvoice('Text');
-
- // [THEN] AnalyzeInvoice returns an error
- LibraryAssert.ExpectedError('Copilot capability has not been set.');
- end;
-
- [Test]
- procedure AnalyzeInvoiceCapabilityInactive()
- var
- AzureDI: Codeunit "Azure Document Intelligence";
- begin
- // [SCENARIO] AnalyzeInvoice returns an error when capability is not active
-
- EnvironmentInfoTestLibrary.SetTestabilitySoftwareAsAService(false);
-
- // [GIVEN] Capability is set
- RegisterCapability(Enum::"Copilot Capability"::"Text Capability");
- AzureDI.SetCopilotCapability(Enum::"Copilot Capability"::"Text Capability");
- CopilotTestLibrary.SetCopilotStatus(Enum::"Copilot Capability"::"Text Capability", GetModuleAppId(), Enum::"Copilot Status"::Inactive);
-
- // [WHEN] AnalyzeInvoice is called
- asserterror AzureDI.AnalyzeInvoice('Test');
-
- // [THEN] AnalyzeInvoice returns an error
- LibraryAssert.ExpectedError('Copilot capability ''Text Capability'' has not been enabled. Please contact your system administrator.');
- end;
-
- local procedure RegisterCapability(Capability: Enum "Copilot Capability")
- var
- AzureDI: Codeunit "Azure Document Intelligence";
- CopilotCapability: Codeunit "Copilot Capability";
- begin
- if CopilotCapability.IsCapabilityRegistered(Capability) then
- exit;
-
- AzureDI.RegisterCopilotCapability(Capability, Enum::"Copilot Availability"::Preview, '');
- end;
-
- local procedure GetModuleAppId(): Guid
- var
- CurrentModuleInfo: ModuleInfo;
- begin
- NavApp.GetCurrentModuleInfo(CurrentModuleInfo);
- exit(CurrentModuleInfo.Id());
- end;
-}
\ No newline at end of file