Skip to content

Commit e0ea026

Browse files
author
Magnus Hartvig Grønbech
committed
Can we decouple OpenAI and Copilot module
1 parent 8b1a432 commit e0ea026

File tree

7 files changed

+112
-71
lines changed

7 files changed

+112
-71
lines changed

src/System Application/App/AI/src/DocumentIntelligence/AzureDI.Codeunit.al src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDI.Codeunit.al

+19-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// ------------------------------------------------------------------------------------------------
55
namespace System.Azure.DI;
66

7+
using System.AI;
8+
79
/// <summary>
810
/// Azure Document Intelligence implementation.
911
/// </summary>
@@ -14,7 +16,7 @@ codeunit 7780 "Azure DI"
1416
InherentPermissions = X;
1517

1618
var
17-
AzureOpenAIImpl: Codeunit "Azure DI Impl.";
19+
AzureDIImpl: Codeunit "Azure DI Impl.";
1820

1921

2022
/// <summary>
@@ -28,7 +30,7 @@ codeunit 7780 "Azure DI"
2830
CallerModuleInfo: ModuleInfo;
2931
begin
3032
NavApp.GetCallerModuleInfo(CallerModuleInfo);
31-
exit(AzureOpenAIImpl.AnalyzeInvoice(Base64Data, CallerModuleInfo));
33+
exit(AzureDIImpl.AnalyzeInvoice(Base64Data, CallerModuleInfo));
3234
end;
3335

3436
/// <summary>
@@ -42,8 +44,22 @@ codeunit 7780 "Azure DI"
4244
CallerModuleInfo: ModuleInfo;
4345
begin
4446
NavApp.GetCallerModuleInfo(CallerModuleInfo);
45-
exit(AzureOpenAIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo));
47+
exit(AzureDIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo));
4648
end;
4749

4850

51+
/// <summary>
52+
/// Sets the copilot capability that the API is running for.
53+
/// </summary>
54+
/// <param name="CopilotCapability">The copilot capability to set.</param>
55+
[NonDebuggable]
56+
procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability")
57+
var
58+
CopilotCapabilityImpl: Codeunit "Copilot Capability Impl";
59+
CallerModuleInfo: ModuleInfo;
60+
begin
61+
NavApp.GetCallerModuleInfo(CallerModuleInfo);
62+
CopilotCapabilityImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureDIImpl.GetAzureAIDocumentIntelligenceCategory());
63+
end;
64+
4965
}

src/System Application/App/AI/src/DocumentIntelligence/AzureDIImpl.Codeunit.al src/System Application/App/AI/src/Azure AI Document Intelligence/AzureDIImpl.Codeunit.al

+22-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace System.Azure.DI;
66
using System.AI;
77
using System.Globalization;
8+
using System.Privacy;
89
using System.Telemetry;
910
using System;
1011

@@ -87,7 +88,6 @@ codeunit 7779 "Azure DI Impl."
8788
Result := ALCopilotResponse.Result();
8889
end;
8990

90-
9191
local procedure AddTelemetryCustomDimensions(var CustomDimensions: Dictionary of [Text, Text]; CallerModuleInfo: ModuleInfo)
9292
var
9393
Language: Codeunit Language;
@@ -123,13 +123,34 @@ codeunit 7779 "Azure DI Impl."
123123
exit(JsonText);
124124
end;
125125

126+
procedure GetAzureAIDocumentIntelligenceCategory(): Code[50]
127+
begin
128+
exit(AzureAiDocumentIntelligenceTxt);
129+
end;
130+
131+
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Privacy Notice", OnRegisterPrivacyNotices, '', false, false)]
132+
local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary)
133+
begin
134+
TempPrivacyNotice.Init();
135+
TempPrivacyNotice.ID := AzureAiDocumentIntelligenceTxt;
136+
TempPrivacyNotice."Integration Service Name" := AzureAiDocumentIntelligenceTxt;
137+
if not TempPrivacyNotice.Insert() then;
138+
end;
139+
126140
var
141+
// CopilotSettings: Record "Copilot Settings";
142+
// CopilotCapabilityCU: Codeunit "Copilot Capability";
143+
// CopilotCapabilityImpl: Codeunit "Copilot Capability Impl";
127144
FeatureTelemetry: Codeunit "Feature Telemetry";
145+
// TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true;
146+
// TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true;
147+
// CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability';
128148
AzureDocumentIntelligenceCapabilityTok: Label 'ADI', Locked = true;
129149
TelemetryAnalyzeInvoiceFailureLbl: Label 'Analyze invoice failed.', Locked = true;
130150
TelemetryAnalyzeInvoiceCompletedLbl: Label 'Analyze invoice completed.', Locked = true;
131151
TelemetryAnalyzeReceiptFailureLbl: Label 'Analyze receipt failed.', Locked = true;
132152
TelemetryAnalyzeReceiptCompletedLbl: Label 'Analyze receipt completed.', Locked = true;
133153
GenerateRequestFailedErr: Label 'The request did not return a success status code.';
154+
AzureAiDocumentIntelligenceTxt: Label 'Azure AI Document Intelligence', Locked = true;
134155

135156
}

src/System Application/App/AI/src/Azure OpenAI/AzureOpenAI.Codeunit.al

+3-1
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,13 @@ codeunit 7771 "Azure OpenAI"
265265
[NonDebuggable]
266266
procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability")
267267
var
268+
CopilotCapabilityImpl: Codeunit "Copilot Capability Impl";
268269
CallerModuleInfo: ModuleInfo;
269270
begin
270271
NavApp.GetCallerModuleInfo(CallerModuleInfo);
271-
AzureOpenAIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo);
272+
CopilotCapabilityImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo, AzureOpenAIImpl.GetAzureOpenAICategory());
272273
end;
274+
273275
#if not CLEAN24
274276
/// <summary>
275277
/// Gets the approximate token count for the input.

src/System Application/App/AI/src/Azure OpenAI/AzureOpenAIImpl.Codeunit.al

+28-48
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ codeunit 7772 "Azure OpenAI Impl"
2222
var
2323
CopilotSettings: Record "Copilot Settings";
2424
CopilotCapabilityCU: Codeunit "Copilot Capability";
25-
CopilotCapabilityImpl: Codeunit "Copilot Capability Impl";
2625
ChatCompletionsAOAIAuthorization: Codeunit "AOAI Authorization";
2726
TextCompletionsAOAIAuthorization: Codeunit "AOAI Authorization";
2827
EmbeddingsAOAIAuthorization: Codeunit "AOAI Authorization";
@@ -39,8 +38,6 @@ codeunit 7772 "Azure OpenAI Impl"
3938
CopilotCapabilityNotSetErr: Label 'Copilot capability has not been set.';
4039
CapabilityBackgroundErr: Label 'Microsoft Copilot Capabilities are not allowed in the background.';
4140
CopilotDisabledForTenantErr: Label 'Copilot is not enabled for the tenant. Please contact your system administrator.';
42-
CapabilityNotRegisteredErr: Label 'Copilot capability ''%1'' has not been registered by the module.', Comment = '%1 is the name of the Copilot Capability';
43-
CapabilityNotEnabledErr: Label 'Copilot capability ''%1'' has not been enabled. Please contact your system administrator.', Comment = '%1 is the name of the Copilot Capability';
4441
MessagesMustContainJsonWordWhenResponseFormatIsJsonErr: Label 'The messages must contain the word ''json'' in some form, to use ''response format'' of type ''json_object''.';
4542
EmptyMetapromptErr: Label 'The metaprompt has not been set, please provide a metaprompt.';
4643
MetapromptLoadingErr: Label 'Metaprompt not found.';
@@ -52,8 +49,6 @@ codeunit 7772 "Azure OpenAI Impl"
5249
TelemetryGenerateChatCompletionLbl: Label 'Chat Completion generated.', Locked = true;
5350
TelemetryChatCompletionToolCallLbl: Label 'Tools called by chat completion.', Locked = true;
5451
TelemetryChatCompletionToolUsedLbl: Label 'Tools added to chat completion.', Locked = true;
55-
TelemetrySetCapabilityLbl: Label 'Set Capability', Locked = true;
56-
TelemetryCopilotCapabilityNotRegisteredLbl: Label 'Copilot capability not registered.', Locked = true;
5752
TelemetryIsEnabledLbl: Label 'Is Enabled', Locked = true;
5853
TelemetryUnableToCheckEnvironmentKVTxt: Label 'Unable to check if environment is allowed to run AOAI.', Locked = true;
5954
TelemetryEnvironmentNotAllowedtoRunCopilotTxt: Label 'Copilot is not allowed on this environment.', Locked = true;
@@ -63,6 +58,7 @@ codeunit 7772 "Azure OpenAI Impl"
6358
TelemetryFunctionCallingFailedErr: Label 'Function calling failed for function: %1', Comment = '%1 is the name of the function', Locked = true;
6459
TelemetryEmptyTenantIdErr: Label 'Empty or malformed tenant ID.', Locked = true;
6560
TelemetryTenantAllowlistedMsg: Label 'Current tenant allowlisted for first party auth.', Locked = true;
61+
AzureOpenAiTxt: Label 'Azure OpenAI', Locked = true;
6662

6763
procedure IsEnabled(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo): Boolean
6864
begin
@@ -109,12 +105,12 @@ codeunit 7772 "Azure OpenAI Impl"
109105
exit(true);
110106

111107
if (not AzureKeyVault.GetAzureKeyVaultSecret(EnabledKeyTok, BlockList)) or (BlockList.Trim() = '') then begin
112-
FeatureTelemetry.LogError('0000KYC', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt);
108+
FeatureTelemetry.LogError('0000KYC', GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryUnableToCheckEnvironmentKVTxt);
113109
exit(false);
114110
end;
115111

116112
if BlockList.Contains(AzureAdTenant.GetAadTenantId()) then begin
117-
FeatureTelemetry.LogError('0000LFP', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryEnvironmentNotAllowedtoRunCopilotTxt);
113+
FeatureTelemetry.LogError('0000LFP', GetAzureOpenAICategory(), TelemetryIsEnabledLbl, TelemetryEnvironmentNotAllowedtoRunCopilotTxt);
118114
exit(false);
119115
end;
120116

@@ -126,7 +122,7 @@ codeunit 7772 "Azure OpenAI Impl"
126122
PrivacyNotice: Codeunit "Privacy Notice";
127123
CopilotNotAvailable: Page "Copilot Not Available";
128124
begin
129-
case PrivacyNotice.GetPrivacyNoticeApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), false) of
125+
case PrivacyNotice.GetPrivacyNoticeApprovalState(GetAzureOpenAICategory(), false) of
130126
Enum::"Privacy Notice Approval State"::Agreed:
131127
exit(true);
132128
Enum::"Privacy Notice Approval State"::Disagreed:
@@ -256,11 +252,11 @@ codeunit 7772 "Azure OpenAI Impl"
256252
SendTokenCountTelemetry(AOAIToken.GetGPT4TokenCount(Metaprompt), AOAIToken.GetGPT4TokenCount(Prompt), CustomDimensions);
257253

258254
if not SendRequest(Enum::"AOAI Model Type"::"Text Completions", TextCompletionsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin
259-
FeatureTelemetry.LogError('0000KVD', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, CompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
255+
FeatureTelemetry.LogError('0000KVD', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, CompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
260256
exit;
261257
end;
262258

263-
FeatureTelemetry.LogUsage('0000KVL', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
259+
FeatureTelemetry.LogUsage('0000KVL', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
264260
Result := AOAIOperationResponse.GetResult();
265261
end;
266262

@@ -283,11 +279,11 @@ codeunit 7772 "Azure OpenAI Impl"
283279
AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
284280
SendTokenCountTelemetry(0, AOAIToken.GetAdaTokenCount(Input), CustomDimensions);
285281
if not SendRequest(Enum::"AOAI Model Type"::Embeddings, EmbeddingsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin
286-
FeatureTelemetry.LogError('0000KVE', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, EmbeddingsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
282+
FeatureTelemetry.LogError('0000KVE', GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, EmbeddingsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
287283
exit;
288284
end;
289285

290-
FeatureTelemetry.LogUsage('0000KVM', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
286+
FeatureTelemetry.LogUsage('0000KVM', GetAzureOpenAICategory(), TelemetryGenerateEmbeddingLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
291287
exit(ProcessEmbeddingResponse(AOAIOperationResponse));
292288
end;
293289

@@ -356,13 +352,13 @@ codeunit 7772 "Azure OpenAI Impl"
356352

357353
SendTokenCountTelemetry(MetapromptTokenCount, PromptTokenCount, CustomDimensions);
358354
if not SendRequest(Enum::"AOAI Model Type"::"Chat Completions", ChatCompletionsAOAIAuthorization, PayloadText, AOAIOperationResponse, CallerModuleInfo) then begin
359-
FeatureTelemetry.LogError('0000KVF', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, ChatCompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
355+
FeatureTelemetry.LogError('0000KVF', GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, ChatCompletionsFailedWithCodeErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
360356
exit;
361357
end;
362358

363359
ProcessChatCompletionResponse(ChatMessages, AOAIOperationResponse, CallerModuleInfo);
364360

365-
FeatureTelemetry.LogUsage('0000KVN', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
361+
FeatureTelemetry.LogUsage('0000KVN', GetAzureOpenAICategory(), TelemetryGenerateChatCompletionLbl, Enum::"AL Telemetry Scope"::All, CustomDimensions);
366362

367363
if (AOAIOperationResponse.GetFunctionResponses().Count() > 0) and (ChatMessages.GetToolInvokePreference() = Enum::"AOAI Tool Invoke Preference"::Automatic) then
368364
GenerateChatCompletion(ChatMessages, AOAIChatCompletionParams, AOAIOperationResponse, CallerModuleInfo);
@@ -421,7 +417,7 @@ codeunit 7772 "Azure OpenAI Impl"
421417
AddTelemetryCustomDimensions(CustomDimensions, CallerModuleInfo);
422418
foreach AOAIFunctionResponse in AOAIOperationResponse.GetFunctionResponses() do
423419
if not AOAIFunctionResponse.IsSuccess() then
424-
FeatureTelemetry.LogError('0000MTB', CopilotCapabilityImpl.GetAzureOpenAICategory(), StrSubstNo(TelemetryFunctionCallingFailedErr, AOAIFunctionResponse.GetFunctionName()), AOAIFunctionResponse.GetError(), AOAIFunctionResponse.GetErrorCallstack(), Enum::"AL Telemetry Scope"::All, CustomDimensions);
420+
FeatureTelemetry.LogError('0000MTB', GetAzureOpenAICategory(), StrSubstNo(TelemetryFunctionCallingFailedErr, AOAIFunctionResponse.GetFunctionName()), AOAIFunctionResponse.GetError(), AOAIFunctionResponse.GetErrorCallstack(), Enum::"AL Telemetry Scope"::All, CustomDimensions);
425421

426422
if ChatMessages.GetToolInvokePreference() in [Enum::"AOAI Tool Invoke Preference"::"Invoke Tools Only", Enum::"AOAI Tool Invoke Preference"::Automatic] then
427423
AOAIOperationResponse.AppendFunctionResponsesToChatMessages(ChatMessages);
@@ -600,36 +596,6 @@ codeunit 7772 "Azure OpenAI Impl"
600596
GlobalLanguage(SavedGlobalLanguageId);
601597
end;
602598

603-
procedure SetCopilotCapability(Capability: Enum "Copilot Capability"; CallerModuleInfo: ModuleInfo)
604-
var
605-
CopilotTelemetry: Codeunit "Copilot Telemetry";
606-
Language: Codeunit Language;
607-
SavedGlobalLanguageId: Integer;
608-
CustomDimensions: Dictionary of [Text, Text];
609-
ErrorMessage: Text;
610-
begin
611-
if not CopilotCapabilityCU.IsCapabilityRegistered(Capability, CallerModuleInfo.Id()) then begin
612-
SavedGlobalLanguageId := GlobalLanguage();
613-
GlobalLanguage(Language.GetDefaultApplicationLanguageId());
614-
CustomDimensions.Add('Capability', Format(Capability));
615-
CustomDimensions.Add('AppId', Format(CallerModuleInfo.Id()));
616-
GlobalLanguage(SavedGlobalLanguageId);
617-
618-
FeatureTelemetry.LogError('0000LFN', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetrySetCapabilityLbl, TelemetryCopilotCapabilityNotRegisteredLbl, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
619-
ErrorMessage := StrSubstNo(CapabilityNotRegisteredErr, Capability);
620-
Error(ErrorMessage);
621-
end;
622-
623-
CopilotSettings.ReadIsolation(IsolationLevel::ReadCommitted);
624-
CopilotSettings.SetLoadFields(Status);
625-
CopilotSettings.Get(Capability, CallerModuleInfo.Id());
626-
if CopilotSettings.Status = Enum::"Copilot Status"::Inactive then begin
627-
ErrorMessage := StrSubstNo(CapabilityNotEnabledErr, Capability);
628-
Error(ErrorMessage);
629-
end;
630-
CopilotTelemetry.SetCopilotCapability(Capability, CallerModuleInfo.Id());
631-
end;
632-
633599
local procedure CheckEnabled(CallerModuleInfo: ModuleInfo)
634600
begin
635601
if not IsEnabled(CopilotSettings.Capability, true, CallerModuleInfo) then
@@ -688,7 +654,7 @@ codeunit 7772 "Azure OpenAI Impl"
688654
ModuleInfo: ModuleInfo;
689655
begin
690656
if Metaprompt.Unwrap().Trim() = '' then begin
691-
FeatureTelemetry.LogError('0000LO8', CopilotCapabilityImpl.GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, EmptyMetapromptErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
657+
FeatureTelemetry.LogError('0000LO8', GetAzureOpenAICategory(), TelemetryGenerateTextCompletionLbl, EmptyMetapromptErr, '', Enum::"AL Telemetry Scope"::All, CustomDimensions);
692658

693659
NavApp.GetCurrentModuleInfo(ModuleInfo);
694660
if ModuleInfo.Publisher = 'Microsoft' then
@@ -747,15 +713,29 @@ codeunit 7772 "Azure OpenAI Impl"
747713
EntraTenantIdAsText := AzureAdTenant.GetAadTenantId();
748714

749715
if (EntraTenantIdAsText = '') or not Evaluate(EntraTenantIdAsGuid, EntraTenantIdAsText) or IsNullGuid(EntraTenantIdAsGuid) then begin
750-
Session.LogMessage('0000MLN', TelemetryEmptyTenantIdErr, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CopilotCapabilityImpl.GetAzureOpenAICategory());
716+
Session.LogMessage('0000MLN', TelemetryEmptyTenantIdErr, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GetAzureOpenAICategory());
751717
exit(false);
752718
end;
753719

754720
if not AllowlistedTenants.Contains(EntraTenantIdAsText) then
755721
exit(false);
756722

757-
Session.LogMessage('0000MLE', TelemetryTenantAllowlistedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CopilotCapabilityImpl.GetAzureOpenAICategory());
723+
Session.LogMessage('0000MLE', TelemetryTenantAllowlistedMsg, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', GetAzureOpenAICategory());
758724
exit(true);
759725
end;
760726

727+
procedure GetAzureOpenAICategory(): Code[50]
728+
begin
729+
exit(AzureOpenAiTxt);
730+
end;
731+
732+
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Privacy Notice", 'OnRegisterPrivacyNotices', '', false, false)]
733+
local procedure CreatePrivacyNoticeRegistrations(var TempPrivacyNotice: Record "Privacy Notice" temporary)
734+
begin
735+
TempPrivacyNotice.Init();
736+
TempPrivacyNotice.ID := AzureOpenAiTxt;
737+
TempPrivacyNotice."Integration Service Name" := AzureOpenAiTxt;
738+
if not TempPrivacyNotice.Insert() then;
739+
end;
740+
761741
}

0 commit comments

Comments
 (0)