Codeunit 2016 Entity Text Prompts, source in 26

Source242526272829metadata in 26

src/System Application/App/Entity Text/src/EntityTextPrompts.Codeunit.al148 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace System.Text;

using System.Azure.KeyVault;
using System.Telemetry;

codeunit 2016 "Entity Text Prompts"
{
    Access = Internal;
    InherentEntitlements = X;
    InherentPermissions = X;

    [NonDebuggable]
    internal procedure GetAzureKeyVaultSecret(var SecretValue: Text; SecretName: Text)
    var
        EntityTextImpl: Codeunit "Entity Text Impl.";
        AzureKeyVault: Codeunit "Azure Key Vault";
        FeatureTelemetry: Codeunit "Feature Telemetry";
    begin
        if not AzureKeyVault.GetAzureKeyVaultSecret(SecretName, SecretValue) then begin
            FeatureTelemetry.LogError('0000N5D', EntityTextImpl.GetFeatureName(), 'Get prompt from Key Vault', TelemetryConstructingPromptFailedErr);
            Error(ConstructingPromptFailedErr);
        end;
    end;

    [NonDebuggable]
    internal procedure GetGenerateProdMktAdFuncPrompt(TextFormat: Enum "Entity Text Format"): Text
    var
        PromptObject: JsonObject;
        FunctPropArrayToken: JsonToken;
        FunctPropReqArrayToken: JsonToken;
        FuncPropArray: JsonArray;
        FuncPropReqArray: JsonArray;
        FunctionPropObject: JsonToken;
        FunctionPropReqObject: JsonToken;
        BCETGenerateProdMAdFuncPrompt: Text;
        BCETPromptObject: Text;
    begin
        GetAzureKeyVaultSecret(BCETPromptObject, 'BCETPromptObject');
        PromptObject.ReadFrom(BCETPromptObject);
        PromptObject.Get('function-properties', FunctPropArrayToken);
        FuncPropArray := FunctPropArrayToken.AsArray();
        FuncPropArray.Get(TextFormat.AsInteger(), FunctionPropObject);

        PromptObject.Get('required-properties', FunctPropReqArrayToken);
        FuncPropReqArray := FunctPropReqArrayToken.AsArray();
        FuncPropReqArray.Get(TextFormat.AsInteger(), FunctionPropReqObject);

        GetAzureKeyVaultSecret(BCETGenerateProdMAdFuncPrompt, 'BCETGenerateProdMAdFuncPrompt');
        BCETGenerateProdMAdFuncPrompt := StrSubstNo(BCETGenerateProdMAdFuncPrompt, Format(FunctionPropObject), Format(FunctionPropReqObject));

        exit(BCETGenerateProdMAdFuncPrompt);
    end;

    [NonDebuggable]
    internal procedure GetMagicFunctionPrompt(): Text
    var
        BCETMagicFunctionPrompt: Text;
    begin
        GetAzureKeyVaultSecret(BCETMagicFunctionPrompt, 'BCETMagicFunctionPrompt');
        exit(BCETMagicFunctionPrompt);
    end;

    [NonDebuggable]
    procedure BuildPrompts(FactsList: Text; Category: Text; Tone: Enum "Entity Text Tone"; TextFormat: Enum "Entity Text Format"; TextEmphasis: Enum "Entity Text Emphasis"; var SystemPrompt: Text; var UserPrompt: Text)
    var
        EntityTextAOAISettings: Codeunit "Entity Text AOAI Settings";
        PromptObject: JsonObject;
        SystemPromptJson: JsonToken;
        UserPromptJson: JsonToken;
        BCETPromptObject: Text;
        LanguageName: Text;
    begin
        LanguageName := EntityTextAOAISettings.GetLanguageName();

        GetAzureKeyVaultSecret(BCETPromptObject, 'BCETPromptObject');
        PromptObject.ReadFrom(BCETPromptObject);
        PromptObject.Get('system', SystemPromptJson);
        PromptObject.Get('user', UserPromptJson);

        SystemPrompt := BuildSinglePrompt(SystemPromptJson.AsObject(), LanguageName, FactsList, Category, Tone, TextFormat, TextEmphasis);
        UserPrompt := BuildSinglePrompt(UserPromptJson.AsObject(), LanguageName, FactsList, Category, Tone, TextFormat, TextEmphasis);
    end;

    [NonDebuggable]
    local procedure BuildSinglePrompt(PromptInfo: JsonObject; LanguageName: Text; FactsList: Text; Category: Text; Tone: Enum "Entity Text Tone"; TextFormat: Enum "Entity Text Format"; TextEmphasis: Enum "Entity Text Emphasis") Prompt: Text
    var
        PromptHints: JsonToken;
        PromptOrder: JsonToken;
        PromptArray: JsonArray;
        PromptHint: JsonToken;
        CategoryIndex: Integer;
        HintName: Text;
        NewLineChar: Char;
        PromptIndex: Integer;
    begin
        NewLineChar := 10;

        PromptInfo.Get('prompt', PromptHints);
        PromptInfo.Get('order', PromptOrder);

        PromptArray := PromptOrder.AsArray();
        CategoryIndex := PromptArray.IndexOf('category');

        if CategoryIndex <> -1 then
            if Category = '' then
                PromptArray.RemoveAt(CategoryIndex);

        foreach PromptHint in PromptArray do begin
            HintName := PromptHint.AsValue().AsText();
            if PromptHints.AsObject().Get(HintName, PromptHint) then begin
                // found the hint
                if PromptHint.IsArray() then begin
                    PromptIndex := 0; // default value
                    case HintName of
                        'tone':
                            PromptIndex := Tone.AsInteger();
                        'format':
                            PromptIndex := TextFormat.AsInteger();
                        'emphasis':
                            PromptIndex := TextEmphasis.AsInteger();
                    end;

                    if not PromptHint.AsArray().Get(PromptIndex, PromptHint) then
                        PromptHint.AsArray().Get(0, PromptHint);
                end;

                Prompt += StrSubstNo(PromptHint.AsValue().AsText(), NewLineChar, LanguageName, FactsList, Category);
            end;
        end;
    end;

    [TryFunction]
    [NonDebuggable]
    procedure HasPromptInfo()
    var
        BCETPromptObject: Text;
    begin
        GetAzureKeyVaultSecret(BCETPromptObject, 'BCETPromptObject');
    end;

    var
        ConstructingPromptFailedErr: label 'There was an error with sending the call to Copilot. Log a Business Central support request about this.', Comment = 'Copilot is a Microsoft service name and must not be translated';
        TelemetryConstructingPromptFailedErr: label 'There was an error with constructing the chat completion prompt from the Key Vault.', Locked = true;
}