Skip to content

Commit

Permalink
Azure Document Intelligence Module (#2834)
Browse files Browse the repository at this point in the history
Azure Document Intelligence support
- OnPrem for now. Uptake available for 1p apps.
- Move capabilities to Capabilities Impl Codeunit. 
- Introduce Azure AI Service Enum.
- Hide Azure Doc Int. capabilities on the copilot pages based on PM
feedback.



[AB#563692](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/563692)

---------

Co-authored-by: Magnus Hartvig Grønbech <[email protected]>
Co-authored-by: Jose Antonio Garcia Garcia <[email protected]>
Co-authored-by: encimita <[email protected]>
  • Loading branch information
4 people authored Feb 11, 2025
1 parent 7ec065f commit a70b07c
Show file tree
Hide file tree
Showing 15 changed files with 661 additions and 206 deletions.
2 changes: 1 addition & 1 deletion src/System Application/App/AI/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"idRanges": [
{
"from": 7757,
"to": 7778
"to": 7780
}
],
"target": "OnPrem",
Expand Down
Original file line number Diff line number Diff line change
@@ -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.DocumentIntelligence;

/// <summary>
/// The supported model types for Azure Document Intelligence.
/// </summary>
enum 7779 "ADI Model Type"
{
Access = Public;
Extensible = false;

/// <summary>
/// Invoice model type.
/// </summary>
value(0; Invoice)
{
}

/// <summary>
/// Receipt model type.
/// </summary>
value(1; Receipt)
{
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// ------------------------------------------------------------------------------------------------
// 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;

/// <summary>
/// Azure Document Intelligence implementation.
/// </summary>
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;

/// <summary>
/// Analyze a single invoice.
/// </summary>
/// <param name="Base64Data">Data to analyze.</param>
/// <param name="CallerModuleInfo">The module info of the caller.</param>
/// <returns>The analyzed result.</returns>
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;

/// <summary>
/// Analyze a single receipt.
/// </summary>
/// <param name="Base64Data">Data to analyze.</param>
/// <param name="CallerModuleInfo">The module info of the caller.</param>
/// <returns>The analyzed result.</returns>
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;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// ------------------------------------------------------------------------------------------------
// 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;

/// <summary>
/// Provides functionality to invoke Azure Document Intelligence services.
/// </summary>
codeunit 7780 "Azure Document Intelligence"
{
Access = Public;
InherentEntitlements = X;
InherentPermissions = X;

var
AzureDIImpl: Codeunit "Azure DI Impl.";

/// <summary>
/// Analyze the invoice.
/// </summary>
/// <param name="Base64Data">Data to analyze.</param>
/// <returns>The analyzed result.</returns>
[Scope('OnPrem')]
procedure AnalyzeInvoice(Base64Data: Text): Text
var
CallerModuleInfo: ModuleInfo;
begin
NavApp.GetCallerModuleInfo(CallerModuleInfo);
exit(AzureDIImpl.AnalyzeInvoice(Base64Data, CallerModuleInfo));
end;

/// <summary>
/// Analyze the Receipt.
/// </summary>
/// <param name="Base64Data">Data to analyze.</param>
/// <returns>The analyzed result.</returns>
[Scope('OnPrem')]
procedure AnalyzeReceipt(Base64Data: Text): Text
var
CallerModuleInfo: ModuleInfo;
begin
NavApp.GetCallerModuleInfo(CallerModuleInfo);
exit(AzureDIImpl.AnalyzeReceipt(Base64Data, CallerModuleInfo));
end;

/// <summary>
/// Register a capability for Azure Document Intelligence.
/// </summary>
/// <param name="CopilotCapability">The capability.</param>
/// <param name="CopilotAvailability">The availability.</param>
/// <param name="LearnMoreUrl">The learn more url.</param>
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;

/// <summary>
/// Sets the copilot capability that the API is running for.
/// </summary>
/// <param name="CopilotCapability">The copilot capability to set.</param>
procedure SetCopilotCapability(CopilotCapability: Enum "Copilot Capability")
var
CallerModuleInfo: ModuleInfo;
begin
NavApp.GetCallerModuleInfo(CallerModuleInfo);
AzureDIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo);
end;

}
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ codeunit 7771 "Azure OpenAI"
NavApp.GetCallerModuleInfo(CallerModuleInfo);
AzureOpenAIImpl.SetCopilotCapability(CopilotCapability, CallerModuleInfo);
end;

#if not CLEAN24
/// <summary>
/// Gets the approximate token count for the input.
Expand Down
Loading

0 comments on commit a70b07c

Please sign in to comment.