From 954d04a3aa28f64c502c44b682ffa31a89b27187 Mon Sep 17 00:00:00 2001
From: Jesper Schulz-Wedde <JesperSchulz@users.noreply.github.com>
Date: Wed, 23 Oct 2024 14:28:42 +0200
Subject: [PATCH] =?UTF-8?q?[25.x][Number=20Series=20Copilot]=20Bug=20Fixes?=
 =?UTF-8?q?=20in=20Intent=20Understanding=20and=20Field=20V=E2=80=A6=20(#2?=
 =?UTF-8?q?251)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

…alidation (#2111)

<!-- Thank you for submitting a Pull Request. If you're new to
contributing to BCApps please read our pull request guideline below
* https://github.com/microsoft/BCApps/Contributing.md -->

#### Summary <!-- Provide a general summary of your changes -->

This PR addresses three critical bugs in the Number Series Copilot:

1. **Bug 1: Incorrect Intent Mapping for "Generate" Command**
- The system was misinterpreting the "generate" command, often preparing
Number Series for the next period instead of creating a new one. The fix
involves updating the tools definition and prompt to clarify the
distinction between creating new Number Series and generating Number
Series for the next period.

2. **Bug 2: Incorrect Field Check for Existing Number Series**
- The system was incorrectly performing checks on all fields for
existing Number Series. The fix modifies the behavior to exclude
pre-existing Number Series from the field validation checks, ensuring
that only mandatory fields are validated for AI generated series.

3. **Bug 3: Blank Fields Causing Validation Errors**
- The system was generating Number Series without values for optional
fields, causing validation errors. The fix refines the validation logic
so that only mandatory fields are checked, allowing optional fields to
remain blank if necessary.

**Steps to Implement:**

1. Refactor the LLM prompt and tools definition to improve the
understanding of user intent between creating and preparing Number
Series for different periods.
2. Update the logic to exclude field checks for existing Number Series
and restrict checks to mandatory fields.

**Acceptance Criteria:**

- Users can create Number Series without mistakenly triggering
next-period Number Series generation when using the word "generate."
- Existing Number Series are excluded from unnecessary checks, and only
mandatory fields are validated for new series.

These fixes aim to enhance the accuracy and reliability of the Number
Series Copilot, ensuring it correctly interprets user commands and
validates the necessary fields.


#### Work Item(s) <!-- Add the issue number here after the #. The issue
needs to be open and approved. Submitting PRs with no linked issues or
unapproved issues is highly discouraged. -->

Fixes #2106

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

Co-authored-by: Dmitry Katson <dmitry.katson@gmail.com>
---
 .../NoSeriesCopilotObjects.permissionset.al   | 15 ++++
 .../Copilot/NoSeriesCopilotImpl.Codeunit.al   | 74 +++++++++++++------
 .../src/Copilot/NoSeriesGeneration.Page.al    | 16 ++--
 .../NoSeriesTextMatchImpl.Codeunit.al}        | 32 ++++++--
 .../Tools/NoSeriesCopAddIntent.Codeunit.al    |  6 +-
 .../Tools/NoSeriesCopChangeIntent.Codeunit.al |  6 +-
 .../Tools/NoSeriesCopNxtYrIntent.Codeunit.al  |  4 +-
 .../Tools/NoSeriesCopToolsImpl.Codeunit.al    | 21 ++----
 .../NoSeriesCopilotRegister.Codeunit.al       |  2 +-
 9 files changed, 118 insertions(+), 58 deletions(-)
 create mode 100644 src/Business Foundation/App/NoSeriesCopilot/Permissions/NoSeriesCopilotObjects.permissionset.al
 rename src/Business Foundation/App/NoSeriesCopilot/src/Copilot/{Tools/RecordMatchImpl.Codeunit.al => Search/Classic/NoSeriesTextMatchImpl.Codeunit.al} (83%)

diff --git a/src/Business Foundation/App/NoSeriesCopilot/Permissions/NoSeriesCopilotObjects.permissionset.al b/src/Business Foundation/App/NoSeriesCopilot/Permissions/NoSeriesCopilotObjects.permissionset.al
new file mode 100644
index 0000000000..db8cdf26b4
--- /dev/null
+++ b/src/Business Foundation/App/NoSeriesCopilot/Permissions/NoSeriesCopilotObjects.permissionset.al	
@@ -0,0 +1,15 @@
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+permissionset 330 "No. Series Copilot - Objects"
+{
+    Access = Internal;
+    Assignable = false;
+    Permissions =
+        codeunit "No. Series Copilot Impl." = X,
+        codeunit "No. Series Text Match Impl." = X;
+}
\ No newline at end of file
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesCopilotImpl.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesCopilotImpl.Codeunit.al
index 22c284f72e..c4f485c914 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesCopilotImpl.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesCopilotImpl.Codeunit.al	
@@ -150,7 +150,7 @@ codeunit 324 "No. Series Copilot Impl."
         Telemetry: Codeunit Telemetry;
         ToolsSelectionPrompt: Text;
     begin
-        if not AzureKeyVault.GetAzureKeyVaultSecret('NoSeriesCopilotToolsSelectionPrompt', ToolsSelectionPrompt) then begin
+        if not AzureKeyVault.GetAzureKeyVaultSecret('NoSeriesCopilotToolsSelectionPromptV2', ToolsSelectionPrompt) then begin
             Telemetry.LogMessage('0000NDY', TelemetryToolsSelectionPromptRetrievalErr, Verbosity::Error, DataClassification::SystemMetadata);
             Error(ToolLoadingErr);
         end;
@@ -346,43 +346,69 @@ codeunit 324 "No. Series Copilot Impl."
         for i := 0 to Json.GetCollectionCount() - 1 do begin
             Json.GetObjectFromCollectionByIndex(i, NoSeriesObj);
             Json.InitializeObject(NoSeriesObj);
-            CheckTextPropertyExistAndCheckIfNotEmpty('seriesCode', Json);
-            CheckMaximumLengthOfPropertyValue('seriesCode', Json, 20);
-            CheckTextPropertyExistAndCheckIfNotEmpty('description', Json);
-            CheckTextPropertyExistAndCheckIfNotEmpty('startingNo', Json);
-            CheckMaximumLengthOfPropertyValue('startingNo', Json, 20);
-            CheckTextPropertyExistAndCheckIfNotEmpty('endingNo', Json);
-            CheckMaximumLengthOfPropertyValue('endingNo', Json, 20);
-            CheckTextPropertyExistAndCheckIfNotEmpty('warningNo', Json);
-            CheckMaximumLengthOfPropertyValue('warningNo', Json, 20);
-            CheckIntegerPropertyExistAndCheckIfNotEmpty('incrementByNo', Json);
-            CheckIntegerPropertyExistAndCheckIfNotEmpty('tableId', Json);
-            CheckIntegerPropertyExistAndCheckIfNotEmpty('fieldId', Json);
+            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 CheckTextPropertyExistAndCheckIfNotEmpty(propertyName: Text; var Json: Codeunit Json)
+    local procedure CheckJsonTextProperty(propertyName: Text; var Json: Codeunit Json; IsRequired: Boolean)
     var
         value: Text;
     begin
         Json.GetStringPropertyValueByName(propertyName, value);
-        if value = '' then
-            Error(IncorrectCompletionErr, propertyName);
+        if not IsRequired then
+            exit;
+
+        if value <> '' then
+            exit;
+
+        Error(IncorrectCompletionErr, propertyName);
     end;
 
-    local procedure CheckIntegerPropertyExistAndCheckIfNotEmpty(propertyName: Text; var Json: Codeunit Json)
+    local procedure CheckJsonIntegerProperty(propertyName: Text; var Json: Codeunit Json; IsRequired: Boolean)
     var
         PropertyValue: Integer;
     begin
         Json.GetIntegerPropertyValueFromJObjectByName(propertyName, PropertyValue);
-        if PropertyValue = 0 then
-            Error(IncorrectCompletionErr, propertyName);
+        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)
@@ -390,8 +416,10 @@ codeunit 324 "No. Series Copilot Impl."
         value: Text;
     begin
         Json.GetStringPropertyValueByName(propertyName, value);
-        if StrLen(value) > maxLength then
-            Error(TextLengthIsOverMaxLimitErr, propertyName, maxLength);
+        if StrLen(value) <= maxLength then
+            exit;
+
+        Error(TextLengthIsOverMaxLimitErr, propertyName, maxLength);
     end;
 
     local procedure ReadGeneratedNumberSeriesJArray(Completion: Text) NoSeriesJArray: JsonArray
@@ -445,14 +473,12 @@ codeunit 324 "No. Series Copilot Impl."
     var
         NoSeriesCode: Text;
         NoSeriesObj: Text;
-        IsExists: Boolean;
     begin
         Json.GetObjectFromCollectionByIndex(i, NoSeriesObj);
         Json.InitializeObject(NoSeriesObj);
-        Json.GetBoolPropertyValueFromJObjectByName('exists', IsExists);
         Json.GetStringPropertyValueByName('seriesCode', NoSeriesCode);
 
-        if NoSeriesCodes.Contains(NoSeriesCode) and (not IsExists) then begin
+        if NoSeriesCodes.Contains(NoSeriesCode) and (CheckIfNumberSeriesIsGenerated(Json)) then begin
             Json.RemoveJObjectFromCollection(i);
             exit;
         end;
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesGeneration.Page.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesGeneration.Page.al
index 7dee646d97..e4db90211b 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesGeneration.Page.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/NoSeriesGeneration.Page.al	
@@ -114,23 +114,23 @@ page 332 "No. Series Generation"
             {
                 Caption = 'Prepare for next year';
 
-                action(SetupForNextYear)
+                action(PrepareForNextYear)
                 {
-                    Caption = 'Set up number series for the next year';
+                    Caption = 'Prepare number series for the next year';
                     ToolTip = 'Sample prompt for setting up number series for the next year.';
                     trigger OnAction()
                     begin
-                        InputText := SetupForNextYearLbl;
+                        InputText := PrepareForNextYearLbl;
                         CurrPage.Update();
                     end;
                 }
-                action(SetupModuleForNextYear)
+                action(PrepareModuleForNextYear)
                 {
-                    Caption = 'Set up number series for the [sales] module for the next year';
+                    Caption = 'Prepare number series for the [sales] module for the next year';
                     ToolTip = 'Sample prompt for setting up number series for a specific module for the next year. Replace [sales] with the module you want to set up number series for.';
                     trigger OnAction()
                     begin
-                        InputText := SetupModuleForNextYearLbl;
+                        InputText := PrepareModuleForNextYearLbl;
                         CurrPage.Update();
                     end;
                 }
@@ -178,8 +178,8 @@ page 332 "No. Series Generation"
         CreateNoSeriesForModuleWithPatternLbl: Label 'Create number series for [specify here] module in the format ';
         CreateNoSeriesForCompanyLbl: Label 'Create numbers series for the new company';
         ChangeNumberLbl: Label 'Change the [specify here] number to ';
-        SetupForNextYearLbl: Label 'Set up number series for the next year';
-        SetupModuleForNextYearLbl: Label 'Set up number series for the [specify here] module for the next year';
+        PrepareForNextYearLbl: Label 'Prepare number series for the next year';
+        PrepareModuleForNextYearLbl: Label 'Prepare number series for the [specify here] module for the next year';
 
     trigger OnAfterGetCurrRecord()
     begin
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/RecordMatchImpl.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Search/Classic/NoSeriesTextMatchImpl.Codeunit.al
similarity index 83%
rename from src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/RecordMatchImpl.Codeunit.al
rename to src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Search/Classic/NoSeriesTextMatchImpl.Codeunit.al
index ae9b640c59..43a891d0a8 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/RecordMatchImpl.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Search/Classic/NoSeriesTextMatchImpl.Codeunit.al	
@@ -5,12 +5,24 @@
 
 namespace Microsoft.Foundation.NoSeries;
 
-codeunit 337 "Record Match Impl."
+codeunit 337 "No. Series Text Match Impl."
 {
     Access = Internal;
     InherentPermissions = X;
     InherentEntitlements = X;
 
+    procedure IsRelevant(FirstString: Text; SecondString: Text): Boolean
+    var
+        Score: Decimal;
+    begin
+        FirstString := RemoveShortWords(FirstString);
+        SecondString := RemoveShortWords(SecondString);
+
+        Score := CalculateStringNearness(FirstString, SecondString, GetMatchLengthTreshold(), 100) / 100;
+
+        exit(Score >= RequiredNearness());
+    end;
+
     /// <summary>
     /// Computes a nearness score between strings. Nearness is based on repeatedly finding longest common substrings.
     /// Substring matches below Threshold are not considered.
@@ -21,7 +33,7 @@ codeunit 337 "Record Match Impl."
     /// <param name="Threshold">Substring matches below Threshold are not considered</param>
     /// <param name="NormalizingFactor">Max value returned by this procedure</param>
     /// <returns>A number between 0 and NormalizingFactor, representing how much of the strings was matched</returns>
-    procedure CalculateStringNearness(FirstString: Text; SecondString: Text; Threshold: Integer; NormalizingFactor: Integer): Integer
+    local procedure CalculateStringNearness(FirstString: Text; SecondString: Text; Threshold: Integer; NormalizingFactor: Integer): Integer
     var
         Result: Text;
         TotalMatchedChars: Integer;
@@ -98,11 +110,11 @@ codeunit 337 "Record Match Impl."
         exit(MinThreshold <= Length);
     end;
 
-    procedure RemoveShortWords(OriginalText: Text[250]): Text[250];
+    local procedure RemoveShortWords(OriginalText: Text): Text;
     var
         Words: List of [Text];
-        Word: Text[250];
-        Result: Text[250];
+        Word: Text;
+        Result: Text;
     begin
         Words := OriginalText.Split(' '); // split the text by spaces into a list of words
         foreach Word in Words do // loop through each word in the list
@@ -112,4 +124,14 @@ codeunit 337 "Record Match Impl."
         OriginalText := Result; // assign the result back to the text parameter
         exit(OriginalText);
     end;
+
+    local procedure GetMatchLengthTreshold(): Decimal
+    begin
+        exit(2);
+    end;
+
+    local procedure RequiredNearness(): Decimal
+    begin
+        exit(0.9)
+    end;
 }
\ No newline at end of file
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopAddIntent.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopAddIntent.Codeunit.al
index b2377be60d..164b896655 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopAddIntent.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopAddIntent.Codeunit.al	
@@ -28,6 +28,7 @@ codeunit 331 "No. Series Cop. Add Intent" implements "AOAI Function"
         NumberOfAddedTablesPlaceholderLbl: Label '{number_of_tables}', Locked = true;
         TelemetryTool1PromptRetrievalErr: Label 'Unable to retrieve the prompt for No. Series Copilot Tool 1 from Azure Key Vault.', Locked = true;
         TelemetryTool1DefinitionRetrievalErr: Label 'Unable to retrieve the definition for No. Series Copilot Tool 1 from Azure Key Vault.', Locked = true;
+        ToolProgressDialogTextLbl: Label 'Searching for tables with number series related to your query';
         ToolLoadingErr: Label 'Unable to load the No. Series Copilot Tool 1. Please try again later.';
         ExistingNoSeriesMessageLbl: Label 'Number series already configured. If you wish to modify the existing series, please use the `Modify number series` prompt.';
 
@@ -62,7 +63,9 @@ codeunit 331 "No. Series Cop. Add Intent" implements "AOAI Function"
         NewNoSeriesPrompt, CustomPatternsPromptList, TablesYamlList, EmptyList : List of [Text];
         NumberOfToolResponses, i, ActualTablesChunkSize : Integer;
         NumberOfAddedTables: Integer;
+        Progress: Dialog;
     begin
+        Progress.Open(ToolProgressDialogTextLbl);
         GetTablesRequireNoSeries(Arguments, TempSetupTable, TempNoSeriesField);
         ToolsImpl.GetUserSpecifiedOrExistingNumberPatternsGuidelines(Arguments, CustomPatternsPromptList, EmptyList, false);
 
@@ -80,7 +83,8 @@ codeunit 331 "No. Series Cop. Add Intent" implements "AOAI Function"
                                                      .Replace(NumberOfAddedTablesPlaceholderLbl, Format(ActualTablesChunkSize)));
 
                 ToolResults.Add(ToolsImpl.ConvertListToText(NewNoSeriesPrompt), ActualTablesChunkSize);
-            end
+            end;
+        Progress.Close();
     end;
 
     local procedure GetTablesRequireNoSeries(var Arguments: JsonObject; var TempSetupTable: Record "Table Metadata" temporary; var TempNoSeriesField: Record "Field" temporary)
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopChangeIntent.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopChangeIntent.Codeunit.al
index b4dfc2698a..4c78718f90 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopChangeIntent.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopChangeIntent.Codeunit.al	
@@ -32,6 +32,7 @@ codeunit 334 "No. Series Cop. Change Intent" implements "AOAI Function"
         NumberOfAddedTablesPlaceholderLbl: Label '{number_of_tables}', Locked = true;
         TelemetryTool2PromptRetrievalErr: Label 'Unable to retrieve the prompt for No. Series Copilot Tool 2 from Azure Key Vault.', Locked = true;
         TelemetryTool2DefinitionRetrievalErr: Label 'Unable to retrieve the definition for No. Series Copilot Tool 2 from Azure Key Vault.', Locked = true;
+        ToolProgressDialogTextLbl: Label 'Searching for tables with number series related to your query';
         ToolLoadingErr: Label 'Unable to load the No. Series Copilot Tool 2. Please try again later.';
 
     procedure GetName(): Text
@@ -71,12 +72,14 @@ codeunit 334 "No. Series Cop. Change Intent" implements "AOAI Function"
         ChangeNoSeriesPrompt, CustomPatternsPromptList, TablesYamlList, ExistingNoSeriesToChangeList : List of [Text];
         NumberOfToolResponses, i, ActualTablesChunkSize : Integer;
         NumberOfChangedTables: Integer;
+        Progress: Dialog;
     begin
         if not CheckIfUserSpecifiedNoSeriesToChange(Arguments) then begin
             NoSeriesCopilotImpl.SendNotification(GetLastErrorText());
             exit;
         end;
 
+        Progress.Open(ToolProgressDialogTextLbl);
         GetTablesWithNoSeries(Arguments, TempSetupTable, TempNoSeriesField, ExistingNoSeriesToChangeList);
         ToolsImpl.GetUserSpecifiedOrExistingNumberPatternsGuidelines(Arguments, CustomPatternsPromptList, ExistingNoSeriesToChangeList, UpdateForNextYear);
 
@@ -97,7 +100,8 @@ codeunit 334 "No. Series Cop. Change Intent" implements "AOAI Function"
                                                         .Replace(NumberOfAddedTablesPlaceholderLbl, Format(ActualTablesChunkSize)));
 
                 ToolResults.Add(ToolsImpl.ConvertListToText(ChangeNoSeriesPrompt), ActualTablesChunkSize);
-            end
+            end;
+        Progress.Close();
     end;
 
     [TryFunction]
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopNxtYrIntent.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopNxtYrIntent.Codeunit.al
index 21d5effda1..9babc47e9f 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopNxtYrIntent.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopNxtYrIntent.Codeunit.al	
@@ -17,7 +17,7 @@ codeunit 349 "No. Series Cop. Nxt Yr. Intent" implements "AOAI Function"
 
     var
         Telemetry: Codeunit Telemetry;
-        FunctionNameLbl: Label 'GenerateNextYearNumberSeries', Locked = true;
+        FunctionNameLbl: Label 'PrepareNextYearNumberSeries', Locked = true;
         TelemetryTool3DefinitionRetrievalErr: Label 'Unable to retrieve the definition for No. Series Copilot Tool 3 from Azure Key Vault.', Locked = true;
         ToolLoadingErr: Label 'Unable to load the No. Series Copilot Tool 3. Please try again later.';
 
@@ -45,7 +45,7 @@ codeunit 349 "No. Series Cop. Nxt Yr. Intent" implements "AOAI Function"
     var
         AzureKeyVault: Codeunit "Azure Key Vault";
     begin
-        if not AzureKeyVault.GetAzureKeyVaultSecret('NoSeriesCopilotTool3Definition', Definition) then begin
+        if not AzureKeyVault.GetAzureKeyVaultSecret('NoSeriesCopilotTool3DefinitionV2', Definition) then begin
             Telemetry.LogMessage('0000ND9', TelemetryTool3DefinitionRetrievalErr, Verbosity::Error, DataClassification::SystemMetadata);
             Error(ToolLoadingErr);
         end;
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopToolsImpl.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopToolsImpl.Codeunit.al
index 7fc97a5beb..c132c22a73 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopToolsImpl.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Copilot/Tools/NoSeriesCopToolsImpl.Codeunit.al	
@@ -225,32 +225,21 @@ codeunit 336 "No. Series Cop. Tools Impl."
 
     procedure IsRelevant(TableMetadata: Record "Table Metadata"; Field: Record "Field"; Entities: List of [Text]): Boolean
     var
-        RecordMatchMgtCopy: Codeunit "Record Match Impl.";
+        TextMatchImpl: Codeunit "No. Series Text Match Impl.";
         Entity: Text[250];
         String1: Text[250];
         String2: Text[250];
-        Score: Decimal;
     begin
         foreach Entity in Entities do begin
-            String1 := RecordMatchMgtCopy.RemoveShortWords(RemoveTextPart(TableMetadata.Caption, ' Setup') + ' ' + RemoveTextParts(Field.FieldName, GetNoSeriesAbbreviations()));
-            String2 := RecordMatchMgtCopy.RemoveShortWords(Entity);
-            Score := RecordMatchMgtCopy.CalculateStringNearness(String1, String2, GetMatchLengthThreshold(), 100) / 100;
-            if Score >= RequiredNearness() then
+            String1 := RemoveTextPart(TableMetadata.Name, '& ') + ' ' + Field.FieldName;
+            String2 := Entity;
+
+            if TextMatchImpl.IsRelevant(String1, String2) then
                 exit(true);
         end;
         exit(false);
     end;
 
-    local procedure GetMatchLengthThreshold(): Decimal
-    begin
-        exit(2);
-    end;
-
-    local procedure RequiredNearness(): Decimal
-    begin
-        exit(0.9)
-    end;
-
     procedure GenerateChunkedTablesListInYamlFormat(var TablesYamlList: List of [Text]; var TempSetupTable: Record "Table Metadata" temporary; var TempNoSeriesField: Record "Field" temporary; var NumberOfAddedTables: Integer)
     begin
         Clear(TablesYamlList);
diff --git a/src/Business Foundation/App/NoSeriesCopilot/src/Register/NoSeriesCopilotRegister.Codeunit.al b/src/Business Foundation/App/NoSeriesCopilot/src/Register/NoSeriesCopilotRegister.Codeunit.al
index c206500036..cbab6bd7b7 100644
--- a/src/Business Foundation/App/NoSeriesCopilot/src/Register/NoSeriesCopilotRegister.Codeunit.al	
+++ b/src/Business Foundation/App/NoSeriesCopilot/src/Register/NoSeriesCopilotRegister.Codeunit.al	
@@ -41,6 +41,6 @@ codeunit 327 "No. Series Copilot Register"
     end;
 
     var
-        LearnMoreUrlTxt: Label 'https://go.microsoft.com/fwlink/?linkid=[id]', Locked = true; //TODO: replace 'id' when docs page is ready
+        LearnMoreUrlTxt: Label 'https://go.microsoft.com/fwlink/?linkid=2294514', Locked = true;
 
 }
\ No newline at end of file