Codeunit 324 No. Series Copilot Impl. in 26
- App
- Business Foundation
- Namespace
- Microsoft.Foundation.NoSeries
Versions171819202122232425262728latest
Source in 26
src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesCopilotImpl.Codeunit.al612 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 Microsoft.Foundation.NoSeries;
using System.Telemetry;
using System.Azure.KeyVault;
using System.Environment;
using System.AI;
using System.Text.Json;
codeunit 324 "No. Series Copilot Impl."
{
Access = Internal;
InherentPermissions = X;
InherentEntitlements = X;
var
NoSeriesCopilotTelemetry: Codeunit "No. Series Copilot Telemetry";
IncorrectCompletionErr: Label 'Incorrect completion. The property %1 is empty', Comment = '%1 = property name';
EmptyCompletionErr: Label 'Incorrect completion. The completion is empty.';
IncorrectCompletionNumberOfGeneratedNoSeriesErr: Label 'Incorrect completion. The number of generated number series is incorrect. Expected %1, but got %2', Comment = '%1 = Expected Number, %2 = Actual Number';
TextLengthIsOverMaxLimitErr: Label 'The property %1 exceeds the maximum length of %2', Comment = '%1 = property name, %2 = maximum length';
DateSpecificPlaceholderLbl: Label '{current_date}', Locked = true;
TheResponseShouldBeAFunctionCallErr: Label 'The response should be a function call.';
ChatCompletionResponseErr: Label 'Sorry, something went wrong. Please rephrase and try again.';
GeneratingNoSeriesForLbl: Label 'Generating number series %1', Comment = '%1 = No. Series';
FeatureNameLbl: Label 'Number Series with AI', Locked = true;
TelemetryToolsSelectionPromptRetrievalErr: Label 'Unable to retrieve the prompt for No. Series Copilot Tools Selection from Azure Key Vault.', Locked = true;
ToolLoadingErr: Label 'Unable to load the No. Series Copilot Tool. Please try again later.';
InvalidPromptTxt: Label 'Sorry, I couldn''t generate a good result from your input. Please rephrase and try again.\Error details: %1', Comment = '%1 = Error details';
procedure GetNoSeriesSuggestions()
var
NoSeriesCopilotRegister: Codeunit "No. Series Copilot Register";
AzureOpenAI: Codeunit "Azure OpenAI";
begin
NoSeriesCopilotRegister.RegisterCapability();
if not AzureOpenAI.IsEnabled(Enum::"Copilot Capability"::"No. Series Copilot") then
exit;
NoSeriesCopilotTelemetry.LogFeatureDiscovery();
Page.Run(Page::"No. Series Generation");
end;
procedure Generate(var NoSeriesGeneration: Record "No. Series Generation"; var GeneratedNoSeries: Record "No. Series Generation Detail"; InputText: Text)
var
TokenCountImpl: Codeunit "AOAI Token";
SystemPromptTxt: SecretText;
CompletePromptTokenCount: Integer;
Completion: Text;
begin
SystemPromptTxt := GetToolsSelectionSystemPrompt();
CompletePromptTokenCount := TokenCountImpl.GetGPT35TokenCount(SystemPromptTxt) + TokenCountImpl.GetGPT35TokenCount(InputText);
if CompletePromptTokenCount <= MaxInputTokens() then begin
Completion := GenerateNoSeries(SystemPromptTxt, InputText);
if CheckIfCompletionMeetAllRequirements(Completion) then begin
SaveGenerationHistory(NoSeriesGeneration, InputText);
CreateNoSeries(NoSeriesGeneration, GeneratedNoSeries, Completion);
end else
Error(InvalidPromptTxt, GetLastErrorText());
end else
SendNotification(GetChatCompletionResponseErr());
end;
procedure ApplyGeneratedNoSeries(var GeneratedNoSeries: Record "No. Series Generation Detail")
begin
GeneratedNoSeries.SetRange(Exists, false);
if GeneratedNoSeries.FindSet() then
repeat
InsertNoSeriesWithLines(GeneratedNoSeries);
ApplyNoSeriesToSetup(GeneratedNoSeries);
until GeneratedNoSeries.Next() = 0;
end;
local procedure InsertNoSeriesWithLines(var GeneratedNoSeries: Record "No. Series Generation Detail")
begin
InsertNoSeries(GeneratedNoSeries);
InsertNoSeriesLine(GeneratedNoSeries);
end;
local procedure InsertNoSeries(var GeneratedNoSeries: Record "No. Series Generation Detail")
var
NoSeries: Record "No. Series";
begin
NoSeries.Init();
NoSeries.Code := GeneratedNoSeries."Series Code";
NoSeries.Description := GeneratedNoSeries.Description;
NoSeries."Manual Nos." := true;
NoSeries."Default Nos." := true;
if not NoSeries.Insert(true) then
NoSeries.Modify(true);
end;
local procedure InsertNoSeriesLine(var GeneratedNoSeries: Record "No. Series Generation Detail")
var
NoSeriesLine: Record "No. Series Line";
Implementation: Enum "No. Series Implementation";
begin
NoSeriesLine.Init();
NoSeriesLine."Series Code" := GeneratedNoSeries."Series Code";
NoSeriesLine."Line No." := GetNoSeriesLineNo(GeneratedNoSeries."Series Code", GeneratedNoSeries."Is Next Year");
NoSeriesLine.Validate("Starting Date", GeneratedNoSeries."Starting Date");
NoSeriesLine.Validate("Starting No.", GeneratedNoSeries."Starting No.");
NoSeriesLine.Validate("Ending No.", GeneratedNoSeries."Ending No.");
if GeneratedNoSeries."Warning No." <> '' then
NoSeriesLine.Validate("Warning No.", GeneratedNoSeries."Warning No.");
NoSeriesLine.Validate("Increment-by No.", GeneratedNoSeries."Increment-by No.");
NoSeriesLine.Validate(Implementation, Implementation::Normal);
if not NoSeriesLine.Insert(true) then
NoSeriesLine.Modify(true);
end;
local procedure GetNoSeriesLineNo(SeriesCode: Code[20]; NewLineNo: Boolean): Integer
var
NoSeriesLine: Record "No. Series Line";
NoSeries: Codeunit "No. Series";
begin
if not NoSeries.GetNoSeriesLine(NoSeriesLine, SeriesCode, 0D, true) then
exit(10000);
if NewLineNo then
exit(NoSeriesLine."Line No." + 10000);
exit(NoSeriesLine."Line No.");
end;
local procedure ApplyNoSeriesToSetup(var GeneratedNoSeries: Record "No. Series Generation Detail")
var
RecRef: RecordRef;
FieldRef: FieldRef;
begin
RecRef.Open(GeneratedNoSeries."Setup Table No.");
if not RecRef.FindFirst() then
exit;
FieldRef := RecRef.Field(GeneratedNoSeries."Setup Field No.");
FieldRef.Validate(GeneratedNoSeries."Series Code");
RecRef.Modify(true);
end;
[NonDebuggable]
local procedure GetToolsSelectionSystemPrompt() ToolsSelectionSystemPrompt: SecretText
var
AzureKeyVault: Codeunit "Azure Key Vault";
Telemetry: Codeunit Telemetry;
ToolsSelectionPrompt: Text;
begin
if not AzureKeyVault.GetAzureKeyVaultSecret('NoSeriesCopilotToolsSelectionPromptV2', ToolsSelectionPrompt) then begin
Telemetry.LogMessage('0000NDY', TelemetryToolsSelectionPromptRetrievalErr, Verbosity::Error, DataClassification::SystemMetadata);
Error(ToolLoadingErr);
end;
ToolsSelectionSystemPrompt := ToolsSelectionPrompt.Replace(DateSpecificPlaceholderLbl, Format(Today(), 0, 4));
end;
local procedure GenerateNoSeries(SystemPromptTxt: SecretText; InputText: Text): Text
var
AzureOpenAI: Codeunit "Azure OpenAI";
AOAIChatCompletionParams: Codeunit "AOAI Chat Completion Params";
AOAIOperationResponse: Codeunit "AOAI Operation Response";
AOAIChatMessages: Codeunit "AOAI Chat Messages";
AddNoSeriesIntent: Codeunit "No. Series Cop. Add Intent";
ChangeNoSeriesIntent: Codeunit "No. Series Cop. Change Intent";
AOAIDeployments: Codeunit "AOAI Deployments";
NextYearNoSeriesIntent: Codeunit "No. Series Cop. Nxt Yr. Intent";
CompletionAnswerTxt: Text;
begin
if not AzureOpenAI.IsEnabled(Enum::"Copilot Capability"::"No. Series Copilot") then
exit;
AzureOpenAI.SetAuthorization(Enum::"AOAI Model Type"::"Chat Completions", AOAIDeployments.GetGPT41Latest());
AzureOpenAI.SetCopilotCapability(Enum::"Copilot Capability"::"No. Series Copilot");
AOAIChatCompletionParams.SetMaxTokens(MaxOutputTokens());
AOAIChatCompletionParams.SetTemperature(0);
AOAIChatMessages.SetPrimarySystemMessage(SystemPromptTxt);
AOAIChatMessages.AddUserMessage(InputText);
AOAIChatMessages.AddTool(AddNoSeriesIntent);
AOAIChatMessages.AddTool(ChangeNoSeriesIntent);
AOAIChatMessages.AddTool(NextYearNoSeriesIntent);
NoSeriesCopilotTelemetry.ResetDurationTracking();
NoSeriesCopilotTelemetry.StartDurationTracking();
AzureOpenAI.GenerateChatCompletion(AOAIChatMessages, AOAIChatCompletionParams, AOAIOperationResponse);
NoSeriesCopilotTelemetry.StopDurationTracking();
if not AOAIOperationResponse.IsSuccess() then
Error(AOAIOperationResponse.GetError());
CompletionAnswerTxt := AOAIChatMessages.GetLastMessage(); // the model can answer to rephrase the question, if the user input is not clear
if AOAIOperationResponse.IsFunctionCall() then
CompletionAnswerTxt := GenerateNoSeriesUsingToolResult(AzureOpenAI, InputText, AOAIOperationResponse, AddNoSeriesIntent.GetExistingNoSeries())
else
NoSeriesCopilotTelemetry.LogToolNotInvoked(AOAIOperationResponse);
exit(CompletionAnswerTxt);
end;
[NonDebuggable]
local procedure GenerateNoSeriesUsingToolResult(var AzureOpenAI: Codeunit "Azure OpenAI"; InputText: Text; var AOAIOperationResponse: Codeunit "AOAI Operation Response"; ExistingNoSeriesArray: Text): Text
var
AOAIChatCompletionParams: Codeunit "AOAI Chat Completion Params";
AOAIChatMessages: Codeunit "AOAI Chat Messages";
AOAIFunctionResponse: Codeunit "AOAI Function Response";
NoSeriesCopToolsImpl: Codeunit "No. Series Cop. Tools Impl.";
NoSeriesGenerateTool: Codeunit "No. Series Cop. Generate";
SystemPrompt: Text;
ToolResponse: Dictionary of [Text, Integer]; // tool response can be a list of strings, as the response can be too long and exceed the token limit. In this case each string would be a separate message, each of them should be called separately. The integer is the number of tables used in the prompt, so we can test if the LLM answer covers all tables
GeneratedNoSeriesArray: Text;
FinalResults: List of [Text]; // The final response will be the concatenation of all the LLM responses (final results).
FunctionResponses: List of [Codeunit "AOAI Function Response"];
Progress: Dialog;
begin
if ExistingNoSeriesArray <> '' then
FinalResults.Add(ExistingNoSeriesArray);
FunctionResponses := AOAIOperationResponse.GetFunctionResponses();
foreach AOAIFunctionResponse in FunctionResponses do begin
if not AOAIFunctionResponse.IsSuccess() then
Error(AOAIFunctionResponse.GetError());
ToolResponse := AOAIFunctionResponse.GetResult();
foreach SystemPrompt in ToolResponse.Keys() do begin
Progress.Open(StrSubstNo(GeneratingNoSeriesForLbl, NoSeriesCopToolsImpl.ExtractAreaWithPrefix(SystemPrompt)));
AOAIChatCompletionParams.SetTemperature(0);
AOAIChatCompletionParams.SetMaxTokens(MaxOutputTokens());
AOAIChatMessages.SetPrimarySystemMessage(SystemPrompt);
AOAIChatMessages.AddUserMessage(InputText);
AOAIChatMessages.AddTool(NoSeriesGenerateTool);
AOAIChatMessages.SetToolChoice(NoSeriesGenerateTool.GetDefaultToolChoice());
// call the API again to get the final response from the model
if not GenerateAndReviewToolCompletionWithRetry(AzureOpenAI, AOAIChatMessages, AOAIChatCompletionParams, GeneratedNoSeriesArray, GetExpectedNoSeriesCount(ToolResponse, SystemPrompt)) then
Error(GetLastErrorText());
FinalResults.Add(GeneratedNoSeriesArray);
Clear(AOAIChatMessages);
Progress.Close();
end;
end;
exit(ConcatenateToolResponse(FinalResults));
end;
[NonDebuggable]
local procedure GetExpectedNoSeriesCount(ToolResponse: Dictionary of [Text, Integer]; Message: Text) ExpectedNoSeriesCount: Integer
begin
ToolResponse.Get(Message, ExpectedNoSeriesCount);
end;
local procedure GenerateAndReviewToolCompletionWithRetry(var AzureOpenAI: Codeunit "Azure OpenAI"; var AOAIChatMessages: Codeunit "AOAI Chat Messages"; var AOAIChatCompletionParams: Codeunit "AOAI Chat Completion Params"; var GeneratedNoSeriesArrayText: Text; ExpectedNoSeriesCount: Integer): Boolean
var
AOAIOperationResponse: Codeunit "AOAI Operation Response";
AOAIFunctionResponse: Codeunit "AOAI Function Response";
MaxAttempts: Integer;
Attempt: Integer;
begin
MaxAttempts := 3;
for Attempt := 1 to MaxAttempts do begin
NoSeriesCopilotTelemetry.ResetDurationTracking();
NoSeriesCopilotTelemetry.StartDurationTracking();
AzureOpenAI.GenerateChatCompletion(AOAIChatMessages, AOAIChatCompletionParams, AOAIOperationResponse);
NoSeriesCopilotTelemetry.StopDurationTracking();
if not AOAIOperationResponse.IsSuccess() then
Error(AOAIOperationResponse.GetError());
if not AOAIOperationResponse.IsFunctionCall() then
Error(TheResponseShouldBeAFunctionCallErr);
AOAIFunctionResponse := AOAIOperationResponse.GetFunctionResponses().Get(1); // There is only one tool
if not AOAIFunctionResponse.IsSuccess() then
Error(AOAIFunctionResponse.GetError());
GeneratedNoSeriesArrayText := AOAIFunctionResponse.GetResult();
if CheckIfValidResult(GeneratedNoSeriesArrayText, AOAIFunctionResponse.GetFunctionName(), ExpectedNoSeriesCount) then begin
NoSeriesCopilotTelemetry.LogGenerationCompletion(ReadGeneratedNumberSeriesJArray(GeneratedNoSeriesArrayText).Count, ExpectedNoSeriesCount, Attempt);
exit(true);
end;
AOAIChatMessages.DeleteMessage(AOAIChatMessages.GetHistory().Count); // remove the last message with wrong assistant response, as we need to regenerate the completion
Sleep(500);
end;
exit(false);
end;
local procedure CheckIfValidResult(GeneratedNoSeriesArrayText: Text; FunctionName: Text; ExpectedNoSeriesCount: Integer): Boolean
var
AddNoSeriesIntent: Codeunit "No. Series Cop. Add Intent";
begin
if not CheckIfCompletionMeetAllRequirements(GeneratedNoSeriesArrayText) then
exit(false);
if FunctionName = AddNoSeriesIntent.GetName() then
exit(CheckIfExpectedNoSeriesCount(GeneratedNoSeriesArrayText, ExpectedNoSeriesCount));
exit(true);
end;
[TryFunction]
local procedure CheckIfExpectedNoSeriesCount(GeneratedNoSeriesArrayText: Text; ExpectedNoSeriesCount: Integer)
var
ResultJArray: JsonArray;
ResultedAccuracy: Decimal;
begin
ResultJArray := ReadGeneratedNumberSeriesJArray(GeneratedNoSeriesArrayText);
if ResultJArray.Count = ExpectedNoSeriesCount then
exit;
if ExpectedNoSeriesCount = 0 then
exit;
ResultedAccuracy := ResultJArray.Count / ExpectedNoSeriesCount;
if ResultedAccuracy < MinimumAccuracy() then
Error(IncorrectCompletionNumberOfGeneratedNoSeriesErr, ExpectedNoSeriesCount, ResultJArray.Count);
end;
local procedure ConcatenateToolResponse(var FinalResults: List of [Text]) ConcatenatedResponse: Text
var
Result: Text;
ResultJArray: JsonArray;
JsonTok: JsonToken;
JsonArr: JsonArray;
i: Integer;
begin
foreach Result in FinalResults do begin
ResultJArray := ReadGeneratedNumberSeriesJArray(Result);
for i := 0 to ResultJArray.Count - 1 do begin
ResultJArray.Get(i, JsonTok);
JsonArr.Add(JsonTok);
end;
end;
JsonArr.WriteTo(ConcatenatedResponse);
end;
[TryFunction]
local procedure CheckIfCompletionMeetAllRequirements(GeneratedNoSeriesArrayText: Text)
var
Json: Codeunit Json;
NoSeriesArrText: Text;
NoSeriesObj: Text;
i: Integer;
begin
ReadGeneratedNumberSeriesJArray(GeneratedNoSeriesArrayText).WriteTo(NoSeriesArrText);
Json.InitializeCollection(NoSeriesArrText);
CheckIfArrayIsNotEmpty(Json.GetCollectionCount());
for i := 0 to Json.GetCollectionCount() - 1 do begin
Json.GetObjectFromCollectionByIndex(i, NoSeriesObj);
Json.InitializeObject(NoSeriesObj);
CheckMandatoryProperties(Json);
end;
end;
local procedure CheckMandatoryProperties(var Json: Codeunit Json)
begin
if not CheckIfNumberSeriesIsGenerated(Json) then
exit;
CheckJsonTextProperty('seriesCode', Json, true);
CheckMaximumLengthOfPropertyValue('seriesCode', Json, 20);
CheckJsonTextProperty('description', Json, true);
CheckJsonTextProperty('startingNo', Json, true);
CheckMaximumLengthOfPropertyValue('startingNo', Json, 20);
CheckJsonTextProperty('endingNo', Json, false);
CheckMaximumLengthOfPropertyValue('endingNo', Json, 20);
CheckJsonTextProperty('warningNo', Json, false);
CheckMaximumLengthOfPropertyValue('warningNo', Json, 20);
CheckJsonIntegerProperty('incrementByNo', Json, false);
CheckJsonIntegerProperty('tableId', Json, true);
CheckJsonIntegerProperty('fieldId', Json, true);
end;
local procedure CheckIfNumberSeriesIsGenerated(var Json: Codeunit Json): Boolean
var
IsExists: Boolean;
begin
Json.GetBoolPropertyValueFromJObjectByName('exists', IsExists);
exit(not IsExists);
end;
local procedure CheckIfArrayIsNotEmpty(NumberOfGeneratedNoSeries: Integer)
begin
if NumberOfGeneratedNoSeries = 0 then
Error(EmptyCompletionErr);
end;
local procedure CheckJsonTextProperty(propertyName: Text; var Json: Codeunit Json; IsRequired: Boolean)
var
value: Text;
begin
Json.GetStringPropertyValueByName(propertyName, value);
if not IsRequired then
exit;
if value <> '' then
exit;
Error(IncorrectCompletionErr, propertyName);
end;
local procedure CheckJsonIntegerProperty(propertyName: Text; var Json: Codeunit Json; IsRequired: Boolean)
var
PropertyValue: Integer;
begin
Json.GetIntegerPropertyValueFromJObjectByName(propertyName, PropertyValue);
if not IsRequired then
exit;
if PropertyValue <> 0 then
exit;
Error(IncorrectCompletionErr, propertyName);
end;
local procedure CheckMaximumLengthOfPropertyValue(propertyName: Text; var Json: Codeunit Json; maxLength: Integer)
var
value: Text;
begin
Json.GetStringPropertyValueByName(propertyName, value);
if StrLen(value) <= maxLength then
exit;
Error(TextLengthIsOverMaxLimitErr, propertyName, maxLength);
end;
local procedure ReadGeneratedNumberSeriesJArray(Completion: Text) NoSeriesJArray: JsonArray
begin
NoSeriesJArray.ReadFrom(Completion);
exit(NoSeriesJArray);
end;
local procedure SaveGenerationHistory(var NoSeriesGeneration: Record "No. Series Generation"; InputText: Text)
begin
NoSeriesGeneration.Init();
NoSeriesGeneration."No." := NoSeriesGeneration.Count + 1;
NoSeriesGeneration.SetInputText(InputText);
NoSeriesGeneration.Insert(true);
end;
local procedure CreateNoSeries(var NoSeriesGeneration: Record "No. Series Generation"; var GeneratedNoSeries: Record "No. Series Generation Detail"; Completion: Text)
var
Json: Codeunit Json;
NoSeriesArrText: Text;
NoSeriesObj: Text;
i: Integer;
begin
ReadGeneratedNumberSeriesJArray(Completion).WriteTo(NoSeriesArrText);
ReassembleDuplicates(NoSeriesArrText);
Json.InitializeCollection(NoSeriesArrText);
for i := 0 to Json.GetCollectionCount() - 1 do begin
Json.GetObjectFromCollectionByIndex(i, NoSeriesObj);
InsertGeneratedNoSeries(GeneratedNoSeries, NoSeriesObj, NoSeriesGeneration."No.");
end;
end;
local procedure ReassembleDuplicates(var NoSeriesArrText: Text)
var
Json: Codeunit Json;
i: Integer;
NoSeriesCodes: List of [Text];
begin
Json.InitializeCollection(NoSeriesArrText);
for i := 0 to Json.GetCollectionCount() - 1 do
ProcessNoSeries(i, NoSeriesCodes, Json);
NoSeriesArrText := Json.GetCollectionAsText()
end;
local procedure ProcessNoSeries(i: Integer; var NoSeriesCodes: List of [Text]; var Json: Codeunit Json)
var
NoSeriesCode: Text;
NoSeriesObj: Text;
begin
Json.GetObjectFromCollectionByIndex(i, NoSeriesObj);
Json.InitializeObject(NoSeriesObj);
Json.GetStringPropertyValueByName('seriesCode', NoSeriesCode);
if NoSeriesCodes.Contains(NoSeriesCode) and (CheckIfNumberSeriesIsGenerated(Json)) then begin
Json.RemoveJObjectFromCollection(i);
exit;
end;
NoSeriesCodes.Add(NoSeriesCode);
end;
local procedure InsertGeneratedNoSeries(var GeneratedNoSeries: Record "No. Series Generation Detail"; NoSeriesObj: Text; GenerationNo: Integer)
var
Json: Codeunit Json;
RecRef: RecordRef;
begin
Json.InitializeObject(NoSeriesObj);
RecRef.GetTable(GeneratedNoSeries);
RecRef.Init();
SetGenerationNo(RecRef, GenerationNo, GeneratedNoSeries.FieldNo("Generation No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'seriesCode', GeneratedNoSeries.FieldNo("Series Code"));
Json.GetValueAndSetToRecFieldNo(RecRef, 'description', GeneratedNoSeries.FieldNo(Description));
Json.GetValueAndSetToRecFieldNo(RecRef, 'startingNo', GeneratedNoSeries.FieldNo("Starting No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'endingNo', GeneratedNoSeries.FieldNo("Ending No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'warningNo', GeneratedNoSeries.FieldNo("Warning No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'incrementByNo', GeneratedNoSeries.FieldNo("Increment-by No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'tableId', GeneratedNoSeries.FieldNo("Setup Table No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'fieldId', GeneratedNoSeries.FieldNo("Setup Field No."));
Json.GetValueAndSetToRecFieldNo(RecRef, 'nextYear', GeneratedNoSeries.FieldNo("Is Next Year"));
Json.GetValueAndSetToRecFieldNo(RecRef, 'exists', GeneratedNoSeries.FieldNo(Exists));
Json.GetValueAndSetToRecFieldNo(RecRef, 'message', GeneratedNoSeries.FieldNo(Message));
if RecRef.Insert(true) then;
ValidateGeneratedNoSeries(RecRef);
end;
local procedure ValidateGeneratedNoSeries(var RecRef: RecordRef)
var
GeneratedNoSeries: Record "No. Series Generation Detail";
begin
ValidateRecFieldNo(RecRef, GeneratedNoSeries.FieldNo("Is Next Year"));
RecRef.Modify(true);
end;
local procedure ValidateRecFieldNo(var RecRef: RecordRef; FieldNo: Integer)
var
FieldRef: FieldRef;
begin
FieldRef := RecRef.Field(FieldNo);
FieldRef.Validate();
end;
local procedure SetGenerationNo(var RecRef: RecordRef; GenerationId: Integer; FieldNo: Integer)
var
FieldRef: FieldRef;
begin
FieldRef := RecRef.Field(FieldNo);
FieldRef.Value(GenerationId);
end;
local procedure MinimumAccuracy(): Decimal
begin
exit(0.9);
end;
local procedure MaxInputTokens(): Integer
begin
exit(MaxModelTokens() - MaxOutputTokens());
end;
local procedure MaxOutputTokens(): Integer
begin
exit(4096);
end;
local procedure MaxModelTokens(): Integer
begin
exit(16385); //gpt-4o-latest
end;
procedure IsCopilotVisible(): Boolean
var
EnvironmentInformation: Codeunit "Environment Information";
begin
if not EnvironmentInformation.IsSaaSInfrastructure() then
exit(false);
exit(true);
end;
procedure GetChatCompletionResponseErr(): Text
begin
exit(ChatCompletionResponseErr);
end;
local procedure GetNotificationId(): Guid
begin
exit('1fd2bfd6-6542-4574-8a88-f8247f4b8334');
end;
procedure RecallNotification()
var
Notification: Notification;
begin
Notification.Id := GetNotificationId();
Notification.Recall();
end;
procedure SendNotification(NotificationMessage: Text)
var
Notification: Notification;
begin
Notification.Id := GetNotificationId();
Notification.Scope := NotificationScope::LocalScope;
Notification.Recall();
Notification.Message := NotificationMessage;
Notification.Send();
end;
procedure FeatureName(): Text
begin
exit(FeatureNameLbl);
end;
}
Procedures, 8
| Name | Parameters | Returns | Access | Obsolete |
|---|---|---|---|---|
| GetNoSeriesSuggestions | () | public | - | |
| Generate | (var Record No. Series Generation, var Record No. Series Generation Detail, Text) | public | - | |
| ApplyGeneratedNoSeries | (var Record No. Series Generation Detail) | public | - | |
| IsCopilotVisible | () | Boolean | public | - |
| GetChatCompletionResponseErr | () | Text | public | - |
| RecallNotification | () | public | - | |
| SendNotification | (Text) | public | - | |
| FeatureName | () | Text | public | - |